Commit 15669d09e26002c8915e00f6b9d200a161c44277
1 parent
f4ebe53335
Exists in
smarc_8mm-imx_v2018.03_4.14.98_2.0.0_ga
and in
5 other branches
MLK-16094-1 pinctrl: Add pinctrl driver for i.MX8 platform
Add pinctrl driver for i.MX8 platform (QM/QXP). The driver can parse the iomuxc settings from DTB and set them through IPC to SCU. Enable CONFIG_PINCTRL_IMX8 to use this pinctrl driver. Signed-off-by: Ye Li <ye.li@nxp.com> (cherry picked from commit e881106f14f879a0861caa39ce0cdc278e0806a6)
Showing 6 changed files with 254 additions and 93 deletions Side-by-side Diff
drivers/pinctrl/nxp/Kconfig
1 | 1 | config PINCTRL_IMX |
2 | 2 | bool |
3 | 3 | |
4 | +config PINCTRL_IMX_SCU | |
5 | + bool | |
6 | + | |
4 | 7 | config PINCTRL_IMX5 |
5 | 8 | bool "IMX5 pinctrl driver" |
6 | 9 | depends on ARCH_MX5 && PINCTRL_FULL |
... | ... | @@ -66,6 +69,21 @@ |
66 | 69 | Say Y here to enable the imx8m pinctrl driver |
67 | 70 | |
68 | 71 | This provides a simple pinctrl driver for i.MX8M SoC familiy. |
72 | + This feature depends on device tree configuration. This driver | |
73 | + is different from the linux one, this is a simple implementation, | |
74 | + only parses the 'fsl,pins' property and configure related | |
75 | + registers. | |
76 | + | |
77 | +config PINCTRL_IMX8 | |
78 | + bool "IMX8 pinctrl driver" | |
79 | + depends on ARCH_IMX8 && PINCTRL_FULL | |
80 | + select DEVRES | |
81 | + select PINCTRL_IMX | |
82 | + select PINCTRL_IMX_SCU | |
83 | + help | |
84 | + Say Y here to enable the imx8 pinctrl driver | |
85 | + | |
86 | + This provides a simple pinctrl driver for i.MX8 SoC familiy. | |
69 | 87 | This feature depends on device tree configuration. This driver |
70 | 88 | is different from the linux one, this is a simple implementation, |
71 | 89 | only parses the 'fsl,pins' property and configure related |
drivers/pinctrl/nxp/Makefile
... | ... | @@ -3,5 +3,7 @@ |
3 | 3 | obj-$(CONFIG_PINCTRL_IMX6) += pinctrl-imx6.o |
4 | 4 | obj-$(CONFIG_PINCTRL_IMX7) += pinctrl-imx7.o |
5 | 5 | obj-$(CONFIG_PINCTRL_IMX7ULP) += pinctrl-imx7ulp.o |
6 | +obj-$(CONFIG_PINCTRL_IMX8) += pinctrl-imx8.o | |
6 | 7 | obj-$(CONFIG_PINCTRL_IMX8M) += pinctrl-imx8m.o |
8 | +obj-$(CONFIG_PINCTRL_IMX_SCU) += pinctrl-scu.o |
drivers/pinctrl/nxp/pinctrl-imx.c
1 | 1 | /* |
2 | 2 | * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com> |
3 | + * Copyright 2017 NXP | |
3 | 4 | * |
4 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
5 | 6 | */ |
... | ... | @@ -29,7 +30,9 @@ |
29 | 30 | |
30 | 31 | dev_dbg(dev, "%s: %s\n", __func__, config->name); |
31 | 32 | |
32 | - if (info->flags & SHARE_MUX_CONF_REG) | |
33 | + if (info->flags & IMX8_USE_SCU) | |
34 | + pin_size = SHARE_IMX8_PIN_SIZE; | |
35 | + else if (info->flags & SHARE_MUX_CONF_REG) | |
33 | 36 | pin_size = SHARE_FSL_PIN_SIZE; |
34 | 37 | else |
35 | 38 | pin_size = FSL_PIN_SIZE; |
36 | 39 | |
37 | 40 | |
38 | 41 | |
39 | 42 | |
40 | 43 | |
41 | 44 | |
42 | 45 | |
43 | 46 | |
44 | 47 | |
45 | 48 | |
46 | 49 | |
47 | 50 | |
48 | 51 | |
49 | 52 | |
50 | 53 | |
... | ... | @@ -59,112 +62,116 @@ |
59 | 62 | |
60 | 63 | npins = size / pin_size; |
61 | 64 | |
62 | - /* | |
63 | - * Refer to linux documentation for details: | |
64 | - * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
65 | - */ | |
66 | - for (i = 0; i < npins; i++) { | |
67 | - mux_reg = pin_data[j++]; | |
65 | + if (info->flags & IMX8_USE_SCU) { | |
66 | + imx_pinctrl_scu_process_pins(info, pin_data, npins); | |
67 | + } else { | |
68 | + /* | |
69 | + * Refer to linux documentation for details: | |
70 | + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
71 | + */ | |
72 | + for (i = 0; i < npins; i++) { | |
73 | + mux_reg = pin_data[j++]; | |
68 | 74 | |
69 | - if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) | |
70 | - mux_reg = -1; | |
75 | + if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) | |
76 | + mux_reg = -1; | |
71 | 77 | |
72 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
73 | - conf_reg = mux_reg; | |
74 | - } else { | |
75 | - conf_reg = pin_data[j++]; | |
76 | - if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg) | |
77 | - conf_reg = -1; | |
78 | - } | |
78 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
79 | + conf_reg = mux_reg; | |
80 | + } else { | |
81 | + conf_reg = pin_data[j++]; | |
82 | + if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg) | |
83 | + conf_reg = -1; | |
84 | + } | |
79 | 85 | |
80 | - if ((mux_reg == -1) || (conf_reg == -1)) { | |
81 | - dev_err(dev, "Error mux_reg or conf_reg\n"); | |
82 | - devm_kfree(dev, pin_data); | |
83 | - return -EINVAL; | |
84 | - } | |
86 | + if ((mux_reg == -1) || (conf_reg == -1)) { | |
87 | + dev_err(dev, "Error mux_reg or conf_reg\n"); | |
88 | + devm_kfree(dev, pin_data); | |
89 | + return -EINVAL; | |
90 | + } | |
85 | 91 | |
86 | - input_reg = pin_data[j++]; | |
87 | - mux_mode = pin_data[j++]; | |
88 | - input_val = pin_data[j++]; | |
89 | - config_val = pin_data[j++]; | |
92 | + input_reg = pin_data[j++]; | |
93 | + mux_mode = pin_data[j++]; | |
94 | + input_val = pin_data[j++]; | |
95 | + config_val = pin_data[j++]; | |
90 | 96 | |
91 | - dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, " | |
92 | - "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n", | |
93 | - mux_reg, conf_reg, input_reg, mux_mode, input_val, | |
94 | - config_val); | |
97 | + dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, " | |
98 | + "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n", | |
99 | + mux_reg, conf_reg, input_reg, mux_mode, input_val, | |
100 | + config_val); | |
95 | 101 | |
96 | - if (config_val & IMX_PAD_SION) | |
97 | - mux_mode |= IOMUXC_CONFIG_SION; | |
102 | + if (config_val & IMX_PAD_SION) | |
103 | + mux_mode |= IOMUXC_CONFIG_SION; | |
98 | 104 | |
99 | - config_val &= ~IMX_PAD_SION; | |
105 | + config_val &= ~IMX_PAD_SION; | |
100 | 106 | |
101 | - /* Set Mux */ | |
102 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
103 | - clrsetbits_le32(info->base + mux_reg, info->mux_mask, | |
104 | - mux_mode << mux_shift); | |
105 | - } else { | |
106 | - writel(mux_mode, info->base + mux_reg); | |
107 | - } | |
107 | + /* Set Mux */ | |
108 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
109 | + clrsetbits_le32(info->base + mux_reg, info->mux_mask, | |
110 | + mux_mode << mux_shift); | |
111 | + } else { | |
112 | + writel(mux_mode, info->base + mux_reg); | |
113 | + } | |
108 | 114 | |
109 | - dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg, | |
110 | - mux_mode); | |
115 | + dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg, | |
116 | + mux_mode); | |
111 | 117 | |
112 | - /* | |
113 | - * Set select input | |
114 | - * | |
115 | - * If the select input value begins with 0xff, it's a quirky | |
116 | - * select input and the value should be interpreted as below. | |
117 | - * 31 23 15 7 0 | |
118 | - * | 0xff | shift | width | select | | |
119 | - * It's used to work around the problem that the select | |
120 | - * input for some pin is not implemented in the select | |
121 | - * input register but in some general purpose register. | |
122 | - * We encode the select input value, width and shift of | |
123 | - * the bit field into input_val cell of pin function ID | |
124 | - * in device tree, and then decode them here for setting | |
125 | - * up the select input bits in general purpose register. | |
126 | - */ | |
127 | - | |
128 | - if (input_val >> 24 == 0xff) { | |
129 | - u32 val = input_val; | |
130 | - u8 select = val & 0xff; | |
131 | - u8 width = (val >> 8) & 0xff; | |
132 | - u8 shift = (val >> 16) & 0xff; | |
133 | - u32 mask = ((1 << width) - 1) << shift; | |
134 | 118 | /* |
135 | - * The input_reg[i] here is actually some IOMUXC general | |
136 | - * purpose register, not regular select input register. | |
119 | + * Set select input | |
120 | + * | |
121 | + * If the select input value begins with 0xff, it's a quirky | |
122 | + * select input and the value should be interpreted as below. | |
123 | + * 31 23 15 7 0 | |
124 | + * | 0xff | shift | width | select | | |
125 | + * It's used to work around the problem that the select | |
126 | + * input for some pin is not implemented in the select | |
127 | + * input register but in some general purpose register. | |
128 | + * We encode the select input value, width and shift of | |
129 | + * the bit field into input_val cell of pin function ID | |
130 | + * in device tree, and then decode them here for setting | |
131 | + * up the select input bits in general purpose register. | |
137 | 132 | */ |
138 | - val = readl(info->base + input_reg); | |
139 | - val &= ~mask; | |
140 | - val |= select << shift; | |
141 | - writel(val, info->base + input_reg); | |
142 | - } else if (input_reg) { | |
143 | - /* | |
144 | - * Regular select input register can never be at offset | |
145 | - * 0, and we only print register value for regular case. | |
146 | - */ | |
147 | - if (info->input_sel_base) | |
148 | - writel(input_val, info->input_sel_base + | |
149 | - input_reg); | |
150 | - else | |
151 | - writel(input_val, info->base + input_reg); | |
152 | 133 | |
153 | - dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n", | |
154 | - input_reg, input_val); | |
155 | - } | |
134 | + if (input_val >> 24 == 0xff) { | |
135 | + u32 val = input_val; | |
136 | + u8 select = val & 0xff; | |
137 | + u8 width = (val >> 8) & 0xff; | |
138 | + u8 shift = (val >> 16) & 0xff; | |
139 | + u32 mask = ((1 << width) - 1) << shift; | |
140 | + /* | |
141 | + * The input_reg[i] here is actually some IOMUXC general | |
142 | + * purpose register, not regular select input register. | |
143 | + */ | |
144 | + val = readl(info->base + input_reg); | |
145 | + val &= ~mask; | |
146 | + val |= select << shift; | |
147 | + writel(val, info->base + input_reg); | |
148 | + } else if (input_reg) { | |
149 | + /* | |
150 | + * Regular select input register can never be at offset | |
151 | + * 0, and we only print register value for regular case. | |
152 | + */ | |
153 | + if (info->input_sel_base) | |
154 | + writel(input_val, info->input_sel_base + | |
155 | + input_reg); | |
156 | + else | |
157 | + writel(input_val, info->base + input_reg); | |
156 | 158 | |
157 | - /* Set config */ | |
158 | - if (!(config_val & IMX_NO_PAD_CTL)) { | |
159 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
160 | - clrsetbits_le32(info->base + conf_reg, | |
161 | - ~info->mux_mask, config_val); | |
162 | - } else { | |
163 | - writel(config_val, info->base + conf_reg); | |
159 | + dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n", | |
160 | + input_reg, input_val); | |
164 | 161 | } |
165 | 162 | |
166 | - dev_dbg(dev, "write config: offset 0x%x val 0x%x\n", | |
167 | - conf_reg, config_val); | |
163 | + /* Set config */ | |
164 | + if (!(config_val & IMX_NO_PAD_CTL)) { | |
165 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
166 | + clrsetbits_le32(info->base + conf_reg, | |
167 | + (~info->mux_mask), config_val); | |
168 | + } else { | |
169 | + writel(config_val, info->base + conf_reg); | |
170 | + } | |
171 | + | |
172 | + dev_dbg(dev, "write config: offset 0x%x val 0x%x\n", | |
173 | + conf_reg, config_val); | |
174 | + } | |
168 | 175 | } |
169 | 176 | } |
170 | 177 | |
... | ... | @@ -194,6 +201,9 @@ |
194 | 201 | priv->dev = dev; |
195 | 202 | priv->info = info; |
196 | 203 | |
204 | + if (info->flags & IMX8_USE_SCU) | |
205 | + return 0; | |
206 | + | |
197 | 207 | addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", |
198 | 208 | &size); |
199 | 209 | |
... | ... | @@ -238,6 +248,9 @@ |
238 | 248 | { |
239 | 249 | struct imx_pinctrl_priv *priv = dev_get_priv(dev); |
240 | 250 | struct imx_pinctrl_soc_info *info = priv->info; |
251 | + | |
252 | + if (info->flags & IMX8_USE_SCU) | |
253 | + return 0; | |
241 | 254 | |
242 | 255 | if (info->input_sel_base) |
243 | 256 | unmap_sysmem(info->input_sel_base); |
drivers/pinctrl/nxp/pinctrl-imx.h
... | ... | @@ -41,14 +41,28 @@ |
41 | 41 | #define FSL_PIN_SIZE 24 |
42 | 42 | #define SHARE_FSL_PIN_SIZE 20 |
43 | 43 | |
44 | +/* Each pin on imx8qm/qxp consists of 2 u32 PIN_FUNC_ID and 1 u32 CONFIG */ | |
45 | +#define SHARE_IMX8_PIN_SIZE 12 | |
46 | + | |
44 | 47 | #define SHARE_MUX_CONF_REG 0x1 |
45 | 48 | #define ZERO_OFFSET_VALID 0x2 |
46 | 49 | #define CONFIG_IBE_OBE 0x4 |
50 | +#define IMX8_USE_SCU 0x8 | |
47 | 51 | |
48 | 52 | #define IOMUXC_CONFIG_SION (0x1 << 4) |
49 | 53 | |
50 | 54 | int imx_pinctrl_probe(struct udevice *dev, struct imx_pinctrl_soc_info *info); |
51 | 55 | |
52 | 56 | int imx_pinctrl_remove(struct udevice *dev); |
57 | + | |
58 | +#ifdef CONFIG_PINCTRL_IMX_SCU | |
59 | +int imx_pinctrl_scu_process_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data, int npins); | |
60 | +#else | |
61 | +static inline int imx_pinctrl_scu_process_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data, int npins) | |
62 | +{ | |
63 | + return 0; | |
64 | +} | |
65 | +#endif | |
66 | + | |
53 | 67 | #endif /* __DRIVERS_PINCTRL_IMX_H */ |
drivers/pinctrl/nxp/pinctrl-imx8.c
1 | +/* | |
2 | + * Copyright 2017 NXP | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm/device.h> | |
9 | +#include <dm/pinctrl.h> | |
10 | + | |
11 | +#include "pinctrl-imx.h" | |
12 | + | |
13 | +DECLARE_GLOBAL_DATA_PTR; | |
14 | + | |
15 | +static struct imx_pinctrl_soc_info imx8_pinctrl_soc_info = { | |
16 | + .flags = IMX8_USE_SCU, | |
17 | +}; | |
18 | + | |
19 | +static int imx8_pinctrl_probe(struct udevice *dev) | |
20 | +{ | |
21 | + struct imx_pinctrl_soc_info *info = | |
22 | + (struct imx_pinctrl_soc_info *)dev_get_driver_data(dev); | |
23 | + | |
24 | + info->base = (void *)gd->arch.ipc_channel_handle; | |
25 | + return imx_pinctrl_probe(dev, info); | |
26 | +} | |
27 | + | |
28 | +static const struct udevice_id imx8_pinctrl_match[] = { | |
29 | + { .compatible = "fsl,imx8qm-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, | |
30 | + { .compatible = "fsl,imx8qxp-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, | |
31 | + { /* sentinel */ } | |
32 | +}; | |
33 | + | |
34 | +U_BOOT_DRIVER(imx8_pinctrl) = { | |
35 | + .name = "imx8_pinctrl", | |
36 | + .id = UCLASS_PINCTRL, | |
37 | + .of_match = of_match_ptr(imx8_pinctrl_match), | |
38 | + .probe = imx8_pinctrl_probe, | |
39 | + .remove = imx_pinctrl_remove, | |
40 | + .priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv), | |
41 | + .ops = &imx_pinctrl_ops, | |
42 | + .flags = DM_FLAG_PRE_RELOC, | |
43 | +}; |
drivers/pinctrl/nxp/pinctrl-scu.c
1 | +/* | |
2 | + * Copyright 2017 NXP | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <errno.h> | |
9 | +#include <asm/io.h> | |
10 | +#include <asm/mach-imx/sci/sci.h> | |
11 | +#include "pinctrl-imx.h" | |
12 | + | |
13 | +#define PADRING_IFMUX_EN_SHIFT 31 | |
14 | +#define PADRING_IFMUX_EN_MASK (1 << 31) | |
15 | +#define PADRING_GP_EN_SHIFT 30 | |
16 | +#define PADRING_GP_EN_MASK (1 << 30) | |
17 | +#define PADRING_IFMUX_SHIFT 27 | |
18 | +#define PADRING_IFMUX_MASK (0x7 << 27) | |
19 | + | |
20 | + | |
21 | +static int imx_pinconf_scu_set(struct imx_pinctrl_soc_info *info, | |
22 | + unsigned int pin_id, unsigned int mux, unsigned int val) | |
23 | +{ | |
24 | + sc_err_t err = SC_ERR_NONE; | |
25 | + sc_ipc_t ipcHndl = (sc_ipc_t)info->base; | |
26 | + | |
27 | + /* | |
28 | + * Mux should be done in pmx set, but we do not have a good api | |
29 | + * to handle that in scfw, so config it in pad conf func | |
30 | + */ | |
31 | + | |
32 | + if (ipcHndl == 0) { | |
33 | + printf("IPC handle not initialized!\n"); | |
34 | + return -EIO; | |
35 | + } | |
36 | + | |
37 | + val |= PADRING_IFMUX_EN_MASK; | |
38 | + val |= PADRING_GP_EN_MASK; | |
39 | + val |= (mux << PADRING_IFMUX_SHIFT) & PADRING_IFMUX_MASK; | |
40 | + | |
41 | + err = sc_pad_set(ipcHndl, pin_id, val); | |
42 | + | |
43 | + if (err != SC_ERR_NONE) | |
44 | + return -EIO; | |
45 | + | |
46 | + return 0; | |
47 | +} | |
48 | + | |
49 | + | |
50 | +int imx_pinctrl_scu_process_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data, int npins) | |
51 | +{ | |
52 | + int pin_id, mux, config_val; | |
53 | + int i, j = 0; | |
54 | + int ret; | |
55 | + | |
56 | + /* | |
57 | + * Refer to linux documentation for details: | |
58 | + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
59 | + */ | |
60 | + for (i = 0; i < npins; i++) { | |
61 | + pin_id = pin_data[j++]; | |
62 | + mux = pin_data[j++]; | |
63 | + config_val = pin_data[j++]; | |
64 | + | |
65 | + ret = imx_pinconf_scu_set(info, pin_id, mux, config_val); | |
66 | + if (ret) | |
67 | + printf("Set pin %d, mux %d, val %d, error\n", pin_id, mux, config_val); | |
68 | + } | |
69 | + | |
70 | + return 0; | |
71 | +} |