编写虚拟的Pinctrl驱动程序之实现框架

本文详细介绍了Linux内核的pinctrl功能及其在虚拟pincontroller驱动程序中的实现,包括引脚枚举、复用和配置,以及如何编写设备树和驱动程序实例。涵盖了关键概念如pinctrl_desc、pinmux和pinconf操作,并展示了如何在硬件功能和实际应用中操作。

参考资料:
Linux 4.x内核:

  • Documentation\pinctrl.txt
  • Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
  • arch/arm/boot/dts/imx6ull-14x14-evk.dts
  • arch/arm/boot/dts/100ask_imx6ull-14x14.dts
  • drivers\pinctrl\freescale\pinctrl-imx6ul.c
  • drivers\pinctrl\freescale\pinctrl-imx.c

一、回顾Pinctrl的三大作用

pinctrl的三大作用:

  • 引脚枚举与命名(Enumerating and naming)
    • 单个引脚
    • 各组引脚
  • 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
  • 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度

Pinctrl驱动程序的核心是构造一个pinctrl_desc结构体:

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
};

1.1 作用1:描述、获得引脚

分为2部分:

  • 描述、获得单个引脚的信息
  • 描述、获得某组引脚的信息
struct pinctrl_desc {
	const char *name;
	const struct pinctrl_pin_desc *pins;		//单个引脚
	unsigned int npins;		//单个引脚个数
	const struct pinctrl_ops *pctlops;		//各组引脚
	//....
};
struct pinctrl_ops {
	int (*get_groups_count) (struct pinctrl_dev *pctldev);
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);
	int (*get_group_pins) (struct pinctrl_dev *pctldev,
			       unsigned selector,
			       const unsigned **pins,
			       unsigned *num_pins);
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
			  unsigned offset);
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);
	void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

1.2 作用2:引脚复用

用来把某组引脚(group)复用为某个功能(function)。

struct pinctrl_desc {
	//...
	const struct pinmux_ops *pmxops;
	//...
};
struct pinmux_ops {
    int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
    int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
    int (*get_functions_count) (struct pinctrl_dev *pctldev);
    const char *(*get_function_name) (struct pinctrl_dev *pctldev,
                      unsigned selector);
    int (*get_function_groups) (struct pinctrl_dev *pctldev,
                  unsigned selector,
                  const char * const **groups,
                  unsigned *num_groups);
    int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
            unsigned group_selector);
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
                    struct pinctrl_gpio_range *range,
                    unsigned offset);
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
                   struct pinctrl_gpio_range *range,
                   unsigned offset);
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
                   struct pinctrl_gpio_range *range,
                   unsigned offset,
                   bool input);
    bool strict;
};

1.3 作用3:引脚配置

用来配置:某个引脚(pin)或某组引脚(group)。

struct pinctrl_desc {
	//...
	const struct pinconf_ops *confops;
	//...
};
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
	int (*pin_config_get) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *config);
	int (*pin_config_set) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *configs,
			       unsigned num_configs);
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *config);
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
	int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
					   const char *arg,
					   unsigned long *config);
	void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,
				     struct seq_file *s,
				     unsigned offset);
	void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
					   struct seq_file *s,
					   unsigned selector);
	void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
					    struct seq_file *s,
					    unsigned long config);
};

二、硬件功能

假设这个虚拟的pin controller有4个引脚:
在这里插入图片描述

  • pin0,1,2,3都可以配置为GPIO功能
  • pin0,1还可以配置为I2C功能
  • pin2,3还可以配置为UART功能

三、编写Pinctrl驱动程序要做什么

  • 编写pinctroller的设备树
  • 编写使用pinctroller的client设备树
virtual_pincontroller {
        compatible = "100ask,virtual_pinctrl";
        i2cgrp: i2cgrp {
                        functions = "i2c", "i2c";
                        groups = "pin0", "pin1";
                        configs = <0x11223344  0x55667788>;
        };
};

