一:Pinctrl子系统
大多数SOC的pin配置都很繁琐,复用、上拉、下拉、速度、驱动能力等等,对此Linux内核针对PIN的配置引入了pinctrl子系统。
pinctrl子系统主要功能:
1、获取设备树中pin信息。
2、根据获取到的pin信息来设置pin的复用功能。
3、根据获取到的pin信息来设置pin的电气特性,比如上/下拉,速度,驱动能力等。
二:PIN配置信息
在pinctrl子系统中,PIN的配置信息会被写入设备树公用文件“imx6ull.dtsi”中,对应的节点名为“iomuxc”
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
};
在特定目标板设备树文件“imx6ull-ull-my-emmc.dts”下会对“iomuxc”节点进行信息补充:
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */
>;
};
pinctrl_csi1: csi1grp {
fsl,pins = <
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088
MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b088
MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b088
>;
};
... ...
}
以“MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059”为例分析每个部分的作用。
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19:是一个宏定义,展开为
“#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0”
(1)0x0090:mux_reg寄存器偏移地址,表示“IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B”寄存器地址为0x020e0090=0x020e0000+0x0090;
(2)0x031C:conf_reg寄存器偏移地址,表示“IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B”寄存器地址为0x020e031C=0x020e0000+0x031C;
(3)0x0000:input_reg寄存器偏移地址。该PIN用作UART1_RTS_B时不需要input_reg寄存器,所以为0;
(4)0x5:mux_reg寄存器的值,相当于该PIN复用为GPIO1_IO19;
(5)0x0:input_reg寄存器的值。
(6)0x17059:conf_reg寄存器的值,作用为设置IO的上/下拉,速度,驱动能力等。
三:PIN驱动程序
根据节点“iomuxc”的“compatible”属性查找引用文件:
static struct of_device_id imx6ul_pinctrl_of_match[] = {
{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },
{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },
{ /* sentinel */ }
};
static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct imx_pinctrl_soc_info *pinctrl_info;
match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);
if (!match)
return -ENODEV;
pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;
return imx_pinctrl_probe(pdev, pinctrl_info);
}
static struct platform_driver imx6ul_pinctrl_driver = {
.driver = {
.name = "imx6ul-pinctrl",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
},
.probe = imx6ul_pinctrl_probe,
.remove = imx_pinctrl_remove,
};
static int __init imx6ul_pinctrl_init(void)
{
return platform_driver_register(&imx6ul_pinctrl_driver);
}
pinctrl子系统挂在platform驱动框架上,当设备和驱动匹配后则会调用probe函数,即imx6ul_pinctrl_probe函数为PIN配置的入口函数。
-->>imx6ul_pinctrl_probe
-->>imx_pinctrl_probe
-->>imx_pinctrl_probe_dt
-->>imx_pinctrl_parse_functions
-->>imx_pinctrl_parse_groups
-->>pinctrl_register
1、imx_pinctrl_parse_groups函数的作用为解析设备树PIN的配置信息
static int imx_pinctrl_parse_groups(struct device_node *np,
struct imx_pin_group *grp,
struct imx_pinctrl_soc_info *info,
u32 index)
{
... ...
for (i = 0; i < grp->npins; i++) {
u32 mux_reg = be32_to_cpu(*list++);
u32 conf_reg;
unsigned int pin_id;
struct imx_pin_reg *pin_reg;
struct imx_pin *pin = &grp->pins[i];
... ...
pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
pin_reg = &info->pin_regs[pin_id];
pin->pin = pin_id;
grp->pin_ids[i] = pin_id;
pin_reg->mux_reg = mux_reg;
pin_reg->conf_reg = conf_reg;
pin->input_reg = be32_to_cpu(*list++);
pin->mux_mode = be32_to_cpu(*list++);
pin->input_val = be32_to_cpu(*list++);
/* SION bit is in mux register */
config = be32_to_cpu(*list++);
if (config & IMX_PAD_SION)
pin->mux_mode |= IOMUXC_CONFIG_SION;
pin->config = config & ~IMX_PAD_SION;
dev_dbg(info->dev, "%s: 0x%x 0x%08lx", info->pins[pin_id].name,
pin->mux_mode, pin->config);
}
... ...
}
2、pinctrl_register函数的作用为向LInux内核注册一个PIN控制器。
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data)
struct pinctrl_desc {
const char *name;
struct pinctrl_pin_desc const *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
};
其中传入的pinctrl_desc结构体中三个ops结构体包含了许多操作函数,通过这些函数就可以完成一个PIN的配置,一般由半导体厂商提供。
int imx_pinctrl_probe(struct platform_device *pdev,
struct imx_pinctrl_soc_info *info)
{
struct pinctrl_desc *imx_pinctrl_desc;
... ...
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
}
static const struct pinctrl_ops imx_pctrl_ops = {
.get_groups_count = imx_get_groups_count,
.get_group_name = imx_get_group_name,
.get_group_pins = imx_get_group_pins,
.pin_dbg_show = imx_pin_dbg_show,
.dt_node_to_map = imx_dt_node_to_map,
.dt_free_map = imx_dt_free_map,
};
static const struct pinmux_ops imx_pmx_ops = {
.get_functions_count = imx_pmx_get_funcs_count,
.get_function_name = imx_pmx_get_func_name,
.get_function_groups = imx_pmx_get_groups,
.set_mux = imx_pmx_set,
.gpio_request_enable = imx_pmx_gpio_request_enable,
.gpio_set_direction = imx_pmx_gpio_set_direction,
};
static const struct pinconf_ops imx_pinconf_ops = {
.pin_config_get = imx_pinconf_get,
.pin_config_set = imx_pinconf_set,
.pin_config_dbg_show = imx_pinconf_dbg_show,
.pin_config_group_dbg_show = imx_pinconf_group_dbg_show,
};
四:添加pinctrl节点
创建设备节点“pinctrl_test”,前缀必须为“pinctrl_”,属性名字必须为“fsl,pins”
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO0x__GPIO1_IO0x 0xXXXXXX
>;
};