达芬奇DM6446的LED驱动及GPIO驱动分析

达芬奇DM6446的GPIO驱动


转载:http://www.61ic.com/Article/DaVinci/DM644X/201108/36918.html


上周五总监提了个需求,希望设备正常运行时能不断闪烁LED灯,于是我周一周二紧张忙活了两天,完成了GPIO驱动程序修改和应用编程。现在应用程序正常跑了起来,遂发文与网友分享。

其间硬件工程师帮我把LED灯焊接到了GPIO第19个引脚,并借签了网络上的技术资料,在此也感谢其它网友的分享。

一、移植和修改GPIO驱动源码

#include <linux/device.h>

#include <linux/fs.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/types.h>

#include <linux/cdev.h>

#include <linux/delay.h>

#include <asm/uaccess.h>

#include <asm/io.h>



#include <asm/arch/hardware.h>

#include <asm/arch/gpio.h>

#define DEVICE_NAME "dm644x_gpios"

#define GPIO_MAJOR 199

static int davinci_dm644x_gpio_open(struct inode *inode, struct file *file) //对于gpio,这儿没啥要写的
{
      return 0;
}

//modify ioctl by zjt 20110823 简化ioctl函数,原版MVlinux带的ioctl是TI外包给印度阿三写的,很罗嗦,阿三水平不行

static int davinci_dm644x_gpio_ioctl(struct inode *inode, struct file *file, unsigned int gpio, unsigned long value)
{
      if (gpio<0 || gpio>=71) //6446支持71个gpio
         return -1;
     if (value<0 || value>2)

         return -2;

     switch(value)
    {
 
      case 2: //2为读取
         return gpio_get_value(gpio);
         break;
     default: 0为设置低电平 1为设置高电平
          gpio_direction_output(gpio, value);
          break;
     }
     return 0;
}

static const struct file_operations davinci_dm644x_gpio_fileops = {

.owner = THIS_MODULE,

.open = davinci_dm644x_gpio_open,

.ioctl = davinci_dm644x_gpio_ioctl,

};

static int __init davinci_dm644x_gpio_init(void)

{
      int ret;
      gpio_direction_output(DM644X_GPIO_LED0, 1); //led0 is on 读者需要根据自己实际的电路修改这个LED0的宏,我们公司用的gpio bank0的第19引脚
       printk("davinci_dm644x_gpio_init ......................................start\n");
       ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &davinci_dm644x_gpio_fileops);
       if(ret < 0) {
           printk(DEVICE_NAME " register falid!\n");
           return ret;
        }
       printk (DEVICE_NAME" initialized ok\n");
       return ret;
}

static void __exit davinci_dm644x_gpio_exit(void)
{

       unregister_chrdev(GPIO_MAJOR, DEVICE_NAME);

}

module_init(davinci_dm644x_gpio_init);
module_exit(davinci_dm644x_gpio_exit);
MODULE_AUTHOR("ZhouJiangtao <>");
MODULE_DESCRIPTION("Davinci DM644x GPIO driver");
MODULE_LICENSE("GPL");

这个驱动放在drivers/char下编译,配置等下还会说,看上去比较简单,因为复杂的部分被gpio.c封装了,gpio.c放在arch/arm/mach-davinci下,封装了寄存器操作的函数。
在tms320dm6446.pdf有gpio寄存器简单的描述,详细的描述在sprue25.pdf里有,这文档在ti网站和csdn都有的下载。

二、gpio.c详细内容(依照源码版本不同稍有差异)
/*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/bitops.h>

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/clock.h>

#include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <asm/arch/cpu.h>

#include <asm/mach/irq.h>

/*
该文件实现了gpio的各种应用功能和向内核注册gpio的中断例程等功能。
用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio,
可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向,

调用gpio_get_value和gpio_set_value获取设置值。


逻辑上各个gpio都有自己的中断号,这样做的目的是使得驱动程序能通过request_irq()函数来申请该gpio的中断,而实际上除了gpio0-gpio7外,其他gpio都是共享四个gpiobank中断号的。通过gpio_irq_handler例程实现了共享中断号的目的。
*/

static DEFINE_SPINLOCK(gpio_lock);

/* 总共有DAVINCI_N_GPIO(71)个gpio引脚,故使用相应多的bit来记录这些引脚的使用状态 */


static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO);


/*申请一个gpio,其实就是检查该gpio是否空闲,如果空闲就可以使用并将该gpio相应的bit置位(在gpio_in_use中)。*/

int gpio_request(unsigned gpio, const char *tag)
{
      if (gpio >= DAVINCI_N_GPIO)
           return -EINVAL;
      if (test_and_set_bit(gpio, gpio_in_use))
           return -EBUSY;

       return 0;
}
EXPORT_SYMBOL(gpio_request);

/*
释放一个gpio,其实就是清除gpio相应的控制bit位(在gpio_in_use中)。
*/
void gpio_free(unsigned gpio)
{
      if (gpio >= DAVINCI_N_GPIO)
          return;
      clear_bit(gpio, gpio_in_use);
}
EXPORT_SYMBOL(gpio_free);

/* 获得gpio_controller结构体指针,gpio_controller结构体是gpio的核心控制单元,里面包含
gpio的设置和数据寄存器。该结构体和__gpio_to_controller函数在/include/asm-arm/
arch-davinci/gpio.h中定义,具体如下:
struct gpio_controller {
u32 dir;
u32 out_data;
u32 set_data;
u32 clr_data;
u32 in_data;
u32 set_rising;
u32 clr_rising;
u32 set_falling;
u32 clr_falling;
u32 intstat;
};

static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr;

if (gpio >= DAVINCI_N_GPIO)
return NULL;

if (gpio < 32)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < 64)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < 96)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);

return ptr;
}
由上面的定义和ti的SPRUE25.pdf手册可以看出,__gpio_to_controller函数返回的是
gpio_controller结构体到第一个成员dir的虚拟地址。获取了这个结构体指针后,
便可以控制相应的gpio了。dm644x共有71个gpio,
所以使用三个gpio_controller结构体控制,关于这个后面会由更详细的分析,
*/
/* create a non-inlined version */
static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
{
return __gpio_to_controller(gpio);
}

/*
向某个gpio设置值,0或1。如果向gpio写1,则向set_data寄存器相应的位置1,如果写0,
则向clr_data寄存器相应的位置1.__gpio_mask函数在gpio.h中定义,定义如下,
static inline u32 __gpio_mask(unsigned gpio)
{
return 1 << (gpio % 32);
}
因为71个引脚由3个结构体控制,第一个控制前32个gpio,第二个控制次32个gpio,
最后一个控制剩余的7个gpio,故__gpio_mask函数的作用是找到在其相应控制结构体里的偏移数,
比如gpio34,那么其由第二个结构体控制,在这个机构体里的偏移是3(从0开始算,就是第二位)。
使用这个函数之前,必须确认该gpio设置成输出模式。
*/
/*
* Assuming the pin is muxed as a gpio output, set its output value.
*/
void __gpio_set(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);

