一、pinctrl与gpio子系统概述

1.1 gpio子系统

主要负责读/写引脚值、设置引脚为输入或输出模式、中断等基本操作。

1.2 pinctrl子系统

管理所有可控制的引脚,包括引脚的复用功能、电气特性等。‌

1.2.1 pinctrl子系统的主要功能包括

  1. ‌引脚复用‌:大多数引脚都可以通过配置寄存器来选择复用成不同的功能。
  2. ‌引脚配置‌:包括设置引脚的上拉/下拉电阻、速度、驱动能力等电气特性。

1.2.2与gpio子系统的关系

        部分soc gpio驱动会调用pinctrl系统接口设置pin脚状态,gpio控制器下gpio-ranges属性会引用都pinctrl设备节点,同步gpio-ranges到pinctrl dev的gpio_ranges链表

二、pinctrl子系统

2.1 pinctrl节点设备树配置

rk:

pinctrl: pinctrl {
        compatible = "rockchip,rk3568-pinctrl";
        rockchip,grf = <&grf>;
        rockchip,pmu = <&pmugrf>;
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        gpio0: gpio@fdd60000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfdd60000 0x0 0x100>;
            interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;

            gpio-controller;
            #gpio-cells = <2>;
            gpio-ranges = <&pinctrl 0 0 32>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };

......

ti:

    main_pmx0: pinctrl@11c000 {
        compatible = "pinctrl-single";//通用pinctrl驱动
        /* Proxy 0 addressing */
        reg = <0x0 0x11c000 0x0 0x2b4>;
        #pinctrl-cells = <1>;
        pinctrl-single,register-width = <32>;
        pinctrl-single,function-mask = <0xffffffff>;
    };

......

&main_pmx0 {
......

    gpio_main_pins_default: gpio-main-pins-default {
        pinctrl-single,pins = <
            J721E_IOPAD(0x18c, PIN_INPUT, 7) /* (V23) RGMII6_RX_CTL.GPIO0_98 */
            J721E_IOPAD(0x1a4, PIN_INPUT, 7) /* (W26) RGMII6_RXC.GPIO0_104 */
        >;
    };

......

1、pinctrl驱动为soc厂商单独驱动

2、通用pinctrl驱动,compatible="pinctrl-single",引脚节点属性用pinctrl-single,pins进行配置

2.2 pinctrl子系统注册过程

2.2.1 注册过程

1、准备struct pinctrl_desc数据结构,pinctrl_pin_desc数据类里面会存所有的pin脚,包括每个pin脚的名字,每个pin脚在该pin controller的编号,npins指示多少个pin脚,pctlops,pmxops,confops提供相应的操作函数。

/**

 * struct pinctrl_desc - pin controller descriptor, register this to pin

 * control subsystem

 * @name: name for the pin controller

 * @pins: an array of pin descriptors describing all the pins handled by

 *  this pin controller

 * @npins: number of descriptors in the array, usually just ARRAY_SIZE()

 *  of the pins field above

 * @pctlops: pin control operation vtable, to support global concepts like

 *  grouping of pins, this is optional.

 * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver

 * @confops: pin config operations vtable, if you support pin configuration in

 *  your driver

 * @owner: module providing the pin controller, used for refcounting

 * @num_custom_params: Number of driver-specific custom parameters to be parsed

 *  from the hardware description

 * @custom_params: List of driver_specific custom parameters to be parsed from

 *  the hardware description

 * @custom_conf_items: Information how to print @params in debugfs, must be

 *  the same size as the @custom_params, i.e. @num_custom_params

 */

struct pinctrl_desc {

    const char *name;

    const struct pinctrl_pin_desc *pins;

    unsigned int npins;

    const struct pinctrl_ops *pctlops;

    const struct pinmux_ops *pmxops;

    const struct pinconf_ops *confops;

    struct module *owner;

#ifdef CONFIG_GENERIC_PINCONF

    unsigned int num_custom_params;

    const struct pinconf_generic_params *custom_params;

    const struct pin_config_item *custom_conf_items;

#endif

};

/**

 * struct pinctrl_dev - pin control class device

 * @node: node to include this pin controller in the global pin controller list

 * @desc: the pin controller descriptor supplied when initializing this pin

 *  controller

 * @pin_desc_tree: each pin descriptor for this pin controller is stored in

 *  this radix tree

 * @pin_group_tree: optionally each pin group can be stored in this radix tree

 * @num_groups: optionally number of groups can be kept here

 * @pin_function_tree: optionally each function can be stored in this radix tree

 * @num_functions: optionally number of functions can be kept here

 * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,

 *  ranges are added to this list at runtime

 * @dev: the device entry for this pin controller

 * @owner: module providing the pin controller, used for refcounting

 * @driver_data: driver data for drivers registering to the pin controller

 *  subsystem

 * @p: result of pinctrl_get() for this device

 * @hog_default: default state for pins hogged by this device

 * @hog_sleep: sleep state for pins hogged by this device

 * @mutex: mutex taken on each pin controller specific action

 * @device_root: debugfs root for this device

 */

struct pinctrl_dev {

    struct list_head node;

    struct pinctrl_desc *desc;

    struct radix_tree_root pin_desc_tree;

#ifdef CONFIG_GENERIC_PINCTRL_GROUPS

    struct radix_tree_root pin_group_tree;

    unsigned int num_groups;

#endif

#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS

    struct radix_tree_root pin_function_tree;

    unsigned int num_functions;

#endif

    struct list_head gpio_ranges;

    struct device *dev;

    struct module *owner;

    void *driver_data;

    struct pinctrl *p;

    struct pinctrl_state *hog_default;

    struct pinctrl_state *hog_sleep;

    struct mutex mutex;

#ifdef CONFIG_DEBUG_FS

    struct dentry *device_root;

#endif

};

    ctrldesc->name = "rockchip-pinctrl";

    ctrldesc->owner = THIS_MODULE;

    ctrldesc->pctlops = &rockchip_pctrl_ops;

    ctrldesc->pmxops = &rockchip_pmx_ops;

    ctrldesc->confops = &rockchip_pinconf_ops;

    pindesc = devm_kcalloc(&pdev->dev,

                   info->ctrl->nr_pins, sizeof(*pindesc),

                   GFP_KERNEL);

    if (!pindesc)

        return -ENOMEM;

    ctrldesc->pins = pindesc;

    ctrldesc->npins = info->ctrl->nr_pins;

    pdesc = pindesc;

    for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {

        pin_bank = &info->ctrl->pin_banks[bank];

        for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {

            pdesc->number = k;

            pdesc->name = kasprintf(GFP_KERNEL, "%s-%d",

                        pin_bank->name, pin);

            pdesc++;

        }

    }

2、将pinctrl_desc数据结构,私有结构info传递给devm_pinctrl_register进行注册,会返回一个struct pinctrl_dev,所有信息填充在pinctrl_dev,可以将pinctrl_dev看作是pin controller的一个实例。

info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);