virtual_i2c {
        compatible = "100ask,virtual_i2c";
        pinctrl-names = "default";
        pinctrl-0 = <&i2cgrp>;
};
  • 编写Pinctroller驱动程序
  • 编写client驱动程序

四、编写Pinctroller驱动程序

4.1 核心pinctrl_desc

分配、设置、注册pinctrl_desc结构体

4.2 辅助函数

include/linux/of.h
	for_each_child_of_node
	of_get_child_count
	of_find_property
	of_property_read_u32
	of_property_read_u32_index
	of_property_read_string_index

4.3 编写pinctroller驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include "core.h"

static struct pinctrl_dev *g_pinctrl_dev;
static const struct pinctrl_pin_desc pins[] = {
        { 0, "pin0", NULL },    //描述4个引脚,有num,名字,私有数据
        { 1, "pin1", NULL },
        { 2, "pin2", NULL },
        { 3, "pin3", NULL },
};

static unsigned long g_configs[4];

struct virtual_functions_desc {
        const char *func_name;
        const char **groups;
        int num_groups;
};

static const char *function_group0[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *function_group1[] = {"pin0", "pin1"};
static const char *function_group2[] = {"pin2", "pin3"};
static struct virtual_functions_desc g_funcs_des[] = {  //不同的复用功能对应的引脚
        { "gpio", function_group0, 4 },
        { "i2c",  function_group1, 2 },
        { "uart", function_group2, 2 },
};

//从引脚信息中获取引脚个数
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
        return pctldev->desc->npins;
}

//直接返回引脚的名字
static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
        return pctldev->desc->pins[selector].name;
}

//获取组的所有引脚信息
static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
                const unsigned **pins, unsigned *npins)
{
        if(selector >= pctldev->desc->npins)
                return -EINVAL;

        *pins = &pctldev->desc->pins[selector].number;  //引脚组的序号
        *npins = 1;

        return 0;
}

//打印debug信息
static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset)
{
        seq_printf(s, "%s", dev_name(pctldev->dev));
}

/*
        i2cgrp: i2cgrp {
                        functions = "i2c", "i2c";
                        groups = "pin0", "pin1";
                        configs = <0x11223344  0x55667788>;
        };

        one pin ===> two pinctrl_map (one for mux, one for config)
*/

//把设备树中的节点转换成pinctrl_map
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
                struct pinctrl_map **map, unsigned *num_maps)
{
        int i;
        int num_pins = 0;
        const char *pin;
        const char *function;
        unsigned int config;
        struct pinctrl_map *new_map;
        unsigned long *configs;

        /* 1.确定引脚个数 */
        while(1) {
                if(of_property_read_string_index(np, "groups", num_pins, &pin) == 0)
                        num_pins++;
                else
                        break;
        }

        new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);

        for(i=0;i<num_pins;i++) {
                /* 2.取出引脚 */
                of_property_read_string_index(np, "functions", i, &function);
                of_property_read_string_index(np, "groups", i, &pin);
                of_property_read_u32_index(np, "configs", i, &config);

                /* 3.存入pinctrol_map */
                configs = kmalloc(sizeof(*configs), GFP_KERNEL);

                /* 设置复用值 */
                new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;
                new_map[i*2].data.mux.function= function;
                new_map[i*2].data.mux.group = pin;
                /* 设置配置值 */
                new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN;
                new_map[i*2+1].data.configs.group_or_pin = pin;
                new_map[i*2+1].data.configs.configs = configs;
                configs[0] = config;
                new_map[i*2+1].data.configs.num_configs = 1;
        }

        *map = new_map;
        *num_maps = num_pins * 2;

        return 0;
}

//释放pinctrl_map
static void virtual_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map,
                unsigned num_maps)
{
        while(num_maps) {

                if(map->type == PIN_MAP_TYPE_CONFIGS_PIN)
                        kfree(map->data.configs.configs);

                kfree(map);
                map++;
        }
}

