

GPIO使用
===========

J6X Acore芯片内中共有3个sys有gpio设备，分别是peri, cam和video，每个设备最多有32个gpio引脚，并且每个gpio引脚都支持中断。


.. image:: ./media/gpio_devs.png
   :align: left
   :scale: 40 %
   :alt:


驱动层使用
-------------

GPIO DTS配置
~~~~~~~~~~~~~~~~~~~~~~~~~~

GPIO设备树节点的属性命名方式一般为<names>-gpios或<names>-gpio，举例如下：

.. code:: c

   /**
   * peri_port0表示peri sys的第一个gpio设备
   * 下列设备节点共有四个gpio引脚，分别是：
   *       peri sys的第一个gpio设备 16号引脚(从0开始)
   *       peri sys的第二个gpio设备 17号引脚(从0开始)
   *        cam sys的第一个gpio设备 28号引脚(从0开始)
   *      video sys的第一个gpio设备 18号引脚(从0开始)
   * GPIO_ACTIVE_HIGH表示高电平有效，一般设为GPIO_ACTIVE_HIGH
   */
   gpio-test {
           test-gpios = <&peri_port0 16 GPIO_ACTIVE_HIGH
                         &peri_port1 17 GPIO_ACTIVE_HIGH
                         &cam_port0 28 GPIO_ACTIVE_HIGH
                         &video_port0 18 GPIO_ACTIVE_HIGH>;
   };

GPIO驱动API
~~~~~~~~~~~~~~~~~~~~~~~~~~

包含头文件

.. code:: c

   #include <linux/gpio/consumer.h>


从设备树中获取释放gpio
^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: c

   struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
                                       enum gpiod_flags flags)

   struct gpio_desc *gpiod_get_index(struct device *dev,
                                             const char *con_id, unsigned int idx,
                                             enum gpiod_flags flags)


   struct gpio_desc *gpiod_get_optional(struct device *dev,
                                       const char *con_id,
                                       enum gpiod_flags flags)

   struct gpio_desc *gpiod_get_index_optional(struct device *dev,
                                             const char *con_id,
                                                      unsigned int index,
                                                      enum gpiod_flags flags)


   struct gpio_descs *gpiod_get_array(struct device *dev,
                                             const char *con_id,
                                             enum gpiod_flags flags)


   struct gpio_descs {
         struct gpio_array *info;
         unsigned int ndescs;
         struct gpio_desc *desc[];
   }


   struct gpio_descs *gpiod_get_array_optional(struct device *dev,
                                                      const char *con_id,
                                                      enum gpiod_flags flags)


   struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
                                    enum gpiod_flags flags)

   struct gpio_desc *devm_gpiod_get_index(struct device *dev,
                                          const char *con_id,
                                          unsigned int idx,
                                          enum gpiod_flags flags)

   struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
                                             const char *con_id,
                                             enum gpiod_flags flags)

   struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev,
                                                   const char *con_id,
                                                   unsigned int index,
                                                   enum gpiod_flags flags)

   struct gpio_descs *devm_gpiod_get_array(struct device *dev,
                                          const char *con_id,
                                          enum gpiod_flags flags)

   struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev,
                                                   const char *con_id,
                                                   enum gpiod_flags flags)


   void gpiod_put(struct gpio_desc *desc)


   void gpiod_put_array(struct gpio_descs *descs)



   void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)

   void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)

gpio的使用：方向设置，输出，输入
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: c

   int gpiod_direction_input(struct gpio_desc *desc)
   int gpiod_direction_output(struct gpio_desc *desc, int value)



   int gpiod_get_direction(const struct gpio_desc *desc)



   int gpiod_get_value(const struct gpio_desc *desc);
   void gpiod_set_value(struct gpio_desc *desc, int value);



   int gpiod_get_raw_value(const struct gpio_desc *desc)
   void gpiod_set_raw_value(struct gpio_desc *desc, int value)

   int gpiod_direction_output_raw(struct gpio_desc *desc, int value)


   int gpiod_get_array_value(unsigned int array_size,
                           struct gpio_desc **desc_array,
                           struct gpio_array *array_info,
                           unsigned long *value_bitmap);
   int gpiod_get_raw_array_value(unsigned int array_size,
                                 struct gpio_desc **desc_array,
                                 struct gpio_array *array_info,
                                 unsigned long *value_bitmap);

   int gpiod_set_array_value(unsigned int array_size,
                           struct gpio_desc **desc_array,
                           struct gpio_array *array_info,
                           unsigned long *value_bitmap)
   int gpiod_set_raw_array_value(unsigned int array_size,
                                 struct gpio_desc **desc_array,
                                 struct gpio_array *array_info,
                                 unsigned long *value_bitmap)

