当前位置: 首页 > article >正文

04_Linux驱动_05_pinctrl子系统

以下代码都在pinctrl相关的驱动函数和设备树中

pinctrl-rockchip.c驱动,对应的是那个(那些)设备树呢?

答案:通过.compatible = "rockchip,rk3568-pinctrl"连接到rk3568.dtsi根节点下的pinctrl节点

一,pinctrl_desc结构体

/*
    用于描述 Pin controller
*/
struct pinctrl_desc {
	const char *name;                    //1,名字
	const struct pinctrl_pin_desc *pins; //2,一个引脚
	unsigned int npins;                  //3,一共有多少引脚
	const struct pinctrl_ops *pctlops;   //4,xxx操作集合
	const struct pinmux_ops *pmxops;     //5,xxx操作集合
	const struct pinconf_ops *confops;   //6,xxx操作集合
	struct module *owner;                //7,模块的拥有者
#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
};

        构建完 pinctrl结构体之后,会调用pinctrl_register函数注册一个pinctrl控制器,得到一个pinctrl_dev

1.1pinctrl_desc结构体在probe中的使用

不同的芯片厂,的pinctrl的驱动不一样,我的被打包到了rockchip_pinctrl结构体之中了!!!


struct rockchip_pinctrl {
	struct regmap			*regmap_base;
	int				reg_size;
	struct regmap			*regmap_pull;
	struct regmap			*regmap_pmu;
	struct device			*dev;
	struct rockchip_pin_ctrl	*ctrl;
	struct pinctrl_desc		pctl;            //在这里
	struct pinctrl_dev		*pctl_dev;
	struct rockchip_pin_group	*groups;
	unsigned int			ngroups;
	struct rockchip_pmx_func	*functions;
	unsigned int			nfunctions;
};

1.2用rockchip_pinctrl_register函数进行注册。

ret = rockchip_pinctrl_register(pdev, info);
if (ret)
	return ret;

--->>进入rockchip_pinctrl_register函数
{
    struct pinctrl_desc *ctrldesc = &info->pctl;
	struct pinctrl_pin_desc *pindesc, *pdesc;
	struct rockchip_pin_bank *pin_bank;
	int pin, bank, ret;
	int k;

	ctrldesc->name = "rockchip-pinctrl";    //驱动名字和compatible配对
	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++;
		}
	}


    //在devm_pinctrl_register中调用pinctrl_register函数进行注册
	ret = rockchip_pinctrl_parse_dt(pdev, info);
	if (ret)
		return ret;

	info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
	if (IS_ERR(info->pctl_dev)) {
		dev_err(&pdev->dev, "could not register pinctrl driver\n");
		return PTR_ERR(info->pctl_dev);
	}


}

二,pinctrl 子系统函数操作集

2.1 groups和function

groups(引脚组):

引脚组是一组具有相似功能、约束条件或共同工作的引脚的集合。

function(功能):

定义了芯片上具有外设功能的功能。每个功能节点对应于一个或多个 IO 组(group)的配置信息。这些功能可以是串口、SPI、I2C 等外设功能。

在.dtsi文件中:

    can0 {                              //这是一个function
		/omit-if-no-ref/                //这是一组groups
		can0m0_pins: can0m0-pins {
			rockchip,pins =
				/* can0_rxm0 */
				<0 RK_PB4 2 &pcfg_pull_none>,
				/* can0_txm0 */
				<0 RK_PB3 2 &pcfg_pull_none>;
		};

		/omit-if-no-ref/                 //这是一另组groups
		can0m1_pins: can0m1-pins {
			rockchip,pins =
				/* can0_rxm1 */
				<2 RK_PA2 4 &pcfg_pull_none>,
				/* can0_txm1 */
				<2 RK_PA1 4 &pcfg_pull_none>;
		};
	};
    
	can1 {                               //这是一另个function                  
		/omit-if-no-ref/
		can1m0_pins: can1m0-pins {
			rockchip,pins =
				/* can1_rxm0 */
				<1 RK_PA0 3 &pcfg_pull_none>,
				/* can1_txm0 */
				<1 RK_PA1 3 &pcfg_pull_none>;
		};

		/omit-if-no-ref/
		can1m1_pins: can1m1-pins {
			rockchip,pins =
				/* can1_rxm1 */
				<4 RK_PC2 3 &pcfg_pull_none>,
				/* can1_txm1 */
				<4 RK_PC3 3 &pcfg_pull_none>;
		};
	};

可已这么说:can0和can1对应2个function,为CAN0控制器和CAN1控制器,而每个控制器中又有2给不同的groups引脚组

2.2pinctrl_ops结构体:

里面都是函数。

