PCI Express interrupts in driver - linux-device-driver

Hello iam developing PCIe communication between Xilinx FPGA and Intel PC...
I have written a kernel module(linux driver), i am using INTx interrupts.
I am facing the problem in interrupt handling....
Before loading kernel module:
from lspci: INT A-->11
from config read : INT A-->11
from /proc/interrupts : Nothing because irq not registerd
After loading kernel module:
from lspci: INT A-->16
from config read : INT A-->11
from /proc/interrupts : INT 11 registerd
When i run the program in FPGA it was sending interrupt to IRQ-16 and saying no body cared and it was disabled.
in my module_init:
request_irq(dev->gIrq, XPCIe_IRQHandler, IRQF_SHARED, gDrvrName, gDev));
My irq handler:
static irqreturn_t XPCIe_IRQHandler(int irq, void *dev_id, struct pt_regs *regs)
{ return IRQ_HANDLED; }
So anybody can say what the problem may be....

You don't show where your dev->gIrq is set from, but your kernel module should be taking the interrupt number from the struct pci_dev associated with your device. See this comment in include/linux/pci.h:
struct pci_dev {
...
/*
* Instead of touching interrupt line and base address registers
* directly, use the values stored here. They might be different!
*/
unsigned int irq;

Yeah Gil,
thanks for your reply..
in the code
request_irq(dev->gIrq, XPCIe_IRQHandler, IRQF_SHARED, gDrvrName, gDev));
dev->gIrq is nothing but which i have taken from the pci_dev struct only.
and
Andy iam not using any MSI or MSI-X interrupts to use pci_alloc_irq_vectors().

Related

I2C transmit with DMA and HAL not working

