Hello,
I’m using imx6dl/linux to fetch data from an ADC via SPI with the sampling rate of 250Hz (i.e. 4ms between samples) where the ADC triggers the sampling by setting a DRDY pin low. To sum up, this is what I need to do within a 4ms window:
- Wait for DRDY pin to go low (4ms window opens)
- Issue an SPI read/write to ADC1 to acquire samples
- Issue an SPI read/write to ADC2 to acquire samples (both ADCs are triggered with the same gpios and one DRDY triggers both of them)
If I can’t complete these steps within the window I lose a sample. My first approach at this was to write a userspace program:
- poll() the DRDY gpio pin using the gpio sysfs driver
- Issue an SPI read/write via the generic spi (using ioctl()) (do this 2 times)
This works reasonably well when the system is idle with some jitter (which is OK since the ADC is driven by an external clock) but when a load hits the system I encounter latency spikes from DRDY->SPI CS DOWN that exceeded 4ms. The average latency between DRDY->SPI CS DOWN is around 100us.
To remedy this I proceeded in writing a kernel module to perform this task. I used the iio driver subsystem. The disappointing result of that work is that the driver has pretty much the same performance as the userspace program so my problem persists, I’m still losing samples under load. 100us strikes me as an oddly long period, especially consider this post ( Writing a Linux Kernel Module — Part 1: Introduction | derekmolloy.ie ) from Derek Molloy where he manages a <20us turnaround time for what is admittedly a simpler task and on a different (but similar) cpu.
Here is a snipped from my dts:
&ecspi4 {
status = "okay";
ti_ads1198_0: ads1198@0 {
spi-max-frequency = <20000000>;
compatible = "ti_ads1198";
reg = <0>;
enable-gpios = <&gpio5 6 0 &gpio5 5 0 &gpio4 16 0 &gpio5 7 0>;
spi-cpha;
gpios = <&gpio5 6 0 /* ADS1198 RESET */
&gpio5 5 0 /* ADS1198 START */
&gpio4 16 0 /* ADS1198-0 DRDY */
&gpio5 7 0 /* ADS1198-0 PWDN */
>;
interrupt-parent = <&gpio4>;
interrupts = <16 IRQ_TYPE_EDGE_FALLING>; /* DRDY */
};
ti_ads1198_1: ads1198@1 {
spi-max-frequency = <20000000>;
compatible = "ti_ads1198";
reg = <1>;
spi-cpha;
};
};
I have modeled my driver mostly after the MPU6050 driver Stefan mentioned in another post on this message board with this trigger setup:
ret = request_irq(st->irq_drdy, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(&st->spi->dev),
st->trig);
I attempted to put an spi_async() in the irq_handler function but without noticeable success.
One thing that I notice are the following debug messages I get on boot:
[ 0.187883] of_dma_request_slave_channel: dma-names property of node '/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02014000' missing or empty
[ 0.200817] spi_imx 2014000.ecspi: cannot get the TX DMA channel!
[ 0.206944] spi_imx 2014000.ecspi: dma setup error,use pio instead
To remedy this I tried adding the dma and dma-names properties to the imx6qdl.dtsi (essentially copy-pasted from the imx6sx.dtsi), yielding the following entry for my spi peripheral:
ecspi4: ecspi@02014000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x02014000 0x4000>;
interrupts = <0 34 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_ECSPI4>,
<&clks IMX6QDL_CLK_ECSPI4>;
clock-names = "ipg", "per";
status = "disabled";
dmas = <&sdma 9 7 1>, <&sdma 10 7 2>;
dma-names = "rx", "tx";
};
This did not fix the latency issues, but the warnings disappeared.
So my questions are:
- Is the 100us latency as I described above “normal” or am I possibly doing something wrong?
- Can you think of anything that can get me reliably within the 4ms window or do I just have to accept the fact that I’m putting realtime constraints on a non-realtime OS and fix the problem with a messier solution (i.e. sample padding)?
- Applying PREEMPT_RT patches would be one potential solution, I’m just a little concerned that applying that patch might compromise the stability of the system, is that a route that you have researched?