Problem in AXI GPIO interrupt handling in ZYNQ - linux-device-driver

I have a custom ZYNQ7000-based board. I want to insert an AXI GPIO that directly generate an interrupt. I want to handle the interrupt in a kernel module. All things sound to be correct but it does not work.
My Toolset:
Petalinux 2021.2 installed on Ubuntu 20.04.03
Vivado 2021.2
I have only one interrupt that I connect it to the processor interrupt input.
I connect a push button to AXI GPIO 1 and configure it to generate interrupt.
Based on my pl-DTS file
/ {
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_0: gpio#41200000 {
#gpio-cells = <3>;
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";
gpio-controller ;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x1>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x4>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x0>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
axi_gpio_1: gpio#41210000 {
#gpio-cells = <3>;
#interrupt-cells = <2>;
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";
gpio-controller;
interrupt-controller;
interrupt-names = "ip2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x41210000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x1>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
};
The interrupt should be at 61 (29 + 32 = 61): interrupts = <0 29 4>;.
I enable the interrupt enable register and global interrupt register.
I want to write a kernel module and register the axi-gpio interrupt in that and register a ISR to handle it. the main part of my code is as follows:
if (request_irq(IRQ_NUM, isr, 0, DEVICE_NAME, NULL)) {
printk(KERN_ERR "my_init: Cannot register IRQ %d\n", IRQ_NUM);
return -EIO;
} else {
printk(KERN_INFO "my_init: Registered IRQ %d\n", IRQ_NUM);
}
printk(KERN_INFO "my_init: Initialize Module \"%s\"\n", DEVICE_NAME);
pdev = platform_device_register_simple(DEVICE_NAME, 0, NULL, 0);
if (pdev == NULL) {
printk(KERN_WARNING "my_init: Adding platform device \"%s\" failed\n", DEVICE_NAME);
kfree(pdev);
return -ENODEV;
}
PROBLEMS:
Interrupt 61 could not be registered.
In fact, I found "all free interrupts" and tried "all" with my axi-gpio. None of them works correctly.
I do not want to use GPIO-keys or UIO because they need a blocking read BUT I want to write a kernel module and register the axi-gpio interrupt in that by interrupt request function (request_irq()) and register a ISR for it.
I searched and read all previous problems in the Xilinx and other venders. I tried all solutions but I cannot find anything.
BTW:
The connection between push button and processor is okay because I could activate it by the GPIO-keys. GPIO-keys interrupt catches the push button actions.

Related

Why MaxTouch amtel_mxt_ts driver can't register interrupt on Yocto BeagleBone am335x?

