c - BCM2708 (RPi) Rasbpian FIQ 未触发

标签 c assembly linux-kernel arm inline-assembly

我编写了一个 Linux 可加载内核模块,它试图连接到 FIQ 以服务 GPIO 边缘转换。有问题的 Pin 在 GPIO0 (IRQ 49) 上,所以我尝试按如下方式配置 FIQ:

#ifndef GPIO_BASE
#define GPIO_BASE       0x7E200000
#endif
#define GPIO_LEN        0x60
#define GPIO_GPEDS0     0x10
#define GPIO_GPEDS1     0x11
#define GPIO_GPREN0     0x13
#define GPIO_GPREN1     0x14
#define GPIO_GPFEN0     0x16
#define GPIO_GPFEN1     0x17

#define AIR_BASE    0x7E00B200
#define AIR_LEN     0x28
#define AIR_IP2     2    // IRQ pending source 63:32
#define AIR_FIQ     3    // FIQ Config register
#define AIR_FIQ_AN  0x80L   //      FIQ enable field mask
#define AIR_FIQ_SRC 0x7FL   //      FIQ Source field mask
#define AIR_EN2     5    // IRQ Enable IRQ source 63:32
#define AIR_DE2     8    // IRQ Disable IRQ source 63:32

#define AIR_GPIO0_IRQ   49

struct {
    uint32_t wr_p;       
} fiq_data_s;

extern unsigned char pulse_logger_fiq_handler, pulse_logger_fiq_handler_end;

static struct fiq_handler pulse_logger_fh = {
    .name   = "pulse_logger_fiq_handler"
};

static int __init pulse_logger_init(void)
{
    gpioReg = ioremap(GPIO_BASE, GPIO_LEN);
    aircReg = ioremap(AIR_BASE, AIR_LEN);
    fiq_data_s.wr_p  = (uint32_t)&wr_p;                         // Log offset to store system counter

    printk(KERN_INFO "Enabling FIQ...\n");

    printk(KERN_INFO "\tdisable GPIO0 IRQ\n");
    // Disable IRQ tied to FIQ
    disable_irq_nosync(AIR_GPIO0_IRQ);

    printk(KERN_INFO "\tConfig GPIO Edge Events\n");
    writel(AIR_GPIO0_MSK, gpioReg + GPIO_GPEDS0);
    gpren0 = readl((const volatile void *)(gpioReg + GPIO_GPREN0));
    gpren0 |= AIR_GPIO0_MSK;
    writel(gpren0, gpioReg + GPIO_GPREN0);
    gpfen0 = readl((const volatile void *)(gpioReg + GPIO_GPFEN0));
    gpfen0 |= AIR_GPIO0_MSK;
    writel(gpfen0, gpioReg + GPIO_GPFEN0);

    printk(KERN_INFO "\tClaim FIQ\n");
    // Reserve the FIQ
    ret = claim_fiq(&pulse_logger_fh);
    if (ret){
        printk(KERN_INFO "Failed to claim FIQ (%d)!\n", ret);
        goto fail1;
    }

    // Copy FIQ to vector location
    printk(KERN_INFO "\tcopying handler\n");
    set_fiq_handler(&pulse_logger_fiq_handler,
            &pulse_logger_fiq_handler_end - &pulse_logger_fiq_handler);

    // Store symbol pointers in FIQ registers
    printk(KERN_INFO "\tstoring registers\n");
    memset(&regs,0, sizeof(regs));
    regs.ARM_r8  = (long)&fiq_data_s;
    set_fiq_regs(&regs);

    printk(KERN_INFO "\tEnable FIQ\n");
    // Enable the FIQ
    enable_fiq(AIR_GPIO0_IRQ);
    local_fiq_enable();

    return 0;
}

然后我发现 FIQ 寄存器有一个无效条目 0x54,所以我强制 FIQ 寄存器:

writel(0x80 | AIR_GPIO0_IRQ, (volatile void *)(aircReg + AIR_FIQ));

我写的FIQ如下:

#define DATA_ACK_OFFSET     12

ENTRY(pulse_logger_fiq_handler)
        /* Acknowledge the interrupt */
        /* Write PIN_ACK to the EVENT_STATUS_OFFSET register */
        LDR R14, [R8, #DATA_ACK_OFFSET]     /* Load Address */
        MOV R12, #PIN_ACK
        STR R12, [R14, #0]

        LDR R14, [R8, #0]   /* Load Address */
        LDR R14, [R14, #0]  /* Load Value */
        ADD R14, R14, #1
        LDR R12, [R8, #0]   /* Load Address */
        STR R14, [R12, #0]

        /* return from fiq */
        subs  pc, lr, #4
pulse_logger_fiq_handler_end:

然后我切换线路 20 次(使用另一个 GPIO)但在运行结束时 wr_p == 1

如果我将代码作为标准 ISR wr_p == 20 运行,让我相信 ISR 代码是有效的,我只需要了解为什么 FIQ 似乎只触发一次。

static irqreturn_t pulse_isr(int irq, void *data)
{
    uint32_t fiq_data_addr = (uint32_t)&fiq_data_s;
    *((uint32_t*)*((uint32_t*)fiq_data_addr)) += 1;

    __asm__ __volatile__ (
        "MOV R8,  %[fiqbase]    \n"

        "LDR R14, [R8, #12]    \n\t"     /* Load Address */
        "MOV R12, #0x00000010    \n\t"
        "STR R12, [R14, #0]    \n\t"

        "LDR R14, [R8, #0]    \n\t"/* Load Address */
        "LDR R14, [R14, #0]                  \n\t"/* Load Value */
        "ADD R14, R14, #1                    \n\t"
        "LDR R12, [R8, #0]    \n\t"   /* Load Address */
        "STR R14, [R12, #0]                  \n\t"
        :: [fiqbase] "r" (fiq_data_addr)
        : "r8", "r12", "r14");
    return IRQ_HANDLED;
}

如果我深入研究[local_]enable_fiq(),它看起来根本不会触及硬件,这就是为什么我认为我必须写入寄存器,但是,我没有在我在网上查看的其他驱动程序中看到这种情况,所以我很困惑。

为什么这只会触发一次(即使我不写入 FIQ 寄存器)?

最佳答案

我想通了,r13-r14 实际上不是通用寄存器。这些是 SP 和 LR。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337h/Chdedegj.html

重写为使用 r8-r12 可以正常工作。

关于c - BCM2708 (RPi) Rasbpian FIQ 未触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32194264/

相关文章:

c - 人们什么时候开始想到 'C is portable assembler' 的?

c - 如何有效地跟踪 C 中的嵌套函数调用?

linux - 网络 block 设备 - 某些读取请求失败 - 内核 OOPS

android boot - 在 init.c 中读取的 init.%hardware%.rc 在哪里?服务从哪里开始?

c++ - C/C++ 创建一个带有负值的枚举,而不必对其编号

c - 修改系统环境变量

c - 第一个 C 编译器是如何编写的?

c++ - 内联汇编,错误为

assembly - 如何在汇编中使用 28 位 PIO 正确从磁盘读取多个扇区?

java - 如何在 Linux 上安装 Tomcat8 tar.gz?