// 设置gpio的值

__raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
}
EXPORT_SYMBOL(__gpio_set);

/*
通过读取in_data寄存器相应该gpio的位来读取gpio的值。
使用这个函数之前,必须确认该gpio设置成输入模式,否则获得到值不可预料。
*/
/*
* Read the pin's value (works even if it's set up as output);
* returns zero/nonzero.
*
* Note that changes are synched to the GPIO clock, so reading values back
* right after you've set them may give old values.
*/
int __gpio_get(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);

/* 读取gpio的值,!!的目的是使得返回的值为0或1.*/
return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
} }
EXPORT_SYMBOL(__gpio_get);

/*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输入,故设置dir寄存器为1.
正如应为所说的,必须确认该引脚是作为gpio功能,而不是某个模块到功能,比如spi。通过PINMUX0
和PINMUX1两个寄存器来设置。
*/
/*--------------------------------------------------------------------------*/

/*
* board setup code *MUST* set PINMUX0 and PINMUX1 as
* needed, and enable the GPIO clock.
*/
int gpio_direction_input(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask;

if (!g)
return -EINVAL;

spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp |= mask; // 设置成1

__raw_writel(temp, &g->dir); // 设置该gpio为输入

spin_unlock(&gpio_lock);
return 0;
}
EXPORT_SYMBOL(gpio_direction_input);

/*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输出,故设置dir寄存器为0.
value参数用于选择gpio设置成输出后该gpio输出的值。
*/
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask;

if (!g)
return -EINVAL;

spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp &= ~mask; // 设置成0


//设置该gpio输出值

__raw_writel(mask, value ? &g->set_data : &g->clr_data);
__raw_writel(temp, &g->dir); // 设置gpio为输出

spin_unlock(&gpio_lock);
return 0;
}
EXPORT_SYMBOL(gpio_direction_output);

/*
向gpio设置值,0或1。
*/
void gpio_set_value(unsigned gpio, int value)
{
if (__builtin_constant_p(value)) {
struct gpio_controller *__iomem g;
u32 mask;

if (gpio >= DAVINCI_N_GPIO)
__error_inval_gpio();

g = __gpio_to_controller(gpio);
mask = __gpio_mask(gpio);
if (value)
__raw_writel(mask, &g->set_data); // 该gpio输出高

else
__raw_writel(mask, &g->clr_data); // 该gpio输出低

return;
}

__gpio_set(gpio, value);
}
EXPORT_SYMBOL(gpio_set_value);

/*
读取gpio的值,0或1.
*/
int gpio_get_value(unsigned gpio)
{
struct gpio_controller *__iomem g;

if (!__builtin_constant_p(gpio))/* 判断该gpio值是否为编译时常数,如果是常数,
函数返回 1,否则返回 0 */
return __gpio_get(gpio);

if (gpio >= DAVINCI_N_GPIO)
return __error_inval_gpio();

g = __gpio_to_controller(gpio);

// 读取该gpio的值

return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
}
EXPORT_SYMBOL(gpio_get_value);

/*
* We expect irqs will normally be set up as input pins, but they can also be
* used as output pins ... which is convenient for testing.
*
* NOTE: GPIO0..GPIO7 also have direct INTC hookups, which work in addition
* to their GPIOBNK0 irq (but with a bit less overhead). But we don't have
* a good way to hook those up ...
*
* All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also
* serve as EDMA event triggers.
*/

/*
禁止相应该irq的gpio的中断。每个gpio都可以作为中断的来源,其中gpio0-gpio7是独立的中断来源,
也就是分配独立的中断号,其他gpio则共用5个GPIOBNK中断线。其优先级可以在board-evm.c
中设置(已经介绍过)。在dm644x平台上,中断是电平边缘触发的,禁止中断其实就是既不设置
上升沿触发,也不设置下降沿触发。
*/
static void gpio_irq_disable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq));

__raw_writel(mask, &g->clr_falling); // 清除下降沿触发

__raw_writel(mask, &g->clr_rising); // 清除上升沿触发

}

/*
中断使能。
在dm644x平台上,中断是电平边缘触发的,其实就是设置为上升沿或下降沿中断。
*/
static void gpio_irq_enable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq));

// 如果先前为下降沿中断,则使能为下降沿中断

if (irq_desc[irq].status & IRQT_FALLING)
__raw_writel(mask, &g->set_falling);

// 如果先前为上升沿中断,则使能为上升沿中断

if (irq_desc[irq].status & IRQT_RISING)
__raw_writel(mask, &g->set_rising);
}

/*
设置中断类型。
在dm644x平台上,中断有上升沿和下降沿两种触发方式。
*/
static int gpio_irq_type(unsigned irq, unsigned trigger)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq));

if (trigger & ~(IRQT_FALLING | IRQT_RISING))
return -EINVAL;

irq_desc[irq].status &= ~IRQT_BOTHEDGE;
irq_desc[irq].status |= trigger;

__raw_writel(mask, (trigger & IRQT_FALLING)
? &g->set_falling : &g->clr_falling); // 设置为下降沿触发

__raw_writel(mask, (trigger & IRQT_RISING)
? &g->set_rising : &g->clr_rising); // 设置为上升沿触发

return 0;
}

/*
该结构体用于注册到所有irq的中断描述结构体中(struct irqdesc),
而所有中断描述结构体定义成一个全局数组irq_desc 。
*/
static struct irqchip gpio_irqchip = {
.unmask = gpio_irq_enable, /* 用于使能中断,
在enable_irq()等内核函数中会用到。*/
.mask = gpio_irq_disable,/* 用于禁止中断,
在disable_irq()等内核函数中会用到。*/
.type = gpio_irq_type, /* 用于设置中断类型,
在set_irq_type()内核函数中会用到。*/
};

/*
该函数将在下面的davinci_gpio_irq_setup中使用,将被注册到五个gpio bank中断的
irq_desc结构中,目的是处理所有级联的gpio中断。所谓级联的中断, 就是指有n个中断
共用同一个中断线。
在dm644x平台中,除了gpio0-gpio7外,其他63个gpio都共用五个gpiobank中断线,在这里,
gpio0-gpio7也被注册到gpiobank中断线,但实际上他们会在irq.c中重新注册。其中,gpio0-gpio15共用IRQ_GPIOBNK0(56)中断线,gpio16-gpio31共用
IRQ_GPIOBNK1(57)中断线,gpio32-gpio47共用IRQ_GPIOBNK2(58)中断线,
gpio48-gpio63共用IRQ_GPIOBNK4(59)中断线,gpio64-gpio70共用
IRQ_GPIOBNK5(60)中断线,
因为寄存器是32位的,所以实际上只有三组寄存器,第一组包含bank0和bank1,
也就是gpio0-gpio31,第二组包含bank2和bank3,也就是gpio32-gpio63,
第三组包含bank4和bank5,也就是gpio64-gpio70,剩余了25个位没有使用。
*/
static void
gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = 0xffff;