可睡眠api的使用和场景
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: c

   /* 一般加_cansleep后缀的可睡眠的api用于i2c，spi或者其他接口的扩展GPIO */

   /* 查看gpio是否可睡眠 */

   int gpiod_cansleep(const struct gpio_desc *desc)

   /* 可睡眠api使用 */
   int gpiod_get_value_cansleep(const struct gpio_desc *desc)
   void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
   int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
   void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)

   int gpiod_get_array_value_cansleep(unsigned int array_size,
                                    struct gpio_desc **desc_array,
                                    struct gpio_array *array_info,
                                    unsigned long *value_bitmap);
   int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
                                    struct gpio_desc **desc_array,
                                    struct gpio_array *array_info,
                                    unsigned long *value_bitmap);

   int gpiod_set_array_value_cansleep(unsigned int array_size,
                                    struct gpio_desc **desc_array,
                                    struct gpio_array *array_info,
                                    unsigned long *value_bitmap)
   int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
                                          struct gpio_desc **desc_array,
                                          struct gpio_array *array_info,
                                          unsigned long *value_bitmap)

gpio描述符和中断号的转换
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


.. code:: c

   int gpiod_to_irq(const struct gpio_desc *desc)

gpio描述符和gpio号的转换
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: c

   int desc_to_gpio(const struct gpio_desc *desc)
   struct gpio_desc *gpio_to_desc(unsigned gpio)


用户层使用
-------------

在用户层可以使用/sys/class/gpio节点来进行相关gpio的操作。

在sys节点下存在如下节点：

.. code:: shell

   #申请gpio
   echo <gpio_num> > /sys/class/gpio/export
   #释放gpio
   echo <gpio_num> > /sys/class/gpio/unexport
   #设置gpio为输出
   #当设置dir为out时，可以向value中echo 1/0，分别表示输入高低电平。
   echo out > /sys/class/gpio/gpio<gpio_num>/direction
   #设置高电平
   echo 1 > /sys/class/gpio/gpio<gpio_num>/value
   #设置低电平
   echo 0 > /sys/class/gpio/gpio<gpio_num>/value

   #设置gpio为输入, 当设置dir为in时，cat value表示输入的值(0-低，1-高)。
   echo in > /sys/class/gpio/gpio<gpio_num>/direction
   #读取gpio的外部值
   cat /sys/class/gpio/gpio<gpio_num>/value


   #查看gpio的debug接口
   cat /sys/kernel/debug/gpio

   #查看gpio和pinctrl的关系
   #可以看到系统的引脚和gpio的号关系
   cat /sys/kernel/debug/pinctrl/<pinctrl_dev>/gpio-ranges



sysfs接口介绍
~~~~~~~~~~~~~~~~~~~~~~~~~~


export&unexport
^^^^^^^^^^^^^^^^^^

/sys/class/gpio/export和/sys/class/gpio/unexport，这两个节点只能写不能读。

用户程序通过写入gpio的编号来向内核申请将某个gpio的控制权导出到用户空间，前提是没有内核代码申请这个gpio端口，如用户申请编号为480的GPIO的命令：

.. code:: shell

   echo 480 > export

上述操作会为480号gpio创建一个节点gpio480，此时/sys/class/gpio目录下边生成一个gpio480的目录。

/sys/class/gpio/unexport和导出的效果相反，比如移除gpio480这个节点操作命令：

.. code:: shell

   echo 480 > unexport #该操作将会移除gpio480这个节点，释放序号为480的gpio。

direction
^^^^^^^^^^^^

其中direction表示gpio端口的方向，读取结果是in或out。也可以对该文件进行写操作，写入out时该gpio设为输出，写入in时该gpio设为输入。


value
^^^^^^^^

当设置dir为in时，cat value表示输入的值(0-低，1-高)。

当设置dir为out时，可以向value中echo 1/0，分别表示输入高低电平。


edge
^^^^^^^^

用户层设置中断时，direction需要设置为in，然后向edge中设置相应的值。

======== ==========================
edge的值 含义
======== ==========================
none     表示引脚为输入，不是中断引脚
rising   表示引脚为中断输入，上升沿触发
falling  表示引脚为中断输入，下降沿触发
both     表示引脚为中断输入，边沿触发
======== ==========================


调试
--------

.. code:: shell

   cat /sys/kernel/debug/gpio

查询上述节点，可以获取到当前系统中正在使用的gpio和所处的状态（in，out，IRQ）。

