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.

tscadc: cat: read error: Resource temporarily unavailable

Hi All,

Sometimes ADC gives below error when it is read using "cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw",

cat: read error: Resource temporarily unavailable

In file " drivers/iio/adc/ti_am335x_adc.c", the below functiion is there,

static int tiadc_read_raw(struct iio_dev *indio_dev,
                struct iio_chan_spec const *chan,
                int *val, int *val2, long mask)
{
        struct tiadc_device *adc_dev = iio_priv(indio_dev);
        int i;
        unsigned int fifo1count, read, stepid;
        u32 step = UINT_MAX;
        bool found = false;
        u32 step_en;
        unsigned long timeout = jiffies + usecs_to_jiffies
                                (IDLE_TIMEOUT * adc_dev->channels);

        step_en = get_adc_chan_step_mask(adc_dev, chan);
        if (!step_en)
        {
                printk("ADC: step_en = 0\n");
                return -EINVAL;
        }

        am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);

        /* Wait for Fifo threshold interrupt */
        while (!(tiadc_readl(adc_dev, REG_RAWIRQSTATUS) & IRQENB_FIFO1THRES)) {
                if (time_after(jiffies, timeout)) {
                        am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
                        printk("ADC: time_after returns true value\n");
                        return -EAGAIN;
                }
        }

.........

This function returns EAGAIN, when timeout happens. Timeout happens after 10 micro-seconds. Within this 10 micro-seconds, FIFO is not being updated with data sample.

(IDLE_TIMEOUT * adc_dev->channels) = 5 * 2 = 10 micro-seconds.

In "include/linux/mfd/ti_am335x_tscadc.h"

/*
* ADC runs at 3MHz, and it takes
* 15 cycles to latch one data output.
* Hence the idle time for ADC to
* process one sample data would be
* around 5 micro seconds.
*/

#define IDLE_TIMEOUT 5 /* microsec */

What is the solution for this? Increasing "IDLE_TIMEOUT" may solve this problem? What is the fail-safe value? How to find it?

Regards,