/* we only care about one bank */
// 如果bank中断线是寄数,则说明该中断的中断状态位在INTSTATn寄存器的高16位

if (irq & 1)
mask <<= 16;

/* temporarily mask (level sensitive) parent IRQ */
desc->chip->ack(irq);// 该ack函数会在arch/arm/mach-davinci/irq.c中注册。

while (1) {
u32 status;
struct irqdesc *gpio;
int n;
int res;

/* ack any irqs */
/*gpio中断发生后,硬件会在INTSTATn寄存器中置位相应位,
以备程序查询,确定是哪个gpio*/
status = __raw_readl(&g->intstat) & mask;
if (!status)
break;
__raw_writel(status, &g->intstat); // 向该位写1清除

if (irq & 1)
status >>= 16;

/* now demux them to the right lowlevel handler */
// 从下面的davinci_gpio_irq_setup函数可以看出来以下程序的运作。

n = (int)get_irq_data(irq); // 获取该bank对应的第一个gpio号

gpio = &irq_desc[n]; // 获取该bank第一个gpio号对应的中断描述符

while (status) { // 该bank可能有多个gpio发生了中断

res = ffs(status); // 获取第一个发生了中断的位(1-32)

n += res; /* 获得该gpio的中断线(系统实际上只有64(0-63)个中断线,
但那些共用的gpio的中断也有自己的断描述符和中断线(从64开始),
这样做的目的是使得驱动程序能通过request_irq()函数来申请该gpio的中断。*/
gpio += res; // 获得该gpio的中断描述符


/* 调用下面注册的do_simple_IRQ例程
其又会调用用户通过request_irq()
注册的中断例程
*/
desc_handle_irq(n - 1, gpio - 1, regs);
status >>= res;
}
}
desc->chip->unmask(irq); // 打开该irq中断线

/* now it may re-trigger */
}

/*
* NOTE: for suspend/resume, probably best to make a sysdev (and class)
* with its suspend/resume calls hooking into the results of the set_wake()
* calls ... so if no gpios are wakeup events the clock can be disabled,
* with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
* can be set appropriately for GPIOV33 pins.
*/
/*
注册gpio中断例程到内核中,并初始化了一些寄存器。
该函数将会被board_evm.c(其浅析已经发表)中的evm_init()函数调用。具体调用过程如下:
start_kernel()-->setup_arch()-->init_machine = mdesc->init_machine
(init_machine是个全局函数指针变量,其指向的就是已经注册到机器描述符里evm_init());
调用函数指针init_machine()的例程是customize_machine(),其定义为
arch_initcall(customize_machine),所以,接下来的调用过程是:
start_kernel()-->do_basic_setup()-->do_initcalls()-->customize_machine()-->
init_machine()(也就是evm_init())-->davinci_gpio_irq_setup。
从上可以看出经历了两个过程,才调用davinci_gpio_irq_setup例程来初始化gpio中断。
*/
int __init davinci_gpio_irq_setup(void)
{
unsigned gpio, irq, bank, banks;
struct clk *clk;

clk = clk_get(NULL, "gpio"); // 获取时钟

if (IS_ERR(clk)) {
printk(KERN_ERR "Error %ld getting gpio clock?/n",
PTR_ERR(clk));
return 0;
}

clk_enable(clk); // 使能gpio时钟并打开该模块电源


for (gpio = 0, irq = gpio_to_irq(0), bank = (cpu_is_davinci_dm355() ?
IRQ_DM355_GPIOBNK0 : (cpu_is_davinci_dm6467() ?
IRQ_DM646X_GPIOBNK0 : IRQ_GPIOBNK0)); // dm644x的IRQ_GPIOBNK0(56)

gpio < DAVINCI_N_GPIO; bank++) { // dm644x的DAVINCI_N_GPIO(71)

struct gpio_controller *__iomem g = gpio2controller(gpio);
unsigned i;

// 关该bank所有gpio的中断

__raw_writel(~0, &g->clr_falling);
__raw_writel(~0, &g->clr_rising);

/* set up all irqs in this bank */
// 同一个bank的所有gpio共用一个中断例程gpio_irq_handler

set_irq_chained_handler(bank, gpio_irq_handler);
set_irq_chipdata(bank, g);
set_irq_data(bank, (void *)irq);

for (i = 0; i < 16 && gpio < DAVINCI_N_GPIO;
i++, irq++, gpio++) {
set_irq_chip(irq, &gpio_irqchip); /* 注册用于gpio中断禁止、设能
和类型选择的回调例程 */
set_irq_chipdata(irq, g); // 保存控制结构体(寄存器)的地址

set_irq_handler(irq, do_simple_IRQ);/* 为每个gpio中断设置同一个中
断例程do_simple_IRQ*/
set_irq_flags(irq, IRQF_VALID); // fiq中断有效

}
}
/*
一个共用bank中断线的gpio中断发生后的大致的流程是:
--> gpio_irq_handler --> do_simple_IRQ --> __do_irq -->
action->handler(用户使用request_irq()注册的中断例程)
*/
/* BINTEN -- per-bank interrupt enable. genirq would also let these
* bits be set/cleared dynamically.
*/
if (cpu_is_davinci_dm355())
banks = 0x3f;
else
banks = 0x1f;

// 向BINTEN寄存器写入0x1f(共5个位,每个位控制1个bank),打开所有的bank中断

__raw_writel(banks, (void *__iomem)
IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08));

printk(KERN_INFO "DaVinci: %d gpio irqs/n", irq - gpio_to_irq(0));

return 0;
}





gpio.h








/*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/

#ifndef __DAVINCI_GPIO_H
#define __DAVINCI_GPIO_H

/*
* basic gpio routines
*
* board-specific init should be done by arch/.../.../board-XXX.c (maybe
* initializing banks together) rather than boot loaders; kexec() won't
* go through boot loaders.
*
* the gpio clock will be turned on when gpios are used, and you may also
* need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
* used as gpios, not with other peripherals.
*
* GPIOs are numbered 0..(DAVINCI_N_GPIO-1). For documentation, and maybe
* for later updates, code should write GPIO(N) or:
* - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
* - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
*
* For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
* for now, that's != GPIO(N)
*/
#define GPIO(X) (X) /* 0 <= X <= 70 */
#define GPIOV18(X) (X) /* 1.8V i/o; 0 <= X <= 53 */
#define GPIOV33(X) ((X)+54) /* 3.3V i/o; 0 <= X <= 17 */

