Hello experts,
I am working on IMX9352 processors ADC, I am able to read adc using libiio cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw.
In my project requirement of reading adc is at very fast interval. so, looking at imx93_adc.c driver I found that below function takes much time.
ret = wait_for_completion_interruptible_timeout(&adc->completion, IMX93_ADC_TIMEOUT);
Measuring execution time using below
ktime_t start, end;
s64 delta_time_ns;
start = ktime_get();
ret = wait_for_completion_interruptible_timeout(&adc->completion,IMX93_ADC_TIMEOUT);
end = ktime_get();
delta_time_ns = ktime_to_ns(ktime_sub(end, start));
printk(KERN_INFO "Execution time: %lld ns\n", delta_time_ns);
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[ 5680.026447] Execution time: 167833 ns
3775
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[ 5681.053120] Execution time: 183459 ns
3780
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[ 5681.955820] Execution time: 157406 ns
3774
I found this function takes around average 150 microseconds and more. However, ADC clock is 24MHZ and much faster.
1. So, can anyone please let me know how can I reduce this execution time of reading ADC value?
2. Is there any way to read ADC in real-time as we do in microcontroller with very less latency in user space.
Solved! Go to Solution.
Dear LFGP,
Thanks for your suggestion,
I have slightly modified my execution time measurement logic as below, Now storing end = ktime_get(); variable in imx93_adc_isr as below.
start = ktime_get();
ret = wait_for_completion_interruptible_timeout(&adc->completion,IMX93_ADC_TIMEOUT);
//end = ktime_get();
delta_time_ns = ktime_to_ns(ktime_sub(end, start));
printk(KERN_INFO "Execution time: start, end, delta %lld, %lld, %lld ns\n", start, end, delta_time_ns);
static irqreturn_t imx93_adc_isr(int irq, void *dev_id)
{
struct imx93_adc *adc = dev_id;
u32 isr, eoc, unexpected;
end = ktime_get();
isr = readl(adc->regs + IMX93_ADC_ISR);
if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) {
eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK;
writel(eoc, adc->regs + IMX93_ADC_ISR);
complete(&adc->completion);
}
unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK;
if (unexpected) {
writel(unexpected, adc->regs + IMX93_ADC_ISR);
dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
Below is time delta of conversion start time and interrupt generation.
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12122.109633] Execution time: start, end, delta 12122502929521, 12122502953563, 24042 ns
3775
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12166.448276] Execution time: start, end, delta 12166842653231, 12166842689690, 36459 ns
3786
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12168.208182] Execution time: start, end, delta 12168602600091, 12168602636592, 36501 ns
3779
1. Here my question is that is it possible to minimize this latency? Because ADC interrupt generation takes too long here.
dear @NZP ,
if you only comment the line end = ktime_get(); you can't be sure for the value in ""end"" variable, in other words, from my point of view, it is a garbage value.
On the other hand, the best way to obtain tha ADC value is performing an interrupt due the function
wait_for_completion_interruptible_timeout
depends of the Linux scheduler ( it is a consult to the kernel and not an interrupt as well), so, there isn't any efective method to reduce the elapsed time.
dear @NZP ,
the values of "Execution time: ...." you are seeing it doesn't depend of the ADC sped conversion, they depend of the Linux scheduler ( excerpt: "Completions are built on top of the waitqueue and wakeup infrastructure of the Linux scheduler") . In other words, you need to use/develop a direct interrupt for the ADC. Into the driver you are using there is a function intended to do the interrupt, could please check it.
Dear LFGP,
Thanks for your suggestion,
I have slightly modified my execution time measurement logic as below, Now storing end = ktime_get(); variable in imx93_adc_isr as below.
start = ktime_get();
ret = wait_for_completion_interruptible_timeout(&adc->completion,IMX93_ADC_TIMEOUT);
//end = ktime_get();
delta_time_ns = ktime_to_ns(ktime_sub(end, start));
printk(KERN_INFO "Execution time: start, end, delta %lld, %lld, %lld ns\n", start, end, delta_time_ns);
static irqreturn_t imx93_adc_isr(int irq, void *dev_id)
{
struct imx93_adc *adc = dev_id;
u32 isr, eoc, unexpected;
end = ktime_get();
isr = readl(adc->regs + IMX93_ADC_ISR);
if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) {
eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK;
writel(eoc, adc->regs + IMX93_ADC_ISR);
complete(&adc->completion);
}
unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK;
if (unexpected) {
writel(unexpected, adc->regs + IMX93_ADC_ISR);
dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
Below is time delta of conversion start time and interrupt generation.
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12122.109633] Execution time: start, end, delta 12122502929521, 12122502953563, 24042 ns
3775
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12166.448276] Execution time: start, end, delta 12166842653231, 12166842689690, 36459 ns
3786
cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw
[12168.208182] Execution time: start, end, delta 12168602600091, 12168602636592, 36501 ns
3779
1. Here my question is that is it possible to minimize this latency? Because ADC interrupt generation takes too long here.