I need to integrate a Maxtouch Touchpanel (atmel_mxt_ts) Driver to an BeagleBoneBlack based Yocto Dunfell 3.1 with linux-ti-staging Kernel 5.4 system. The driver is set as loadable kernel module. The Yocto project integrates meta-ti and meta-arm from the dunfell branches.
The Interrupt should use gpio0[30] at address 0x870 on the BeagleBones P9 header. For that I set the mode ofthe gpio to 7.
I wrote a DTS to the sources and add the resulting DTB it to be loaded at startup. So far everything is working. The DTB is created and loaded during boot up.
The only problem is that when the driver is loaded by the kernel it complains about to be unable to register the interrupt.
[ 2.823173] atmel_mxt_ts 1-004a: Failed to register interrupt
[ 3.040633] atmel_mxt_ts: probe of 1-004a failed with error -22
Can anyone explain what I'm doing wrong?
This is my main DTS file
/*
* Copyright (C) 2015 Jumpnow Technologies, LLC - http://jumpnowtek.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/;
#include "am33xx.dtsi"
#include "am335x-bone-common.dtsi"
#include "bbb-i2c1.dtsi"
#include "bbb-dcan1.dtsi"
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am33xx";
};
&ldo3_reg {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
&mmc1 {
vmmc-supply = <&vmmcsd_fixed>;
};
&mmc2 {
vmmc-supply = <&vmmcsd_fixed>;
pinctrl-names = "default";
pinctrl-0 = <&emmc_pins>;
bus-width = <8>;
status = "okay";
};
&am33xx_pinmux {
touch_pins: touch_pins {
pinctrl-single,pins = <
AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLDOWN, MUX_MODE7) /* P9.11 0x870 Touch IRQ */
AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* P9.30 0x998 Touch RST */
>;
};
can_pins: can_pins {
pinctrl-single,pins = <
AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT_PULLUP, MUX_MODE2) /* P9.24 0x984 CAN rx */
AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_OUTPUT_PULLUP, MUX_MODE2) /* P9.26 0x980 CAN tx */
>;
};
};
&dcan0 {
status = "disabled";
};
&rtc {
system-power-controller;
};
&i2c1 {
status = "okay";
pinctrl-names = "default";
clock-frequency = <100000>;
atmel_mxt_ts#4a {
status = "okay";
compatible = "atmel,atmel_mxt_ts";
reg = <0x4a>;
interrupt-parent = <&gpio0>; /* P9.11 gpio0
interrupts = <30>; /* gpio0[30], Falling edge only 0x02*/
pinctrl-names = "default";
pinctrl-0 = <&touch_pins>;
};
};
&i2c2 {
status = "okay";
};
&dcan1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&can_pins>;
};
and this is the content of bbb-i2c1.dtsi
&am33xx_pinmux {
i2c1_pins: i2c1_pins {
pinctrl-single,pins = <
AM33XX_IOPAD(0x958, SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE2) /* P9.18, i2c1_sda */
AM33XX_IOPAD(0x95c, SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE2) /* P9.17, i2c1_scl */
>;
};
};
&i2c1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
};
and this is the content of bbb-dcan1.dtsi
&am33xx_pinmux {
dcan1_pins: dcan1_pins {
pinctrl-single,pins = <
AM33XX_IOPAD(0x984, PIN_INPUT_PULLUP | MUX_MODE2) /* P9.24, ddcan1_rx */
AM33XX_IOPAD(0x980, PIN_OUTPUT_PULLUP | MUX_MODE2) /* P9.26, ddcan1_tx */
>;
};
};
&dcan1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&dcan1_pins>;
};
Thanks for your support
Found the bug by myself. It was a missing closing command tag here
interrupt-parent = <&gpio0>; /* P9.11 gpio0
I opened a multiline comment without closing and all included attributes were not set

I cannot access STM32 DMA register when configuring it

I currently use standard peripheral library to write a driver that enable memory to USART6_TX DMA transfer on a STM32 F407 ZGT6 chip. However, I tried for a long time but the initialization keeps on failing: DMA_GetCmdStatus always returns DISABLE. By using GDB, I found that after the DMA_Init try to write configuration into DMA register, DMA CR register remains 0. The DMA initialize code and execution are as follow:
void DMA_USART6_Init(char* DMA_Start_Pos, uint32_t DMA_Buffer_Size){
DMA_Buffer_Size_GV = DMA_Buffer_Size;
DMA_Start_Pos_GV = DMA_Start_Pos;
/*RCC config*/
RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);
/*DMA init*/
//DMA_DeInit(DMA2_Stream7);
DMA_Cmd(DMA2_Stream7, DISABLE);
while ((DMA_GetCmdStatus(DMA2_Stream7) == ENABLE)){}
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_Channel = DMA_Channel_5;
DMA_InitStruct.DMA_PeripheralBaseAddr = USART6_BASE + 0x04;//(uint32_t)&USART6->DR;
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t) dubuff;//(uint32_t) DMA_Start_Pos;
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStruct.DMA_BufferSize = (uint16_t)sizeof(dubuff);//DMA_Buffer_Size;
printf("buffer size should be %d \r\n", (uint16_t)sizeof(dubuff));
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_Init(DMA2_Stream7, &DMA_InitStruct);
printf("data counter after init %d \r\n", DMA_GetCurrDataCounter(DMA2_Stream7));
}
for the ENABLE code
void DMA_USART6_Enable_DMA(char* DMA_Start_Pos, uint32_t DMA_Buffer_Size, int MB){
Max_Buffer = MB;
USART6_init_for_DMA();
DMA_USART6_Init(DMA_Start_Pos, DMA_Buffer_Size);
DMA_USART6_NVIC_Init();
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream7, ENABLE);
while ((DMA_GetCmdStatus(DMA2_Stream7) == DISABLE)){}//program stucked in this loop
}
for the USART6 code:
void USART6_init_for_DMA(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 57600;//115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART6, &USART_InitStructure);
USART_Cmd(USART6, ENABLE);
USART_DMACmd(USART6, USART_DMAReq_Tx, ENABLE);
}
and the GDB result:
the value that should be written into DMA CR register
text in the picture 1:
367 DMAy_Streamx->CR = tmpreg;
(gdb) print tmpreg
$7 = 167904576
the DMA CR register remains 0
text in the picture2:
371 tmpreg = DMAy_Streamx->FCR;
(gdb) print *DMAy_Streamx
$10 = {CR = 0, NDTR = 0, PAR = 0, M0AR = 0, M1AR = 0, FCR = 0}
Please tell me if there is anything I can provide about my problem or things I can try further...
Note: In the same program, my USART6 and GPIO works fine.
When the desired stream is activated, the relevant registers can no longer be configured. Only in dual buffer mode can memory addresses 0 and 1 be updated according to the bit CT function. You must first deactivate the relevant channel and then apply the changes.
I am OP.
At the start, I have a feeling that it could be an obvious mistake and that's true.
It turns out that I should use RCC_AHB1PeriphClockCmd instead of RCC_AHB1PeriphResetCmd because OBVIOUSLY the later accessed RSTR (RCC Reset register) rather than ENR an thus clock is not enable (about that Codo is right).
Well, Hope guys whoever sees this question won't repeat this embarrass mistake ;(

Linux Device Tree for 2 i2c devices on same bus