/*
寄存器都是32位到,每位对应一个gpio。
*/
struct gpio_controller {
u32 dir; // gpio方向设置寄存器

u32 out_data; // gpio设置为输出时,表示输出状态(0或1)

u32 set_data; // gpio设置为输出时,用于输出高电平

u32 clr_data; // gpio设置为输出时,用于输出低电平

u32 in_data; // gpio设置为输入时,用于读取输入值

u32 set_rising; // gpio中断上升沿触发设置

u32 clr_rising; // gpio中断上升沿触发清除

u32 set_falling; // gpio中断下降沿触发设置

u32 clr_falling; // gpio中断下降沿触发清除

u32 intstat; // gpio中断状态位,由硬件设置,可读取,写1时清除。

};

/* The __gpio_to_controller() and __gpio_mask() functions inline to constants
* with constant parameters; or in outlined code they execute at runtime.
*
* You'd access the controller directly when reading or writing more than
* one gpio value at a time, and to support wired logic where the value
* being driven by the cpu need not match the value read back.
*
* These are NOT part of the cross-platform GPIO interface
*/
static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr;

if (gpio >= DAVINCI_N_GPIO)
return NULL;

if (gpio < 32)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < 64)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < 96)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);

return ptr;
}

static inline u32 __gpio_mask(unsigned gpio)
{
return 1 << (gpio % 32);
}

/* The get/set/clear functions will inline when called with constant
* parameters, for low-overhead bitbanging. Illegal constant parameters
* cause link-time errors.
*
* Otherwise, calls with variable parameters use outlined functions.
*/
extern int __error_inval_gpio(void);

extern void __gpio_set(unsigned gpio, int value);
extern int __gpio_get(unsigned gpio);

/* Returns zero or nonzero; works for gpios configured as inputs OR
* as outputs.
*
* NOTE: changes in reported values are synchronized to the GPIO clock.
* This is most easily seen after calling gpio_set_value() and then immediatly
* gpio_get_value(), where the gpio_get_value() would return the old value
* until the GPIO clock ticks and the new value gets latched.
*/
extern int gpio_get_value(unsigned gpio);
extern void gpio_set_value(unsigned gpio, int value);


/* powerup default direction is IN */
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value);

#include <asm-generic/gpio.h> /* cansleep wrappers */

extern int gpio_request(unsigned gpio, const char *tag);
extern void gpio_free(unsigned gpio);

static inline int gpio_to_irq(unsigned gpio)
{
return DAVINCI_N_AINTC_IRQ + gpio;
}

static inline int irq_to_gpio(unsigned irq)
{
return irq - DAVINCI_N_AINTC_IRQ;
}

#endif /* __DAVINCI_GPIO_H */





三、修改内核配置文件



在linux-2.6.18_pro500/drivers/char目录下,

修改Kconfig文件,在menu "Character devices"下面,加入



config DAVINCI_DM644X_GPIOS

tristate "Davinci DM644x GPIO GPIOs"

depends on ARCH_DAVINCI

help

This option enables support for LEDs and Buzzer connected to GPIO lines

on Ti Davinci DM644x CPUs, such as the DM6446。

修改Makefile文件,在128行

obj-$(CONFIG_DAVINCI_DM646X_TSIF) += tsif_control.o tsif_data.o下面,加入:

obj-$( DAVINCI_DM644X_GPIOS) += davinci_dm644x_gpios.o

选上“Character devices”里的“Davinci DM644x GPIOs”,保存修改后的配置,然后make uImage,对内核的编译;



五、文件系统节点添加

文件系统里,/etc/init.d/rcS文件

# Run /etc/rc.d/rc.local if it exists

[ -x /etc/rc.d/rc.local ] && /etc/rc.d/rc.local

前面加mknod /dev/dm644x_gpios c 199 0或在shell命令行下执行mknod /dev/dm644x_gpios c 199 0;这就是静态分配设备节点的做法。

运行系统,编辑代码

int fdGPIO = open("/dev/dm644x_gpios", 0);

int nLedState(0);

if (fdGPIO>0)
{
ioctl(fdGPIO, GPIO_LED_PIN, nLedState);
nLedState = !nLedState;
}

反复执行if的这一段代码,可以看到LED灯闪烁。达芬奇DM6446的GPIO驱动


转载:http://www.61ic.com/Article/DaVinci/DM644X/201108/36918.html


上周五总监提了个需求,希望设备正常运行时能不断闪烁LED灯,于是我周一周二紧张忙活了两天,完成了GPIO驱动程序修改和应用编程。现在应用程序正常跑了起来,遂发文与网友分享。
 
     其间硬件工程师帮我把LED灯焊接到了GPIO第19个引脚,并借签了网络上的技术资料,在此也感谢其它网友的分享。
 
一、移植和修改GPIO驱动源码
 
#include <linux/device.h>
 
#include <linux/fs.h>
 
#include <linux/module.h>
 
#include <linux/errno.h>
 
#include <linux/kernel.h>
 
#include <linux/init.h>
 
#include <linux/platform_device.h>
 
#include <linux/types.h>
 
#include <linux/cdev.h>
 
#include <linux/delay.h>
 
#include <asm/uaccess.h>
 
#include <asm/io.h>
 
 
 
#include <asm/arch/hardware.h>
 
#include <asm/arch/gpio.h>
 
 
 
#define DEVICE_NAME "dm644x_gpios"
 
#define GPIO_MAJOR 199
 
 
 
static int davinci_dm644x_gpio_open(struct inode *inode, struct file *file) //对于gpio,这儿没啥要写的
 
{
 
    return 0;
 
}
 
 //modify ioctl by zjt 20110823  简化ioctl函数,原版MVlinux带的ioctl是TI外包给印度阿三写的,很罗嗦,阿三水平不行
 
static int davinci_dm644x_gpio_ioctl(
 
       struct inode *inode,

       struct file *file,

       unsigned int gpio,

       unsigned long value)
 
{
 
       if (gpio<0 || gpio>=71) //6446支持71个gpio
 
              return -1;
 
       if (value<0 || value>2)
 
              return -2;
 
      

       switch(value)

       {
 
       case 2:    //2为读取
 
              return gpio_get_value(gpio);
 
              break;
 
       default:  0为设置低电平 1为设置高电平
 
              gpio_direction_output(gpio, value);
 
              break;
 
       }
 
       return 0;
 
}
 
 
 
static const struct file_operations davinci_dm644x_gpio_fileops = {
 
       .owner   = THIS_MODULE,
 
       .open    = davinci_dm644x_gpio_open,
 
       .ioctl       = davinci_dm644x_gpio_ioctl,
 
};
 
 static int __init davinci_dm644x_gpio_init(void)
 
