Sending enable interrupts (0xF4) command to ps2 keyboard results in system crash. OSDEV - c

I have just finished initializing the IDT and started the ps2 stuff. I got the ps2 controller initializer and now I want to enable the interrupts in the first ps2 port or the keyboard. Here is my code for initializing the ps2 controller:
uint8_t initPS2()
{
// Start by disabling devices
disableFirstPS2Port();
disableSecondPS2Port();
// Flush the input buffer
inb(PS2DATA);
// Set the state of the status register.
outb(PS2COMMAND, 0x20);
uint8_t status = inb(PS2DATA);
uint8_t dual0 = status & 0b00100000 == 0b00100000; // if dual = 0, there is no second channel
status |= 0b01000011;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// Perform self test
outb(PS2COMMAND, 0xAA);
uint8_t response = inb(PS2DATA);
if (response == 0xFC)
return 0xFC;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// test dual channel controller
uint8_t dual = 0xFF;
if (dual0 == 1)
{
outb(PS2COMMAND, 0xAE);
outb(PS2COMMAND, 0x20);
status = inb(PS2DATA);
uint8_t dual1 = status & 0b00100000 == 0b00100000;
// if dual1 == 0, dual channel exists
outb(PS2COMMAND, 0xAD);
dual = !dual1;
}
else
{
dual = 0;
}
// Perform interface tests
outb(PS2COMMAND, 0xAB);
uint8_t responseIT1 = inb(PS2DATA);
uint8_t responseIT2;
if (dual == 1)
{
outb(PS2COMMAND, 0xA9);
responseIT2 = inb(PS2DATA);
}
if (responseIT1 != 0x0)
return responseIT1;
if (responseIT2 != 0x0 && dual == 1)
return responseIT2;
// Enable devices
enableFirstPS2Port();
if (dual == 1)
enableSecondPS2Port();
writeToFirstPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
writeToSecondPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
return 0;
}
I follow the tutorial on OSDev wiki for initializing the ps2 controller.
Now I try to enable scanning on the keyboard with the 0xf4 command:
uint8_t commandKeyboard(unsigned char byt)
{
writeToFirstPS2Port(byt);
uint8_t response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
}
}
return response;
}
uint8_t initKeyboard()
{
uint8_t res = commandKeyboard(0xF4);
return res;
}
Note that the writeToFirstPS2Port and likewise other ps2 function do have a proper controllerWait function which makes sure that the data port is writable/readable.
I call this code before doing sti after I load my idt:
idt_flush(&currentIDT);
uint8_t ps2init = initPS2();
printk("PS2 Initalized, returned with 0x%x\n", ps2init);
while (1)
;
uint8_t keyboardinit = initKeyboard();
printk("PS2 Initalized, returned with 0x%x\n", keyboardinit);
outb(PIC1_DATA, 0);
outb(PIC2_DATA, 0);
//int d = 5 / 0;
asm volatile("sti");
Also here is my IRQ Handler:
void irq1_handler(interrupt_frame_t *frame)
{
inb(0x60);
default_irq_handler(frame);
}
I think the keyboard controller required for me to read 0x60 port before sending another interrupt so I thought that may help.
With this code here is what happens. I successfully initialize the IDT and then call the initializePS2 which returns with 0, meaning it succeeded. After that I start the initKeyboard call which doesn't even return let alone succeed. When I send the command to the keyboard, the system crashes. I am running this using qemu and I first thought this was a triple fault so I added -d int to see what kind of exceptions were raised but it seems that no exception is raised before the system closes itself.
Here is the github link to the project:
https://github.com/Danyy427/NEWOSDEV/tree/master/new%2064%20bit
The files are under src/Kernel/Hardware/PS2 and .../Hardware/Keyboard
The IDT is under src/Kernel/IDT
Any help is appreciated. Thank you.

First add the interrupt attribute on your interrupt handler :
void __attribute__((interrupt)) Irq1Handler(interrupt_frame* frame);
This will cause the compiler to get parameters from stack rather than getting them using __cdecl calling convention. And also performing an iret (Interrupt Return) rather than a normal ret (Return). Also add a parameter to gcc when compiling your interrupts file "-mgeneral-regs-only". This will prevent the compiler from using sse registers and generating a warning about that.

Related

C PIC32 USART_BufferAddRead lost bytes

