Accessing kernel driver data from FIQ interrupt ha

2020-04-07 04:30发布

问题:

On ARM FIQ interrupts, we have some registers reserved only for FIQ use, and those are a handy way to "save the state" for example of data transfer between FIQ calls.

Currently I'm triggering some GPIO pins from the FIQ, and it is working as expected. When setting up the FIQ handler, I pass the pointers to the data registers, that were mapped with ioremap. Working code looks like this:

//Driver initialization:
static char* dout0;
static char* din0;
...
static int driver_probe(struct platform_device *pdev) 
{
struct pt_regs regs;
...
dout0 = ioremap(HW_PINCTRL_DOUT0, 0xffff);
din0  = ioremap(HW_PINCTRL_DIN0, 0xffff);
...
regs.ARM_r8 = (long) dout0;
regs.ARM_r9 = (long) din0;
set_fiq_regs(&regs);
...

//In the FIQ handler:
LDR r12, [r8]
ORR r12, r12, #0x20 /* set pin 5 high in dout0 register */
STR r12, [r8]

The above code performs as expected, the pin 5 is set high after FIQ handler is executed.

For more complicated operation, I want to prepare a struct, that will hold data-pointers, and other processing related data, including more mappings to different registers - and pass this to the FIQ handler. But something is going wrong there already when migrating the above code to this.

I modified the code above to look like this

//In Driver:
struct fiq_processing {
    char* din0;
    char* dout0;
};

static fiq_processing * pdata; //Pointer to our processing data structure

...
static int driver_probe(struct platform_device *pdev) 
{
struct pt_regs regs;
pdata = kmalloc(sizeof(*pdata), GFP_KERNEL); //Allocate memory for struct
printk("Size of the data struct %d \n", sizeof(*pdata)); //I get "8" as the size
...
pdata->din0  = ioremap(HW_PINCTRL_DIN0, 0xffff);
pdata->dout0 = ioremap(HW_PINCTRL_DOUT0, 0xffff);
...
regs.ARM_r8 = (long) pdata;
set_fiq_regs(&regs);
...

//In the FIQ handler:
#define OFFSET_DIN0 0x0 
#define OFFSET_DOUT0 0x4 //We know size is 8, so offset for dout is half from that
...
LDR r12, [r8, #OFFSET_DOUT0]
ORR r12, r12, #0x20 /* set pin 5 high in dout0 register */
STR r12, [r8, #OFFSET_DOUT0] /* This will do nothing ? */

After I have allocated the memory for the structure, and mapped the register pointers, I pass the address for pdata structure to the FIQ handler. In the FIQ handler, I have offsets for din0 and dout0, which I have thought to be 0x0 and 0x4 respectively (deducted from the structure size of 8).

But for some reason, now my FIQ is unable to set the output pin high anymore - I can't figure out what I'm doing wrong here.. Am I calculating the offset wrong there ? OR is the call STR r12, [r8, #OFFSET_DOUT0] incorrect ? The actual FIQ is somewhat more longer (it reads input state, and input from that creates some conditions), but even basic bit set seems to fail now..

回答1:

Case 1

regs.ARM_r8 = (long) dout0;
set_fiq_regs(&regs);

...

//In the FIQ handler:
LDR r12, [r8]
ORR r12, r12, #0x20 /* set pin 5 high in dout0 register */
STR r12, [r8]

Here the value in r8 is the address of the memory mapped I/O register. De-referencing it reads from the correct location.

Case 2

struct fiq_processing {
    char* din0;
    char* dout0;
};
static fiq_processing * pdata; //Pointer to our processing data structure
pdata->din0  = ioremap(HW_PINCTRL_DIN0, 0xffff);
pdata->dout0 = ioremap(HW_PINCTRL_DOUT0, 0xffff);
...
//In the FIQ handler:
LDR r12, [r8, #OFFSET_DOUT0]
ORR r12, r12, #0x20 /* set pin 5 high in dout0 register */
STR r12, [r8, #OFFSET_DOUT0] /* This will do nothing ? */

In this case, you have retrieved the address of the GPIO controller and your are ORRing with the address! You need to de-reference once again.

LDR r11, [r8, #OFFSET_DOUT0]  # Get address of controller
LDR R12, [R11]                # Get current I/O value.
ORR r12, r12, #0x20           # Set pin 5 high in dout0 register */
STR r12, [r11]                # Write it out.