{
 
       int ret;      

       gpio_direction_output(DM644X_GPIO_LED0, 1);      //led0 is on  读者需要根据自己实际的电路修改这个LED0的宏,我们公司用的gpio bank0的第19引脚
 
       printk("davinci_dm644x_gpio_init  ......................................start\n");
 
 
 
       ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &davinci_dm644x_gpio_fileops);
 
       if(ret < 0)
 
       {
 
              printk(DEVICE_NAME " register falid!\n");
 
              return ret;
 
       }
 
 
 
       printk (DEVICE_NAME" initialized ok\n");
 
 
 
       return ret;
 
}
 
 
 
static void __exit davinci_dm644x_gpio_exit(void)
 
{
 
       unregister_chrdev(GPIO_MAJOR, DEVICE_NAME);
 
}
 
 
 
module_init(davinci_dm644x_gpio_init);
 
module_exit(davinci_dm644x_gpio_exit);
 
 
 
MODULE_AUTHOR("ZhouJiangtao <>");
 
MODULE_DESCRIPTION("Davinci DM644x GPIO driver");
 
MODULE_LICENSE("GPL");
 
这个驱动放在drivers/char下编译,配置等下还会说,看上去比较简单,因为复杂的部分被gpio.c封装了,gpio.c放在arch/arm/mach-davinci下,封装了寄存器操作的函数。
 
在tms320dm6446.pdf有gpio寄存器简单的描述,详细的描述在sprue25.pdf里有,这文档在ti网站和csdn都有的下载。
 
 
 
二、gpio.c详细内容(依照源码版本不同稍有差异)
 

 

 

/*
  * TI DaVinci GPIO Support
  *
  * Copyright (c) 2006 David Brownell
  * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
 
#include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/bitops.h>
 
#include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/hardware/clock.h>
 
#include <asm/arch/irqs.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/cpu.h>
 
#include <asm/mach/irq.h>
 
/*
 该文件实现了gpio的各种应用功能和向内核注册gpio的中断例程等功能。
  用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio,
  可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向,
  调用gpio_get_value和gpio_set_value获取设置值。
 

 

逻辑上各个gpio都有自己的中断号,这样做的目的是使得驱动程序能通过request_irq()函数来申请该gpio的中断,而实际上除了gpio0-gpio7外,其他gpio都是共享四个gpiobank中断号的。通过gpio_irq_handler例程实现了共享中断号的目的。
 
*/
 
static DEFINE_SPINLOCK(gpio_lock);
 
/* 总共有DAVINCI_N_GPIO(71)个gpio引脚,故使用相应多的bit来记录这些引脚的使用状态 */
 static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO);
 
/*
  申请一个gpio,其实就是检查该gpio是否空闲,如果空闲就可以使用并将该gpio相应的bit置位
 (在gpio_in_use中)。
 */
 int gpio_request(unsigned gpio, const char *tag)
 {
     if (gpio >= DAVINCI_N_GPIO)
         return -EINVAL;
 
    if (test_and_set_bit(gpio, gpio_in_use))
         return -EBUSY;
 
    return 0;
 }
 EXPORT_SYMBOL(gpio_request);
 
/*
  释放一个gpio,其实就是清除gpio相应的控制bit位(在gpio_in_use中)。
 */
 void gpio_free(unsigned gpio)
 {
     if (gpio >= DAVINCI_N_GPIO)
         return;
 
    clear_bit(gpio, gpio_in_use);
 }
 EXPORT_SYMBOL(gpio_free);
 
/* 获得gpio_controller结构体指针,gpio_controller结构体是gpio的核心控制单元,里面包含
  gpio的设置和数据寄存器。该结构体和__gpio_to_controller函数在/include/asm-arm/
  arch-davinci/gpio.h中定义,具体如下:
 struct gpio_controller {
     u32    dir;
     u32    out_data;
     u32    set_data;
     u32    clr_data;
     u32    in_data;
     u32    set_rising;
     u32    clr_rising;
     u32    set_falling;
     u32    clr_falling;
     u32    intstat;
 };
 
static inline struct gpio_controller *__iomem
 __gpio_to_controller(unsigned gpio)
 {
     void *__iomem ptr;
 
    if (gpio >= DAVINCI_N_GPIO)
         return NULL;
 
    if (gpio < 32)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
     else if (gpio < 64)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
     else if (gpio < 96)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
     else
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);
 
    return ptr;
 }
  由上面的定义和ti的SPRUE25.pdf手册可以看出,__gpio_to_controller函数返回的是
  gpio_controller结构体到第一个成员dir的虚拟地址。获取了这个结构体指针后,
  便可以控制相应的gpio了。dm644x共有71个gpio,
  所以使用三个gpio_controller结构体控制,关于这个后面会由更详细的分析,
 */
 /* create a non-inlined version */
 static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
 {
     return __gpio_to_controller(gpio);
 }
 
/*
 向某个gpio设置值,0或1。如果向gpio写1,则向set_data寄存器相应的位置1,如果写0,
  则向clr_data寄存器相应的位置1.__gpio_mask函数在gpio.h中定义,定义如下,
  static inline u32 __gpio_mask(unsigned gpio)
  {
     return 1 << (gpio % 32);
  }
  因为71个引脚由3个结构体控制,第一个控制前32个gpio,第二个控制次32个gpio,
  最后一个控制剩余的7个gpio,故__gpio_mask函数的作用是找到在其相应控制结构体里的偏移数,
  比如gpio34,那么其由第二个结构体控制,在这个机构体里的偏移是3(从0开始算,就是第二位)。
  使用这个函数之前,必须确认该gpio设置成输出模式。
 */
 /*
  * Assuming the pin is muxed as a gpio output, set its output value.
  */
 void __gpio_set(unsigned gpio, int value)
 {
     struct gpio_controller *__iomem g = gpio2controller(gpio);
 
    // 设置gpio的值
 
    __raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
}
 EXPORT_SYMBOL(__gpio_set);
 
/*
 通过读取in_data寄存器相应该gpio的位来读取gpio的值。
  使用这个函数之前,必须确认该gpio设置成输入模式,否则获得到值不可预料。
 */
 /*
  * Read the pin's value (works even if it's set up as output);
  * returns zero/nonzero.
  *
  * Note that changes are synched to the GPIO clock, so reading values back
  * right after you've set them may give old values.
  */
 int __gpio_get(unsigned gpio)
 {
     struct gpio_controller *__iomem g = gpio2controller(gpio);
    
     /* 读取gpio的值,!!的目的是使得返回的值为0或1.*/
     return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));   
 }                                                                                                                  }
 EXPORT_SYMBOL(__gpio_get);
 
