Linux interrupt is not handled by the wrapper driver - linux

I am writing a device specific DMA driver for Zynq AXI DMA. The driver is actually a wrapper driver and uses Xilinx DMA driver under the DMA engine driver framework like this:
+------------------+
| Wrapper driver |
+------------------+
| DMA framework |
+------------------+
| Xilinx DMA Driver|
+------------------+
+ Kernel +
+------------------+
I use interrupt 61 for transfer complete and 62 for receive complete. The interrupt handler for Xilinx DMA driver is properly installed (I checked /proc/interrupts). However, I the driver seems not to handle the interrupt properly by showing me this:
I checked /proc/interrupts. IRQ62 is not handled. Here is the code where the interrupt is registered:
0986 /* find the IRQ line, if it exists in the device tree */
0987 chan->irq = irq_of_parse_and_map(node, 0);
0988 err = devm_request_irq(xdev->dev, chan->irq, dma_intr_handler,
0989 IRQF_SHARED,
0990 "xilinx-dma-controller", chan);
And here is the output of cat /proc/interrupt:
I have two questions:
1) If I only install interrupt handler for Xilinx DMA driver but not the wrapper driver, while I use the wrapper driver as the char device, interrupt will not be handled by the Xilinx DMA driver?
2) If so, how do I let Xilinx DMA driver to handle the interrupt in this case? Is 'irqpoll' the only solution? Is there any performance issue compared to direct interrupt handling rather than polling?

Your devicetree isn't good, more precisely, your pl.dtsi file. My pl file looks like this:
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_dma_0: dma#40400000 {
compatible = "xlnx,axi-dma";
interrupt-parent = <&intc>;
interrupts = <0 29 4 0 30 4>;
reg = <0x40400000 0x10000>;
xlnx,include-sg ;
dma-channel#40400000 {
compatible = "xlnx,axi-dma-mm2s-channel";
interrupts = <0 29 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
dma-channel#40400030 {
compatible = "xlnx,axi-dma-s2mm-channel";
interrupts = <0 30 4>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
};
};

Related

Share GPIO pins between two instances of the SPI Linux driver for AD7606 ADC

I'm trying to enable two instances of AD7606 SPI driver for AD7606C. In order for ADC to start the conversion GPIO pins for CONVST (conversion start), interrupt pin (BUSY pin) and RESET pin have to be defined in the device tree for the driver to use.
Two ADCs are on the SPI bus, each ADC has it's own chip select pin and interrupt/BUSY pin but CONVST and RESET are hardwired and shared between two ADCs, on the same pins.
Device tree is given below, and platform used is STM32MP157C Odyssey.
&spi2 {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi2_pins_mx>;
pinctrl-1 = <&spi2_sleep_pins_mx>;
status = "okay";
num-cs = <2>;
cs-gpios = <&gpiob 12 GPIO_ACTIVE_LOW>, // ADC CS1
<&gpiod 4 GPIO_ACTIVE_LOW>; // ADC CS2
adc0#0 {
compatible = "adi,ad7606b";
reg = <0>;
spi-max-frequency = <10000000>; // <60MHz if Vdrive > 2.7V, <40MHz if < 2.7V
spi-cpol;
spi-cpha;
avcc-supply = <&vref>;
interrupts = <10 IRQ_TYPE_EDGE_FALLING>; // Port and pin number where BUSY pin is connected
interrupt-parent = <&gpioe>;
adi,conversion-start-gpios = <&gpiof 10 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpioc 0 GPIO_ACTIVE_HIGH>;
adi,sw-mode;
};
adc1#1 {
compatible = "adi,ad7606b";
reg = <1>;
spi-max-frequency = <10000000>;
spi-cpol;
spi-cpha;
avcc-supply = <&vref>;
interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpiod>;
adi,conversion-start-gpios = <&gpiof 10 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpioc 0 GPIO_ACTIVE_HIGH>;
adi,sw-mode;
};
};
The second driver returns probe error -16 (Device or resource busy) because it can't use CONVST and RESET pins that previous driver already allocated.
The second ADC actually works if I assign some random unused GPIOs, read the value from the first ADC (in order to activate CONVST pin), then read the value from second ADC. This is a workaround used currently.
My question is, is it possible somehow to share GPIO pins between two instances of the same driver in Linux?