2.2.2 devm_pinctrl_register函数说明

devm_pinctrl_register---->pinctrl_register------->pinctrl_init_controller
                                                                       |
                                                                       |
                                                                        ----->pinctrl_enable

static struct pinctrl_dev *pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev,

            void *driver_data)

1、分配一个pinctrl_dev

pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);

2、填充pinctrl_dev,前面传递过来的描述pin conroller的数据结构pinctrl_desc,pinctrl的驱动的私有数据结构都填充到pinctrl_dev,dev赋值为设备树pinctrl节点设备

    /* Initialize pin control device struct */

    pctldev->owner = pctldesc->owner;

    pctldev->desc = pctldesc;

    pctldev->driver_data = driver_data;

.....

    pctldev->dev = dev;

3、初始化三个基数树,两个链表,

pin_desc_tree:存放描述pin的数据结构struct pin_desc,pctldesc->pins也存放有数组方式存放的描述pin的struct pinctrl_pin_desc

pin_group_tree:存放struct group_desc的基数树,某个功能的pin组信息,map转setting的时候,枚举比对name,拿到group的全局编号,私有驱动可以自己的方式存放

pin_function_tree:存放struct function_desc,某个功能的描述信息,map转setting的时候,枚举比对name,拿到func的全局编号,私有驱动可以自己的方式存放

node:挂载到全局变量pinctrldev_list,创建pinctrl的时候会通过pin脚节点查找父目录pinctrl dev节点,枚举pinctrldev_list,比对of_node来查找pin脚节点对应的pinctrl dev

gpio_ranges:struct pinctrl_gpio_range信息挂载链表

list_add_tail(&pctldev->node, &pinctrldev_list);

    INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);

#ifdef CONFIG_GENERIC_PINCTRL_GROUPS

    INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL);

#endif

#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS

    INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL);

#endif

    INIT_LIST_HEAD(&pctldev->gpio_ranges);

    INIT_LIST_HEAD(&pctldev->node);

4、将pctldesc->pins以数组存放的描述pin的数据结构struct pinctrl_pin_desc以基数树的形式再存到pctldev->pin_desc_tree中

ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);

......

static int pinctrl_register_pins(struct pinctrl_dev *pctldev,

                 const struct pinctrl_pin_desc *pins,

                 unsigned num_descs)

{

    unsigned i;

    int ret = 0;

    for (i = 0; i < num_descs; i++) {

        ret = pinctrl_register_one_pin(pctldev, &pins[i]);

        if (ret)

            return ret;

    }

    return 0;

}

......

static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,

                    const struct pinctrl_pin_desc *pin)

{

    struct pin_desc *pindesc;

    pindesc = pin_desc_get(pctldev, pin->number);

    if (pindesc) {

        dev_err(pctldev->dev, "pin %d already registered\n",

            pin->number);

        return -EINVAL;

    }

    pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);

    if (!pindesc)

        return -ENOMEM;

    /* Set owner */

    pindesc->pctldev = pctldev;

    /* Copy basic pin info */

    if (pin->name) {

        pindesc->name = pin->name;

    } else {

        pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", pin->number);

        if (!pindesc->name) {

            kfree(pindesc);

            return -ENOMEM;

        }

        pindesc->dynamic_name = true;

    }

    pindesc->drv_data = pin->drv_data;

    radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);

    pr_debug("registered pin %d (%s) on %s\n",

         pin->number, pindesc->name, pctldev->desc->name);

    return 0;

}

2.2.3 各个数据结构关系

三、消费者使用pinctrl方法

        前面章节讲的是pinctrl控制器怎么注册到系统,控制器的引脚数量,引脚分组,引脚的复用功能、电气特性寄存器信息,操作函数等注册到系统中,本章节说明作为消费者,其他驱动或内核代码怎么使用pinctrl配置复用功能及电气特性,通过使用来了解pinctrl架构为什么这样设计。

3.1 设备树中配置pinctrl处理过程

3.1.1 要使用pinctrl配置引脚,怎么配置设备树

要使用pinctrl配置引脚的复用功能及电气特性,需要在要使用的设备节点里加上属性pinctrl-0、 pinctrl-1等, 命名pinctrl-names = "default","......"等,pinctrl-0取名对应pinctrl-names的第一个字符串,pinctrl-1取名对应pinctrl-names第二个字符串,如果没有"init"名的pinctrl配置,那么会在driver_probe_device--->really_probe--->pinctrl_bind_pins调用时配置成"default"的pinctrl的配置。
pinctrl-x所引用的pins group设备节点必须在pinctrl设备节点里面,pins group设备节点怎么样写取决于pinctrl驱动,解析pins group设备节点是调用驱动的dt_node_to_map函数处理的

pins脚功能处理

&main_pmx0 {

......

        main_i2c0_pins_default: main-i2c0-pins-default {
        pinctrl-single,pins = <
            J721E_IOPAD(0x220, PIN_INPUT_PULLUP, 0) /* (AC5) I2C0_SCL */
            J721E_IOPAD(0x224, PIN_INPUT_PULLUP, 0) /* (AA5) I2C0_SDA */
        >;
    };

......

}

&main_i2c0 {
    pinctrl-names = "default";
    pinctrl-0 = <&main_i2c0_pins_default>;
    clock-frequency = <400000>;
};

3.1.2 配置完后,内核是怎么处理的

设备注册的时候