/*
 通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
  该函数是设置成输入,故设置dir寄存器为1.
  正如应为所说的,必须确认该引脚是作为gpio功能,而不是某个模块到功能,比如spi。通过PINMUX0
  和PINMUX1两个寄存器来设置。
 */
 /*--------------------------------------------------------------------------*/
 
/*
  * board setup code *MUST* set PINMUX0 and PINMUX1 as
  * needed, and enable the GPIO clock.
  */
 int gpio_direction_input(unsigned gpio)
 {
     struct gpio_controller *__iomem g = gpio2controller(gpio);
     u32 temp;
     u32 mask;
 
    if (!g)
         return -EINVAL;
 
    spin_lock(&gpio_lock);
     mask = __gpio_mask(gpio);
     temp = __raw_readl(&g->dir);
     temp |= mask;    // 设置成1
 
    __raw_writel(temp, &g->dir);    // 设置该gpio为输入
 
    spin_unlock(&gpio_lock);
     return 0;
 }
 EXPORT_SYMBOL(gpio_direction_input);
 
/*
  通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
  该函数是设置成输出,故设置dir寄存器为0.
  value参数用于选择gpio设置成输出后该gpio输出的值。
 */
 int gpio_direction_output(unsigned gpio, int value)
 {
     struct gpio_controller *__iomem g = gpio2controller(gpio);
     u32 temp;
     u32 mask;
 
    if (!g)
         return -EINVAL;
 
    spin_lock(&gpio_lock);
     mask = __gpio_mask(gpio);
     temp = __raw_readl(&g->dir);
     temp &= ~mask;    // 设置成0
 
   
     //设置该gpio输出值
 
    __raw_writel(mask, value ? &g->set_data : &g->clr_data);
     __raw_writel(temp, &g->dir);    // 设置gpio为输出
 
    spin_unlock(&gpio_lock);
     return 0;
 }
 EXPORT_SYMBOL(gpio_direction_output);
 
/*
  向gpio设置值,0或1。
 */
 void gpio_set_value(unsigned gpio, int value)
 {
     if (__builtin_constant_p(value)) {
         struct gpio_controller *__iomem g;
         u32 mask;
 
        if (gpio >= DAVINCI_N_GPIO)
             __error_inval_gpio();
 
        g = __gpio_to_controller(gpio);
         mask = __gpio_mask(gpio);
         if (value)
             __raw_writel(mask, &g->set_data);    // 该gpio输出高
 
        else
             __raw_writel(mask, &g->clr_data);    // 该gpio输出低
 
        return;
     }
 
    __gpio_set(gpio, value);
 }
 EXPORT_SYMBOL(gpio_set_value);
 
/*
  读取gpio的值,0或1.
 */
 int gpio_get_value(unsigned gpio)
 {
     struct gpio_controller *__iomem g;
 
    if (!__builtin_constant_p(gpio))/* 判断该gpio值是否为编译时常数,如果是常数,
                                      函数返回 1,否则返回 0 */
         return __gpio_get(gpio);
 
    if (gpio >= DAVINCI_N_GPIO)
         return __error_inval_gpio();
 
    g = __gpio_to_controller(gpio);
    
     // 读取该gpio的值
 
    return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
 }
 EXPORT_SYMBOL(gpio_get_value);
 
/*
  * We expect irqs will normally be set up as input pins, but they can also be
  * used as output pins ... which is convenient for testing.
  *
  * NOTE: GPIO0..GPIO7 also have direct INTC hookups, which work in addition
  * to their GPIOBNK0 irq (but with a bit less overhead). But we don't have
  * a good way to hook those up ...
  *
  * All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also
  * serve as EDMA event triggers.
  */
 
/*
  禁止相应该irq的gpio的中断。每个gpio都可以作为中断的来源,其中gpio0-gpio7是独立的中断来源,
  也就是分配独立的中断号,其他gpio则共用5个GPIOBNK中断线。其优先级可以在board-evm.c
  中设置(已经介绍过)。在dm644x平台上,中断是电平边缘触发的,禁止中断其实就是既不设置
  上升沿触发,也不设置下降沿触发。
 */
 static void gpio_irq_disable(unsigned irq)
 {
     struct gpio_controller *__iomem g = get_irq_chipdata(irq);
     u32 mask = __gpio_mask(irq_to_gpio(irq));
 
    __raw_writel(mask, &g->clr_falling);    // 清除下降沿触发
 
    __raw_writel(mask, &g->clr_rising);        // 清除上升沿触发
 
}
 
/*
  中断使能。
  在dm644x平台上,中断是电平边缘触发的,其实就是设置为上升沿或下降沿中断。
 */
 static void gpio_irq_enable(unsigned irq)
 {
     struct gpio_controller *__iomem g = get_irq_chipdata(irq);
     u32 mask = __gpio_mask(irq_to_gpio(irq));
 
    // 如果先前为下降沿中断,则使能为下降沿中断
 
    if (irq_desc[irq].status & IRQT_FALLING)
         __raw_writel(mask, &g->set_falling);
    
     // 如果先前为上升沿中断,则使能为上升沿中断
 
    if (irq_desc[irq].status & IRQT_RISING)   
         __raw_writel(mask, &g->set_rising);
 }
 
/*
  设置中断类型。
  在dm644x平台上,中断有上升沿和下降沿两种触发方式。
 */
 static int gpio_irq_type(unsigned irq, unsigned trigger)
 {
     struct gpio_controller *__iomem g = get_irq_chipdata(irq);
     u32 mask = __gpio_mask(irq_to_gpio(irq));
 
    if (trigger & ~(IRQT_FALLING | IRQT_RISING))
         return -EINVAL;
 
    irq_desc[irq].status &= ~IRQT_BOTHEDGE;
     irq_desc[irq].status |= trigger;
 
    __raw_writel(mask, (trigger & IRQT_FALLING)
          ? &g->set_falling : &g->clr_falling);     // 设置为下降沿触发
 
    __raw_writel(mask, (trigger & IRQT_RISING)
          ? &g->set_rising : &g->clr_rising);    // 设置为上升沿触发
 
    return 0;
 }
 
/*
 该结构体用于注册到所有irq的中断描述结构体中(struct irqdesc),
  而所有中断描述结构体定义成一个全局数组irq_desc 。
  */
 static struct irqchip gpio_irqchip = {
     .unmask        = gpio_irq_enable, /* 用于使能中断,
                                      在enable_irq()等内核函数中会用到。*/   
     .mask        = gpio_irq_disable,/* 用于禁止中断,
                                      在disable_irq()等内核函数中会用到。*/
     .type        = gpio_irq_type, /* 用于设置中断类型,
                                      在set_irq_type()内核函数中会用到。*/
 };
 
