现在的位置: 首页 > 综合 > 正文

Linux GPIO 驱动(基于GPIOLIB)

2013年05月14日 ⁄ 综合 ⁄ 共 2383字 ⁄ 字号 评论关闭

为了实现EVB板上Linux系统中SD卡插拔自动检测,最近进行了GPIO驱动的开发,基于GPIOLIB。

做个记录,以备以后参考。。。

 

 

参考已有的驱动文件:arch/arm/plat-mxc/gpio.c, 基本只需要将该文件复制过来,针对自己的
平台进行修改即可。这个文件中有一个gpio初始化函数,其原型是:
    int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt);
 
对mxc_gpio_init()的调用,参考arch/arm/mach-mx1/devices.c文件, 如下所示:

/* GPIO port description */
static struct mxc_gpio_port imx_gpio_ports[] = {
 {
  .chip.label = "gpio-0",
  .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR),
  .irq = GPIO_INT_PORTA,
  .virtual_irq_start = MXC_GPIO_IRQ_START,
 }, {
  .chip.label = "gpio-1",
  .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x100),
  .irq = GPIO_INT_PORTB,
  .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
 }, {
  .chip.label = "gpio-2",
  .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x200),
  .irq = GPIO_INT_PORTC,
  .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
 }, {
  .chip.label = "gpio-3",
  .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x300),
  .irq = GPIO_INT_PORTD,
  .virtual_irq_start = MXC_GPIO_IRQ_START + 96,
 }
};

int __init mxc_register_gpios(void)
{
 return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
}

对于一款SOC应用处理芯片,必然会有一套中断处理系统,对应不同模块有一定数量
中断入口及中断号,例如UART,I2C,LCD,I2S等,当然也包括一个或多个GPIO中断,如上
面的GPIO_INT_PORTA,GPIO_INT_PORTB,GPIO_INT_PORTC,GPIO_INT_PORTD就是四个GPIO
端口的1级中断号。每个GPIO端口通常都对应一个下一级的中断控制器,因为每个端口都对应
1-32个GPIO,每个GPIO引脚通常都可以进行中断配置,每个GPIO引脚都对应一个2级中断号。
GPIO扩展的2级IRQ编号就是从virtual_irq_start开始。Linux内核中有一个NR_IRQS的宏,
代表系统中所有的IRQ数,在实现GPIO驱动时,NR_IRQS也要相应扩充,增加由GPIO扩充了的
2级IRQ中断数量。

申请一个GPIO的IRQ时,只需要调用 request_irq(gpio_to_irq(gpio),handler2,...);即可,
不需要申请gpio对应的1级GPIO中断。request_irq()函数中很重要的一步就是enable_irq,
而在mxc_gpio_init()函数中,已经使能所有1级GPIO中断号,通过以下调用:
 set_irq_chained_handler(port[i].irq, handler1);
 
当GPIO中断来临,首先跳到handler1, 在mx3_gpio_irq_handler中,会根据中断号找到对应的
1级GPIO端口,从1级GPIO中断控制寄存器找读出中断状态,从而找到对应的引起中断的GPIO,
最后跳到request_irq时传入的handler2中处理。

/* handle 32 interrupts in one status register */
static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
{
 u32 gpio_irq_no_base = port->virtual_irq_start;

 while (irq_stat != 0) {
  int irqoffset = fls(irq_stat) - 1;

  if (port->both_edges & (1 << irqoffset))
   mxc_flip_edge(port, irqoffset);

  generic_handle_irq(gpio_irq_no_base + irqoffset);

  irq_stat &= ~(1 << irqoffset);
 }
}

/* MX1 and MX3 has one interrupt *per* gpio port */
static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
 u32 irq_stat;
 struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq);

 irq_stat = __raw_readl(port->base + GPIO_ISR) &
   __raw_readl(port->base + GPIO_IMR);

 mxc_gpio_irq_handler(port, irq_stat);
}

抱歉!评论已关闭.