struct pinctrl_ops {
    //获取设备树中的groups用了多少个
	int (*get_groups_count) (struct pinctrl_dev *pctldev);
	//获取指定groups的名字
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);
	//获取指定groups的管脚
    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);
	//节点转化为map
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);
	//dt_node_to_map的反操作
    void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

2.3pinmux_ops结构体:

/*  
    pinctrl_dev:代表着一个 pin controller

*/
struct pinmux_ops {
    //1,申请引脚
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
    //2,释放引脚
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
    //3,获取 pin controller 中 function 的数量
    int (*get_functions_count) (struct pinctrl_dev *pctldev);
    //4,获取指定 function 的名字
    const char *(*get_function_name) (struct pinctrl_dev *pctldev,
					  unsigned selector);
    //5,获取指定 function 的 groups
    int (*get_function_groups) (struct pinctrl_dev *pctldev,
				  unsigned selector,
				  const char * const **groups,
				  unsigned *num_groups);
    //6,设定引脚的 复用功能 (GPIO/I2C/SPI...)
    int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
			unsigned group_selector);
    //7,申请并使能 GPIO
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
				    struct pinctrl_gpio_range *range,
				    unsigned offset);
    //8,禁止并释放 GPIO
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset);
    //9,设置GPIO的方向
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset,
				   bool input);
    //不允许同时在同一个引脚上使用 GPIO 和 其他功能 (在同一时间,只能用一个功能)
    bool strict;
};

2.4pinconf_ops结构体:

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
	bool is_generic;
#endif
    //1,获取某个引脚的配置
	int (*pin_config_get) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *config);
    //2,设置某个引脚的配置
	int (*pin_config_set) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long *configs,
			       unsigned num_configs);
    //3,设置某个组的引脚配置
	int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *config);
    //4,获取某个组的引脚配置
	int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
				     unsigned selector,
				     unsigned long *configs,
				     unsigned num_configs);
    //6,解析和修改调试信息
	int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,
					   const char *arg,
					   unsigned long *config);
    //789,调试使用
	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);
};

2.5rockchip_pinctrl 进一步分析

在1.1中我看到了struct pinctrl_desc,在pinctrl_desc结构体里有:

struct rockchip_pin_group *groups;   //瑞芯微芯片引脚组指针
unsigned int ngroups;                //引脚组数量
struct rockchip_pmx_func *functions; //瑞芯微芯片引脚功能指针
unsigned int nfunctions;             //引脚功能数量

这四个变量被在pinctrl的probe函数中-->rockchip_pinctrl_register函数中的rockchip_pinctrl_parse_dt函数处理:


static int rockchip_pinctrl_register(struct platform_device *pdev,
					struct rockchip_pinctrl *info)
{
	struct pinctrl_desc *ctrldesc = &info->pctl;
	struct pinctrl_pin_desc *pindesc, *pdesc;
	struct rockchip_pin_bank *pin_bank;
	int pin, bank, ret;
	int k;

	ctrldesc->name = "rockchip-pinctrl";
	ctrldesc->owner = THIS_MODULE;
	ctrldesc->pctlops = &rockchip_pctrl_ops;    //瑞芯微封装的操作集1
	ctrldesc->pmxops = &rockchip_pmx_ops;       //瑞芯微封装的操作集2
	ctrldesc->confops = &rockchip_pinconf_ops;  //瑞芯微封装的操作集3

	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++;
		}
	}
    
    //rockchip_pinctrl_parse_dt在这个函数里处理了上面四个变量
	ret = rockchip_pinctrl_parse_dt(pdev, info);
	if (ret)
		return ret;

	info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
	if (IS_ERR(info->pctl_dev)) {
		dev_err(&pdev->dev, "could not register pinctrl driver\n");
		return PTR_ERR(info->pctl_dev);
	}

	return 0;
}

2.6函数操作集的具体实现

   都是调用pinctrl_dev_get_drvdata函数来处理

 

static const struct pinctrl_ops rockchip_pctrl_ops = {
	.get_groups_count	= rockchip_get_groups_count,
	.get_group_name		= rockchip_get_group_name,
	.get_group_pins		= rockchip_get_group_pins,
	.dt_node_to_map		= rockchip_dt_node_to_map,
	.dt_free_map		= rockchip_dt_free_map,
};

static const struct pinmux_ops rockchip_pmx_ops = {
	.get_functions_count	= rockchip_pmx_get_funcs_count,
	.get_function_name	= rockchip_pmx_get_func_name,
	.get_function_groups	= rockchip_pmx_get_groups,
	.set_mux		= rockchip_pmx_set,
};

static const struct pinconf_ops rockchip_pinconf_ops = {
	.pin_config_get			= rockchip_pinconf_get,
	.pin_config_set			= rockchip_pinconf_set,
	.is_generic			= true,
};

