BCM2708 (RPi) Rasbpian FIQ 未触发
BCM2708 (RPi) Rasbpian FIQ not triggered
我写了一个 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(®s,0, sizeof(regs));
regs.ARM_r8 = (long)&fiq_data_s;
set_fiq_regs(®s);
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 可以正常工作。
我写了一个 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(®s,0, sizeof(regs));
regs.ARM_r8 = (long)&fiq_data_s;
set_fiq_regs(®s);
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 可以正常工作。