.. code:: shell

   root@hobot:~# cat /sys/kernel/debug/gpio
   gpiochip4: GPIOs 423-438, parent: i2c/3-0076, 3-0076, can sleep:
   gpio-423 (                    |io-ser-reset0       ) out hi
   gpio-424 (                    |io-ser-reset1       ) out hi
   gpio-425 (                    |io-ser-reset2       ) out hi
   gpio-426 (                    |io-ser-reset3       ) out hi

   gpiochip3: GPIOs 439-455, parent: platform/360b0000.gpio, 360b0000.gpio:

   gpiochip2: GPIOs 456-473, parent: platform/370f5000.gpio, 370f5000.gpio:

   gpiochip1: GPIOs 474-479, parent: platform/39500000.gpio, 39500000.gpio:

   gpiochip0: GPIOs 480-511, parent: platform/394f0000.gpio, 394f0000.gpio:
   gpio-481 (                    |sysfs               ) out hi ACTIVE LOW
   gpio-491 (                    |sysfs               ) in  hi
   gpio-495 (                    |sysfs               ) in  hi
   gpio-496 (                    |sysfs               ) in  hi
   gpio-498 (                    |sysfs               ) in  hi
   gpio-503 (                    |io-ext-reset        ) out lo
   root@hobot:~#


确定gpio-index
---------------

kernel_index = base + offset，base 通过 “/sys/class/gpio” 或者“/sys/kernel/debug/gpio" 获取，offset通过dts获取。

以pin sensor8_err为例：通过查看 hobot-pinctrl-j6e.dtsi，可以知道sensor8_err对应的gpio chip是"video_port0:  gpio@360b0000"，对应的offset是 13。
查看/sys/kernel/debug/gpio，可知“video_port0:  gpio@360b0000”对应的base是439，则sensor8_err对应的gpio kernel index是：439 + 13 = 452。

查看hobot-pinctrl-j6e.dtsi，获取offset和base。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面设备树可知sensor8_err对应video_sensor8_err，对应的gpio chip是"video_port0:  gpio@360b0000"。
video_gnss_int的offset为0，递增计数，则video_sensor8_err的offset为13。

.. code:: shell

   pinctrl_video: pinctrl@36090000 {
      compatible = "horizon,j6e-pinctrl";
      reg = <0x0 0x36090000 0x0 0x1000>,
            <0x0 0x360a0000 0x0 0x1000>;
      pctldev-name = "video";
      status = "okay";

      video_gpio: video_gpio_func {
         pinmux {
            function = "video_gpio";
            pins = "video_gnss_int", "video_peri_rsto",
                     "video_cam_pint", "video_sd_1v8", "video_sd_bus_pow",
                     "video_sensor0_err", "video_sensor1_err",
                     "video_sensor2_err", "video_sensor3_err", "video_sensor4_err",
                     "video_sensor5_err", "video_sensor6_err",
                     "video_sensor7_err", "video_sensor8_err",
                     "video_sensor9_err", "video_sensor10_err", "video_sensor11_err";
         };
         pinconf {
            pins = "video_gnss_int", "video_peri_rsto",
                     "video_cam_pint", "video_sd_1v8", "video_sd_bus_pow",
                     "video_sensor0_err", "video_sensor1_err",
                     "video_sensor2_err", "video_sensor3_err", "video_sensor4_err",
                     "video_sensor5_err", "video_sensor6_err",
                     "video_sensor7_err", "video_sensor8_err",
                     "video_sensor9_err", "video_sensor10_err", "video_sensor11_err";
            drive-strength = <1>;
         };
      };

如这行：gpiochip3: GPIOs 439-455, parent: platform/360b0000.gpio, 360b0000.gpio:
"GPIOs 439-455"代表base是439

.. code:: shell

   cat /sys/kernel/debug/gpio

.. code:: shell

   root@hobot:~# cat /sys/kernel/debug/gpio
   gpiochip4: GPIOs 423-438, parent: i2c/3-0076, 3-0076, can sleep:
   gpio-423 (                    |io-ser-reset0       ) out hi
   gpio-424 (                    |io-ser-reset1       ) out hi
   gpio-425 (                    |io-ser-reset2       ) out hi
   gpio-426 (                    |io-ser-reset3       ) out hi

   gpiochip3: GPIOs 439-455, parent: platform/360b0000.gpio, 360b0000.gpio:

   gpiochip2: GPIOs 456-473, parent: platform/370f5000.gpio, 370f5000.gpio:
   gpio-464 (                    |scl                 ) out lo
   gpio-465 (                    |sda                 ) in  lo
   gpio-466 (                    |scl                 ) out lo
   gpio-467 (                    |sda                 ) in  lo
   gpio-468 (                    |scl                 ) out lo
   gpio-469 (                    |sda                 ) in  lo
   gpio-470 (                    |scl                 ) out lo
   gpio-471 (                    |sda                 ) in  lo
   gpio-472 (                    |scl                 ) out lo
   gpio-473 (                    |sda                 ) in  lo

   gpiochip1: GPIOs 474-479, parent: platform/39500000.gpio, 39500000.gpio:

   gpiochip0: GPIOs 480-511, parent: platform/394f0000.gpio, 394f0000.gpio:
   gpio-495 (                    |scl                 ) out lo
   gpio-496 (                    |sda                 ) in  lo
   gpio-503 (                    |io-ext-reset        ) out lo