三,dt_node_to_map函数

        设备树(Device Tree)中存放的是对硬件设备信息的描述,包含了硬件设备的配置和连接信息,例如在pinctrl节点中的引脚的配置和映射关系。而rockchip_dt_node_to_map函数的作用就是根据设备树中的节点信息,生成对应的引脚映射数组。这个映射数组将描述硬件功能(如 复用功能和配置信息)与设备树中的引脚信息进行绑定。

3.1pinctrl_map结构体

struct pinctrl_map{
    const char*dev_name; //设备名称
    const char*name;     //映射名称
    enum pinctrl_map_type type;//映射类型
    const char *ctrl_dev_name; //控制设备名称
    union{
        struct pinctrl_map_mux mux;         //复用映射数据
        struct pinctrl_map_configs configs; //配置映射数据
    }data;
};

需要将设备树上的信息来转化为驱动中可以用的函数,放在pinctrl_map结构体中

3.2dt_node_to_map流程分析

/*
 *传入参数:
 *1,pin control class device
 *2,设备树中的一个node信息
 *3,略
 *4,略
 */
static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev,
				 struct device_node *np,
				 struct pinctrl_map **map, unsigned *num_maps)
{
    //获取pinctrl信息
	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
	const struct rockchip_pin_group *grp;
	struct pinctrl_map *new_map;            //要转化为的map数据
	struct device_node *parent;             //存放传入节点的父节点
	int map_num = 1;
	int i;

	/*
	 * 通过节点的名字获取组的信息,并且检查是否需要创建配置映射
	 */
	grp = pinctrl_name_to_group(info, np->name);
	if (!grp) {
		dev_err(info->dev, "unable to find group for node %pOFn\n",
			np);
		return -EINVAL;
	}

    /*
     *grp->npins:是获取的组里引脚的数量
	 */
    map_num += grp->npins;
    
    /*
     *分配map_num数量的内存,首地址放在new_map中
	 */
	new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL);
	if (!new_map)
		return -ENOMEM;

	*map = new_map;
	*num_maps = map_num;

	/* create mux map */
	parent = of_get_parent(np);
	if (!parent) {
		kfree(new_map);
		return -EINVAL;
	}
    //引脚配置信息的父节点写入map中
	new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
	new_map[0].data.mux.function = parent->name;
	new_map[0].data.mux.group = np->name;
	of_node_put(parent);

	/* create config map */
	new_map++;
	for (i = 0; i < grp->npins; i++) {                //遍历每一个成员
        //引脚配置信息写入map中
		new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
		new_map[i].data.configs.group_or_pin =
				pin_get_name(pctldev, grp->pins[i]);
		new_map[i].data.configs.configs = grp->data[i].configs;
		new_map[i].data.configs.num_configs = grp->data[i].nconfigs;
	}

	dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
		(*map)->data.mux.function, (*map)->data.mux.group, map_num);

	return 0;
}

四,开发板上用到的pinctrl

在设备树下的两个部分:

//1,在rk3568-evb1-ddr4-v10.dtsi文件的根目录下
    //是一个设备,pinctrl的客户端
	rk_485_ctl: rk-485-ctl {
		compatible = "topeet,rs485_ctl";
		gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&rk_485_gpio>;    //这里就是引用了2部分的rk_485_gpio
	};


//2,在rk3568-evb1-ddr4-v10.dtsi里的
//服务端
&pinctrl {
	cam {
		camera_pwr: camera-pwr {
			rockchip,pins =
				/* camera power en */
				<0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
	headphone {
		hp_det: hp-det {
			rockchip,pins = <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>;
		};
	};

	rk_485{
		rk_485_gpio:rk-485-gpio {
			rockchip,pins = <3 13 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};


http://www.kler.cn/a/586560.html

相关文章:

  • 阿里云oss开发实践:大文件分片、断点续传、实时进度 React+Node+Socket.IO
  • 【接口封装】——22、读写文件
  • iOS底层原理系列03-Objective-C运行时机制
  • ubuntu24.04执行nvidia-smi报错,实际生产报错,处理过程
  • Docker部署Laravel项目
  • 数据结构与算法-图论-二分图
  • Unity3D手游内存深度优化指南
  • PL/SQL语言的神经网络
  • Python语言的代码重构
  • ubuntu20.04装nv驱动的一些坑
  • 《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(54)落宝金钱寻最优 - 跳跃游戏(贪心策略)
  • 洛谷 P1068 [NOIP 2009 普及组] 分数线划定 python
  • 【Kubernets】Deployment 和 StatefulSet 有什么区别?什么时候用 StatefulSet?
  • 内存泄漏的防范:检测与预防
  • 稳定运行的以Oracle数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
  • Windows下安装MongoDB 8
  • 星越L_电动车窗使用及初始化讲解
  • [数据结构]排序之 直接选择排序
  • pytest快速入门 - 目录:半天掌握pytest
  • 数据结构(泛型)