上周五总监提了个需求,希望设备正常运行时能不断闪烁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. <[email protected]> * * 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. <[email protected]> * * 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灯闪烁。