device_add--->bus_probe_device--->device_initial_probe--->__device_attach--->__device_attach_driver--->driver_probe_device--->really_probe--->pinctrl_bind_pins

driver注册的时候

driver_register--->bus_add_driver--->driver_attach--->__driver_attach--->device_driver_attach--->driver_probe_device--->really_probe--->pinctrl_bind_pins

在driver或device注册过程中,匹配到合适的设备或驱动调用probe的函数里会调用pinctrl_bind_pins,设备节点里的配置的pinctrl-x主要在这里进行自动引脚配置。

int pinctrl_bind_pins(struct device *dev)

1、struct device结构体有个struct dev_pin_info    *pins变量,用来存放设备节点的pinctrl信息的,第一步先获取pinctrl

struct dev_pin_info {

    struct pinctrl *p;

    struct pinctrl_state *default_state;

    struct pinctrl_state *init_state;

#ifdef CONFIG_PM

    struct pinctrl_state *sleep_state;

    struct pinctrl_state *idle_state;

#endif

};

......

/**

 * struct pinctrl - per-device pin control state holder

 * @node: global list node

 * @dev: the device using this pin control handle

 * @states: a list of states for this device

 * @state: the current state

 * @dt_maps: the mapping table chunks dynamically parsed from device tree for

 *  this device, if any

 * @users: reference count

 */

struct pinctrl {

    struct list_head node;

    struct device *dev;

    struct list_head states;

    struct pinctrl_state *state;

    struct list_head dt_maps;

    struct kref users;

};

    dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);

    if (!dev->pins)

        return -ENOMEM;

    dev->pins->p = devm_pinctrl_get(dev);

struct pinctrl *devm_pinctrl_get(struct device *dev)

1.1、先看看是不是已经存在p = find_pinctrl(dev),先从全局链表pinctrl_list查找,以p->dev == dev作匹配条件

1.2、没找到创建一个pinctl

return create_pinctrl(dev, NULL);

static struct pinctrl *create_pinctrl(struct device *dev,struct pinctrl_dev *pctldev)

1.2.1 分配一个pinctrl,并初始化一些变量

    p = kzalloc(sizeof(*p), GFP_KERNEL);

    if (!p)

        return ERR_PTR(-ENOMEM);

    p->dev = dev;

    INIT_LIST_HEAD(&p->states);

    INIT_LIST_HEAD(&p->dt_maps);

1.2.2 解析设备节点的pinctrl设置,并用struct pinctrl_map数据结构保存信息

ret = pinctrl_dt_to_map(p, pctldev);

int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)

1.2.2.1 循环查找pinctrl-%d,直到of_find_property失败

    /* For each defined state ID */

    for (state = 0; ; state++) {

        /* Retrieve the pinctrl-* property */

        propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);

        prop = of_find_property(np, propname, &size);

        kfree(propname);

        if (!prop) {

            if (state == 0) {

                of_node_put(np);

                return -ENODEV;

            }

            break;

        }

1.2.2.2 枚举pinctrl-x所有引用的句柄,调用dt_to_map_one_config进行解析pin脚节点

for (config = 0; config < size; config++) {

            phandle = be32_to_cpup(list++);

            /* Look up the pin configuration node */

            np_config = of_find_node_by_phandle(phandle);

            if (!np_config) {

                dev_err(p->dev,

                    "prop %s index %i invalid phandle\n",

                    prop->name, config);

                ret = -EINVAL;

                goto err;

            }

            /* Parse the node */

            ret = dt_to_map_one_config(p, pctldev, statename,

                           np_config);

static int dt_to_map_one_config(struct pinctrl *p, struct pinctrl_dev *hog_pctldev,const char *statename,struct device_node *np_config)

1.2.2.2.1 查找pins节点的父节点,一步步往父节点查,直到找到pinctrl dev节点,以pctldev->dev->of_node == np进行判断

    /* Find the pin controller containing np_config */

    np_pctldev = of_node_get(np_config);

    for (;;) {

        if (!allow_default)

            allow_default = of_property_read_bool(np_pctldev,

                                  "pinctrl-use-default");

        np_pctldev = of_get_next_parent(np_pctldev);

list_for_each_entry(pctldev, &pinctrldev_list, node)

        if (pctldev->dev->of_node == np) {

            mutex_unlock(&pinctrldev_list_mutex);

            return pctldev;

        }

1.2.2.2.2 调用pinctrl_dev的pinctrl_desc的pctlops函数,真正的开始解析设备树pin脚节点,转换成struct pinctrl_map。

/*

     * Call pinctrl driver to parse device tree node, and

     * generate mapping table entries

     */

    ops = pctldev->desc->pctlops;

    if (!ops->dt_node_to_map) {

        dev_err(p->dev, "pctldev %s doesn't support DT\n",

            dev_name(pctldev->dev));

        return -ENODEV;

    }

    ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);

1.2.2.2.3 把struct pinctrl_map信息进一步填充并存下来,一是以struct pinctrl_dt_map存下来,挂载到pinctrl的dt_maps链表;另一个是存入struct pinctrl_maps,挂载到全局链表pinctrl_maps

    /* Initialize common mapping table entry fields */

    for (i = 0; i < num_maps; i++) {

        const char *devname;

        devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);

        if (!devname)

            goto err_free_map;

        map[i].dev_name = devname;

        map[i].name = statename;

        if (pctldev)

            map[i].ctrl_dev_name = dev_name(pctldev->dev);

    }

    /* Remember the converted mapping table entries */

    dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);

    if (!dt_map)

        goto err_free_map;

    dt_map->pctldev = pctldev;

    dt_map->map = map;

    dt_map->num_maps = num_maps;

    list_add_tail(&dt_map->node, &p->dt_maps);

1.2.3 扫描1.2.2.2.3存入的pinctrl_maps链表,比对当前设备节点的名字与map的dev_name是否匹配,匹配说明是当前设备节点配置的pinctrl。