This seems to be a problem that is somewhat common, but I have been unsuccessful with any of the solutions I have found online. Specifically I am trying to transmit a 1024 byte buffer (full 128x64 px image) to a SSD1306 display via I2C/DMA and the HAL generated in cubeIDE. I am using a STML432 nucleo board. I have no problem transmitting the buffer without DMA using HAL_I2C_Mem_Write
Based on other questions I have seen, the problem lies in the fact that the DMA finishes while the I2C bus is still working on the transmit. I just don't know how to remedy this and the examples given usually don't use the HAL (unfortunately, despite my efforts I am not quite competent to correctly apply them to the HAL myself I guess). I have tried using the interrupts for I2c and DMA with no luck, only about the first 254 bytes get transferred (just shy of two rows showing on the screen).
Here is my code for sending the buffer:
static void ssd1306_WriteMData_DMA(const uint8_t *data, uint16_t size)
{
while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY);
HAL_I2C_Mem_Write_DMA(&hi2c1, I2C_ADDR, SSD1306_REG_MDAT, 1, (uint8_t*)data, size);
}
and the code for each interrupt handler:
void I2C1_EV_IRQHandler(void)
{
/* USER CODE BEGIN I2C1_EV_IRQn 0 */
if(I2C1->ISR & I2C_ISR_TCR){
I2C1->CR2 |= (I2C_CR2_STOP);// stop i2c
I2C1->ICR |= (I2C_ICR_STOPCF);// Reset the ICR flag.
// stop DMA
DMA1->IFCR |= DMA_IFCR_CTCIF6;
// clear flag
DMA1_Channel6->CCR &= ~DMA_CCR_EN;
}
/* USER CODE END I2C1_EV_IRQn 0 */
//HAL_I2C_EV_IRQHandler(&hi2c1);
/* USER CODE BEGIN I2C1_EV_IRQn 1 */
/* USER CODE END I2C1_EV_IRQn 1 */
}
void DMA1_Channel6_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
// stop DMA
DMA1->IFCR |= DMA_IFCR_CTCIF6;
// clear flag
DMA1_Channel6->CCR &= ~DMA_CCR_EN;
/* USER CODE END DMA1_Channel6_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_i2c1_tx);
/* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
/* USER CODE END DMA1_Channel6_IRQn 1 */
}
I think that is all the pertinent code, let me know if there is something else I am missing. All of the initialization code for the peripherals was done through cubeMX, but I can post that if need be, or the settings. I feel like it is something really simple that I'm missing, but this is a bit over my head to be honest so I don't quite grasp exactly what's going on...
Thanks for any help!
Problem is in your custom DMA1_Channel6_IRQHandler and I2C1_EV_IRQHandler. Those functions will be called right after I2C transfers 255 bytes, which is MAX_NBYTE_SIZE for NBYTES. HAL already have all required interrupt routines inside stm32l4xx_hal_i2c.c:
Sets I2C transfer IRQ handler to I2C_Master_ISR_DMA;
Checks if data size is larger than 255 bytes and uses reload mode.
Sets I2C DMA complete callback to I2C_DMAMasterTransmitCplt;
Starts DMA using HAL_DMA_Start_IT()
Configures I2C registers using I2C_TransferConfig()
HAL driver will handle all I2C+DMA interrupts using I2C_Master_ISR_DMA and I2C_DMAMasterTransmitCplt:
I2C_DMAMasterTransmitCplt will restart DMA for each chunk of 255 (MAX_NBYTE_SIZE) or less bytes.
I2C_Master_ISR_DMA will reset RELOAD/NBYTES registers using I2C_TransferConfig.
For last block of data I2C_AUTOEND_MODE is used.
So all you need is
remove "user code" from DMA1_Channel6_IRQHandler and I2C1_EV_IRQHandler functions
enable I2C1 event interrupt in STM32 Device Configuration Tool
configure DMA with data width byte/byte
perform a single call of HAL_I2C_Mem_Write_DMA(...) to start transfer
check HAL_I2C_STATE_READY before next transfer
See HAL_I2C_Mem_Write_DMA, I2C_Master_ISR_DMA and I2C_DMAMasterTransmitCplt source code in stm32l4xx_hal_i2c.c to understand how it works.
About why DMA finishes while I2C is still working: HAL driver sends I2C data over DMA using 255 byte chunks, stops DMA, starts DMA, clears I2C_CR2 NBYTES/RELOAD, enables DMA. DMA may be run continuously using DMA_CIRCULAR mode, but currently it is not implemented in HAL I2C drivers. Here is example of using I2C with DMA_CIRCULAR mode:
// DMA enabled single time
hi2c1.hdmatx->XferCpltCallback = MY_I2C_DMAMasterTransmitCplt;
HAL_DMA_Start_IT(hi2c1.hdmatx, (uint32_t)&i2cBuffer, (uint32_t)&hi2c1.Instance->TXDR, I2C_BUFFER_SIZE);
MY_I2C_TransferConfig(&hi2c1, (uint16_t)DAC_ADDR, 254, I2C_RELOAD_MODE, I2C_GENERATE_START_WRITE); // in first call using I2C_GENERATE_START_WRITE
uint32_t tmpisr = I2C_IT_TCI;
__HAL_I2C_ENABLE_IT(&hi2c1, tmpisr);
hi2c1.Instance->CR1 |= I2C_CR1_TXDMAEN;
Still need to clear I2C_CR2 NBYTES/RELOAD using MY_I2C_TransferConfig each 254 bytes (I do not use 255 to align interrupt firing to even index in array):
static HAL_StatusTypeDef MY_I2C_Master_ISR_DMA(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags, uint32_t ITSources)
{
if (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_TCR) == SET)
{
MY_I2C_TransferConfig(&hi2c1, (uint16_t)DAC_ADDR, 254, I2C_RELOAD_MODE, I2C_NO_STARTSTOP); // in repeated calls using I2C_NO_STARTSTOP
}
return HAL_OK;
}
With this approach DMA circular buffer size is not limited to 255 bytes:
#define I2C_BUFFER_SIZE 1024
uint8_t i2cBuffer[I2C_BUFFER_SIZE];
Main.c should have MY_I2C_TransferConfig() function, which is copy pasted version of private function HAL_I2C_TransferConfig() from stm32l4xx_hal_i2c.c. On earlier STM32 microcontrollers there is no NBYTES/RELOAD fields and I2C_CR2 does not need to be updated this way.
Using DMA in circular mode allows to achieve highest frame rate, you just need to fill DMA buffers in time using XferHalfCpltCallback and XferCpltCallback callbacks. Frames may be copied from larger buffer by using memcpy() or DMA MEMTOMEM transfer.
You haven't said which STM32 you are using. They have different bit definitions (because the I2C peripherals in the earlier released parts were rubbish) but it looks like you are using one of the later ones.
Basically you can find what you need in the bit definitions for the I2C registers in the reference manual. If you are setting stop before it has finished you need to look for a BUSY bit that gets cleared or BTF (byte transfer finished) bit that gets set when it is time for you to send stop.