/*
  该函数将在下面的davinci_gpio_irq_setup中使用,将被注册到五个gpio bank中断的
  irq_desc结构中,目的是处理所有级联的gpio中断。所谓级联的中断, 就是指有n个中断
  共用同一个中断线。
  在dm644x平台中,除了gpio0-gpio7外,其他63个gpio都共用五个gpiobank中断线,在这里,
  gpio0-gpio7也被注册到gpiobank中断线,但实际上他们会在irq.c中重新注册。其中,gpio0-gpio15共用IRQ_GPIOBNK0(56)中断线,gpio16-gpio31共用
  IRQ_GPIOBNK1(57)中断线,gpio32-gpio47共用IRQ_GPIOBNK2(58)中断线,
  gpio48-gpio63共用IRQ_GPIOBNK4(59)中断线,gpio64-gpio70共用
  IRQ_GPIOBNK5(60)中断线,
  因为寄存器是32位的,所以实际上只有三组寄存器,第一组包含bank0和bank1,
  也就是gpio0-gpio31,第二组包含bank2和bank3,也就是gpio32-gpio63,
  第三组包含bank4和bank5,也就是gpio64-gpio70,剩余了25个位没有使用。
 */
 static void
 gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
 {
     struct gpio_controller *__iomem g = get_irq_chipdata(irq);
     u32 mask = 0xffff;
 
    /* we only care about one bank */
     // 如果bank中断线是寄数,则说明该中断的中断状态位在INTSTATn寄存器的高16位
 
    if (irq & 1)
         mask <<= 16;
 
    /* temporarily mask (level sensitive) parent IRQ */
     desc->chip->ack(irq);// 该ack函数会在arch/arm/mach-davinci/irq.c中注册。
 
    while (1) {
         u32        status;
         struct irqdesc    *gpio;
         int        n;
         int        res;
 
        /* ack any irqs */
         /*gpio中断发生后,硬件会在INTSTATn寄存器中置位相应位,
          以备程序查询,确定是哪个gpio*/
         status = __raw_readl(&g->intstat) & mask;
        if (!status)
             break;
         __raw_writel(status, &g->intstat);    // 向该位写1清除
 
        if (irq & 1)
             status >>= 16;
 
        /* now demux them to the right lowlevel handler */
         // 从下面的davinci_gpio_irq_setup函数可以看出来以下程序的运作。
 
        n = (int)get_irq_data(irq);    // 获取该bank对应的第一个gpio号
 
        gpio = &irq_desc[n];    // 获取该bank第一个gpio号对应的中断描述符
 
        while (status) {    // 该bank可能有多个gpio发生了中断
 
            res = ffs(status);    // 获取第一个发生了中断的位(1-32)
 
            n += res;    /* 获得该gpio的中断线(系统实际上只有64(0-63)个中断线,
                         但那些共用的gpio的中断也有自己的断描述符和中断线(从64开始),
                         这样做的目的是使得驱动程序能通过request_irq()函数来申请该gpio的中断。*/
             gpio += res;    //     获得该gpio的中断描述符
 
           
             /* 调用下面注册的do_simple_IRQ例程
              其又会调用用户通过request_irq()
              注册的中断例程
             */
             desc_handle_irq(n - 1, gpio - 1, regs);   
             status >>= res;       
         }
     }
     desc->chip->unmask(irq);    // 打开该irq中断线
 
    /* now it may re-trigger */
 }
 
/*
  * NOTE: for suspend/resume, probably best to make a sysdev (and class)
  * with its suspend/resume calls hooking into the results of the set_wake()
  * calls ... so if no gpios are wakeup events the clock can be disabled,
  * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
  * can be set appropriately for GPIOV33 pins.
  */
 /*
  注册gpio中断例程到内核中,并初始化了一些寄存器。
  该函数将会被board_evm.c(其浅析已经发表)中的evm_init()函数调用。具体调用过程如下:
  start_kernel()-->setup_arch()-->init_machine = mdesc->init_machine
  (init_machine是个全局函数指针变量,其指向的就是已经注册到机器描述符里evm_init());
  调用函数指针init_machine()的例程是customize_machine(),其定义为
  arch_initcall(customize_machine),所以,接下来的调用过程是:
  start_kernel()-->do_basic_setup()-->do_initcalls()-->customize_machine()-->
  init_machine()(也就是evm_init())-->davinci_gpio_irq_setup。
  从上可以看出经历了两个过程,才调用davinci_gpio_irq_setup例程来初始化gpio中断。
 */
 int __init davinci_gpio_irq_setup(void)
 {
     unsigned    gpio, irq, bank, banks;
     struct clk    *clk;
 
    clk = clk_get(NULL, "gpio");    // 获取时钟
 
    if (IS_ERR(clk)) {
         printk(KERN_ERR "Error %ld getting gpio clock?/n",
          PTR_ERR(clk));
         return 0;
     }
 
    clk_enable(clk);    // 使能gpio时钟并打开该模块电源
 

    for (gpio = 0, irq = gpio_to_irq(0), bank = (cpu_is_davinci_dm355() ?
      IRQ_DM355_GPIOBNK0 : (cpu_is_davinci_dm6467() ?
      IRQ_DM646X_GPIOBNK0 : IRQ_GPIOBNK0));    // dm644x的IRQ_GPIOBNK0(56)
 
     gpio < DAVINCI_N_GPIO; bank++) {    // dm644x的DAVINCI_N_GPIO(71)
 
        struct gpio_controller    *__iomem g = gpio2controller(gpio);
         unsigned        i;
 
        // 关该bank所有gpio的中断
 
        __raw_writel(~0, &g->clr_falling);
         __raw_writel(~0, &g->clr_rising);
 
        /* set up all irqs in this bank */
         // 同一个bank的所有gpio共用一个中断例程gpio_irq_handler
 
        set_irq_chained_handler(bank, gpio_irq_handler);
         set_irq_chipdata(bank, g);
         set_irq_data(bank, (void *)irq);
 
        for (i = 0; i < 16 && gpio < DAVINCI_N_GPIO;
          i++, irq++, gpio++) {
             set_irq_chip(irq, &gpio_irqchip);    /* 注册用于gpio中断禁止、设能
                                                  和类型选择的回调例程 */
             set_irq_chipdata(irq, g);            // 保存控制结构体(寄存器)的地址
 
            set_irq_handler(irq, do_simple_IRQ);/* 为每个gpio中断设置同一个中
                                                     断例程do_simple_IRQ*/
             set_irq_flags(irq, IRQF_VALID);        // fiq中断有效
 
        }
     }
 /*   
 一个共用bank中断线的gpio中断发生后的大致的流程是:
  --> gpio_irq_handler --> do_simple_IRQ --> __do_irq -->
 action->handler(用户使用request_irq()注册的中断例程)
 */
     /* BINTEN -- per-bank interrupt enable. genirq would also let these
      * bits be set/cleared dynamically.
      */
     if (cpu_is_davinci_dm355())
         banks = 0x3f;
     else
         banks = 0x1f;
    
     // 向BINTEN寄存器写入0x1f(共5个位,每个位控制1个bank),打开所有的bank中断
 
    __raw_writel(banks, (void *__iomem)
          IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08));
 
    printk(KERN_INFO "DaVinci: %d gpio irqs/n", irq - gpio_to_irq(0));
 
    return 0;
 }
 

 


