Commit 38b6686f05d4609334b5796bc352ec01d54c5eb4
Committed by
Stefano Babic
1 parent
8b2a31f133
Exists in
smarc_8mq_lf_v2020.04
and in
12 other branches
pinctrl: Add pinctrl driver for i.MX8
Add pinctrl driver for i.MX8. The pads configuration is controlled by SCU, so need to ask SCU to configure pads through scfw API. Add pinctrl-scu to invoke sc_pad_set to configure pads. Add a new flag IMX8_USE_SCU to differentiate i.MX8 from other platforms which could directly configure pads from Acore side. Add CONFIG_PINCTRL_IMX8 as the built gate. Signed-off-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Anatolij Gustschin <agust@denx.de> Cc: Stefano Babic <sbabic@denx.de>
Showing 6 changed files with 258 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 |
... | ... | @@ -55,5 +58,20 @@ |
55 | 58 | This feature depends on device tree configuration. This driver |
56 | 59 | is different from the linux one, this is a simple implementation, |
57 | 60 | only parses the 'fsl,pins' property and configure related |
61 | + registers. | |
62 | + | |
63 | +config PINCTRL_IMX8 | |
64 | + bool "IMX8 pinctrl driver" | |
65 | + depends on ARCH_IMX8 && PINCTRL_FULL | |
66 | + select DEVRES | |
67 | + select PINCTRL_IMX | |
68 | + select PINCTRL_IMX_SCU | |
69 | + help | |
70 | + Say Y here to enable the imx8 pinctrl driver | |
71 | + | |
72 | + This provides a simple pinctrl driver for i.MX8 SoC familiy. | |
73 | + This feature depends on device tree configuration. This driver | |
74 | + is different from the linux one, this is a simple implementation, | |
75 | + only parses the 'fsl,pins' property and configures related | |
58 | 76 | registers. |
drivers/pinctrl/nxp/Makefile
drivers/pinctrl/nxp/pinctrl-imx.c
... | ... | @@ -28,7 +28,9 @@ |
28 | 28 | |
29 | 29 | dev_dbg(dev, "%s: %s\n", __func__, config->name); |
30 | 30 | |
31 | - if (info->flags & SHARE_MUX_CONF_REG) | |
31 | + if (info->flags & IMX8_USE_SCU) | |
32 | + pin_size = SHARE_IMX8_PIN_SIZE; | |
33 | + else if (info->flags & SHARE_MUX_CONF_REG) | |
32 | 34 | pin_size = SHARE_FSL_PIN_SIZE; |
33 | 35 | else |
34 | 36 | pin_size = FSL_PIN_SIZE; |
35 | 37 | |
36 | 38 | |
37 | 39 | |
38 | 40 | |
39 | 41 | |
40 | 42 | |
41 | 43 | |
42 | 44 | |
43 | 45 | |
44 | 46 | |
45 | 47 | |
46 | 48 | |
47 | 49 | |
48 | 50 | |
49 | 51 | |
... | ... | @@ -58,112 +60,127 @@ |
58 | 60 | |
59 | 61 | npins = size / pin_size; |
60 | 62 | |
61 | - /* | |
62 | - * Refer to linux documentation for details: | |
63 | - * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
64 | - */ | |
65 | - for (i = 0; i < npins; i++) { | |
66 | - mux_reg = pin_data[j++]; | |
63 | + if (info->flags & IMX8_USE_SCU) { | |
64 | + imx_pinctrl_scu_conf_pins(info, pin_data, npins); | |
65 | + } else { | |
66 | + /* | |
67 | + * Refer to linux documentation for details: | |
68 | + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
69 | + */ | |
70 | + for (i = 0; i < npins; i++) { | |
71 | + mux_reg = pin_data[j++]; | |
67 | 72 | |
68 | - if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) | |
69 | - mux_reg = -1; | |
73 | + if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) | |
74 | + mux_reg = -1; | |
70 | 75 | |
71 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
72 | - conf_reg = mux_reg; | |
73 | - } else { | |
74 | - conf_reg = pin_data[j++]; | |
75 | - if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg) | |
76 | - conf_reg = -1; | |
77 | - } | |
76 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
77 | + conf_reg = mux_reg; | |
78 | + } else { | |
79 | + conf_reg = pin_data[j++]; | |
80 | + if (!(info->flags & ZERO_OFFSET_VALID) && | |
81 | + !conf_reg) | |
82 | + conf_reg = -1; | |
83 | + } | |
78 | 84 | |
79 | - if ((mux_reg == -1) || (conf_reg == -1)) { | |
80 | - dev_err(dev, "Error mux_reg or conf_reg\n"); | |
81 | - devm_kfree(dev, pin_data); | |
82 | - return -EINVAL; | |
83 | - } | |
85 | + if ((mux_reg == -1) || (conf_reg == -1)) { | |
86 | + dev_err(dev, "Error mux_reg or conf_reg\n"); | |
87 | + devm_kfree(dev, pin_data); | |
88 | + return -EINVAL; | |
89 | + } | |
84 | 90 | |
85 | - input_reg = pin_data[j++]; | |
86 | - mux_mode = pin_data[j++]; | |
87 | - input_val = pin_data[j++]; | |
88 | - config_val = pin_data[j++]; | |
91 | + input_reg = pin_data[j++]; | |
92 | + mux_mode = pin_data[j++]; | |
93 | + input_val = pin_data[j++]; | |
94 | + config_val = pin_data[j++]; | |
89 | 95 | |
90 | - dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, " | |
91 | - "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n", | |
92 | - mux_reg, conf_reg, input_reg, mux_mode, input_val, | |
93 | - config_val); | |
96 | + dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, " | |
97 | + "input_reg 0x%x, mux_mode 0x%x, " | |
98 | + "input_val 0x%x, config_val 0x%x\n", | |
99 | + mux_reg, conf_reg, input_reg, mux_mode, | |
100 | + input_val, config_val); | |
94 | 101 | |
95 | - if (config_val & IMX_PAD_SION) | |
96 | - mux_mode |= IOMUXC_CONFIG_SION; | |
102 | + if (config_val & IMX_PAD_SION) | |
103 | + mux_mode |= IOMUXC_CONFIG_SION; | |
97 | 104 | |
98 | - config_val &= ~IMX_PAD_SION; | |
105 | + config_val &= ~IMX_PAD_SION; | |
99 | 106 | |
100 | - /* Set Mux */ | |
101 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
102 | - clrsetbits_le32(info->base + mux_reg, info->mux_mask, | |
103 | - mux_mode << mux_shift); | |
104 | - } else { | |
105 | - writel(mux_mode, info->base + mux_reg); | |
106 | - } | |
107 | + /* Set Mux */ | |
108 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
109 | + clrsetbits_le32(info->base + mux_reg, | |
110 | + info->mux_mask, | |
111 | + mux_mode << mux_shift); | |
112 | + } else { | |
113 | + writel(mux_mode, info->base + mux_reg); | |
114 | + } | |
107 | 115 | |
108 | - dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg, | |
109 | - mux_mode); | |
116 | + dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", | |
117 | + mux_reg, mux_mode); | |
110 | 118 | |
111 | - /* | |
112 | - * Set select input | |
113 | - * | |
114 | - * If the select input value begins with 0xff, it's a quirky | |
115 | - * select input and the value should be interpreted as below. | |
116 | - * 31 23 15 7 0 | |
117 | - * | 0xff | shift | width | select | | |
118 | - * It's used to work around the problem that the select | |
119 | - * input for some pin is not implemented in the select | |
120 | - * input register but in some general purpose register. | |
121 | - * We encode the select input value, width and shift of | |
122 | - * the bit field into input_val cell of pin function ID | |
123 | - * in device tree, and then decode them here for setting | |
124 | - * up the select input bits in general purpose register. | |
125 | - */ | |
126 | - | |
127 | - if (input_val >> 24 == 0xff) { | |
128 | - u32 val = input_val; | |
129 | - u8 select = val & 0xff; | |
130 | - u8 width = (val >> 8) & 0xff; | |
131 | - u8 shift = (val >> 16) & 0xff; | |
132 | - u32 mask = ((1 << width) - 1) << shift; | |
133 | 119 | /* |
134 | - * The input_reg[i] here is actually some IOMUXC general | |
135 | - * purpose register, not regular select input register. | |
120 | + * Set select input | |
121 | + * | |
122 | + * If the select input value begins with 0xff, | |
123 | + * it's a quirky select input and the value should | |
124 | + * be interpreted as below. | |
125 | + * 31 23 15 7 0 | |
126 | + * | 0xff | shift | width | select | | |
127 | + * It's used to work around the problem that the | |
128 | + * select input for some pin is not implemented in | |
129 | + * the select input register but in some general | |
130 | + * purpose register. We encode the select input | |
131 | + * value, width and shift of the bit field into | |
132 | + * input_val cell of pin function ID in device tree, | |
133 | + * and then decode them here for setting up the select | |
134 | + * input bits in general purpose register. | |
136 | 135 | */ |
137 | - val = readl(info->base + input_reg); | |
138 | - val &= ~mask; | |
139 | - val |= select << shift; | |
140 | - writel(val, info->base + input_reg); | |
141 | - } else if (input_reg) { | |
142 | - /* | |
143 | - * Regular select input register can never be at offset | |
144 | - * 0, and we only print register value for regular case. | |
145 | - */ | |
146 | - if (info->input_sel_base) | |
147 | - writel(input_val, info->input_sel_base + | |
148 | - input_reg); | |
149 | - else | |
150 | - writel(input_val, info->base + input_reg); | |
151 | 136 | |
152 | - dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n", | |
153 | - input_reg, input_val); | |
154 | - } | |
137 | + if (input_val >> 24 == 0xff) { | |
138 | + u32 val = input_val; | |
139 | + u8 select = val & 0xff; | |
140 | + u8 width = (val >> 8) & 0xff; | |
141 | + u8 shift = (val >> 16) & 0xff; | |
142 | + u32 mask = ((1 << width) - 1) << shift; | |
143 | + /* | |
144 | + * The input_reg[i] here is actually some | |
145 | + * IOMUXC general purpose register, not | |
146 | + * regular select input register. | |
147 | + */ | |
148 | + val = readl(info->base + input_reg); | |
149 | + val &= ~mask; | |
150 | + val |= select << shift; | |
151 | + writel(val, info->base + input_reg); | |
152 | + } else if (input_reg) { | |
153 | + /* | |
154 | + * Regular select input register can never be | |
155 | + * at offset 0, and we only print register | |
156 | + * value for regular case. | |
157 | + */ | |
158 | + if (info->input_sel_base) | |
159 | + writel(input_val, | |
160 | + info->input_sel_base + | |
161 | + input_reg); | |
162 | + else | |
163 | + writel(input_val, | |
164 | + info->base + input_reg); | |
155 | 165 | |
156 | - /* Set config */ | |
157 | - if (!(config_val & IMX_NO_PAD_CTL)) { | |
158 | - if (info->flags & SHARE_MUX_CONF_REG) { | |
159 | - clrsetbits_le32(info->base + conf_reg, | |
160 | - ~info->mux_mask, config_val); | |
161 | - } else { | |
162 | - writel(config_val, info->base + conf_reg); | |
166 | + dev_dbg(dev, "select_input: offset 0x%x val " | |
167 | + "0x%x\n", input_reg, input_val); | |
163 | 168 | } |
164 | 169 | |
165 | - dev_dbg(dev, "write config: offset 0x%x val 0x%x\n", | |
166 | - conf_reg, config_val); | |
170 | + /* Set config */ | |
171 | + if (!(config_val & IMX_NO_PAD_CTL)) { | |
172 | + if (info->flags & SHARE_MUX_CONF_REG) { | |
173 | + clrsetbits_le32(info->base + conf_reg, | |
174 | + ~info->mux_mask, | |
175 | + config_val); | |
176 | + } else { | |
177 | + writel(config_val, | |
178 | + info->base + conf_reg); | |
179 | + } | |
180 | + | |
181 | + dev_dbg(dev, "write config: offset 0x%x val " | |
182 | + "0x%x\n", conf_reg, config_val); | |
183 | + } | |
167 | 184 | } |
168 | 185 | } |
169 | 186 | |
... | ... | @@ -193,6 +210,9 @@ |
193 | 210 | priv->dev = dev; |
194 | 211 | priv->info = info; |
195 | 212 | |
213 | + if (info->flags & IMX8_USE_SCU) | |
214 | + return 0; | |
215 | + | |
196 | 216 | addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", |
197 | 217 | &size); |
198 | 218 | |
... | ... | @@ -237,6 +257,9 @@ |
237 | 257 | { |
238 | 258 | struct imx_pinctrl_priv *priv = dev_get_priv(dev); |
239 | 259 | struct imx_pinctrl_soc_info *info = priv->info; |
260 | + | |
261 | + if (info->flags & IMX8_USE_SCU) | |
262 | + return 0; | |
240 | 263 | |
241 | 264 | if (info->input_sel_base) |
242 | 265 | unmap_sysmem(info->input_sel_base); |
drivers/pinctrl/nxp/pinctrl-imx.h
... | ... | @@ -40,14 +40,30 @@ |
40 | 40 | #define FSL_PIN_SIZE 24 |
41 | 41 | #define SHARE_FSL_PIN_SIZE 20 |
42 | 42 | |
43 | +/* Each pin on imx8qm/qxp consists of 2 u32 PIN_FUNC_ID and 1 u32 CONFIG */ | |
44 | +#define SHARE_IMX8_PIN_SIZE 12 | |
45 | + | |
43 | 46 | #define SHARE_MUX_CONF_REG 0x1 |
44 | 47 | #define ZERO_OFFSET_VALID 0x2 |
45 | 48 | #define CONFIG_IBE_OBE 0x4 |
49 | +#define IMX8_USE_SCU 0x8 | |
46 | 50 | |
47 | 51 | #define IOMUXC_CONFIG_SION (0x1 << 4) |
48 | 52 | |
49 | 53 | int imx_pinctrl_probe(struct udevice *dev, struct imx_pinctrl_soc_info *info); |
50 | 54 | |
51 | 55 | int imx_pinctrl_remove(struct udevice *dev); |
56 | + | |
57 | +#ifdef CONFIG_PINCTRL_IMX_SCU | |
58 | +int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, | |
59 | + u32 *pin_data, int npins); | |
60 | +#else | |
61 | +static inline int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, | |
62 | + u32 *pin_data, int npins) | |
63 | +{ | |
64 | + return 0; | |
65 | +} | |
66 | +#endif | |
67 | + | |
52 | 68 | #endif /* __DRIVERS_PINCTRL_IMX_H */ |
drivers/pinctrl/nxp/pinctrl-imx8.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright 2018 NXP | |
4 | + */ | |
5 | + | |
6 | +#include <common.h> | |
7 | +#include <dm/device.h> | |
8 | +#include <dm/pinctrl.h> | |
9 | + | |
10 | +#include "pinctrl-imx.h" | |
11 | + | |
12 | +DECLARE_GLOBAL_DATA_PTR; | |
13 | + | |
14 | +static struct imx_pinctrl_soc_info imx8_pinctrl_soc_info = { | |
15 | + .flags = IMX8_USE_SCU, | |
16 | +}; | |
17 | + | |
18 | +static int imx8_pinctrl_probe(struct udevice *dev) | |
19 | +{ | |
20 | + struct imx_pinctrl_soc_info *info = | |
21 | + (struct imx_pinctrl_soc_info *)dev_get_driver_data(dev); | |
22 | + | |
23 | + return imx_pinctrl_probe(dev, info); | |
24 | +} | |
25 | + | |
26 | +static const struct udevice_id imx8_pinctrl_match[] = { | |
27 | + { .compatible = "fsl,imx8qxp-iomuxc", .data = (ulong)&imx8_pinctrl_soc_info }, | |
28 | + { /* sentinel */ } | |
29 | +}; | |
30 | + | |
31 | +U_BOOT_DRIVER(imx8_pinctrl) = { | |
32 | + .name = "imx8_pinctrl", | |
33 | + .id = UCLASS_PINCTRL, | |
34 | + .of_match = of_match_ptr(imx8_pinctrl_match), | |
35 | + .probe = imx8_pinctrl_probe, | |
36 | + .remove = imx_pinctrl_remove, | |
37 | + .priv_auto_alloc_size = sizeof(struct imx_pinctrl_priv), | |
38 | + .ops = &imx_pinctrl_ops, | |
39 | + .flags = DM_FLAG_PRE_RELOC, | |
40 | +}; |
drivers/pinctrl/nxp/pinctrl-scu.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright 2018 NXP | |
4 | + */ | |
5 | + | |
6 | +#include <common.h> | |
7 | +#include <errno.h> | |
8 | +#include <linux/bitops.h> | |
9 | +#include <asm/io.h> | |
10 | +#include <asm/arch/sci/sci.h> | |
11 | +#include <misc.h> | |
12 | + | |
13 | +#include "pinctrl-imx.h" | |
14 | + | |
15 | +#define PADRING_IFMUX_EN_SHIFT 31 | |
16 | +#define PADRING_IFMUX_EN_MASK BIT(31) | |
17 | +#define PADRING_GP_EN_SHIFT 30 | |
18 | +#define PADRING_GP_EN_MASK BIT(30) | |
19 | +#define PADRING_IFMUX_SHIFT 27 | |
20 | +#define PADRING_IFMUX_MASK GENMASK(29, 27) | |
21 | + | |
22 | +static int imx_pinconf_scu_set(struct imx_pinctrl_soc_info *info, u32 pad, | |
23 | + u32 mux, u32 val) | |
24 | +{ | |
25 | + int ret; | |
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 | + val |= PADRING_IFMUX_EN_MASK; | |
33 | + val |= PADRING_GP_EN_MASK; | |
34 | + val |= (mux << PADRING_IFMUX_SHIFT) & PADRING_IFMUX_MASK; | |
35 | + | |
36 | + ret = sc_pad_set(-1, pad, val); | |
37 | + if (ret) | |
38 | + printf("%s %d\n", __func__, ret); | |
39 | + | |
40 | + return 0; | |
41 | +} | |
42 | + | |
43 | +int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data, | |
44 | + int npins) | |
45 | +{ | |
46 | + int pin_id, mux, config_val; | |
47 | + int i, j = 0; | |
48 | + int ret; | |
49 | + | |
50 | + /* | |
51 | + * Refer to linux documentation for details: | |
52 | + * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt | |
53 | + */ | |
54 | + for (i = 0; i < npins; i++) { | |
55 | + pin_id = pin_data[j++]; | |
56 | + mux = pin_data[j++]; | |
57 | + config_val = pin_data[j++]; | |
58 | + | |
59 | + ret = imx_pinconf_scu_set(info, pin_id, mux, config_val); | |
60 | + if (ret) | |
61 | + printf("Set pin %d, mux %d, val %d, error\n", pin_id, | |
62 | + mux, config_val); | |
63 | + } | |
64 | + | |
65 | + return 0; | |
66 | +} |