/* Iterate over the pin control maps to locate the right ones */

    for_each_maps(maps_node, i, map) {

        /* Map must be for this device */

        if (strcmp(map->dev_name, devname))

            continue;

        /*

         * If pctldev is not null, we are claiming hog for it,

         * that means, setting that is served by pctldev by itself.

         *

         * Thus we must skip map that is for this device but is served

         * by other device.

         */

        if (pctldev &&

            strcmp(dev_name(pctldev->dev), map->ctrl_dev_name))

            continue;

1.2.4 map转换成struct pinctrl_setting,然后存到pinctrl数据结构下

ret = add_setting(p, pctldev, map);

static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev,const struct pinctrl_map *map)

1.2.4.1 pinctrl_setting是挂载在state->settings下,state是挂载在p->states,所有要增加setting就要先查找到state,以map->name查找,这个name就是pinctrl-names配置的名字解析过程赋值给map->name的,找不到就创建一个state,然后挂载到p->states,

    struct pinctrl_state *state;

    struct pinctrl_setting *setting;

    int ret;

    state = find_state(p, map->name);

    if (!state)

        state = create_state(p, map->name);

    if (IS_ERR(state))

        return PTR_ERR(state);

1.2.4.2 pinctrl_setting赋值,type, dev_name,pctldev变量赋值

setting->type = map->type;

    if (pctldev)

        setting->pctldev = pctldev;

    else

        setting->pctldev =

            get_pinctrl_dev_from_devname(map->ctrl_dev_name);

    if (!setting->pctldev) {

        kfree(setting);

        /* Do not defer probing of hogs (circular loop) */

        if (!strcmp(map->ctrl_dev_name, map->dev_name))

            return -ENODEV;

        /*

         * OK let us guess that the driver is not there yet, and

         * let's defer obtaining this pinctrl handle to later...

         */

        dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",

            map->ctrl_dev_name);

        return -EPROBE_DEFER;

    }

    setting->dev_name = map->dev_name;

1.2.4.3 填充个pinmux或者pinconf配置。mux配置填充func, group的全局编号(setting->data.mux.func,setting->data.mux.group),conf配置填充setting->data.configs.group_or_pin,setting->data.configs.num_configs,setting->data.configs.configs = map->data.configs.configs。

    switch (map->type) {

    case PIN_MAP_TYPE_MUX_GROUP:

        ret = pinmux_map_to_setting(map, setting);

        break;

    case PIN_MAP_TYPE_CONFIGS_PIN:

    case PIN_MAP_TYPE_CONFIGS_GROUP:

        ret = pinconf_map_to_setting(map, setting);

        break;

    default:

        ret = -EINVAL;

        break;

    }

2、获取default_state,init_state,sleep_state,idle_state这些pinctrl_state,pinctrl_lookup_state查找方法是从struct pinctrl中的states链表找出对应名字的state。

dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,

                    PINCTRL_STATE_DEFAULT);
......

3、有"init" pinctrl配置则选定"init"引脚配置,否则选定"default" pinctrl配置

ret = pinctrl_select_state(dev->pins->p,

                       dev->pins->default_state);

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

3.1 判断pinctrl当前的状态p->state与要设置的状态state是否一致,一致再调用pinctrl_commit_state

/**

 * pinctrl_select_state() - select/activate/program a pinctrl state to HW

 * @p: the pinctrl handle for the device that requests configuration

 * @state: the state handle to select/activate/program

 */

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

{

    if (p->state == state)

        return 0;

    return pinctrl_commit_state(p, state);

}

EXPORT_SYMBOL_GPL(pinctrl_select_state);

static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)

3.1.1 如果之前已经p->state设置过,那么要对&p->state->settings下的所有setting的pin脚如果是mux设置类型,那要进行pinmux_disable_setting,主要是对存放在pctldev->pin_desc_tree的struct pin_desc的mux_usecount,mux_owner ,mux_setting变量进行free设置。

    if (!gpio_range) {

        /*

         * A pin should not be freed more times than allocated.

         */

        if (WARN_ON(!desc->mux_usecount))

            return NULL;

        desc->mux_usecount--;

        if (desc->mux_usecount)

            return NULL;

    }

    /*

     * If there is no kind of request function for the pin we just assume

     * we got it by default and proceed.

     */

    if (gpio_range && ops->gpio_disable_free)

        ops->gpio_disable_free(pctldev, gpio_range, pin);

    else if (ops->free)

        ops->free(pctldev, pin);

    if (gpio_range) {

        owner = desc->gpio_owner;

        desc->gpio_owner = NULL;

    } else {

        owner = desc->mux_owner;

        desc->mux_owner = NULL;

        desc->mux_setting = NULL;

    }

3.1.2 枚举state->settings下的所有配置进行配置

/* Apply all the settings for the new state */

    list_for_each_entry(setting, &state->settings, node) {

        switch (setting->type) {

        case PIN_MAP_TYPE_MUX_GROUP:

            ret = pinmux_enable_setting(setting);

            break;

        case PIN_MAP_TYPE_CONFIGS_PIN:

        case PIN_MAP_TYPE_CONFIGS_GROUP:

            ret = pinconf_apply_setting(setting);

            break;

        default:

            ret = -EINVAL;

            break;

        }

        if (ret < 0) {

            goto unapply_new_state;

        }

        /* Do not link hogs (circular dependency) */

        if (p != setting->pctldev->p)

            pinctrl_link_add(setting->pctldev, p->dev);

    }

int pinmux_enable_setting(const struct pinctrl_setting *setting)

3.1.2.1 获取setting->data.mux.group的pins编号及数量num_pins

    if (pctlops->get_group_pins)

        ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,

                          &pins, &num_pins);

3.1.2.2 枚举所有pins,然后调用pin_request,pin_request:1.判断是不是被别的设备节点占用设置过,是的话返回错误,2、mux_owner,mux_usecount变量进行设置,3、status=ops->request/status=0

    /* Try to allocate all pins in this group, one by one */

    for (i = 0; i < num_pins; i++) {

        ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);

3.1.2.3 设置当前pin_desc的mux_setting

    /* Now that we have acquired the pins, encode the mux setting */

    for (i = 0; i < num_pins; i++) {

        desc = pin_desc_get(pctldev, pins[i]);

        if (desc == NULL) {

            dev_warn(pctldev->dev,

                 "could not get pin desc for pin %d\n",

                 pins[i]);

            continue;

        }

        desc->mux_setting = &(setting->data.mux);

    }