gpio.h
 

 

 

 

/*
  * TI DaVinci GPIO Support
  *
  * Copyright (c) 2006 David Brownell
  * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
 
#ifndef    __DAVINCI_GPIO_H
 #define    __DAVINCI_GPIO_H
 
/*
  * basic gpio routines
  *
  * board-specific init should be done by arch/.../.../board-XXX.c (maybe
  * initializing banks together) rather than boot loaders; kexec() won't
  * go through boot loaders.
  *
  * the gpio clock will be turned on when gpios are used, and you may also
  * need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
  * used as gpios, not with other peripherals.
  *
  * GPIOs are numbered 0..(DAVINCI_N_GPIO-1). For documentation, and maybe
  * for later updates, code should write GPIO(N) or:
  * - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
  * - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
  *
  * For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
  * for now, that's != GPIO(N)
  */
 #define    GPIO(X)        (X)        /* 0 <= X <= 70 */
 #define    GPIOV18(X)    (X)        /* 1.8V i/o; 0 <= X <= 53 */
 #define    GPIOV33(X)    ((X)+54)    /* 3.3V i/o; 0 <= X <= 17 */
 
/*
 寄存器都是32位到,每位对应一个gpio。
 */
 struct gpio_controller {
     u32    dir;            // gpio方向设置寄存器
 
    u32    out_data;        // gpio设置为输出时,表示输出状态(0或1)
 
    u32    set_data;        // gpio设置为输出时,用于输出高电平
 
    u32    clr_data;        // gpio设置为输出时,用于输出低电平
 
    u32    in_data;        // gpio设置为输入时,用于读取输入值
 
    u32    set_rising;        // gpio中断上升沿触发设置
 
    u32    clr_rising;        // gpio中断上升沿触发清除
 
    u32    set_falling;    // gpio中断下降沿触发设置
 
    u32    clr_falling;    // gpio中断下降沿触发清除
 
    u32    intstat;        // gpio中断状态位,由硬件设置,可读取,写1时清除。
 
};
 
/* The __gpio_to_controller() and __gpio_mask() functions inline to constants
  * with constant parameters; or in outlined code they execute at runtime.
  *
  * You'd access the controller directly when reading or writing more than
  * one gpio value at a time, and to support wired logic where the value
  * being driven by the cpu need not match the value read back.
  *
  * These are NOT part of the cross-platform GPIO interface
  */
 static inline struct gpio_controller *__iomem
 __gpio_to_controller(unsigned gpio)
 {
     void *__iomem ptr;
 
    if (gpio >= DAVINCI_N_GPIO)
         return NULL;
 
    if (gpio < 32)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
     else if (gpio < 64)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
     else if (gpio < 96)
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
     else
         ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);
 
    return ptr;
 }
 
static inline u32 __gpio_mask(unsigned gpio)
 {
     return 1 << (gpio % 32);
 }
 
/* The get/set/clear functions will inline when called with constant
  * parameters, for low-overhead bitbanging. Illegal constant parameters
  * cause link-time errors.
  *
  * Otherwise, calls with variable parameters use outlined functions.
  */
 extern int __error_inval_gpio(void);
 
extern void __gpio_set(unsigned gpio, int value);
 extern int __gpio_get(unsigned gpio);
 
/* Returns zero or nonzero; works for gpios configured as inputs OR
  * as outputs.
  *
  * NOTE: changes in reported values are synchronized to the GPIO clock.
  * This is most easily seen after calling gpio_set_value() and then immediatly
  * gpio_get_value(), where the gpio_get_value() would return the old value
  * until the GPIO clock ticks and the new value gets latched.
  */
 extern int gpio_get_value(unsigned gpio);
 extern void gpio_set_value(unsigned gpio, int value);
 

/* powerup default direction is IN */
 extern int gpio_direction_input(unsigned gpio);
 extern int gpio_direction_output(unsigned gpio, int value);
 
#include <asm-generic/gpio.h>    /* cansleep wrappers */
 
extern int gpio_request(unsigned gpio, const char *tag);
 extern void gpio_free(unsigned gpio);
 
static inline int gpio_to_irq(unsigned gpio)
 {
     return DAVINCI_N_AINTC_IRQ + gpio;
 }
 
static inline int irq_to_gpio(unsigned irq)
 {
     return irq - DAVINCI_N_AINTC_IRQ;
 }
 
#endif                /* __DAVINCI_GPIO_H */
 


 
 
三、修改内核配置文件
 
 
 
在linux-2.6.18_pro500/drivers/char目录下,
 
修改Kconfig文件,在menu "Character devices"下面,加入
 
 
 
config DAVINCI_DM644X_GPIOS
 
       tristate "Davinci DM644x GPIO GPIOs"
 
       depends on ARCH_DAVINCI
 
       help
 
        This option enables support for LEDs and Buzzer connected to GPIO lines
 
        on Ti Davinci DM644x CPUs, such as the DM6446。
 
修改Makefile文件,在128行
 
obj-$(CONFIG_DAVINCI_DM646X_TSIF)       += tsif_control.o tsif_data.o下面,加入:
 
obj-$( DAVINCI_DM644X_GPIOS) += davinci_dm644x_gpios.o
 
选上“Character devices”里的“Davinci DM644x GPIOs”,保存修改后的配置,然后make uImage,对内核的编译;
 
 
 
五、文件系统节点添加
 
        文件系统里,/etc/init.d/rcS文件
 
         # Run /etc/rc.d/rc.local if it exists
 
         [ -x /etc/rc.d/rc.local ] && /etc/rc.d/rc.local
 
        前面加mknod /dev/dm644x_gpios c 199 0或在shell命令行下执行mknod /dev/dm644x_gpios c 199 0;这就是静态分配设备节点的做法。
 
       运行系统,编辑代码
 
     int fdGPIO = open("/dev/dm644x_gpios", 0);
 
     int nLedState(0);
 
     if (fdGPIO>0)
      {
          ioctl(fdGPIO, GPIO_LED_PIN, nLedState);
          nLedState = !nLedState;
      }
 
     反复执行if的这一段代码,可以看到LED灯闪烁。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值