Problem related to programing STM32 microcontroller with CAN bus

I am new to STM32 microcontrollers and CAN bus communication protocol and I am working on programing an
STM32F103xx
microcontroller.
I want to use CAN bus for transmitting data to another microcontroller from the same family.
I set up all the necessary settings but when debugging the code it gets stuck in the transmitting pending function and doesn't transmit.
I want the data to be transmitted but it is not.
I don't believe I have a problem with my hardware.
PS:
I have tried both normal mode and LOOPBACK mode for CAN handler and they both didn't work.
int main(void)
{
HAL_Init();
SystemClock_Config();
uint32_t BUTTON_0;
uint32_t BUTTON_1;
uint8_t Data_0[5] = "aaaaa";
uint8_t Data_1[5] = "ZZZZZ";
MX_GPIO_Init();
MX_CAN_Init();
if(HAL_CAN_Init(&hcan) != HAL_OK){
Error_Handler();
}
if(HAL_CAN_Start(&hcan) != HAL_OK){
Error_Handler();
}
while (1)
{
TxHeader.DLC = 5;
TxHeader.StdId = 0x65D;
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
BUTTON_0 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
BUTTON_1 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
if (BUTTON_0 == 0U){
if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, Data_0, &TxMailbox) != HAL_OK ){
Error_Handler();
}
}
if (BUTTON_1 == 0U){
if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, Data_1, &TxMailbox) != HAL_OK){
Error_Handler();
}
}
while (HAL_CAN_IsTxMessagePending(&hcan, TxMailbox));
if (BUTTON_0 && BUTTON_1 == 0U){
printf("Please Press a Button");
}
}
}
You are using
STM32CubeF1 HAL
libraries (probably through STM32CubeMX).
Please check the corresponding
User Manual
- section 9.2.1 recommends the following procedure:
Initialize the CAN low level resources by implementing the HAL_CAN_MspInit():
Enable the CAN interface clock using __HAL_RCC_CANx_CLK_ENABLE()
Configure CAN pins
Enable the clock for the CAN GPIOs
Configure CAN pins as alternate function open-drain
In case of using interrupts [...]
Initialize the CAN peripheral using HAL_CAN_Init() function.
This function resorts to HAL_CAN_MspInit() for low-level initialization.
Configure the reception filters using the following configuration functions:
HAL_CAN_ConfigFilter()
Start the CAN module using HAL_CAN_Start() function.
At this level the node is active on the bus:
it receive messages, and can send messages.
To manage messages transmission, the following Tx control functions can be used:
HAL_CAN_AddTxMessage() to request transmission of a new message.
[...]
HAL_CAN_IsTxMessagePending() to check if a message is pending in a Tx mailbox.
[...]
When a message is received into the CAN Rx FIFOs,
it can be retrieved using the HAL_CAN_GetRxMessage() function.
The function HAL_CAN_GetRxFifoFillLevel() allows to know how many Rx message
are stored in the Rx Fifo.
Calling the HAL_CAN_Stop() function stops the CAN module.
The deinitialization is achieved with HAL_CAN_DeInit() function.
[...]
Polling mode operation / Transmission:
Monitor the Tx mailboxes availability until at least one Tx mailbox is free,
using HAL_CAN_GetTxMailboxesFreeLevel().
Then request transmission of a message using HAL_CAN_AddTxMessage().
Your code sample doesn't show the sub-functions called from main() so you have to check yourself :-) that
CAN/GPIO clocks have been enabled before the corresponding registers are assigned.
GPIO pins are configured as recommended.
Another thought - could it be that you have to check HAL_CAN_GetTxMailboxesFreeLevel() after starting the CAN, even before adding the first message for transmission?
Steps (2.), (4.), (5.) are already taken care of by your code, and
steps (3.), (6.), (7.), (8.) are not related to your problem (but only to reception / deinit).
If you don't want to do all the manual work yourself, you can also use one of the following tools as a starting point.
Both tools are far from perfect (and some of our StackOverflow peers disagree to recommend them at all), but often they already provide a basic structure with most of the relevant steps you need:
The firmware example collection (see their
Application Note
for details).
Code generator
STM32CubeMX