3.1.2.4 真正进行mux配置

    ret = ops->set_mux(pctldev, setting->data.mux.func,

               setting->data.mux.group);

3.1.3 赋值p->state为设置的state

p->state = state;

3.2 驱动中主动调用进行引脚配置

调用pinctrl相关api,进行相关引脚配置,相关接口位于linux/pinctrl/consumer.h

1.部分soc gpio驱动会调用以下驱动进行pinctrl设置。

/* External interface to pin control */

extern bool pinctrl_gpio_can_use_line(unsigned gpio);

extern int pinctrl_gpio_request(unsigned gpio);

extern void pinctrl_gpio_free(unsigned gpio);

extern int pinctrl_gpio_direction_input(unsigned gpio);

extern int pinctrl_gpio_direction_output(unsigned gpio);

extern int pinctrl_gpio_set_config(unsigned gpio, unsigned long config);

2.驱动中使用pinctrl_lookup_state,pinctrl_select_state可以设置设备树中配置好的某种pin脚状态

extern struct pinctrl_state * __must_check pinctrl_lookup_state(

                            struct pinctrl *p,

                            const char *name);

extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);

extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);

extern void devm_pinctrl_put(struct pinctrl *p);

extern int pinctrl_select_default_state(struct device *dev);

static inline struct pinctrl * __must_check pinctrl_get_select(

                    struct device *dev, const char *name);

四、gpio子系统

4.1 gpio驱动

gpio驱动一些操作函数在驱动文件中,系统框架代码在接口代码drivers/gpio/gpiolib.c,我们来看一看soc的gpio驱动怎么写的

1、gpio控制器设备节点

1、soc gpio控制器  

 main_gpio0: gpio@600000 {
        compatible = "ti,j721e-gpio", "ti,keystone-gpio";
        reg = <0x0 0x00600000 0x0 0x100>;
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-parent = <&main_gpio_intr>;
        interrupts = <256>, <257>, <258>, <259>,
                 <260>, <261>, <262>, <263>;
        interrupt-controller;
        #interrupt-cells = <2>;
        ti,ngpio = <128>;
        ti,davinci-gpio-unbanked = <0>;
        power-domains = <&k3_pds 105 TI_SCI_PD_EXCLUSIVE>;
        clocks = <&k3_clks 105 0>;
        clock-names = "gpio";
    };

2、挂在i2c下的外部gpio控制器

