Fork me on GitHub
gpio框架及处理流程分析

gpio框架及处理流程分析gpio作为一种通用的IO接口,使用方法主要如下:
----------------------------------------------------------------------------------------------
Gpio:每个 GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中“球珠”的一个位
标准头文件  <linux/gpio.h> [对外接口]
其中根据是否定义CONFIG_GENERIC_GPIO判断系统是否支持gpio
头文件为 <asm/gpio.h>;实现文件为 <driver/gpio/gpiolib.c>

步骤
1. gpio_request(gpio_num, "xx gpio");  申请GPIO, 返回0为申请成功,否则失败。
2. 设置gpio方向:
int gpio_direction_input(unsigned gpio);                      //设置为输入
int gpio_direction_output(unsigned gpio, int value);          //设置为输出,并初始化值为value.
3. 获取/设置gpio值:    int gpio_cansleep(unsigned gpio); 
a.不可睡眠: 
gpio_get_value(unsigned gpio);                              //返回value
gpio_set_value(unsigned gpio, int value);                    //设置值
b.可睡眠:(对于有些挂载在I2C,SPI总线上的扩展GPIO,读写操作可能会导致睡眠,因此不能在中断函数中使用。使用下面的函数以区别于正常的GPIO)
int gpio_get_value_cansleep(unsigned gpio);                  //输入端口:返回零或非零,可能睡眠
void gpio_set_value_cansleep(unsigned gpio, int value);       //输出端口:可能睡眠
4. void gpio_free(unsigned gpio);                             //释放GPIO
5. int gpio_is_valid(int number);                             //检测此gpio口是否有效
批量初始化方法:
申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));

导出gpio到用户空间:int gpio_export(unsigned gpio, bool direction_may_change);
创建一个sysfs连接到已导出的GPIO节点:
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
取消导出:void gpio_unexport();

Gpio设置中断:
    gpio  --->  irq        int gpio_to_irq(unsigned gpio);
    首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误吗)。返回编号调用:
request_irq()和free_irq()。
    Irq ---> gpio        int irq_to_gpio(unsigned irq);
    返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)
    
-----------------------------------------------------------------------------------------    
gpiolib.c (gpio框架)   drivers/gpio/gpiolib.c + include/asm-generic/gpio.h [对gpio chip接口]
-----------------------------------------------------------------------------------------
gpio_chip作为一个接口负责框架层与控制器层的通讯,主要关注点有:
其申请/释放/方向/获取输入/设置输出/转irq/base+ngpio[见第三部分控制器驱动]

在框架层的主要关注点在:
1. 如何分配不同chip的gpio域
2. 如何管理隶属与不同chip的gpio,并反向追溯到chip以调用控制器的具体寄存器操作
3. 统一提前管理了哪些gpio状态以及是否有必要

在gpiochip_add()中是对gpio chip的注册,并插入到框架gpio的管理中,

全局变量 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
gpio_desc作为整个系统的gpio的管理者,主要包含两个成员:chip 与 flags.
flags为框架层对gpio的整体管理标识,起MASK的作用。[有:是否已申请/是否是输出/是否保留等]

插入的规则实现在:gpiochip_find_base(int ngpio)
从后往前遍历全局gpio desc, 只要是非保留gpio且无宿主chip的连续gpio的空间起址作为base, ngpio则依次向下扩展。
简单的初始化:挂上对应的chip,检查是否有设置输入函数,没有则设置 IS_OUT位到flags.
[其中关于gpio的设备树管理暂时不予关注]

request流程: 检查对应gpio的flags是否FLAG_REQUESTED,如果未被request则调用chip的request接口实现芯片级别的调用。
free流程: 确认已经被REQUESTED, 后启用芯片级的free.

从系统gpio接口传递下来的gpio均是以base为基址,而传递到芯片上都是回归到原始gpio号
------------------------------------------------------------------------------------------
SC8810 gpio控制器驱动
-------------------------------------------------------------------------------------------
这里是整个gpio系统的核心,初步总结需要关注以下几点:
chip支持的gpio section如何划分
gpio如何配置使能即芯片如何管理众多gpio口的多个标识位的功能选项
申请/释放/方向/获取输入/设置输出/转irq/base+ngpio的处理流程及原理
gpio对应irq号的分配及映射规则
配置一个系统gpio需要的必要步骤

section:(GPIO_BASE:0xE0031000/SPRD_MISC_BASE:0xE0037000)
     {   (GPIO_BASE + 0*0x80),    0x10,    GPIO_SECTION_GPIO    },
     {   (GPIO_BASE + 1*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 2*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 3*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 4*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 5*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 6*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 7*0x80),    0x10,    GPIO_SECTION_GPIO    },
    {   (GPIO_BASE + 8*0x80),    0x10,    GPIO_SECTION_GPIO    },

    {   (SPRD_MISC_BASE + 0x480),   0x10, GPIO_SECTION_GPIO    },
    {   (SPRD_MISC_BASE + 0x4c0),   0xe,  GPIO_SECTION_GPIO    },

当获取一个gpio号后,需要获取的基本信息为:在哪个section/偏移量是多大/是何种芯片gpio
获取方法: 
a.((gpio_id>>4) -1) * 0x80 + (u32) GPIO_BASE;
b.gpio_id & 0xF
c.gpio是在数字芯片上还是模拟芯片上:
#define NR_D_DIE_GPIOS 147
即:芯片上的gpio号小于147即位于数字芯片,否则位于模拟芯片

在一个芯片的整个寄存器内存中,采取以section + 功能的管理方式,也就是说对于同一个gpio的不同功能配置需要通过三个步骤,第一首先
需要找到段寄存器(section的基址);第二步是功能偏移的管理寄存器(功能偏移),第三步是根据段内偏移定位到某一位(bit)来配置。

注:
根据不同类型的gpio类型:
enum gpio_section_type {
    GPIO_SECTION_GPI = 0x0,
    GPIO_SECTION_GPO,
    GPIO_SECTION_GPIO,
    GPIO_SECTION_INVALID
};
其相应的功能偏移页不同。

申请:设置其功能寄存器的GPIO_DMSK功能偏移,在对应段内偏移处置位。
释放:清除对应MASK位
输出方向:三个步骤,1.设置GPIO_DIR对应位为1;2.清除GPIO_INEN对应位;3.设置GPIO_DATA对应位为需要输出的值。
输入方向:第一二步相反,无第三步。
设置及获取值:直接设置/读取GPIO_DATA功能寄存器[需要检查一些相关的方向等信息]

to irq
全局的映射数组来管理所有的gpio与irq的映射
static struct gpio_irq_map gpio_irq_table[NR_GPIO_IRQS];
映射规则是:从0-10[仅限映射10个中断号],遍历映射表找到第一个gpio offset相同的表项,返回其对应的irq值。
方向 to gpio 一般kernel不支持使用,但实现原理与上相同。

在gpio芯片管理中,最重要的一块就是irq的管理,包括irq的所有属性管理,如:irq屏蔽使能/触发条件等等。
下一篇笔记将详细描述 kernel irq的管理框架以及gpio的irq分配规则及触发原理。

posted on 2013-04-11 15:01  HackerVirus  阅读(580)  评论(0编辑  收藏  举报