in my project I need to connect over UART a PIC32MZ2048EFH144 and an external device. I set the USART peripheral in this way using Harmony v1.1:
CONFIG_USE_DRV_USART=y
CONFIG_DRV_USART_DRIVER_MODE="STATIC"
CONFIG_DRV_USART_INTERRUPT_MODE=y
CONFIG_DRV_USART_BYTE_MODEL_SUPPORT=n
CONFIG_DRV_USART_READ_WRITE_MODEL_SUPPORT=n
CONFIG_DRV_USART_BUFFER_QUEUE_SUPPORT=y
CONFIG_DRV_USART_SUPPORT_TRANSMIT_DMA=n
CONFIG_DRV_USART_SUPPORT_RECEIVE_DMA=n
CONFIG_DRV_USART_INSTANCES_NUMBER=2
CONFIG_DRV_USART_CLIENTS_NUMBER=2
CONFIG_DRV_USART_PERIPHERAL_ID_IDX1="USART_ID_4"
CONFIG_DRV_USART_BAUD_RATE_IDX1=9600
CONFIG_DRV_USART_XMIT_INT_PRIORITY_IDX1="INT_PRIORITY_LEVEL1"
CONFIG_DRV_USART_XMIT_INT_SUB_PRIORITY_IDX1="INT_SUBPRIORITY_LEVEL0"
CONFIG_DRV_USART_RCV_INT_PRIORITY_IDX1="INT_PRIORITY_LEVEL1"
CONFIG_DRV_USART_RCV_INT_SUB_PRIORITY_IDX1="INT_SUBPRIORITY_LEVEL0"
CONFIG_DRV_USART_ERR_INT_PRIORITY_IDX1="INT_PRIORITY_LEVEL1"
CONFIG_DRV_USART_ERR_INT_SUB_PRIORITY_IDX1="INT_SUBPRIORITY_LEVEL0"
CONFIG_DRV_USART_OPER_MODE_IDX1="DRV_USART_OPERATION_MODE_NORMAL"
CONFIG_DRV_USART_INIT_FLAG_WAKE_ON_START_IDX1=n
CONFIG_DRV_USART_INIT_FLAG_AUTO_BAUD_IDX1=n
CONFIG_DRV_USART_INIT_FLAG_STOP_IN_IDLE_IDX1=n
CONFIG_DRV_USART_LINE_CNTRL_IDX1="DRV_USART_LINE_CONTROL_8NONE1"
CONFIG_DRV_USART_HANDSHAKE_MODE_IDX1="DRV_USART_HANDSHAKE_NONE"
CONFIG_DRV_USART_XMIT_QUEUE_SIZE_IDX1=10
CONFIG_DRV_USART_RCV_QUEUE_SIZE_IDX1=10
CONFIG_DRV_USART_STATIC_RX_MODES_IDX1="USART_HANDSHAKE_MODE_FLOW_CONTROL"
CONFIG_DRV_USART_STATIC_OP_MODES_IDX1="USART_ENABLE_TX_RX_USED"
CONFIG_DRV_USART_STATIC_LINECONTROL_MODES_IDX1="USART_8N1"
CONFIG_DRV_USART_STATIC_TX_ENABLE_IDX1=y
CONFIG_DRV_USART_STATIC_RX_ENABLE_IDX1=y
CONFIG_DRV_USART_STATIC_TX_INTR_MODES_IDX1="USART_TRANSMIT_FIFO_NOT_FULL"
CONFIG_DRV_USART_STATIC_RX_INTR_MODES_IDX1="USART_RECEIVE_FIFO_ONE_CHAR"
At the start, the external device sends a wake-up message that I receive correctly and reply to with a wake-up ack.
After this sequence, I send a requestDevID ... at this point I have some trouble. Through the oscilloscope, I can see that the external device answers correctly to this request but in my code I don't get the whole message.
The answer is: 0xFA 0xFF 0x01 0x04 0x07 0x78 0x26 0x1A 0x3D
What I see (when I stop the debug) is: 0x78 0x26 0x1A 0x3D 0x07
My code is as follow:
void mti710USART1BufferHandler(DRV_USART_BUFFER_EVENT bufferEvent, DRV_USART_BUFFER_HANDLE hBufferEvent, uintptr_t context){
switch(bufferEvent){
case DRV_USART_BUFFER_EVENT_COMPLETE:{
if (context == 1){
// to-do
flag = 1;
countFlag += 1;
}
break;
case DRV_USART_BUFFER_EVENT_ERROR:{
if (context == 1){
// to-do
flag = -1;
}
}
break;
case DRV_USART_BUFFER_EVENT_ABORT:
break;
default:
break;
}
}
bool mti710Initialize(MTi710INSInitDriver_t mti710Init){
mtiData.dvrMTi710INSIndex = mti710Init.dvrMTi710INSIndex;
mtiData.drvUART.handle = DRV_HANDLE_INVALID;
mtiData.drvUART.baudrate = 115200;
memset(&mtiData.drvUART.TX.buffer, 0x00, sizeof(mtiData.drvUART.TX.buffer));
memset(&mtiData.drvUART.RX.buffer, 0x00, sizeof(mtiData.drvUART.RX.buffer));
mtiData.drvUART.handle = DRV_USART_Open(mtiData.dvrMTi710INSIndex, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if (mtiData.drvUART.handle != DRV_HANDLE_INVALID) {
if (mti710SetBaudrate(mtiData.drvUART.handle, mtiData.drvUART.baudrate)){
DRV_USART_BufferEventHandlerSet(mtiData.drvUART.handle, mti710USART1BufferHandler, (uintptr_t) 1);
mtiData.drvMti710INSState = MTi710INS_STATE_IDLE;
return true;
}
}
return false;
}
static void UARTRead(uint8_t* readBuffer, uint32_t bufferSize){
USART_ReceiverOverrunErrorClear_Default(DRV_USART_INDEX_1);
while (DRV_USART_ClientStatus(mtiData.drvUART.handle) != DRV_USART_CLIENT_STATUS_READY);
DRV_USART_BufferAddRead(mtiData.drvUART.handle, &(mtiData.drvUART.RX.bufferHandle), readBuffer, bufferSize);
}
...
...
uint32_t mti710ReadDeviceID(struct XbusParser* parser, const uint32_t timeout_us){
struct XbusMessage requestId = {XMID_ReqDid, 0, NULL};
mtiData.drvMti710INSState = MTi710INS_REQ_READ_ID;
if (flag == 0){
mti710WriteData(&requestId);
flag = 0;
}
mti710ReadData(9);
if (flag == 1){
XbusParser_parseBuffer(parser, (uint8_t*)&mtiData.drvUART.RX.buffer[0], sizeof(mtiData.drvUART.RX.buffer));
flag = 0;
return parser->currentMessage.mid == XMID_DeviceId ? (uint32_t)parser->currentMessage.data : -1;
} else {
return -1;
}
}
Any idea of my mistakes?
regards,
Vincenzo.
First thing:
When you read a hardware register, like the UART RX register, from within your application code, you must disable the UART RX interrupt.
The UART RX register is a shared resource.
UART RX register is shared by the the application code, (executed in background), and the interrupt code (executed in foreground).
If you don't disable the UART RX interrupt than your application code might be interrupted in the middle of the reading and get errors.
Shared resources must be read this way:
char a;
uart__disable_rx_interrupt(); // Begin of critical section
a = UART_RX_REGISTER;
uart__enable_rx_interrupt(); // End of critical section
Yet:
The application code should not disable too frequently the UART RX interrupt because the UART interface may miss some incoming data.
It happened to me back in 1996.
The ideal code application code would be at 9600 Baudrate:
char a;
sleep(100); // 100 ms
uart__disable_rx_interrupt(); // Begin of critical section
a = UART_RX_REGISTER;
uart__enable_rx_interrupt(); // End of critical section
printf(a);
One last thing;
Often, data are placed in a ring buffer
sleep(100); // 100 ms
uart__disable_rx_interrupt(); // Begin of critical section
a = UART_RX_REGISTER;
uart__write_in_ring_buffer(a);
uart__enable_rx_interrupt(); // End of critical section
uart__write_in_ring_buffer(&ch);
printf(ch);
I suggest you to read Jean Labrosse's book: "https://www.amazon.it/Embedded-Systems-Building-Blocks-Ready/dp/0879304405"
Study the UART device driver which Jean wrote making use of ring buffer.

Getting an ACK failure right away after writing slave address in interrupt mode [STM32F401RE]

I see that once slave address is written in the interrupt handler, I get an ACK Failure right away, but when I do the same thing with polling approach, I do get an ACK and goes on to read off the values from the device.
With interrupts, I see ISR gets triggered only once (in which it writes slave address) and never gets called again. My i2c configurations are fine, but there's something with interrupts that I seem to be missing out on.
Polling approach
HAL_Status I2C_HAL_TX_IT () {
GenerateStartCondition();
// validate the completion of start condition
while (!GetFlagStatus(hi2c->Instance, I2C_SR1_SB) && HAL_Timeout(5));
// write slave address along with write bit
I2C_WriteSlaveAddress(hi2c, WRITE);
// wait for address to be sent
while (!GetFlagStatus(hi2c->Instance, I2C_SR1_ADDR) && HAL_Timeout(5));
// clear address flag
I2C_ClearADDRFlag(hi2c->Instance);
// write data to DR ...
}
With interrupts:
void HAL_StartI2CInterrupts() {
GenerateStartCondition();
// setting control bits
hi2c->Instance->CR2 |= I2C_CR2_ITBUFEN;
hi2c->Instance->CR2 |= I2C_CR2_ITEVTEN;
hi2c->Instance->CR2 |= I2C_CR2_ITERREN;
}
void I2C1_EV_IRQHandler ()
{
uint8_t event_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITEVTEN) >> I2C_CR2_ITEVTEN_Pos;
uint8_t buffer_interrupt = (hi2c->Instance->CR2 & I2C_CR2_ITBUFEN) >> I2C_CR2_ITBUFEN_Pos;
uint8_t var;
if (event_interrupt)
{
// validate the completion of START condition
var = (hi2c->Instance->SR1 & I2C_SR1_SB) >> I2C_SR1_SB_Pos;
if (var)
{
if (hi2c->I2C_State == I2C_TX_BUSY)
{
I2C_WriteSlaveAddress(hi2c, WRITE);
}
}
// check ADDR bit ...
}
}

Turning One Raspberry Pi I2C Connected Relay On Turns the Other Relay Off

Currently, I have two, 2-Channel 1 Amp SPDT Signal Relay Controllers connected to my Raspberry Pi 3 via I2C, and when I currently run the function to turn one relay on, the other one will shut off at the same time (one or the other is on). In addition, my button to shut off relay 1 and my button to shut off relay 2 will shut both relays off.
My program is written in a windows form (visual studio), and I am accessing a C Shared Library via Dll Import, but I know for a fact my problem is within my C library. I am very new to C and how shifting works, so the root of my problem lies within the logic and structure of my code. Frankly, I'm confused on how to properly code this.
This is currently the method to turn Relay 1 On. It turns the relay on properly, but this also turns Relay 2 Off at the same time.
void Relay1On() ***CURRENTLY TURNS OTHER OFF WHEN ACTIVATED***
{
// Create I2C bus
int file;
char *bus = "/dev/i2c-1";
if ((file = open(bus, O_RDWR)) < 0)
{
printf("Failed to open the bus. \n");
exit(1);
}
// Get I2C device, MCP23008 I2C address is 0x20(32)
ioctl(file, I2C_SLAVE, 0x20);
// Configure all pins of port as output (0x00)
char config[2] = {0};
config[0] = 0x00;
config[1] = 0x00;
write(file, config, 2);
//Turn the first relay on
char data = 0x01;
config[0] = 0x09;
config[1] = data;
write(file, config, 2);
}
Here is the code for Relay 1 Off, I will not post Relay 2 On/Off because it is basically the same, Relay2On just has an added data += 1; after char data = 0x01;. Both 'Off' methods result in both relays shutting off.
void Relay1Off()
{
// Create I2C bus
int file;
char *bus = "/dev/i2c-1";
if ((file = open(bus, O_RDWR)) < 0)
{
printf("Failed to open the bus. \n");
exit(1);
}
// Get I2C device, MCP23008 I2C address is 0x20(32)
ioctl(file, I2C_SLAVE, 0x20);
// Configure all pins of port as output (0x00)
char config[2] = {0};
config[0] = 0x00;
config[1] = 0x00;
write(file, config, 2);
//Turn the first relay off *****Turns all off at the moment******
char data = 0xFE;
data = (data << 1);
config[0] = 0x09;
config[1] = data;
write(file, config, 2);
}
All I want is the methods to do as described, turn Relay 1 On when the method is called. When Relay1Off is called, shut only Relay 1 off. I'm sure it is simple, but as I've stated above C is quite new to me.
Thank you in advance for any contribution.
I don't know how the fancy ioctl stuff works, but I'd try to do all initialization outside of this function, including setting all the GPIO's to output.
You should probably just have one function call to set/clear a relay. I'd do something like this to start:
void RelayOnOff(unsigned char relay, unsigned char enable)
{
//Init to all off
static unsigned char data = 0x00;
...
if (enable){
data |= ( 1 << relay );
}
else{
data &= ~( 1 << relay );
}
config[0] = 0x09;
config[1] = data;
write(file, config, 2);
}
You pass in what relay you want to control, and a boolean value for enable/disable. If you make the data variable static, it'll "remember" that value from function call to function call. The enable/disable sets/clears the bit for whatever relay you pass in (0-7).

STM32 SPI communication

I am trying SPI communication using STM32F411VE. I am getting some issues with SPI communication. I am following the transmit and receive from this particular code
http://www.handsonembedded.com/stm32f103-spl-tutorial-5/
I am using SPI4. an my Initialization of SPI is like this:
/* SPI4 init function */
void MX_SPI4_Init(void)
{
hspi4.Instance = SPI4;
hspi4.Init.Mode = SPI_MODE_MASTER;
hspi4.Init.Direction = SPI_DIRECTION_2LINES;
hspi4.Init.DataSize = SPI_DATASIZE_8BIT;
hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi4.Init.NSS = SPI_NSS_SOFT;
hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi4.Init.TIMode = SPI_TIMODE_DISABLE;
hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi4.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
Here is the code:
uint8_t SPISend(uint8_t data)
{
SPI4->DR = data;
while (!(SPI4->SR & SPI_SR_TXE));
while (!(SPI4->SR & SPI_SR_RXNE));
while (!(SPI4->SR & SPI_SR_BSY));
return(SPI4->DR);
}
I this particular SPISend() method the control never reaches to the end. It's stuck in the loop while (!(SPI4->SR & SPI_SR_TXE));.
My fuction call is like this:
void write8(char reg, unsigned char data)
{
uint8_t retVal;
enableChip;
reg|=0x80;
retVal = SPISend(reg);
retVal = SPISend(data);
disableChip;
}
Any suggestions.
Off the top of my head, SPI_CR1_SPE and/or SPI_CR1_SSI should be enabled in SPI4->CR1. HAL_SPI_Init() doesn't set one or the other, but the HAL SPI transmit function does, you should do it yourself if you are using your own transmit code.
UPDATE
If you are enabling the SPE and SSI bits in enableChip, it'll be never called, because it's not a function call without the parentheses.
These two lines
SPI4->DR = data;
while (!(SPI4->SR & SPI_SR_TXE));
are in the wrong order. You wait first for TXE != 0, which means that the transmitter queue is ready for more data, and then write the data.
The second line here
while (!(SPI4->SR & SPI_SR_RXNE));
while (!(SPI4->SR & SPI_SR_BSY));
will likely be an endless loop, because by the time SPI_SR_RXNE becomes 1, or shortly thereafter, SPI_SR_BSY will change from 1 to 0.

Linux 4.5 GPIO Interrupt Through Devicetree on Xilinx Zynq Platform

I am using a custom development board with a Zynq XC72010 used to run a Linux 4.5 kernel. I am developing a device driver for a chip we are testing in house and I am having a lot of issues trying to bind a GPIO line to a software IRQ. So far I have tried a few methods and exhausted any google search I can think of. The relevant parts of my devicetree configuration:
/ {
compatible = "xlnx,zynq-7000";
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
ranges;
intc: interrupt-controller#f8f01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xF8F01000 0x1000>,
<0xF8F00100 0x100>;
};
i2c0: i2c#e0004000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <&clkc 38>;
interrupt-parent = <&intc>;
interrupts = <0 25 4>;
reg = <0xe0004000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
// I WANT INTERRUPT TO TRIGGER
// ON THIS DEVICE (axi_gpio_0, pin 2)
device: device#48 {
compatible = "device,name";
reg = <0x48>;
reset-gpios = <&axi_gpio_0 1 0>;
interrupt-parent = <&axi_gpio_0>;
interrupt-gpios = <&axi_gpio_0 2 0>;
};
};
};
amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_0: gpio#41200000 {
#gpio-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller;
interrupt-parent = <&intc>;
interrupts = <0 31 4>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x0>;
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 = <0x10>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
I am trying to assign an interrupt to pin 2 of 'axi_gpio_0' inside of 'device'.
Browsing google yielded 3 common methods for binding the interrupt in driver code:
/* Method 1 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_IN);
if(IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
printk("device: Interrupt GPIO = %d\n",desc_to_gpio(device->interrupt_gpio));
irq = gpiod_to_irq(device->interrupt_gpio);
printk("device: IRQ = %d\n",irq);
ret = devm_request_threaded_irq(&i2c_client->dev, irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
/* Method 2 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_ASIS);
if (IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
if (device->interrupt_gpio) {
dev_info(&i2c_client->dev, "Found interrupt GPIO: %d\n",desc_to_gpio(device->interrupt_gpio));
dev_info(&i2c_client->dev, "IRQ Number: %d\n",gpiod_to_irq(device->interrupt_gpio));
gpio_request(desc_to_gpio(device->interrupt_gpio), "DEVICE_INT"); // Request a GPIO pin from the driver
gpio_direction_input(desc_to_gpio(device->interrupt_gpio)); // Set GPIO as input
gpio_set_debounce(desc_to_gpio(device->interrupt_gpio), 50); // Set a 50ms debounce, adjust to your needs
gpio_export(desc_to_gpio(device->interrupt_gpio), false); // The GPIO will appear in /sys/class/gpio
ret = request_irq(gpiod_to_irq(device->interrupt_gpio), // requested interrupt
(irq_handler_t) irqHandler, // pointer to handler function
IRQF_TRIGGER_RISING, // interrupt mode flag
"DEVICE_IRQ_HANDLER", // used in /proc/interrupts
NULL); // the *dev_id shared interrupt lines, NULL is okay
if (ret != 0) {
dev_err(&i2c_client->dev,
"Failed to request IRQ: %d\n", ret);
}
}
else {
dev_err(&i2c_client->dev, "Failed to get interrupt GPIO pin\n");
}
/* Method 3 */
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
I tried combinations of all of these methods and various devicetree configurations, but none of them achieved the functionality I need.
Method 1 results in this output:
device: Interrupt GPIO = 892
device: IRQ = -6
device 0-0048: Failed to request IRQ: -22
Method 2 results in this output:
device 0-0048: Found interrupt GPIO: 892
device 0-0048: IRQ Number: -6
device 0-0048: Failed to request IRQ: -22
So, trying to use the descriptor GPIO and the old GPIO api's are both unsuccessful in binding an interrupt.
To try method 3, I tweaked the devicetree:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>; // or <&intc>?
interrupts = <0 2 0x02>; // trying to grab pin 2
};
Method 3 results in this output:
genirq: Setting trigger mode 2 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: IRQ requested: 168
genirq: Setting trigger mode 8 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: Failed to request IRQ: -22
It seems the problem is assigning a software interrupt to a specific GPIO in Linux. I don't see what I'm missing here. Any advice is appreciated.
EDIT 1:
I found out that Linux doesn't like low-level interrupts for whatever reason. Changing method 3 to:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>;
interrupts = <0 2 0x04>;
};
And driver code to:
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
allows me to successfully request an IRQ. However, my signal is active low, so this doesn't really help me much. Also, I'm not sure that this method is referencing axi_gpio_0 pin 2 as the interrupt signal. I can use both intc and axi_gpio_0 as interrupt-parent and it maps to the same IRQ number (I see this from cat /proc/interrupts). So, ignoring the polarity of the signal, how do I make sure that my registered interrupt is triggered based on the toggling of axi_gpio_0 pin 2?
EDIT 2:
I traced the issue with requesting an active-low interrupt to the driver for the interrupt controller: kernel/drivers/irqchip/irq-gic.c. This section of code is what causes the problem:
static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
return -EINVAL;
/* SPIs have restrictions on the supported types */
if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
return gic_configure_irq(gicirq, type, base, NULL);
}
Hacking the kernel is not at all what I want to do, but commenting out:
/* SPIs have restrictions on the supported types */
/*if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;*/
allows me to request an active-low interrupt. For testing purposes, this should work temporarily.
UPDATE:
I have successfully created an IRQ on a GPIO pin. My problem was with the GPIO controller I was using. The controller was a Xilinx IP block inside of the Zynq Programmable Logic block and this controller is unable to trigger interrupts on GPIO pins (for reasons unknown to me). I soldered the interrupt pin on the board I was working on to a GPIO pin on a different, more generic controller and now Linux is playing nicely with me.
To summarize, a GPIO controller that matches compatible = "xlnx,xps-gpio-1.00.a"; is unable to bind to software interrupts in Linux. If you have this problem, USE A DIFFERENT GPIO CONTROLLER.
Thanks everyone for the help.
Using the device tree node of method 3, you should be able to retrieve the IRQ using irq_of_parse_and_map(i2c_client->dev.of_node, 0).
The retrieved IRQ can then be requested as you have done it with devm_request_threaded_irq().

Resources