&i2c2 {
    clock-frequency = <400000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c2>;
    status = "okay";

    pca9534: gpio@20 {
        compatible = "nxp,pca9534";
        reg = <0x20>;
        gpio-controller;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_pca9534>;
        interrupt-parent = <&gpio1>;
        interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
        #gpio-cells = <2>;
        wakeup-source;

        /* USB 3.0 OTG (usbotg1) / SATA port switch, set to USB 3.0 */
        usb3-sata-sel-hog {
            gpio-hog;
            gpios = <4 GPIO_ACTIVE_HIGH>;
            output-low;
            line-name = "usb3_sata_sel";
        };

......

2、驱动处理步骤,先准备一个私有结构体,里面含有结构体struct gpio_chip    chip

struct davinci_gpio_controller {

    struct gpio_chip    chip;

    struct irq_domain   *irq_domain;

    /* Serialize access to GPIO registers */

    spinlock_t      lock;

    void __iomem        *regs[MAX_REGS_BANKS];

    int         gpio_unbanked;

    int         irqs[MAX_INT_PER_BANK];

};

3、填充 struct gpio_chip    chip,一些gpio信息和gpio操作函数

    chips->chip.label = dev_name(dev);

    chips->chip.direction_input = davinci_direction_in;

    chips->chip.get = davinci_gpio_get;

    chips->chip.direction_output = davinci_direction_out;

    chips->chip.set = davinci_gpio_set;

    chips->chip.ngpio = ngpio;

    chips->chip.base = pdata->no_auto_base ? pdata->base : -1;

#ifdef CONFIG_OF_GPIO

    chips->chip.of_gpio_n_cells = 2;

    chips->chip.parent = dev;

    chips->chip.of_node = dev->of_node;

    chips->chip.request = gpiochip_generic_request;

    chips->chip.free = gpiochip_generic_free;

#endif

4、创建struct gpio_device *gdev,注册到gpio系统

ret = devm_gpiochip_add_data(dev, &chips->chip, chips);

int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key)

1、分配一个struct gpio_device *gdev,填充gdev,给gdev赋值,如gpio_chip, devname,dev.of_node,分配id,为每个gpio分配一个struct gpio_desc, 未指定起始编号则分配起始编号,最开始注册gpio设备起始编号为int base = ARCH_NR_GPIOS - ngpio

struct gpio_desc {

    struct gpio_device  *gdev;

    unsigned long       flags;

/* flag symbols are bit numbers */

#define FLAG_REQUESTED  0

#define FLAG_IS_OUT 1

#define FLAG_EXPORT 2   /* protected by sysfs_lock */

#define FLAG_SYSFS  3   /* exported via /sys/class/gpio/control */

#define FLAG_ACTIVE_LOW 6   /* value has active low */

#define FLAG_OPEN_DRAIN 7   /* Gpio is open drain type */

#define FLAG_OPEN_SOURCE 8  /* Gpio is open source type */

#define FLAG_USED_AS_IRQ 9  /* GPIO is connected to an IRQ */

#define FLAG_IRQ_IS_ENABLED 10  /* GPIO is connected to an enabled IRQ */

#define FLAG_IS_HOGGED  11  /* GPIO is hogged */

#define FLAG_TRANSITORY 12  /* GPIO may lose value in sleep or reset */

#define FLAG_PULL_UP    13  /* GPIO has pull up enabled */

#define FLAG_PULL_DOWN  14  /* GPIO has pull down enabled */

#define FLAG_BIAS_DISABLE    15 /* GPIO has pull disabled */

#define FLAG_EDGE_RISING     16 /* GPIO CDEV detects rising edge events */

#define FLAG_EDGE_FALLING    17 /* GPIO CDEV detects falling edge events */

    /* Connection label */

    const char      *label;

    /* Name of the GPIO */

    const char      *name;

#ifdef CONFIG_OF_DYNAMIC

    struct device_node  *hog;

#endif

#ifdef CONFIG_GPIO_CDEV

    /* debounce period in microseconds */

    unsigned int        debounce_period_us;

#endif

};

2、将gdev加入链表gpio_devices,编号最大的放在链表头

ret = gpiodev_add_to_list(gdev);

3、处理gpio chip设备树相关的东西,解析设备树gpio-ranges属性,gpio-range同步添加到pinctrl设备,挂载到pctldev->gpio_ranges链表上,of_gpiochip_scan_gpios扫描gpio控制器下的独占脚即gpio控制器设备节点下的child节点gpio-hog属性,gpios属性配置的gpio,gpio请求并配置引脚输入或输出

/**

 * struct gpio_pin_range - pin range controlled by a gpio chip

 * @node: list for maintaining set of pin ranges, used internally

 * @pctldev: pinctrl device which handles corresponding pins

 * @range: actual range of pins controlled by a gpio controller

 */

struct gpio_pin_range {

    struct list_head node;

    struct pinctrl_dev *pctldev;

    struct pinctrl_gpio_range range;

};

ret = of_gpiochip_add(gc);

1、赋值gpio属性参数格式,及从设备树获取gpio编号函数,通用编号获取函数设定设备树指定的gpio编号参数对应gdev->descs数组的索引,gdev->descs[gpio编号参数]即可获取到struct gpio_desc

    if (!chip->of_xlate) {

        chip->of_gpio_n_cells = 2;

        chip->of_xlate = of_gpio_simple_xlate;

    }

2、初始化gpio_chip数据结构的valid_mask变量,将保留的gpio脚有效位清除

of_gpiochip_init_valid_mask(chip);

3、扫描gpio-ranges属性

ret = of_gpiochip_add_pin_range(chip);

4、处理gpio控制器占用的引脚

ret = of_gpiochip_scan_gpios(chip);

4、gpio驱动valid_mask设置,设置gpio dev的gpio_desc flags变量输入输出默认属性,加入驱动中单独设置的gpio_pin_range

    for (i = 0; i < gc->ngpio; i++) {

        struct gpio_desc *desc = &gdev->descs[i];

        if (gc->get_direction && gpiochip_line_is_valid(gc, i)) {

            assign_bit(FLAG_IS_OUT,

                   &desc->flags, !gc->get_direction(gc, i));

        } else {

            assign_bit(FLAG_IS_OUT,

                   &desc->flags, !gc->direction_input);

        }

    }

......
    ret = gpiochip_add_pin_ranges(gc);

5、注册gpio控制器字符设备(应用层可以用来操作gpio),注册gpio控制器到sysfs(调试,应用层操作gpio)

ret = gpiochip_setup_dev(gdev);

......

static int gpiochip_setup_dev(struct gpio_device *gdev)

{

    int ret;

    ret = gcdev_register(gdev, gpio_devt);

    if (ret)

        return ret;

    ret = gpiochip_sysfs_register(gdev);

    if (ret)

        goto err_remove_device;

    /* From this point, the .release() function cleans up gpio_device */

    gdev->dev.release = gpiodevice_release;

    dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,

        gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");

    return 0;

err_remove_device:

    gcdev_unregister(gdev);

    return ret;

}

4.2 内核中使用gpio

        gpio消费者无需关系gpio驱动具体实现方法,使用gpio系统提供的gpio接口来使用gpio

接口代码drivers/gpio/gpiolib.c,通过接口代码实现我们来理解gpio驱动为什么要那么写及框架辑

先说一下内核中使用gpio的步骤

4.2.1 gpio使用步骤

接口头文件在linux/gpio/consumer.h

4.2.1.1 通过gpio编号调用,gpio编号设备树中*-gpio/*-gpios属性指定

1、获取gpio编号(获取gpio也是先通过of_get_named_gpiod_flags获取到gpiod,然后再转换成gpio),获取flags

int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);

of_get_named_gpio_flags(phynode, "reset-gpios", 0, &(data->cfg[i].gpio_flags));

2、gpio_request请求占有gpio,或者用gpio_request_one直接请求,配置输入输出一次性调用完,配置输出功能是直接输出高低电平

int gpio_request(unsigned gpio, const char *label)

/**

 * gpio_request_one - request a single GPIO with initial configuration

 * @gpio:   the GPIO number

 * @flags:  GPIO configuration as specified by GPIOF_*

 * @label:  a literal description string of this GPIO

 */

int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)

3、配置输入输出,输出值是电平高低,没有逻辑转换值

static inline int gpio_direction_input(unsigned gpio)

{

    return gpiod_direction_input(gpio_to_desc(gpio));

}

static inline int gpio_direction_output(unsigned gpio, int value)

{

    return gpiod_direction_output_raw(gpio_to_desc(gpio), value);

}

4、获取输入/设置输出值,这里接口都是gpio电平高低,没有逻辑转换

static inline int gpio_get_value(unsigned int gpio)

{

    return __gpio_get_value(gpio);

}

static inline void gpio_set_value(unsigned int gpio, int value)

{

    __gpio_set_value(gpio, value);

}

/* A platform's <asm/gpio.h> code may want to inline the I/O calls when

 * the GPIO is constant and refers to some always-present controller,

 * giving direct access to chip registers and tight bitbanging loops.

 */

static inline int __gpio_get_value(unsigned gpio)

{

    return gpiod_get_raw_value(gpio_to_desc(gpio));

}

static inline void __gpio_set_value(unsigned gpio, int value)

{

    return gpiod_set_raw_value(gpio_to_desc(gpio), value);

}

4.2.1.2  通过gpio_desc调用接口

1、获取gpio_desc,并进行输入输出设置

devm_gpiod_get_index_optional/devm_gpiod_get_index/gpiod_get_index/gpiod_get_index_optional

这几个函数是嵌套关系,功能主要是通过设备节点指定的gpio进行初始化并使能/不使能,主要做了以下几个动作

