Linux kernel 从FIQ中断处理程序访问内核驱动程序数据失败

Linux kernel 从FIQ中断处理程序访问内核驱动程序数据失败,linux-kernel,arm,linux-device-driver,embedded-linux,interrupt,Linux Kernel,Arm,Linux Device Driver,Embedded Linux,Interrupt,在ARM FIQ中断中,我们有一些只保留给FIQ使用的寄存器,这些寄存器是“保存状态”的简便方法,例如FIQ调用之间的数据传输 目前,我正在从FIQ触发一些GPIO引脚,它正在按预期工作。在设置FIQ处理程序时,我将指针传递给数据寄存器,这些寄存器用ioremap映射。工作代码如下所示: //Driver initialization: static char* dout0; static char* din0; ... static int driver_probe(struct platfo

在ARM FIQ中断中,我们有一些只保留给FIQ使用的寄存器,这些寄存器是“保存状态”的简便方法,例如FIQ调用之间的数据传输

目前,我正在从FIQ触发一些
GPIO
引脚,它正在按预期工作。在设置FIQ处理程序时,我将指针传递给数据寄存器,这些寄存器用ioremap映射。工作代码如下所示:

//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]
上述代码按预期执行,执行FIQ处理程序后,
引脚5
设置为高

对于更复杂的操作,我想准备一个结构,它将保存数据指针和其他与处理相关的数据,包括到不同寄存器的更多映射,并将其传递给FIQ处理程序。但是,当将上述代码迁移到这里时,已经出现了一些问题

我修改了上面的代码,使其看起来像这样

//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 ? */
在为结构分配内存并映射寄存器指针之后,我将pdata结构的地址传递给FIQ处理程序。 在FIQ处理程序中,我有
din0
dout0
的偏移量,我认为它们分别是
0x0
0x4
(从结构大小8中扣除)

但由于某些原因,现在我的FIQ无法再将输出引脚设置得很高-我无法找出我在这里做错了什么。。 我是不是计算错了偏移量?或者调用STR r12、[r8、#OFFSET_DOUT0]不正确? 实际的FIQ稍长一些(它读取输入状态,从中输入会产生一些条件),但即使是基本位集现在似乎也失败了。

情况1

这里,
r8
中的值是内存映射I/O寄存器的地址。取消引用将从正确的位置读取

案例2 在这种情况下,您已检索到GPIO控制器的地址,并且您的
ORR
ing正在使用该地址!您需要再次取消引用

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.

如果完整的代码没有按照您期望的那样运行,请简化它,直到找到负责的部分。如果您不确定程序集语法,请在测试应用程序中编写用户land assembly,或者添加更多的printk。@artlessnoise-Hmm,对不起,我不明白您的意思。在第一种情况下,在
R8
寄存器中,我只有ioremap'ped
HW\u PINCTRL\u DOUT0
DIN0
转到
R9
)。的确,在struct案例中,在struct内部,DIN现在是第一个,但是在FIQ处理程序中,我定义了
#define OFFSET\u DOUT0 0x4
,所以应该是ok,不是@Peter,是的,也许我应该在user land app中尝试一下,至少调试会更容易,因为我不必每次都重新编译和刷新内核..这确实有道理,感谢这个清晰的示例!我今天一下班就试试这个。谢谢再次感谢你的回答,确实是这个问题!顺便说一句,这听起来可能很无聊(因为它是:P),但我不应该在我的处理程序中接触FIQ之外的其他寄存器,对吗?如果我触摸处理程序代码中的R1和R2等,似乎会引起恐慌、锁定等。。我想如果我碰了这些寄存器,我应该保存并恢复它们?是的,这是正确的。将主线Linux代码与FIQ处理程序同步也很困难。我认为最好的方法是在
fiq\u处理
中添加一个原子标志,它可以向fiq处理程序发出停止的信号;或者,当达到缓冲区大小时,将处理程序编码为停止,等等。您可以设置
r13
/
sp
指向堆栈,然后保存
r0-r7
,但使用这些寄存器有点反FIQ。意图是超快速的。除了使用
r8-r15
之外的任何东西,都会让我想知道为什么我们要使用FIQ中断;也许是为了调试。通常你需要
r0-r7
来调用C代码(没有内核)。谢谢你的解释。我可以使用成对的寄存器,现在我已经有了这个指向工作的结构,所以我可以轻松地获得读、写和数据指针信息,并且仍然有一个指向计时器寄存器的句柄。实际上,我目前的计划是监控缓冲区大小,完成后,通知我的触发器(一个外部计时器)停止计数并触发FIQ。FIQ的要求仅适用于某些实时处理,大约需要300毫秒。再次感谢您的投入,它帮助了很多!我认为Linux 4+对FIQ有更好的支持,因为这个答案是创建的。具体来说,Cortex GIC(中断控制器)可能需要在安全的Linux环境中使用FIQ模式。但是,对于较旧的CPU和较旧的Linux版本,以及具有严格实时性条件的用户,此FIQ信息可能仍然有用。
//In the FIQ handler:
LDR r12, [r8]
ORR r12, r12, #0x20 /* set pin 5 high in dout0 register */
STR r12, [r8]
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 ? */
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.