//把每个引脚作为一组
static const struct pinctrl_ops virtual_pinctrl_ops = {
        .get_groups_count = virtual_get_groups_count,
        .get_group_name = virtual_get_group_name,
        .get_group_pins = virtual_get_group_pins,
        .pin_dbg_show = virtual_pin_dbg_show,
        .dt_node_to_map = virtual_dt_node_to_map,
        .dt_free_map = virtual_dt_free_map,
};

//可以复用的功能种类
static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
        return ARRAY_SIZE(g_funcs_des);
}

//指定序号中,可以复用的功能名字
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev, unsigned selector)
{
        return g_funcs_des[selector].func_name;
}

//获得这个功能下,支持的组的信息
static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
                const char *const **groups,
                unsigned * const num_groups)
{
        *groups = g_funcs_des[selector].groups;
        *num_groups = g_funcs_des[selector].num_groups;

        return 0;
}

//对指定组设置为指定功能
static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
                unsigned group)
{
        printk("set %s as %s\n", pctldev->desc->pins[group].name,
                        g_funcs_des[selector].func_name);
        return 0;
}

//复用功能的结构体
static const struct pinmux_ops virtual_pmx_ops = {
        .get_functions_count = virtual_pmx_get_funcs_count,
        .get_function_name = virtual_pmx_get_func_name,
        .get_function_groups = virtual_pmx_get_groups,
        .set_mux = virtual_pmx_set,
};

//获取引脚的配置值
static int virtual_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin_id,
                unsigned long *config)
{
        *config = g_configs[pin_id];
        return 0;
}

//设置引脚的配置
static int virtual_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin_id,
                unsigned long *configs, unsigned num_configs)
{
        if(num_configs != 1)
                return -EINVAL;

        g_configs[pin_id] = *configs;
        printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs);
        return 0;
}

//调试信息
static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,
                struct seq_file *s, unsigned pin_id)
{
        seq_printf(s, "0x%lx", g_configs[pin_id]);
}

static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
                struct seq_file *s, unsigned pin_id)
{
        seq_printf(s, "0x%lx", g_configs[pin_id]);
}

//配置引脚功能
static const struct pinconf_ops virtual_pinconf_ops = {
        .pin_config_get = virtual_pinconf_get,
        .pin_config_set = virtual_pinconf_set,
        .pin_config_dbg_show = virtual_pinconf_dbg_show,
        .pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};

static int virtual_pinctrl_probe(struct platform_device *pdev)
{
        struct pinctrl_desc *pinctrl;
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

        /* 1.分配pinctrl_desc */
        pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);

        /* 2.设置pinctrl_desc */
        pinctrl->name = dev_name(&pdev->dev);
        pinctrl->owner = THIS_MODULE;

        /* b.1 pins and group */
        pinctrl->pins = pins;
        pinctrl->npins = ARRAY_SIZE(pins);              //多少个引脚
        pinctrl->pctlops = &virtual_pinctrl_ops;        //组的信息

        /* b.2 pins mux */
        pinctrl->pmxops = &virtual_pmx_ops;             //设置引脚的复用

        /* b.3 pins config */
        pinctrl->confops = &virtual_pinconf_ops;        //设备引脚配置

        /* 3.注册pinctrl_desc */
        g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pinctrl, NULL);

        return 0;
}

static int virtual_pinctrl_remove(struct platform_device *pdev)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        return 0;
}

static struct of_device_id virtual_pinctrl_of_match[] = {
        { .compatible = "100ask,virtual_pinctrl", }, //通过这个值来查找设备树节点
        { },
};

static struct platform_driver virtual_pinctrl_driver = {
        .probe = virtual_pinctrl_probe,
        .remove = virtual_pinctrl_remove,
        .driver = {
                .name = "100ask_virtual_pinctrl",
                .of_match_table = of_match_ptr(virtual_pinctrl_of_match),
        }
};