a、从设备节点中获取*-gpios/*-gpio属性,第一个参数为gpio控制器节点的引用,从gpio_devices链表中通过比对设备of_node拿到gpio_chip, 获取gpio编号(直接提取或转换,看of_xlate函数是不是单独提供的,这个编号属于gpio控制器内的局部编号,不是整个系统的编号,全局编号chip->base + gpio编号),获取flags,以获取到的gpio编号为索引从gdev->descs数组获取到gpio_desc

说明:还有一种方法是不从设备树获取gpio,驱动中注册gpio设备消费者,通过调用gpiod_add_lookup_table实现

flags值,gpios/gpio属性的第三个值
enum gpio_lookup_flags {

    GPIO_ACTIVE_HIGH        = (0 << 0),

    GPIO_ACTIVE_LOW         = (1 << 0),

    GPIO_OPEN_DRAIN         = (1 << 1),

    GPIO_OPEN_SOURCE        = (1 << 2),

    GPIO_PERSISTENT         = (0 << 3),

    GPIO_TRANSITORY         = (1 << 3),

    GPIO_PULL_UP            = (1 << 4),

    GPIO_PULL_DOWN          = (1 << 5),

    GPIO_LOOKUP_FLAGS_DEFAULT   = GPIO_ACTIVE_HIGH | GPIO_PERSISTENT,

};

b、调用gpiod_request,这个函数的作用1、desc->flags FLAG_REQUESTED是否置位,置位说明占用返回错误,未置为则置为该位表示占用,2、调用gpio_chip的request函数指针,这里面判断如果存在pin_ranges则会调用pin_request,3、设定gpio当前方向

    /*

     * If a connection label was passed use that, else attempt to use

     * the device name as label

     */

    ret = gpiod_request(desc, con_id ? con_id : devname);

/* These "optional" allocation calls help prevent drivers from stomping

 * on each other, and help provide better diagnostics in debugfs.

 * They're called even less than the "set direction" calls.

 */

static int gpiod_request_commit(struct gpio_desc *desc, const char *label)

{

    struct gpio_chip    *gc = desc->gdev->chip;

    int         ret;

    unsigned long       flags;

    unsigned        offset;

    if (label) {

        label = kstrdup_const(label, GFP_KERNEL);

        if (!label)

            return -ENOMEM;

    }

    spin_lock_irqsave(&gpio_lock, flags);

    /* NOTE:  gpio_request() can be called in early boot,

     * before IRQs are enabled, for non-sleeping (SOC) GPIOs.

     */

    if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {

        desc_set_label(desc, label ? : "?");

        ret = 0;

    } else {

        kfree_const(label);

        ret = -EBUSY;

        goto done;

    }

    if (gc->request) {

        /* gc->request may sleep */

        spin_unlock_irqrestore(&gpio_lock, flags);

        offset = gpio_chip_hwgpio(desc);

        if (gpiochip_line_is_valid(gc, offset))

            ret = gc->request(gc, offset);

        else

            ret = -EINVAL;

        spin_lock_irqsave(&gpio_lock, flags);

        if (ret < 0) {

            desc_set_label(desc, NULL);

            kfree_const(label);

            clear_bit(FLAG_REQUESTED, &desc->flags);

            goto done;

        }

    }

    if (gc->get_direction) {

        /* gc->get_direction may sleep */

        spin_unlock_irqrestore(&gpio_lock, flags);

        gpiod_get_direction(desc);

        spin_lock_irqsave(&gpio_lock, flags);

    }

done:

    spin_unlock_irqrestore(&gpio_lock, flags);

    return ret;

}

c、根据从设备树获取到的lookupflags参数设置desc->flags相关位,包括低有效,开漏,开源,上下拉,接口传过来的参数flags如果设置了in/out相关功能则设置输入输出,特别说明这里传过来的flag,是表示有效或无效化pin脚,不是直接高低电平,会经过是不是低有效判断转换成高低电平

/**

 * Optional flags that can be passed to one of gpiod_* to configure direction

 * and output value. These values cannot be OR'd.

 */

enum gpiod_flags {

    GPIOD_ASIS  = 0,

    GPIOD_IN    = GPIOD_FLAGS_BIT_DIR_SET,

    GPIOD_OUT_LOW   = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,

    GPIOD_OUT_HIGH  = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_DIR_VAL,

    GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN,

    GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN,

};

......

ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);

gpiod_direction_output

a、对开漏,开源,推挽进行设置gpio_set_config

b、上下拉进行设置gpio_set_config(desc, bias)//驱动里头gc->set_config可能位空,上下拉也可能在pin里指定

c、调用最后的设置输出并输出值gpiod_direction_output_raw_commit,这个函数里头会调用驱动direction_output函数指针进行设置寄存器,如输入输出方向寄存器,设置输出高低电平寄存器

2、控制gpio输出/获取gpio pin脚输入

gpiod_set_value_cansleep/gpiod_set_value:输出gpio逻辑值,value为逻辑有效值;

gpiod_get_value_cansleep/gpiod_get_value:获取gpio逻辑值,返回值为逻辑有效值;

gpiod_set_raw_value/gpiod_set_raw_value_cansleep:输出gpio电平高低,value为gpio实际高低电平;

gpiod_get_raw_value/gpiod_get_raw_value_cansleep:获取gpio引脚电平,返回值为引脚高低电平;

void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)

void gpiod_set_value(struct gpio_desc *desc, int value)

int gpiod_get_value(const struct gpio_desc *desc)

/* Value get/set from sleeping context */

int gpiod_get_value_cansleep(const struct gpio_desc *desc);

void gpiod_set_raw_value(struct gpio_desc *desc, int value)

int gpiod_get_raw_value(const struct gpio_desc *desc)

int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)

void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)

五、上层应用使用gpio

5.1 通过/sys/class/gpio/操作gpio

1、指令操作

      1. 导出   /sys/class/gpio# echo 203 > export
      2. 设置方向      /sys/class/gpio/gpio203# echo out > direction
      3. 查看方向      /sys/class/gpio/gpio203# cat direction
      4. 设置输出      /sys/class/gpio/gpio203# echo 1 > value
      5. 查看输出值  /sys/class/gpio/gpio203# cat value  
      6. 取消导出     /sys/class/gpio# echo 203 > unexport

2、代码编写

输出高低电平

#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
 