stm32 and external flash (w25q) connection problem

I want to read/write from external flash (Winbond W25Q16BV) with STM32 micro (stm32F030F4). but running process halt on 'HAL_SPI_Init()' function.
I checked the debug process, and found HAL_SPI_STATE_BUSY.
but i don't know why?
I am using STM32CubeMX to generate main project and Keil IDE to write and debug.
SPI_HandleTypeDef hspi1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
uint8_t spiData[2];
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_FATFS_Init();
SPI_HandleTypeDef my_hspi;
HAL_SPI_Init(&my_hspi);
HAL_FLASH_Unlock();
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET); // CS to HIGH
HAL_Delay(10);
//Read data
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET); // CS to low
spiData[0]=0x05;
//transmit register address
HAL_SPI_Transmit(&my_hspi,spiData,1,10);
//read
HAL_SPI_Receive(&my_hspi,&spiData[1],1,10);
...
Here is our schematic:
Unfortunately, I did not find a good example/instruction of how to use external SPI libraries. Any help in this problem is highly appreciated.
I am not able to comment on the software, but according to your comment you want to enable the reading and writing of the flash.
The Write Protect (/WP) pin can be used to prevent the Status Register from being written.
The /WP pin is active low (GND). (Write disable)
The /WP pin is inactive high (VCC). (Write enable)
Its design only allows reading data.
If you want to read and write data, /WP must be connected to Vcc.
You have not set any parameters for the my_hspi struct so your HAL driver doesn't know what he has to do.
Look at the definition of the struct. There are a lot of comments what the different struct elements are used for. For initialization the my_hspi.init part will be most interesting.
Also you have to the the my_hspi.Instance to the desired SPI Channel.
You can generate an example configuration using the free STM32 Cube Mx Software.

STM32L073RZ (rev Z) IAP jump to bootloader (system memory)

I use the STM32L073RZ (Nucleo 64 board).
I would like to jump into the system memory in application programming (IAP).
My code works on the revision B of the STM32L073 microcontroller but fails on the latest revision, rev Z.
I read the errata sheet, no details are given, just a limitation fixed on the dual boot mechanism into system memory according to the BFB2 bit.
Is the system memory no longer supports an IAP jumping to execute its code (to flash firmwares through USB or UART without using the BOOT0 pin) ?
The function is the first line of my main program, it tests if the code has to jump to the booloader:
void jumpBootLoader(void)
{
/* to do jump? */
if ( *((unsigned long *)0x20003FF0) == 0xDEADBEEF )
{
/* erase the label */
*((unsigned long *)0x20003FF0) = 0xCAFEFEED;
/* set stack pointer to the bootloader start address */
__set_MSP(*((uint32_t*)(0x1FF00000)));
/* system memory mapped at 0x00000000 */
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* jump to #bootloader + 4 */
((void (*)(void))(*((uint32_t*)(0x1FF00004))))();
}
}
I call these two lines as soon as the BP1 button is pressed to trig the jump operation after resetting the µC:
*((unsigned long *)0x20003FF0) = 0xDEADBEEF;
NVIC_SystemReset();
I use the HSI 16Mhz clock source.
The solution is to jump twice to the system memory.
First Jump to bootloader startup to initialize Data in RAM until the Program counter will returned to Flash by the Dualbank management.
Second Jump: Jump to the Dualbank bypassed address
How to use: User has first to initialize a variable “ Data_Address” (must be an offset Flash sector aligned address) in Flash to distinguish between first/second Jump.
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Data_Address;
EraseInitStruct.NbPages = 1;
First_jump = *(__IO uint32_t *)(Data_Address);
if (First_jump == 0) {
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Data_Address, 0xAAAAAAAA);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = *(__IO uint32_t *)(0x1FF00004);
}
if (First_jump != 0) {
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
/* Reinitialize the Stack pointer and jump to application address */
JumpAddress = (0x1FF00369);
}
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t *)(0x1FF00000));
Jump_To_Application();
First important thing: you use 0x1FF0 0000 as the addres where SP is stored, this is correct. Then you use 0x1 FF00 0004 as the address from which you load the function pointer. This is not correct - one zero too many.
Note that using __set_MSP() is generally not such a good idea if you also use MSP as your stack pointer (which you most likely are). The recent definition of this function, which marks "sp" as clobbered register, causes your change to be reverted almost immediately. Incidentally today I was doing exactly the same thing you are doing and I've found that problem. In your assembly listing you'll see that SP is saved into some other register before the msr msp, ... instruction and restored right after that.
Finally I wrote that manually (STM32F4, so different addresses):
constexpr uint32_t systemMemoryBase {0x1fff0000};
asm volatile
(
" msr msp, %[sp] \n"
" bx %[pc] \n"
:: [sp] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase)),
[pc] "r" (*reinterpret_cast<const uint32_t*>(systemMemoryBase + 4))
);
BTW - you don't need to set memory remap for the bootloader to work.
Thanks for your help. I have my answer !
The v4.0 bootloader (initial version) does not implement the dual bank mechanism but this feature is supported by v4.1.
Software can jump to bootloader but it will execute the dual boot mechanism.
So the bootloader goes back to bank1 (or bank2 if a code is "valid").
Today it is not possible to bypass the dual bank mechanism to execute the bootloader with my configuration:
The boot0 pin is reset and the protection level is 0 (see "Table 11. Boot pin and BFB2 bit configuration" in the reference manual).
Where is your program counter when you call __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH()?
Remapping a memory region while you're executing out of that same region will end poorly! You may need to relocate this code into SRAM, or execute this code with PC set to the fixed FLASH memory mapping (0x0800xxxx).

