This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM3354: ADC FSM busy issue

Part Number: AM3354

Hi,

Our customer use TSC_ADC with touch panel operation.
When FSM_BUSY in ADCSTAT register becomes busy, touch panel operation does not work.
The condition that this FSM busy becomes busy is not described in the AM335x technical reference manual.
Please let me know the conditions that occur.

Best Regards,
Shigehiro Tsuda

  • Hi,

    Please see this comment by peaves: e2e.ti.com/.../2213021
  • Hello,

    From a software perspective:

    The FSM is in the IDLE state while waiting for a SW event or waiting for a step to be enabled (see "Figure 12-3. Sequencer FSM" in the AM335x TRM, state IDLE). While in the idle state, the idle step is always enabled and applied. During typical operation, the ADC should transition to FSM BUSY while applying all active steps. Once the last active step is completed, we would expect the FSM to return to the IDLE state.

    Regards,
    Nick
  • In my experience working on the Linux side of things, great care must be taken before writing the STEPENABLE register. Specifically, when the state machine is busy you should not write to STEPENABLE. I've seen lockups occur when this is not observed. I suspect the Starterware code doesn't take any precautions in this regard. Specifically I suggest you first poll for ADCSTAT[STEP_ID] == IDLE before writing to STEPENABLE.

    Also, can you clarify on whether this particular application is "touchscreen only" or "touchscreen plus ADC"? In the case of "touchscreen plus ADC", this can be more difficult to handle depending on how the software is structured. You may need a mutex or something along those lines for the touchscreen driver and ADC driver to coordinate accesses to STEPENABLE.
  • Hi Brad,

    Thank you for your reply.

    I will check our customer what happens if ADCSTAT [STEP_ID] == IDLE poll is added before writing STEPENABLE.
    I will ask if they are only using touchscreen.

    It is an additional question.

    The linux touchscreen driver code has the following description.
    IDLECONFIG and CHARGECONFIG have the same setting values.
    There is also no description in TRM, but does this process have to be done?
    Please tell me why this comment and code are there.

    linux/drivers/input/touchscreen/ti_am335x_tsc.c

    titsc_step_config( )

    {

    ....

    /* Make CHARGECONFIG same as IDLECONFIG */

    config = titsc_readl(ts_dev, REG_IDLECONFIG);
    titsc_writel(ts_dev, REG_CHARGECONFIG, config);
    titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);

    ....

    }

    Best Regards,
    Shigehiro Tsuda

  • Hi Brad,

    Thank you for your support.

    I have confirmed the following registers.
    12.5.1.16 IDLECONFIG Register
    12.5.1.17 TS_CHARGE_STEPCONFIG

    It seems that the setting values ​​of the registers are different from each other.

    [linux]
    IDLECONFIG=0x00440500
    TS_CHARGE_STEPCONFIG=0x00440500

    [Starterware]
    IDLECONFIG=0x00040500
    TS_CHARGE_STEPCONFIG=0x00889120

    [customer]
    IDLECONFIG=0x00000500
    TS_CHARGE_STEPCONFIG=0x00081120

    Which value is best ?

    The following is new update information.

    TSC ADC register values ​​were the same as a result of dumping registers 5 times when problems occurred.
    It seems to hang with FSM_busy=Busy, Step_ID=idle.
    Please tell me the conditions to hang in this condition.

    Best Regards,
    Shigehiro Tsuda

  • shigehiro tsuda said:

    The linux touchscreen driver code has the following description.
    IDLECONFIG and CHARGECONFIG have the same setting values.
    There is also no description in TRM, but does this process have to be done?
    Please tell me why this comment and code are there.

    linux/drivers/input/touchscreen/ti_am335x_tsc.c

    titsc_step_config( )

    {

    ....

    /* Make CHARGECONFIG same as IDLECONFIG */

    config = titsc_readl(ts_dev, REG_IDLECONFIG);
    titsc_writel(ts_dev, REG_CHARGECONFIG, config);
    titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);

    ....

    }

    Please see the silicon errata Advisory 1.0.31 TSC_ADC: False Pen-up Interrupts.  Workaround #1 is to make CHARGECONFIG the same as IDLECONFIG.  One other optimization that was done in Linux was to re-order the sampling of the coordinates X, Y, Z -> Y, Z, X as that had the benefit of using the strong drive just before the charge step (i.e. similar to workaround #2 but without requiring an extra step).  This further required that any general purpose ADC conversions occur in the first steps such that X is the very last step.

  • Shigehiro,

    I was on holiday last Friday and yesterday. 

    shigehiro tsuda said:

    [linux]
    IDLECONFIG=0x00440500
    TS_CHARGE_STEPCONFIG=0x00440500

    [Starterware]
    IDLECONFIG=0x00040500
    TS_CHARGE_STEPCONFIG=0x00889120

    [customer]
    IDLECONFIG=0x00000500
    TS_CHARGE_STEPCONFIG=0x00081120

    Which value is best ?

    IDLECONFIG: These are basically the same thing since nothing gets sampled during the charge step.  That said, I'd recommend using the Linux values in general since they are best tested and most widely used.

    TS_CHARGE_STEPCONFIG: Use the Linux value.

    shigehiro tsuda said:
    TSC ADC register values ​​were the same as a result of dumping registers 5 times when problems occurred.
    It seems to hang with FSM_busy=Busy, Step_ID=idle.
    Please tell me the conditions to hang in this condition.

    This is precisely what I described earlier:

    In my experience working on the Linux side of things, great care must be taken before writing the STEPENABLE register. Specifically, when the state machine is busy you should not write to STEPENABLE. I've seen lockups occur when this is not observed.

    I went back to my old notes and confirmed that ADCSTAT = 0x00000030 was the "signature" for the failure, which is exactly what you're reporting.

  • Hi Brad,

    Thank you for your support.
    I understand that it is workaround of "the silicon errata Advisory 1.0.31 TSC_ADC: False Pen-up Interrupts" to make the setting values ​​of CHARGECONFIG and IDLECONFIG the same.

    Best Regards,
    Shigehiro Tsuda

  • Hi Brad,

    Thnak you for your support.
    I understand that the setting value of linux is recommended and  ADCSTAT=0x00000030 indicates an error.

    I push the touch panel, I check the waveform with linux.
    The waveform is as follows.
    It seems to be 0 V with CHARGE STEP, but is there a problem?
    I think that it is necessary to supply power at CHARGE STEP.

    Best Regards,
    Shigehiro Tsuda

  • shigehiro tsuda said:
    I push the touch panel, I check the waveform with linux.
    The waveform is as follows.
    It seems to be 0 V with CHARGE STEP, but is there a problem?
    I think that it is necessary to supply power at CHARGE STEP.

    Ordinarily you would be correct.  However, if you supply power at the charge step that will result in false pen-up events.  That is the topic of Advisory 1.0.31 TSC_ADC: False Pen-up Interrupts.  The implementation I have described to you has been in use in Linux for many years by many customers.

  • Hi Brad,

    When an EOS interrupt occurred, polling processing until ADC_STAT [STEPID] == idle was made, polling processing was not executed at once, and it exited immediately.
    I think that ADCSTAT [STEPID] == idle already when an EOS interrupt occurs, is it correct?
    That is, after the EOS interruption, is it correct that it is not necessary to poll the ADCSTAT [STEP_ID] == IDLE and write STEPENABLE?

    Best Regards,
    Shigehiro Tsuda
  • Hi Brad,

    Thank you for quick reply.
    If the value of linux is used, can the pen-up interrupt be used without problems? Because of this errata, our customer set timeouts, and if interrupt processing does not occur in the meantime, it is detected by software as a pen-up event.

    Best Regards,
    Shigehiro Tsuda
  • shigehiro tsuda said:
    When an EOS interrupt occurred, polling processing until ADC_STAT [STEPID] == idle was made, polling processing was not executed at once, and it exited immediately.
    I think that ADCSTAT [STEPID] == idle already when an EOS interrupt occurs, is it correct?

    In general it would be expected that after the end of sequence has occurred that you would be idle.  I think it's worthwhile to sanity check that is always true so as to avoid a lockup.  The place where it's generally more of an issue is if there's another thread for general purpose ADC sampling.  Did you find out if there are general purpose ADC samples being taken in this system?  Or is it strictly being used as a touchscreen.

  • shigehiro tsuda said:
    Hi Brad,

    Thank you for quick reply.
    If the value of linux is used, can the pen-up interrupt be used without problems? Because of this errata, our customer set timeouts, and if interrupt processing does not occur in the meantime, it is detected by software as a pen-up event.

    Best Regards,
    Shigehiro Tsuda

    Yes, Linux uses the pen-up interrupt.  It works well once you have the workaround from the advisory in place.

  • Hi Brad,

    Thank you for quick reply.
    Yes, I confirmed that their system is used only with touchscreen.

    Best Regards,
    Shigehiro Tsuda

  • Hi Brad,

    Thank you for quick reply.
    I understand that pen-up interrupt can be used if it keeps the workaround of this errata.

    Best Regards,
    Shigehiro Tsuda
  • Hi,

    As a result of our investigation, we have found the following.
    Writing STEPENABLE during the very short time to transition from TS_CHARGE STEP completion (TS_CHARGE_DELAY value time) to IDLE STEP hangs.
    It is confirmed that this hang condition occurs 100% in the above sequence.

    Recovery from this hung state will recover 100% in the following sequence.

    ① ADC disable
    ② Wait until ADCSTAT FSM Busy = 0
    ③ STEPENABLE resetting
    ④ ADC enable
    Is this behavior correct?
    Is it correct that there is a timing that STEPENABLE should not be written during FSM busy?
    
    From this result, it seems to be avoidable with the workaround of resetting STEPENABLE after EOS or resetting STEPENABLE after ADCSTAT [Step_ID] == IDLE.

    Best Regards,
    Shigehiro Tsuda

  • Hi Shigehiro,

    shigehiro tsuda said:
    Writing STEPENABLE during the very short time to transition from TS_CHARGE STEP completion (TS_CHARGE_DELAY value time) to IDLE STEP hangs.
    It is confirmed that this hang condition occurs 100% in the above sequence.

    I don't have this information, but it is very much in line with previous observations.  If you spent time testing the writes at various steps, then it is most likely correct.  I can't say if it is the only way to make it hang though.  The general practice has been not to write to STEPENABLE any time when the state machine is busy.

    shigehiro tsuda said:

    Recovery from this hung state will recover 100% in the following sequence.

    ① ADC disable
    ② Wait until ADCSTAT FSM Busy = 0
    ③ STEPENABLE resetting
    ④ ADC enable
    Is this behavior correct?

    The Linux driver doesn't have any kind of recovery mechanism and I don't recall having ever seen any issues.  I recommend using the proven technique (avoidance) that is used in the Linux driver.  Perhaps you could have your above recovery method as a fail-safe, though I don't expect it to be invoked if your software is handling STEPENABLE safely.

  • Hi Brad,

    Thank you for your support.

    After interruption of End_of_Sequence, I checked by putting a wait code until SETP_ID==IDLE, but it seems that this polling is coming out soon.
    Is the condition of SETP_ID == IDLE already at the time of interrupt of End_of_Sequence?
    After interrupt of End_of_Sequence it seems I do not need the wait code until SETP_ID==IDLE.

    Best Regards,
    Shigehiro Tsuda

  • Shigehiro,

    Yes, I expected that it would already be in the idle state when you get the end of sequence interrupt. It was just an additional safety measure to verify.

    Best regards,
    Brad
  • Hi Brad,

    Thank you for quick reply.
    In fact, is it correct that you do not need the wait code until SETP_ID==IDLE after the End_of_Sequence interrupt?
    Our customer wants to avoid having polling processing with interrupt handling functions.

    Best Regards,
    Shigehiro Tsuda

  • Perhaps instead of polling you might consider a single read where you could flag an error if ever you come across a scenario where it's not idle.  I don't expect that to happen, but if it ever did it would help to have that error logged.

  • The AM335x TRM says "An END_OF_SEQUENCE interrupt is generated after the last active step is completed before going back
    to the IDLE state". Since the interrupt is generated before returning to the IDLE state, I suspect an END_OF_SEQUENCE interrupt does not necessarily mean that STEP_ID == IDLE. I will double-check.

    Regards,
    Nick
  • Hi Nick,

    Thank you for your support.
    Immediately after interruption of end_of_shequnce, it is not necessarily STEP_ID==IDLE, but which state is it?

    Best Regards,
    Shigehiro Tsuda

  • Hi,

    When reading the STEPENABLE register when TSC ADC hangs, it is 0x7F9.
    Since 0x7FF is written, it seems that it is going to step2.
    Is there anything else that causes software control to stop in the middle of step?

    Best Regards,
    Shigehiro Tsuda

  • * Do 100% of the hang conditions you see still occur in the transition from TS_CHARGE STEP completion (TS_CHARGE_DELAY value time) to IDLE STEP? Or does the hang condition you are describing right now happen at a different time?

    * Is this hang associated with writing STEPENABLE, or is it caused by something else?

    Regards,
    Nick
  • Hi Nick,

    Thank you for quick reply.

    When the TSC ADC reads the STEPENABLE register after hang, there seems to be 0x7FF and 0x7F9. Occasionally 0x7F9, 0x7FF is mostly.
    Since STEPENABLE was written after the FIFO 1 threshold interrupt, no code like that written in step 2 was found.
    Step_id of ADCSTAT is IDLE.
    The register is as follows.

    Do you know what is happening?

    Best Regards,
    Shigehiro Tsuda

  • using END_OF_SEQUENCE to determine if STEP_ID==IDLE:
    It sounds like an END_OF_SEQUENCE interrupt is generated when all steps have been serviced in the adc_clk domain. However, the sequencer is in the ocp_clk domain. I was told that in order to safely use END_OF_SEQUENCE to indicate the state, wait the equivalent of two adc_clk cycles after receiving an END_OF_SEQUENCE interrupt before writing to STEPENABLE.

    Perhaps this could be combined with Brad's suggestion to check STEP_ID after receiving an END_OF_SEQUENCE interrupt? (receive END_OF_SEQUENCE, check STEP_ID==IDLE, if STEP_ID != IDLE then wait the equivalent of two adc_clk cycles before writing to STEPENABLE). Or maybe it would be faster to receive END_OF_SEQUENCE, then poll for STEP_ID == IDLE instead of waiting.

    Regards,
    Nick

  • 1) So sometimes when the halt condition occurs, the charge step and steps 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10 are enabled? And other times only the charge step and steps 3, 4, 5, 6, 7, 8, 9, 10 are enabled? Are there any other variations?

    2) Is your code setting STEPENABLE = 0x7FF in one part, and setting STEPENABLE = 0x7F9 in another part? If not, it does look like something is causing the FSM sequencer to stop in the middle of addressing steps. 

    Regards,
    Nick

  • Hi Nick,

    Thank you for quick reply.

    There are only two types of information present.
    Should it be better to check if there are other ones?

    For their code, only STEPENABLE == 0x7FF is set, and code setting STEPENABLE == 0x7F9 has not been found.

    Is the value of STEP_ID of ADCSTAT and clear of STEPENABLE (1 -> 0) updated at the same time?

    Best Regards,
    Shigehiro Tsuda
  • Hello Shigehiro-san,

    I want to make sure I understand. Is this the same hang we talked about earlier in the thread? Can the hang still be fixed by only writing to STEPENABLE when STEP_ID == IDLE? In that case, are you just looking for more information about why the STEPENABLE register has the value it does after the ADC hangs?

    Or is this a new hang, which is not fixed by the suggestions earlier in the thread?

    Regards,
    Nick
  • Hi Nick,

    Thank you for quick reply.

    [your answer]

    using END_OF_SEQUENCE to determine if STEP_ID==IDLE:
    It sounds like an END_OF_SEQUENCE interrupt is generated when all steps have been serviced in the adc_clk domain. However, the sequencer is in the ocp_clk domain. I was told that in order to safely use END_OF_SEQUENCE to indicate the state, wait the equivalent of two adc_clk cycles after receiving an END_OF_SEQUENCE interrupt before writing to STEPENABLE.

    Perhaps this could be combined with Brad's suggestion to check STEP_ID after receiving an END_OF_SEQUENCE interrupt? (receive END_OF_SEQUENCE, check STEP_ID==IDLE, if STEP_ID != IDLE then wait the equivalent of two adc_clk cycles before writing to STEPENABLE). Or maybe it would be faster to receive END_OF_SEQUENCE, then poll for STEP_ID == IDLE instead of waiting.

    [question]

    Is it correct with the following understanding?
    1. There is a period during which STEP_ID == IDLE does not occur after interrupt of END_OF_SEQUENCE.
    2. Writing STEPENABLE during that period will cause a hang problem.
    3. After interruption of END_OF_SEQUENCE, wait 2 adc_clk (667 ns when adc_clk is using at 3 MHz), always make STEP_ID == IDLE.
    4. After this time, by writing STEPENABLE, the problem of hanging does not occur.

    It is an additional question.
    Which of the following timings is generated for interrupt generation of END_OF_SEQUENCE?
    ① Before transition of Charge STEP after final STEP completion
    ② After completion of Charge STEP
    ③ During transition of Charge STEP to IDLE
    ④ After becoming IDLE

    Best Regards,
    Shigehiro Tsuda

  • Hello Shigehiro-san,

    END_OF_SEQUENCE: This interrupt is generated when all steps have been serviced in the adc_clk domain, and the state machine is transitioning to the IDLE state. However, the sequencer is in the ocp_clk domain, so we expect it to take one adc_clk clock cycle for adc_clk domain and ocp_clk domain to both reach an IDLE state. That means we know for sure it will be in an IDLE state after two adc_clk cycles.

    It takes time for the END_OF_SEQUENCE interrupt to reach the ARM's interrupt handling function. So the sequencer might be idle by the time ARM code enters the interrupt handing function. However, the sequencer might not be IDLE when the ARM enters the END_OF_SEQUENCE interrupt handing function. That is why we suggest either polling for STEP_ID == IDLE or waiting two adc_clk cycles before writing to STEPENABLE.

    It sounds like it may be faster to poll STEP_ID == IDLE at the top of the END_OF_SEQUENCE interrupt handling function rather than waiting two adc_clk cycles.

    Regards,
    Nick

  • Hello Shigehiro-san,

    I want to make sure I understand your question about STEPENABLE = 0x7F9. Is this the same hang we talked about earlier in the thread? Can the hang still be fixed by only writing to STEPENABLE when STEP_ID == IDLE? In that case, are you just looking for more information about why the STEPENABLE register has the value it does after the ADC hangs?

    Or is this a new hang, which is not fixed by the suggestions earlier in the thread?

    Regards,
    Nick
  • Hi Nick,

    Thank you for your quick and kind support.

    Since it is called two ADC_CLKs,Is it current at 667ns wait for 3MHz operation?
    At least as an interrupt, I believe it is necessary to perform the following processing after END_OF_SEQUENCE.
    ① ARM interrupt handling
    ② read TSC_ADC_IRQSTATUS
    ③ Judge whether interrupt factor is END_OF_SEQENCE
    ④ Write 1 to clear the END_OF_SEQENCE bit of TSC_ADC_IRQSTATUS
    ⑤ Write TSC_ADC_STEPENABLE

    Interruption latency of ① takes several us, I think that it takes time to access other buses as well.
    In linux, looking at the following site takes several 10us.

    processors.wiki.ti.com/.../Processor_SDK_Linux_Kernel_Performance_Guide

    Therefore, as processing, I think that it takes 2 adc_clk or more, so I have doubts as to whether it is necessary.

    Best Regards,
    Shigehiro Tsuda
  • Hi Nick,

    Thank you for your quick and kind support.

    Our customer have confirmed that hung problems no longer occur by writing STEPENABLE after EOS interruption.
    Since the hung problem no longer occurs, this problem has not occurred after the workaround.
    I am thinking about the reason why the problem occurred,
    They are writing STEPENABLE = 0x7FF, but if that register is read after a TSC ADC hang, I do not know the condition that STEPENABLE=0x7F9 and STEP_ID=IDLE.
    Do you understand something?

    Since the operation of the programmer can not be guaranteed until STEP_ID=IDLE has been, it is correct with the recognition that the STEPENABLE value may become indefinite?

    Best Regrads,
    Shigehiro Tsuda
  • Hello Shigehiro-san,

    Yes, if you have a 3MHz adc_clk you would want to ensure that 667ns elapsed after END_OF_SEQUENCE is generated. If you can guarantee that more time than 667ns has elapsed between END_OF_SEQUENCE generation and writing STEPENABLE, then you should be safe.

    The STEPENABLE value is not defined after a hang.

    Regards,
    Nick
  • Hi Nick,

    Thank you for various support.
    Sorry for my late reply.

    We understood the time to update STEPENABLE.
    Our customer has a problem even if you write STEPENABLE after STEP_ID becomes IDLE after END_OF_SEQUENCE interrupt.
    However, since they are not organized on what conditions they occurred, we will ask them to confirm them again.

    Best Reards,
    Shigehiro Tsuda

  • Hello Shigehiro,

    Was the customer's issue resolved?

    Regards,
    Nick
  • Hello Shigehiro,

    I am closing the thread on my end. Please comment if there are updates from the customer, or create a new thread if customers have found a new problem.

    Regards,
    Nick