zoukankan      html  css  js  c++  java
  • 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分配规则及触发原理。

  • 相关阅读:
    事件冒泡与捕获
    $(this)到底是个啥
    监测代码的作用及用法
    响应消息的内容类型text/html与绑定的text/xml内容类型不匹配
    MySqlConnection using MySql.Data.dll
    mysql 导出导入数据库
    ORA-06550 PLS-00103:出现符号“DROP”在需要下列之一时:
    注册InstallShield Limited Edition for Visual Studio 时无法选择国家解决方法
    访问LINQ的结果
    WPF TextBox中keydown事件组合键
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3014519.html
Copyright © 2011-2022 走看看