//GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq...
//然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[].
//当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。
/*gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号 去 request, dataout ,datain, free. 这时会调用gpio_chip中具体的实现。*/
寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()
gpio是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。
数据状态,输入输出方向,清零,中断(那个边沿触发), 一般是一组(bank)一组的。
//****************linux 中 GPIO模型****************************************//
注册方法:
1:struct gpio_chip: 表示一个gpio controller.通过这个结构抽象化所有的 GPIO源,而让板上其它的模块可以用相同的接口调用使用这些GPIO。
2: struct gpio_desc: 表示一个gpio口,含对应的 gpio_chip.
3: ARCH_NR_GPIOS: 与板相关的GPIO口数量,即是全局GPIO数组:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
4: 注册 gpio_chip时,就是根据 chip 的数据 修改全局 GPIO数组中 gpio_desc 字段(chip, flags)。
//************* twl4030 中 GPIO的实现 *******************//
首先 twl4030中的 GPIO模块: 在 twl4030的驱动注册时,probe中,生成了GPIO对应的 i2c_client 与 platform_device.
twl4030,当加载GPIO的驱动(匹配platform_device)时,首先进行对应的设置,GPIO设置。
然后设置结构: gpio_chip: twl_gpiochip: gpio_twl4030_pulls,pio_twl4030_debounce,
注册 之:ret = gpiochip_add(&twl_gpiochip);//添加到全局的 gpio_desc数组中。即是设置全局gpio_desc中这个gpio_chip对应的几个gpio的字段。
setup之:status = pdata->setup(&pdev->dev, pdata->gpio_base, TWL4030_GPIO_MAX);
setup(...) ==》 twl4030_mmc_init(mmc); //在添加完gpio后,进行探测MMC,然后注册之。Register MMC devices.
//*************** omap3430的实现 注册 GPIO ******************//
static int __init _omap_gpio_init(void)
gpio_bank_count: 多少组gpio组。
这里同时 注册了对应的中断处理例程:
bank 主中断处理:gpio_irq_handler
bank 下的子GPIO全设为:handle_simple_irq。
//注册函数,即是把根据chip设备全局GPIO数组的对应gpio的字段。
int gpiochip_add(struct gpio_chip *chip)
{
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip; //设置全局的gpio_desc中的 对应的成员的 chip 为,注册的这个chip.
gpio_desc[id].flags = !chip->direction_input ? (1 << FLAG_IS_OUT): 0; //及gpio方向。
}
//
"通用调用方法:"
static int __init wl127x_vio_leakage_fix(void)
{
int ret = 0;
ret = gpio_request(WL127X_BTEN_GPIO, "wl127x_bten");
if (ret < 0) {
printk(KERN_ERR "wl127x_bten gpio_%d request fail",
WL127X_BTEN_GPIO);
goto fail;
}
gpio_direction_output(WL127X_BTEN_GPIO, 1);
mdelay(10);
gpio_direction_output(WL127X_BTEN_GPIO, 0);
udelay(64);
gpio_free(WL127X_BTEN_GPIO);
fail:
return ret;
}
//********************//
一. GPIO物理起始地址: 对于要控制GPIO的嵌入式编程人员来讲,GPIO的物理地址是必须要搞清楚的。STB02500SOC中GPIO的物理基本地址是:0x4006 0000空间长度是:0x44。(gpio_base = 0x4006 0000 len = 0x44)
二. GPIO寄存器:
GPIO共有 9个寄存器分别是:
1. GPO: GPO(32bit)寄存器用于设置或清除对应的GPIO引脚值(高,或者低)。具体控制时需要先读出GPIO引脚的值,然后再用”或”,”与”进行相应的逻辑设置和清除操作,很显然这是非原子的操作,在多任务系统里,是有可能发生意外的,需要程序员在软件上实现相应的原子操作,这和ARM系统的 I/O操作相比应该是比较落后的)。
2. GPTC: GPTC(32bit)寄存器用于设置对应的GPIO引脚是输出还是输入(这样比较好理解些)程序设置相应的bit为1对应的GPIO引脚就是输出否则就是输入(呵呵,这里还要声明一下下,该寄存器要起作用还要设置好GPTS寄存器)。
3. GPOS: GPOS(64bit)寄存器用于控制GPIO相应的引脚是由GPO驱动还是有Alt_Output_x(1,2,3)驱动(就这样简单的理解吧,嘿嘿)。
4. GPTS: GPTS(64bit)这个寄存器功能还是比较难说清楚的呀,简单的讲如果需要用GPO驱动GPIO寄存器,就需要设置对应2bit为00选择GPTC作为输出使能控制器,这样GPTC寄存器才能够起作用的。
5. GPOD: GPOD(32bit)这个寄存器是比较单纯的,如果对应位设置1相应的GPIO引脚就处于漏极开路状态,这样可以适应多电压系统,和多输出驱动单输入系统。
6. GPI: GPI(32bit) 就是相应GPIO引脚的值(高电平,还是低电平)。
7. GPIS1:我暂时还没有研究。
8. GPIS2: 我暂时还没有研究。
9. GPIS3: 我暂时还没有研究。
注意:这里要特别声明:这里的GPIO0对应GPO的bit0,GPO的bit0实际是32位GPOd的bit31(以ARM系统来看)。
三:
对于在不支持虚拟内存的操作系统和根本就没有使用操作系统的系统里操作GPIO直接读写对应的GPIO寄存器就可以啦,但是在linux这样的操作系统下,你必须编写一个操作GPIO的驱动,或者是使用一些变通的技巧来操作GPIO.
目前我所知道的在linux下操作GPIO有两种方法:
1. 编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据ioctl命令进行GPIO寄存器的读写,并把结果回送到应用层.
2. 在应用层使用mmap函数在应用层获得GPIO物理基地址对应的虚拟地址指针,然后使用这个指针来读写GPIO寄存器
总结:
虽然GPIO寄存器很多但是熟悉后,使用起来也很简单的,关键是要理解透每个GPIO引脚的功能,和个寄存器的功能特点。其实如果只是做简单的I/O输入输出控制(大多数单片机开发人员最常用用到),只要熟悉 GPO,GPI,GPTC就可以啦。
GPIO是一种通用I/O协议,在读写的时候,需要对特定寄存器进行一些设置,在规范描述的时候,一般会说GPIO 0-12,GPIO 38-43等说法,GPIO和一般的寄存器读写很不一样,因此它是两个号码控制一个状态。
GPIO的一般读写顺序为:
1、首先设置需要进行操作的GPIO范围;
2、在读/写操作前,对GPIO的选择寄存器设置为1/0;
3、读/写寄存器(基地址+偏移)。
//*********************************************//
//***********************************************//
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //全局的gpio_chip 存放数组
static struct gpio_chip twl_gpiochip = {
.label = "twl4030",
.owner = THIS_MODULE,
.request = twl_request,
.free = twl_free,
.direction_input = twl_direction_in,
.get = twl_get,
.direction_output = twl_direction_out,
.set = twl_set,
.to_irq = twl_to_irq,
.can_sleep = 1,
};
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
* While any GPIO is requested, its gpio_chip is not removable;
* each GPIO's "requested" flag serves as a lock and refcount.
*/
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
* @dev: optional device providing the GPIOs
* @owner: helps prevent removal of modules exporting active GPIOs
* @request: optional hook for chip-specific activation, such as
* enabling module power and clock; may sleep
* @free: optional hook for chip-specific deactivation, such as
* disabling module power and clock; may sleep
* @direction_input: configures signal "offset" as input, or returns error
* @get: returns value for signal "offset"; for output signals this
* returns either the value actually sensed, or zero
* @direction_output: configures signal "offset" as output, or returns error
* @set: assigns output value for signal "offset"
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
* implementation may not sleep
* @dbg_show: optional routine to show contents in debugfs; default code
* will be used when this is omitted, but custom code can show extra
* state (such as pullup/pulldown configuration).
* @base: identifies the first GPIO number handled by this chip; or, if
* negative during registration, requests dynamic ID allocation.
* @ngpio: the number of GPIOs handled by this controller; the last GPIO
* handled is (base + ngpio - 1).
* @can_sleep: flag must be set iff get()/set() methods sleep, as they
* must while accessing GPIO expander chips over I2C or SPI
*
* A gpio_chip can help platforms abstract various sources of GPIOs so
* they can all be accessed through a common programing interface.
* Example sources would be SOC controllers, FPGAs, multifunction
* chips, dedicated GPIO expanders, and so on.
*
* Each chip controls a number of signals, identified in method calls
* by "offset" values in the range 0..(@ngpio - 1). When those signals
* are referenced through calls like gpio_get_value(gpio), the offset
* is calculated by subtracting @base from the gpio number.
*/
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
int (*request)(struct gpio_chip *chip,
unsigned offset);
void (*free)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);
int (*get)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base;
u16 ngpio;
unsigned can_sleep:1;
unsigned exported:1;
};