Gangadhar

  • You have already a similar thread opened. Please try to post related questions on the same thread.

  • Hi Gangadhar,

    This seems like a variation of your other thread .

    What is different compared to the other thread that is causing this different message? Is your system using a resistive touch screen while your attempting to read samples from the ADC? Are you attempting to read from multiple channels at the same time? Or reading from the same channel in multiple threads or in different programs? Is your setup similar to your other post? AM335x, SDK 7.0 Linux kernel?

  • Hi Franklin,

    The failure location is different. In this case EAGAIN is returned.

    /* Wait for Fifo threshold interrupt */
            while (!(tiadc_readl(adc_dev, REG_RAWIRQSTATUS) & IRQENB_FIFO1THRES)) {
                    if (time_after(jiffies, timeout)) {
                            am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
                            return -EAGAIN;
                    }   
            }   

    where as in the other case EBUSY is returned,

    if (found == false)
                    return -EBUSY;

    We don't have touch screen. Multiple channels are not read simultaneously. Same channel is not read from diff. threads or programs.

    This ADC problem arises only when Bluetooth application runs. And also drop in bluetooth connection is noticed when ADC is read.

    In dts file, I've

    &tscadc {
            status = "okay";
    /*0 - LED_BIN
      1 - DISP_TEMP
    */
            adc {
                    ti,adc-channels = <0 1>;
            };  
    };

    Regards,

    Gangadhar

  • I'm going to need a bit more insight since bluetooth and ADC should have nothing in common. What peripherals does the Bluetooth module use? Also how are you making these ADC request?

    You mentioned that only one channel is being read but you have two channels listed in your DT file. What is the other channel being used for?
  • Hi Franklin,
    BT uses UART and mcasp (PCM). Two ADC channels - 0,1 are enabled IN DTS file, one for led and other for temperature. Cat is used to read temp. As mentioned in am335x ADC driver wiki. Currently channel -0, led is not at all used, this is future implementation

    Regards,
    Gangadhar
  • So taking a look at the driver I believe the following is going on.

    When you use the ADC driver in one shot mode the ADC function tiadc_read_raw is being ran and is setting the step enable bit which immediately causes an ADC conversation to occur. That while loop your pointing to is waiting to see if the conversation is finished but eventually gives up after a certain period of time. What I'm assuming is that the UART driver IRQ is being ran and is taking too long ( greater than 10 microseconds) which is causing the ADC code to time out. Since tiadc_read_raw is just a regular kernel function and not an interrupt then it can be interrupted at any point. Playing around with the timeout value may help. Or you can remove it completely. Your probably better of raising it to a much higher value.

    In the system probably Bluetooth related something is causing a context switch which is taking too long that the timeout is occurring. Looking at the commit that set the timeout value git.ti.com/.../bba5733c26820332798a92ba525ae03466cf6d94 it seems that the value was set assuming nothing really would interrupt the ADC.

    I also found a patch that was applied to the 3.13 kernel ( apart of SDK 8.0 kernel) that increases the timeout value github.com/.../1a54b7dabf8f20df2894aed9683155ff89fc20e8. This patch increases the IDLE_TIMEOUT to 112 microsec which based on the commit log better represents the amount of time the ADC should take. So increasing it to that value first would be the first thing I would try.
  • Hi Franklin,
    1. When step enable bit is set, ADC conversion starts. The ADC on-chip peripheral does the work independent of CPU. CPU can do any other work. In loop, completion of conversion and availability of data in FIFO is checked for 10 micro-seconds(in our 2 channel case). Even if some interrupt happens (you suspect it to be BT UART interrupt), let the processor execute the ISR, parallely ADC peripheral will be doing it's work. So ADC does not miss on time.

    2. We had increased time-out to 30 micro-seconds. With this time-out problem didn't solve and the drop in bluetooth connection became worse. As you've suggested, we'll increase IDLE_TIMEOUT to 112 micro-seconds and check.

    Regards,
    Gangadhar
  • Hi Gangadhar,

    Your above comments are correct but my point was on a single core processor context switching can occur. The current ADC driver code is essentially looping until a register is set which indicates that the ADC conversion is complete. The ADC conversion is indeed independent of the CPU but the loop that is checking the register isn't. So I was guessing that a context switch could happen at certain times while the ADC code is looping and checking for the register. If this does happen then it may take some time for a context switch to go back to the ADC driver which at that point the timeout code could occur since it may have took to long.

    It is possible that my above guess isn't a problem and no context switch is occurring.

    I did run a test and I'm not able to duplicate the issue.

    On a Beaglebone Black I connected UART2 to my host PC. I set the baud rate of UART 2 to 115200. On my host PC I am running a python script that is continuously blasting a string as fast as possible to the BBB in a infinite while loop via UART. On my BBB I have an infinite while loop reading the value of ADC0.

    What I noticed was that reading from ADC0 occurred alot slower vs when I'm not sending a bunch of UART characters to the BBB. However, I never had a situation where I get a "cat: read error: Resource temporarily unavailable ".

    I also have some additional questions.

    How are you reading the values from the ADC? Shell Script via cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw or a C program using open/fopen and then read.

    How fast are you sampling the single ADC channel? And how often does this error occur?

    What is the baud rate that the Bluetooth module is configuring the UART for?

    Since you mention the PCM/McASP I'm assuming your streaming audio. Do you have connection issues or does this ADC problem only occur when audio is being streamed?

    What kernel are you using and from what SDK?

    It would be helpful if you could reproduce this issue on a simplified setup so I can reproduce this issue myself.

  • Hi Franklin,
    This issue is fixed. Thanks for making me think about interrupts. Thanks for your time and effort in trying to reproduce error case.
    If interrupt happens immediately after entering the loop, CPU services ISR and when it comes back to execute driver code, if timeout has happened, it'll return error even if ADC conversion is completed by now. Added check for "IRQENB_FIFO1THRES" when timeout happens. This solved the problem. Better way would have been to make the reading of "IRQENB_FIFO1THRES" and "timeout" atomic or disable interrupts.

    while (!(tiadc_readl(adc_dev, REG_RAWIRQSTATUS) & IRQENB_FIFO1THRES)) {

    if (time_after(jiffies, timeout)) {

    am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
    return -EAGAIN;
    }
    }

    Regards,
    Gangadhar