DeviceDriver (two): Pinctrl subsystem

One: Pinctrl subsystem

The pin configuration of most SOCs is very cumbersome, such as multiplexing, pull-up, pull-down, speed, drive capability, etc. For this, the Linux kernel introduces the pinctrl subsystem for PIN configuration.

Main functions of pinctrl subsystem:

1. Obtain pin information in the device tree.

2. Set the pin multiplexing function according to the obtained pin information.

3. Set the electrical characteristics of the pin according to the acquired pin information, such as up/down, speed, drive capability, etc.

Two: PIN configuration information

In the pinctrl subsystem, the PIN configuration information will be written into the device tree public file "imx6ull.dtsi", and the corresponding node name is "iomuxc"

iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
};

In the specific target board device tree file "imx6ull-ull-my-emmc.dts", the "iomuxc" node will be supplemented with information:

&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

			>;
		};
... ...
}

Take " MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 " as an example to analyze the role of each part.

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19: is a macro definition, expanded to

“#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19                         0x0090 0x031C 0x0000 0x5 0x0”

(1) 0x0090 : mux_reg register offset address, which means "IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B" register address is 0x020e0090=0x020e0000+0x0090;

(2) 0x031C: conf_reg register offset address, which means "IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B" register address is 0x020e031C=0x020e0000+0x031C;

(3) 0x0000: offset address of input_reg register. This PIN does not need input_reg register when used as UART1_RTS_B , so it is 0;

(4) 0x5: the value of the mux_reg register, which is equivalent to the PIN multiplexed as GPIO1_IO19;

(5) 0x0: the value of the input_reg register.

(6) 0x17059: The value of the conf_reg register, which is used to set the up/down, speed, drive capability, etc. of the IO.

Three: PIN driver

Find the reference file according to the "compatible" attribute of the node "iomuxc":

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

The pinctrl subsystem is hung on the platform driver framework. When the device and the driver are matched, the probe function is called, that is, the imx6ul_pinctrl_probe function is the entry function for the PIN configuration.

-->>imx6ul_pinctrl_probe

     -->>imx_pinctrl_probe

         -->>imx_pinctrl_probe_dt

              -->>imx_pinctrl_parse_functions

                   -->>imx_pinctrl_parse_groups

     -->>pinctrl_register

1. The function of imx_pinctrl_parse_groups is to parse the configuration information of the device tree 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. The function of pinctrl_register is to register a PIN controller with the LInux kernel.

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

The three ops structures of the incoming pinctrl_desc structure contain many operation functions, and a PIN configuration can be completed through these functions, which are generally provided by semiconductor manufacturers.

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

Four: add pinctrl node

Create a device node "pinctrl_test", the prefix must be "pinctrl_", and the attribute name must be "fsl,pins"

pinctrl_test: testgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO0x__GPIO1_IO0x     0xXXXXXX 
	>;
};

 

Guess you like

Origin blog.csdn.net/qq_34968572/article/details/103275579