跳转至

03.pinctrl和gpio子系统

pinctrl子系统

pinctrl 子系统主要工作内容如下: 1、获取设备树中 pin 信息。 2、根据获取到的 pin 信息来设置 pin 的复用功能 3、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目录为 drivers/pinctrl。

主要配置的是&iomuxc中的参数

GPIO子系统

pinctrl 配置好以后就是设置 gpio 了

如在节点信息中,配置某个属性

pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;

检查 PIN 是否被其他外设使用,全局搜索GPIO1_IO03和&gpio1 3,注释相关信息

开发步骤

添加设备节点

在根节点“/”下创建 LED 灯节点,节点名为“mini_led”,节点内容如下:

mini_led {
    compatible = "mini-led";
    pinctrl-names = "default";
    pinctrl-0  = <&pinctrl_led>;
    led-gpio   = <&gpio1 3 GPIO_ACTIVE_LOW>;
    status = "okay";
}; 

第 4 行,pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点。 第 5 行,led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平有效。

添加pinctrl相关配置

在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点,节点内容如下所示:

pinctrl_led: ledgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
    >;
};

编译dts,然后用新的dts文件启动Linux

make dtbs

可以进入到/proc/device-tree/base下查看是否有mini_led这个节点信息。

可以进入到/sys/firmware/devicetree/base/alphaled下查看是否有mini_led这个节点的相关信息,可以查看一下 compatible、status 等属性值是否和我们设置的一致。

驱动框架api

在初始化的时候,先获得设备节点

nd = of_find_node_by_path("/mini_led");

然后获得led的编号

int led_gpio;           /* led所使用的GPIO编号        */
/*获取设备树中的gpio属性,得到LED所使用的LED编号 */
led_gpio = of_get_named_gpio(nd, "led-gpio", 0);

通过编号控制gpio的输出,这个可以在文件打开的时候掉用

/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
ret = gpio_direction_output(led_gpio, 1);

然后就是控制gpio的输出电平,可以在write的时候使用

gpiod_set_value(led.led_gpio, val);

驱动主要代码

一般有两种写法,一种使用gpiod,一种使用gpio

gpio写法

struct led_dev{ 
    struct class *class;        
    struct device *device;
    struct device_node  *nd;
    int led_gpio;
    int major;              
    int minor;              
};

struct led_dev led; /* led设备 */

static int led_drv_open(struct inode *inode, struct file *file)
{
    gpio_direction_output(led.led_gpio, 1);
    return 0;
}

static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;
    char val;
    err = copy_from_user(&val, buf, 1);
    gpio_set_value(led.led_gpio, val);
    return 0;
}

static int led_probe(struct platform_device *dev)
{
    led.nd = of_find_node_by_path("/mini_led");
    led.led_gpio = of_get_named_gpio(nd, "led-gpio", 0);

    /*创建字符设备*/
    return 0;
}

static int led_remove(struct platform_device *dev)
{
    /*删除字符设备*/

    return 0;
}

gpiod写法

struct led_dev{ 
    struct class *class;        
    struct device *device;
    struct device_node  *nd;
    struct gpio_desc *led_gpiod;
    int major;              
    int minor;              
};

struct led_dev led; /* led设备 */

static int led_drv_open(struct inode *inode, struct file *file)
{
    gpiod_direction_output(led.led_gpiod, 1);
    return 0;
}

static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;
    char val;
    err = copy_from_user(&val, buf, 1);
    gpiod_set_value(led.led_gpiod, val);
    return 0;
}

static int led_probe(struct platform_device *dev)
{
    led.led_gpiod = gpiod_get(&dev->dev, "led", 0);

    /*创建字符设备*/
    return 0;
}

static int led_remove(struct platform_device *dev)
{
    /*删除字符设备*/

    //释放gpio
    gpiod_put(led.led_gpiod);

    return 0;
}