/* 1.入口函数 */
static int __init virtual_pinctrl_init(void)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        return  platform_driver_register(&virtual_pinctrl_driver);
}

/* 2. 出口函数 */
static void __exit virtual_pinctrl_exit(void)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        platform_driver_unregister(&virtual_pinctrl_driver);
}

module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);
MODULE_LICENSE("GPL");

五、编写client驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>


static int virtual_client_probe(struct platform_device *pdev)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        return 0;
}

static int virtual_client_remove(struct platform_device *pdev)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        return 0;
}

static struct of_device_id virtual_client_of_match[] = {
        { .compatible = "100ask,virtual_i2c", },
        { },
};

static struct platform_driver virtual_client_driver = {
        .probe = virtual_client_probe,
        .remove = virtual_client_remove,
        .driver = {
                .name = "100ask_virtual_client",
                .of_match_table = of_match_ptr(virtual_client_of_match),
        }
};

/* 1.入口函数 */
static int __init virtual_client_init(void)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        return  platform_driver_register(&virtual_client_driver);
}

/* 2. 出口函数 */
static void __exit virtual_client_exit(void)
{
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        platform_driver_unregister(&virtual_client_driver);
}

module_init(virtual_client_init);
module_exit(virtual_client_exit);
MODULE_LICENSE("GPL");

六、调试

加载了virtual_pinctrl_driver.ko驱动后,可以在开发板的/sys/kernel/debug/pinctrl/目录下,
每一个pinctroller都有一个目录,比如virtual_pincontroller。里面有很多文件,作用如下:

Pinctrl虚拟文件作用解释
pins单个引脚信息
pingroups引脚的组信息
pinmux-pins单个引脚的复用信息
pinmux-functionsfunction下的group(支持该function的group)
pinconf-pins单个引脚的配置
pinconf-groups引脚组的配置
pinconf-config可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值
root@npi:/mnt/home/picture/virtual_pinctrl# insmod virtual_pinctrl_driver.ko
root@npi:/mnt/home/picture/virtual_pinctrl# insmod virtual_pinctrl_client.ko
  • 单个引脚信息

  • 引脚的组信息

root@npi:/sys/kernel/debug/pinctrl/virtual_pincontroller# cat pingroups
registered pin groups:
group: pin0
pin 0 (pin0)

group: pin1
pin 1 (pin1)

group: pin2
pin 2 (pin2)

group: pin3
pin 3 (pin3)
  • 单个引脚的复用信息
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (pin0): virtual_i2c (GPIO UNCLAIMED) function i2c group pin0
pin 1 (pin1): virtual_i2c (GPIO UNCLAIMED) function i2c group pin1
pin 2 (pin2): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3 (pin3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
  • function下的goup(支持该function的group)
root@npi:/sys/kernel/debug/pinctrl/virtual_pincontroller# cat pinmux-functions
function: gpio, groups = [ pin0 pin1 pin2 pin3 ]
function: i2c, groups = [ pin0 pin1 ]
function: uart, groups = [ pin2 pin3 ]
  • 单个引脚的配置
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (pin0): 0x11223344
pin 1 (pin1): 0x55667788
pin 2 (pin2): 0x0
pin 3 (pin3): 0x0
  • 引脚组的配置
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-groups
Pin config settings per pin group
Format: group (name): configs
0 (pin0): 0x11223344
1 (pin1): 0x55667788
2 (pin2): 0x0
3 (pin3): 0x0
  • 修改配置值内核源码
drivers\pinctrl\pinconf.c
    pinconf_dbg_config_write

如果pin controller驱动程序中的pinconf_ops提供了pin_config_dbg_parse_modify函数, 就可以通过pinconf-config文件修改某个pin或某个group的配置值。

# 格式: modify <config> <devicename> <state> <pin_name|group_name> <newvalue>
echo "modify config_pin virtual_i2c default pin0 0xaabb" > /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-config

cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-config
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值