#interrupt-cells is 2 but interrupts is a 3-tuple

I was looking at the device tree for the Beagle Bone Black and started with am57xx-beagle-x15.dts. Drilling down into dra7.dtsi I found gpio1:
gpio1: gpio#4ae10000 {
compatible = "ti,omap4-gpio";
reg = <0x4ae10000 0x200>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "gpio1";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
I had read that #interrupt-cells gave the number of u32s or cells that one would expect in an item in the the interrupts list. But when I look at interrupts I see a 3-tuple: <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>. Would love to know, why does this contain 3 cells and not 2?
It's a very late answer but add one so that someone could get help.
I can't find the exact dts file from my current linux 5.15 source. But the interrupt-parent of the node should have required 3 cells for the interrupts property. For gic, normally it requires 3 values - {interrupt type, interrupt number, flag}. It's in the device binding document of gic (Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml in linux 5.15)
"#interrupt-cells":
const: 3
description: |
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts.
The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered (invalid for SPIs)
4 = active high level-sensitive
8 = active low level-sensitive (invalid for SPIs).
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of
the 8 possible cpus attached to the GIC. A bit set to '1' indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts.
Also note that the configurability of PPI interrupts is IMPLEMENTATION
DEFINED and as such not guaranteed to be present (most SoC available
in 2014 seem to ignore the setting of this flag and use the hardware
default value).

How to set value of i2c PCA9570 gpio expander as early as possible during Linux boot?

I have written a working driver for the PCA9570. Its four outputs are set (and read back) via Linux's GPIO subsystem. e.g.
root#armbox:/sys/class/gpio# echo 508 > export
root#armbox:/sys/class/gpio# echo 509 > export
root#armbox:/sys/class/gpio# echo 510 > export
root#armbox:/sys/class/gpio# echo 511 > export
The problem is that the chip starts with its outputs high. https://www.nxp.com/docs/en/data-sheet/PCA9570.pdf section 8.1.
root#armbox:/sys/class/gpio# cat gpio508/value
1
root#armbox:/sys/class/gpio# cat gpio509/value
1
root#armbox:/sys/class/gpio# cat gpio510/value
1
root#armbox:/sys/class/gpio# cat gpio511/value
1
I can manually set them low from userspace, after boot. e.g.
root#armbox:/sys/class/gpio# echo 0 > gpio510/value
root#armbox:/sys/class/gpio# cat gpio510/value
0
How can I set the chip's outputs low as early as possible during the boot sequence?
I can hack my own driver to do this, during pca9570_probe(), but that feels very hacky. pca9570_probe() currently reads the values from the chip.
static int pca9570_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
...
ret = pca9570_readb(chip, &chip->reg);
if (ret)
goto out_failed;
return 0;
out_failed:
if (chip->client)
i2c_unregister_device(chip->client);
return -1;
}
Is there a correct way in Linux to specify GPIO values during boot, rather than hacking a driver?
P.S. The dts clause is:
pca9570: pca9570#48 {
compatible = "pca9570";
reg = <0x24>;
gpio-controller;
#gpio-cells = <4>;
};
You can use 'GPIO hogging' mechanism as described in DeviceTree gpio binding documentation. Quoting basic information:
GPIO hogging is a mechanism
providing automatic GPIO request and configuration as part of the
gpio-controller's driver probe function.
For example, we have this definition in our device tree for our gpio expander:
gpio_expander: tca6424a#22 {
compatible = "ti,tca6424";
reg = <0x22>;
[...] /* some other gpio expander configuration */
lcd-rst {
gpio-hog;
gpios = <7 GPIO_ACTIVE_LOW>;
output-low;
};
};
You don't need to extend your driver, it is part of the gpio subsystem.

