i.MX93 Adc latency issue

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

i.MX93 Adc latency issue

Jump to solution
260 Views
NZP
Contributor III


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.

0 Kudos
1 Solution
226 Views
NZP
Contributor III

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.

 

View solution in original post

0 Kudos
3 Replies
214 Views
LFGP
NXP TechSupport
NXP TechSupport

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.

0 Kudos
233 Views
LFGP
NXP TechSupport
NXP TechSupport

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.

static irqreturn_t imx93_adc_isr(int irq, void *dev_id)
{
struct imx93_adc *adc = dev_id;
u32 isr, eoc, unexpected;
 
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;
}

 

0 Kudos
227 Views
NZP
Contributor III

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.

 

0 Kudos