I have 2 port expanders connected to the the I2C of my chip. I am able to configure one port expander from NXP and is able to toggle the gpios. But the second one is from microchip and it is not showing gpios listed as in the previous case. Please let me know how to configure this correctly.
&i2c1 {
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <100000>;
pinctrl-names = "default", "gpio";
pinctrl-0 = <&pinctrl_i2c1>;
scl-gpios = <&lsio_gpio0 xx GPIO_ACTIVE_HIGH>;
sda-gpios = <&lsio_gpio0 xx GPIO_ACTIVE_HIGH>;
status = "okay";
mcp23017: gpio#10 {
compatible = "microchip,mcp23017";
reg = <0x10>;
gpio-controller;
#gpio-cells = <2>;
};
pca6416: gpio#11 {
compatible = "ti,tca6416";
reg = <0x11>;
gpio-controller;
#gpio-cells = <2>;
};

Why does platform_get_irq fail to get irq in my mailbox-altera driver?

I'm new to do Linux driver development.
I encounter the problem of failing to get irq when I try to use driver mailbox-altera.
On my platform DTS related to mailbox is as follows:
hps_bridges: bridge#0xc0000000 {
compatible = "altr,bridge-17.1", "simple-bus";
reg = <0xc0000000 0x20000000>,
<0xff200000 0x00200000>;
reg-names = "axi_h2f", "axi_h2f_lw";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0x00000000 0x00020000 0xc0020000 0x00004000>,
<0x00000001 0x00000030 0xff200030 0x00000010>,
<0x00000001 0x00000020 0xff200020 0x00000010>,
<0x00000001 0x00000000 0xff200000 0x00000020>,
<0x00000001 0x00010000 0xff210000 0x00004000>;
...
bci_avalon_mailbox_simple_master_hps: mailbox#0x100000030 {
compatible = "altr,bci_avalon_mailbox_simple-17.0", "altr,mailbox-1.0";
reg = <0x00000001 0x00000030 0x00000010>;
interrupt-parent = <&nios2_gen2_master>;
interrupts = <11>;
#mbox-cells = <1>; /* embeddedsw.dts.params.#mbox-cells type NUMBER */
linux,mailbox-name = "bci_avalon_mailbox_simple_master_hps";
}; //end mailbox#0x100000030 (bci_avalon_mailbox_simple_master_hps)
bci_avalon_mailbox_simple_hps_master: mailbox#0x100000020 {
compatible = "altr,bci_avalon_mailbox_simple-17.0", "altr,mailbox-1.0";
reg = <0x00000001 0x00000020 0x00000010>;
interrupt-parent = <&nios2_gen2_master>;
interrupts = <12>;
#mbox-cells = <1>; /* embeddedsw.dts.params.#mbox-cells type NUMBER */
linux,mailbox-name = "bci_avalon_mailbox_simple_hps_master";
}; //end mailbox#0x100000020 (bci_avalon_mailbox_simple_hps_master)
...
}
How did I trace I fail to get irq--add logs as below:
My logs are:
#insmod mailbox-altera.ko
dev->num_resources=1.
resource[0] type=512.
start=0xff200030, size=16.
dev->num_resources=1.
resource[0] type=512.
platform_get_irq return -6.
dev->num_resources=1.
resource[0] type=512.
start=0xff200020, size=16.
dev->num_resources=1.
resource[0] type=512.
platform_get_irq return -6.
which indicates that the memory region I get in driver exactly match the description of DTS, but platform_get_irq fails, because there is only one resource(which is type of IORESOURCE_MEM ) in the mailbox device.
As DTS description, shouldn't I get the interrupts = <11> or <12> information in software when I trigger the driver? What should I do to get the irq ID I expected?

How can I use mcp2515 & device tree?

My Kernel Source writes platform data to device tree.(kernel version 3.10.9)
The board used is Exynos5422.
So I wrote mcp2515 platform data parameter to .dts
dts:
spi_1: spi#12d30000 {
status = "okay";
spi-src-clk = <0>;
num-cs = <1>;
gpios = <&gpa2 4 0>,
<&gpa2 6 0>,
<&gpa2 7 0>;
can1: mcp251x#0{
compatible ="microchip,mcp2515";
reg = <0>;
spi-max-frequency = <2000000>;
pinctrl-names = "default";
pinctr-0=<&mcp_irq>;
interrupts = < 0 0 0 >;
interrupt-parent = <&gpx1>;
irq_gpio = <&gpx1 5 0>;
reset_gpio = <&gph0 1 0>;
controller-data {
cs-gpio = <&gpa2 5 0>;
samsung,spi-feedback-delay = <0>;
};
};
};
mcp_irq: mcp_irq {
samsung,pins = "gpx1-5";
samsung,pin-function = <0xf>;
samsung,pin-pud = <3>;
samsung,pin-drv = <3>;
};
When the following assignment fails, pdata is null, resulting in fatal error below.
struct mcp251x_platform_data *pdata = spi->dev.platform_data
mcp251x.c:
if (!pdata) {
/* Platform data is required for osc freq */
goto error_out;
}
Settings:
What am I missing?