#define GPIO_INDEX "42"
static char gpio_path[75];
int gpio_init(char *name)
{
    int fd;
 
    sprintf(gpio_path, "/sys/class/gpio/gpio%s", name);
    //查询文件是否存在
    if (access("gpio_path", F_OK)){
        //只读方式打开
        fd = open("/sys/class/gpio/export", O_WRONLY);
        //打开文件失败
        if(fd < 0)
            return 1 ;
    
        write(fd, name, strlen(name));
        close(fd);
    
        //初始化gpio使用的引脚为输出模式
        sprintf(gpio_path, "/sys/class/gpio/gpio%s/direction", name);
        fd = open(gpio_path, O_WRONLY);
        if(fd < 0)
            return 2;
    
        write(fd, "out", strlen("out"));
        close(fd);
    }
 
    return 0;
}
//向unexport文件写入编号,取消导出
int gpio_deinit(char *name)
{
    int fd;
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if(fd < 0)
        return 1;
 
    write(fd, name, strlen(name));
    close(fd);
 
    return 0;
}
 

//输出高电平
int gpio_high(char *name)
{
    int fd;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
    fd = open(gpio_path, O_WRONLY);
    if(fd < 0){
        printf("open gpio%s wrong\n",name);
        return -1;
    }
        
    if(2 != write(fd, "1", sizeof("1")))
        printf("wrong set \n");
    close(fd);
    return 0;
}
 
//输出低电平
int gpio_low(char *name)
{
    int fd;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
    fd = open(gpio_path, O_WRONLY);
    if(fd < 0){
        printf("open gpio%s wrong\n",name);
        return -1;
    }
        
    if(2 != write(fd, "0", sizeof("0")))
        printf("wrong set \n");
    close(fd);
    return 0;
}
 
int main(int argc, char *argv[])
{
    char buf[10];
    int res;
 
    /* 校验传参 */
    if (2 != argc) {
        printf( "usage: %s <PinNum>\n",argv[0]);
        return -1;
    }
    res = gpio_init(argv[1]);
    if(res){
        printf("gpio init error,code = %d",res);
        return 0;
    }
 
    while(1){
        printf("Please input the value : 0--low 1--high q--exit\n");
        scanf("%10s", buf);
 
        switch (buf[0]){
            case '0':
                gpio_low(argv[1]);
                break;
 
            case '1':
                gpio_high(argv[1]);
                break;
 
            case 'q':
                gpio_deinit(argv[1]);
                printf("Exit\n");
                return 0;
 
            default:
                break;
       }
    }
    return 0;
}

获取gpio电平

int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename = "/sys/devices/platform/apb/10010000.pinctrl/gpio/gpio66/value";
    char gpio_in_value;

    if (access(filename, F_OK))
        system("echo 66 > /sys/class/gpio/export");

    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }

    read(fd, &gpio_in_value, 1);
    printf("gpio in value:%d.\r\n", gpio_in_value);

    lseek(fd, 0, SEEK_SET);
    read(fd, &gpio_in_value, 1);
    printf("gpio in value:%d.\r\n", gpio_in_value);

    retvalue = close(fd);
    if(retvalue < 0){
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

gpio应用层中断

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h> 

int main(int argc, char *argv[])
{
    char buff[1024];
    int gpio_id;
    struct pollfd fds[1];
    int gpio_fd = open("/sys/class/gpio/gpio508/value", O_RDONLY);
    if (gpio_fd == -1)
        printf("gpio open");
    fds[0].fd = gpio_fd;
    fds[0].events = POLLPRI;
    int ret = read(gpio_fd, buff, 10);
    if (ret == -1)
        printf("read");

    while (1)
    {
        ret = poll(fds, 1, -1);
        if (ret == -1)
            printf("poll");

        if (fds[0].revents & POLLPRI)
        {
            ret = lseek(gpio_fd, 0, SEEK_SET);
            if (ret == -1)
                printf("lseek");
            ret = read(gpio_fd, buff, 10);
            if (ret == -1)
                printf("read");
            printf("get interrupt\n");
        }
    }
}
 

5.2 通过/dev/gpiochipX字符设备操作gpio

参见kernel/tools/gpio,有控制输入输出,获取中断的例子

5.3 通过写寄存器操作gpio

先对寄存器地址进行mmap,再进行操作寄存器

六、pinctrl调试

CONFIG_DEBUG_FS pinctrl debugfs配置开关

/sys/kernel/debug/pinctrl/底下的几个文件说明

pinctrl-devices:pinctrl控制器设备对应的驱动是否有pmxops,confops

pinctrl-maps:枚举maps_node下的所有map,其实就是所有states下的所有setting

pinctrl-handles:枚举pinctrl_list,显示该pinctrl的设备节点,当前使用的哪个pinctrl-x配置,也就是当前的pinctrl_state,然后枚举p->states,枚举state->settings,显示所有states下的setting

pinctrl设备信息注册到debugfs:

pinctrl_enable会调用pinctrl_init_device_debugfs,这里会创建目录pinctrl_dev->dev名字-pinctrl_desc名字,一般情况下等于设备名-驱动名,下面对创建的文件说明

pins:枚举所有pin脚pctldev->desc->npins,显示pin number,pin名字,gpio_ranges信息,调用驱动pin_dbg_show显示其他信息(pinctrl-single驱动显示:寄存器地址 寄存器值 驱动名)

pingroups:枚举pingroups,显示pin group用的pin 编号与名字

gpio-ranges:显示pctrldev->gpio_ranges信息,gpio范围对应的pin范围

pinmux-pins:枚举所有pin脚,显示所有pin脚被哪个节点占用,功能,pin组

pinmux-functions:显示所有function及function下的pin组

七、gpio调试

相关调试的配置开关

CONFIG_GPIO_CDEV  //控制gpio的字符设备/dev/gpiochip%d

CONFIG_DEBUG_FS   // /sys/kernel/debug/gpio配置开关

CONFIG_GPIO_SYSFS /sys/class/gpio 配置开关

八、参考文章

GPIO子系统-CSDN博客

Linux下通过sysfs方式控制GPIO(/sys/class/gpio)_sysfs gpio-CSDN博客

学习笔记之Linux的GPIO控制_linux gpio-CSDN博客

linux用户态使用gpio中断方法_echo "out" > direction-CSDN博客

Control GPIO using the new Linux user space GPIO API

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