• GPIO控制器驱动- gpio_chip


    前一篇文章中,我们处理了GPIO lines。这些lines通过一个叫做GPIO控制器的特殊设备向系统开放。本章将逐步解释如何为这些设备编写驱动程序,因此包括以下主题:

    • GPIO控制器驱动结构和数据结构
    • GPIO控制器的Sysfs接口
    • GPIO控制器在DT中的表示

    驱动架构和数据结构

    此类设备的驱动程序应提供以下内容:

    • 建立GPIO方向(输入输出)的方法。
    • 用于访问GPIO值的方法(get和set)。
    • 将给定的GPIO映射到IRQ并返回相关的编号的方法。
    • 一个表示对其方法的调用是否可以休眠的标志。这一点非常重要。
    • 一个可选的debugfs转储方法(显示额外的状态,如pullup config)。
    • 一个叫做base number的可选的编号,GPIO编号应该从它开始。如果省略,它将被自动分配。

    在内核中,GPIO控制器被表示为在linux/ GPIO /driver.h中定义的结构体gpio_chip的实例:

    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 (*get_direction)(struct gpio_chip *chip, unsigned offset);
      int (*direction_input)(struct gpio_chip *chip, unsigned offset);
      int (*direction_output)(struct gpio_chip *chip, unsigned offset,
      int value);
      int (*get)(struct gpio_chip *chip,unsigned offset);
      void (*set)(struct gpio_chip *chip, unsigned offset, int value);
      void (*set_multiple)(struct gpio_chip *chip, unsigned long *mask,
      unsigned long *bits);
      int (*set_debounce)(struct gpio_chip *chip, unsigned offset,
      unsigned debounce);
      int (*to_irq)(struct gpio_chip *chip, unsigned offset);
      int base;
      u16 ngpio;
      const char *const *names;
      bool can_sleep;
      bool irq_not_threaded;
      bool exported;
    #ifdef CONFIG_GPIOLIB_IRQCHIP
      /*
       * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip
       * inside the gpiolib to handle IRQs for most practical cases.
       */
      struct irq_chip *irqchip;
      struct irq_domain *irqdomain;
      unsigned int irq_base;
      irq_flow_handler_t irq_handler;
      unsigned int irq_default_type;
    #endif
    #if defined(CONFIG_OF_GPIO)
      /*
       * If CONFIG_OF is enabled, then all GPIO controllers described in the
        * device tree automatically may have an OF translation
       */
      struct device_node *of_node;
      int of_gpio_n_cells;
      int (*of_xlate)(struct gpio_chip *gc,
      const struct of_phandle_args *gpiospec, u32 *flags);
    };

    下面是结构中每个元素的含义:

    • request 是特定芯片激活的可选回调函数。如果提供了,在调用gpio_request()或gpiod_get()时,它会在分配GPIO之前执行。
    • free 是一个可选的回调函数,用于特定芯片的释放。如果提供了,那么在调用gpiod_put()或gpio_free()时,它会在GPIO被释放之前执行。
    • get_direction 在您需要知道方向的时候执行GPIO偏移量。返回值应为0表示out, 1表示in(与GPIOF_DIR_XXX相同),或负错误。
    • direction_input 将信号偏移量offset配置为输入,否则返回错误。
    • get 返回GPIO offset 的值;对于输出信号,这将返回实际感知到的值或0。
    • set 指定一个输出值给GPIO offset。
    • 当需要为 mask 定义的多个信号分配输出值时,调用 set_multiple。如果没有提供,内核将安装一个通用回调函数,它将遍历掩码位并在每个位执行chip->set(i)。

    请看下面的代码,它展示了如何实现这个函数:

    static void gpio_chip_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits)
    {
      if (chip->set_multiple) {
        chip->set_multiple(chip, mask, bits);
      } else {
        unsigned int i;
        /* set outputs if the corresponding mask bit is set */
        for_each_set_bit(i, mask, chip->ngpio)
        chip->set(chip, i, test_bit(i, bits));
      }
    }
    • 如果控制器支持,这个钩子是一个可选的回调函数,用于为指定的GPIO设置防抖时间(GPIO设置为输入时可以设置防抖时间)。
    • to_irq 是一个可选钩子,用于提供GPIO到IRQ的映射。当您想要执行gpio_to_irq()或gpiod_to_irq()函数时,就会调用这个函数。这个实现可能不会休眠。
    • base 标识该芯片处理的第一个GPIO号;或者,如果注册时为负数,内核将自动(动态)分配一个。
    • ngpio 是这个控制器提供的gpio数;它从 base 开始到 (base + ngpio - 1)。
    • names,如果设置的话,对于这个芯片上的GPIOs,必须是一个字符串数组作为一个替代名称来使用。
    • can_sleep 是一个布尔标志,如果get()/set()方法可以休眠,则设置它。对于位于总线上的GPIO控制器(也称为expander),例如I2C或SPI,它的访问可能导致睡眠。这意味着,如果芯片支持IRQ,这些IRQ需要被线程化,因为芯片访问可能会休眠,例如,读取IRQ状态寄存器。对于映射到内存(SoC的一部分)的GPIO控制器,这可以设置为false。
    • irq_not_threads 是一个布尔值标志,如果设置了can_sleep,则必须设置irq_not_threads,但是IRQs不需要被线程化。

    每个芯片导出了一些信号,在方法调用中通过0 (ngpio - 1)范围内的偏移值来识别。当这些信号通过诸如gpio_get_value(gpio)之类的调用被引用时,偏移量通过gpio数减去基数(base)来计算。

    在定义了每个回调函数并设置了其他字段之后,您应该在配置的结构gpio_chip结构上调用gpiochip_add(),以便将控制器注册到内核。当需要注销时,请使用gpiochip_remove()。你可以发现,编写自己的GPIO控制器驱动程序是多么容易。

    一个适用于MCP23016 I2C的GPIO控制器驱动程序来自microchip的I/O扩展器,其数据手册可在 http://ww1.microchip.com/downloads/en/DeviceDoc/20090C.pdf 上获得。

    要编写GPIO controller驱动程序,你需要包含有以下的头文件:

    #include <linux/gpio.h>

    下面是控制器驱动程序的部分摘录:

    #define GPIO_NUM 16
    struct mcp23016 {
      struct i2c_client *client;
      struct gpio_chip chip;
    };
    static int mcp23016_probe(struct i2c_client *client, const struct i2c_device_id *id) {   struct mcp23016 *mcp;   if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))     return -EIO;
      mcp
    = devm_kzalloc(&client->dev, sizeof(*mcp), GFP_KERNEL);   if (!mcp)     return -ENOMEM;
      mcp
    ->chip.label = client->name;   mcp->chip.base = -1;   mcp->chip.dev = &client->dev;   mcp->chip.owner = THIS_MODULE;   mcp->chip.ngpio = GPIO_NUM; /* 16 */   mcp->chip.can_sleep = 1; /* may not be accessed from atomic context */   mcp->chip.get = mcp23016_get_value;   mcp->chip.set = mcp23016_set_value;   mcp->chip.direction_output = mcp23016_direction_output;   mcp->chip.direction_input = mcp23016_direction_input;   mcp->client = client;   i2c_set_clientdata(client, mcp);
      
    return gpiochip_add(&mcp->chip); }

    要从控制器驱动程序中请求一个自有的GPIO,你不应该使用gpio_request()。GPIO驱动程序可以使用以下函数来请求和释放描述符,而不必永远被固定在内核上:

    struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc, const char *label)
    void gpiochip_free_own_desc(struct gpio_desc *desc)

    使用gpiochip_request_own_desc()请求的描述符必须使用gpiochip_free_own_desc()释放。

    Pin controller指南

    取决于你写驱动程序的控制器,你可能需要实现一个引脚控制操作来处理引脚复用,配置,等等:

    • 对于只能做简单GPIO的引脚控制器,一个简单的结构gpio_chip就足够处理它了。没有必要建立一个struct pinctrl_desc结构,只需写个GPIO控制器驱动程序。
    • 如果控制器可以在GPIO功能之上产生中断,必须建立一个irq_chip结构并注册到IRQ子系统。
    • 对于一个具有引脚复用、高级引脚驱动强度和复杂偏置的控制器,您应该设置以下三个接口:
      • struct gpio_chip
      • struct irq_chip
      • struct pinctrl_desc,内核文档中有很好的解释Documentation/pinctrl.txt

    GPIO控制器的Sysfs接口

    gpiochip_add()成功后,将创建一个路径为/sys/class/gpio/gpiochipX/的目录条目,其中X是gpio控制器base(提供以#X开始的gpio的控制器),具有以下属性:

    • base,其值与X相同,对应于gpio_chip.base(如果静态分配)并且是这个芯片管理的第一个GPIO。
    • label,它是为诊断提供的(并不总是唯一的)。
    • ngpio,它告诉了这个控制器提供了多少gpio (N 到 N + ngpio - 1).这与 gpio_chip.ngpios 中定义的相同。

    以上所有属性都是只读的。

    GPIO控制器和DT

    在DT中声明的每个GPIO控制器都必须具有 gpio-controller 的布尔属性集。一些控制器提供映射到GPIO的IRQs。在这种情况下,也应该设置interrupt-cells属性;通常使用2,但这取决于需要。第一个 cell 是引脚号码,第二个 cell 代表中断标志。

    应该设置gpio-cells,以确定使用多少个cell来描述GPIO指示符。通常使用<2>,第一个cell 用来标识GPIO号,第二个cell 用来标识标志。实际上,大多数非内存映射的GPIO控制器不使用标记:

    expander_1: mcp23016@27 {
      compatible = "microchip,mcp23016";
      interrupt-controller;
      gpio-controller;
      #gpio-cells = <2>;
      interrupt-parent = <&gpio6>;
      interrupts = <31 IRQ_TYPE_LEVEL_LOW>;
      reg = <0x27>;  /* i2c slave address */
      #interrupt-cells=<2>;
    };

    上面的示例是我们的一个 gpio-controller设备(mcp23016)的设备树节点。

    本文是编写GPIO控制器驱动程序的基础,它解释了这种设备主要用到的结构。

  • 相关阅读:
    apk应用签名获取
    git强制推送命令
    Maven 将本地jar包添加到本地仓库
    关于Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin的解决方案
    启动React-Native项目步骤
    Git初始化本地项目并提交远程仓库基础操作
    You have not accepted the license agreements of the following SDK components: [Android SDK Build-Tools 26.0.1, Android SDK Platform 26]
    kenkins安装
    Linux关闭防火墙和SELinux
    Linux下nginx安装配置
  • 原文地址:https://www.cnblogs.com/wanglouxiaozi/p/14480296.html
Copyright © 2020-2023  润新知