GPIO IRQ on ARM based Embedded Linux

I'm trying to program an GPIO IRQ on AT91SAM9M10-EKES evaluation board.
I successfully registered the IRQ, and the IRQ is working.
However, some interrupts are missed. I'm sending 26, and I get only 22.
The code:
static irqreturn_t wiegand_interrupt(int irq, void *dev_id){
atomic_inc(&counter);
printk(KERN_WARNING "IRQ recieved, counting... %d\n",atomic_read(&counter));
return 0;
}
irq1 = gpio_to_irq(AT91_PIN_PA21);
if (irq1 < 0) {
err = irq1;
printk("Unable to get irq number for GPIO %d, error %d\n",AT91_PIN_PA21, err);
goto fail;
}
err = request_irq(irq1,wiegand_interrupt,0 ,"wiegand",NULL);
irq2 = gpio_to_irq(AT91_PIN_PA20);
if (irq2 < 0) {
err = irq2;
printk("Unable to get irq number for GPIO %d, error %d\n",AT91_PIN_PA21, err);
goto fail;
}
err = request_irq(irq2,wiegand_interrupt,0 ,"wiegand",NULL);
This is not the whole driver, but this is the actual part that deals with the IRQ.
If someone see a problem in the code, or can suggest a way to know why I lose 4 interrupts, please reply. I'm stuck on this for hours... :(
Thanks.
Ramon.
I assume you are triggering your interrupts with an external system (maybe a microcontroller or something that can toggle the GPIOS). Since I do not see a real ack of the interrupt, I assume the external system does not wait for the interrupt to be handled to maybe trigger a new one.
printk is a very slow function and that's why you can miss some interrupts: a new one can be triggered while you are still handling the previous one.
So I would advise not to use printk in the handler. If you want to achieve something like this, it would be better to use a tasklet or a workqueue as the bottom half of the interrupt handler.
I can only recommend the reading of the Chapter 10 of Linux Device Drivers.
Oh and by the way, your IRQ handler should not return 0 but IRQ_HANDLED.
Ok, actually, the problem is that I used the GPIO pins, while the GPIO pins don't support IRQF_TRIGGER_FALLING flag, which is exactly what I need. so probably, the interrupt handler doesn't recognize the signal correctly.
I found out that I need to use the external pins for IRQF_TRIGGER_FALLING enables IRQ's.