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