Linux driver for quad spi on Zedboard

I am quite new SoC, and I am currently working with the evaluation platform Zedboard. I have included a quad SPI in my block design and want to communicate with it through Linux running on one of the ARM processors. I need the SPI to transmit/receive 32bits in one burst and handle the communication from C++. However, I can't get it working.
The quad SPI is setup as standard, no FIFO and transaction width = 32 bits. I have updated the Linux device tree as follows:
axi_quad_spi_0#41E00000 {
compatible = "xlnx,xps-spi-2.00.b", "xlnx,xps-spi-2.00.a";
#address-cells = <0x1>;
#size-cells = <0x0>;
interrupt-parent = <0x2>;
interrupts = <0x0 0x39 0x1>;
reg = <0x41e00000 0xffff>;
xlnx,fifo-exist = <0x0>;
xlnx,num-ss-bits = <0x2>;
xlnx,num-transfer-bits = <0x20>;
xlnx,sck-ratio = <0x10>;
device#0 {
compatible = "spidev";
reg = <0x0>;
spi-max-frequency = <0x5f5e10>;
};
device#1 {
compatible = "spidev";
reg = <0x1>;
spi-max-frequency = <0x5f5e10>;
};
After this I can see spidev0.0 and spidev.0.1 in /dev as expected. Furthermore, when writing to the device files with bits_per_word = 8 I get an 8bit burst out of the quad SPI but with 32 clock periods, i.e. I get the first byte out corresponding to 1/4 of the 32 bit burst (seen with debug probes in Vivado). I guess I see this because the transaction width is set to 32bits.
However, when I want to change the burst to 32 bit (bits_per_word = 32) it doesn't work - I get the message "xilinx_spi_setup_transfer, unsupported bits_per_word=32" in my Linux terminal.
I use the following code in C++:
uint8_t tx[] = { 0xAD, 0xEF, 0x01, 0x23 };
uint8_t rx[4] = { 0 };
struct spi_ioc_transfer tr[2];
tr[0].tx_buf = (unsigned long)tx;
tr[0].rx_buf = (unsigned long)rx;
tr[0].len = 4;
tr[0].delay_usecs = delay;
tr[0].speed_hz = speed;
tr[0].bits_per_word = 32;
tr[0].cs_change = 0;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
I have tried to compile the xilinx driver xilinx-spi.c but when I try to install it (with "modprobe xilinx_spi" in Linux) it says that the "Device or ressource busy".
How do I get this to support 32bit so I can transmit/receive 32bits in one burst? Can anybody help me out here? Thanks!
Br. Jacob

generating EXTI0 interrupt when extenal temperature sensor is connected to STM32F207VC

I am using STM32F207VC controller . I have my external probe temperature sensor connected to one of the internal ADC channel of stm32.
I want to generate an external interrupt when this is connected to controller and i should start measuring from external temperature sensor.
Please could any one provide me code or any help in this
Thanks
I have an STM32F4 processor interfaced with a Solomon Systems SSD1963 GPU. The GPU has a Tearing Signal (TE) that notifies the processor when it is about to do a vertical refresh. I hope you can use this code as an example and adapt it to your solution.
The TE signal is connected to the CPU's GPIO G7 pin. So first I have to configure the GPIO pin.
//GPIO Pin G7
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOG, &GPIO_InitStructure);
Next I have to configure the interrupt and NVIC.
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource7);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Finally I have to create the interrupt handler. The EXTI9_5_IRQHandler handles external interrupts on lines 5 ~ 7. This method is actually defined in my CPUs startup assembly file as a weak reference. I just need to redefine the method and the linker will do the rest.
extern "C" void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line7) != RESET)
{
//Handle the interrupt
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
I'm using Mentor Graphics' Sourcery Codebench Lite as my toolchain.

Resources