]>
Commit | Line | Data |
---|---|---|
5dc626f8 | 1 | /* |
4e3d8406 MY |
2 | * Copyright (C) 2015-2016 Socionext Inc. |
3 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> | |
5dc626f8 MY |
4 | * |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
6a6b9d5d | 8 | #include <common.h> |
9d922450 | 9 | #include <dm.h> |
5dc626f8 MY |
10 | #include <linux/io.h> |
11 | #include <linux/err.h> | |
510454db | 12 | #include <linux/sizes.h> |
5dc626f8 MY |
13 | #include <dm/pinctrl.h> |
14 | ||
15 | #include "pinctrl-uniphier.h" | |
16 | ||
cdc7e3cb MY |
17 | #define UNIPHIER_PINCTRL_PINMUX_BASE 0x1000 |
18 | #define UNIPHIER_PINCTRL_LOAD_PINMUX 0x1700 | |
6a6b9d5d | 19 | #define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00 |
cdc7e3cb MY |
20 | #define UNIPHIER_PINCTRL_IECTRL 0x1d00 |
21 | ||
64c1cc4c MY |
22 | static const char *uniphier_pinctrl_dummy_name = "_dummy"; |
23 | ||
5dc626f8 MY |
24 | static int uniphier_pinctrl_get_groups_count(struct udevice *dev) |
25 | { | |
26 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
27 | ||
28 | return priv->socdata->groups_count; | |
29 | } | |
30 | ||
31 | static const char *uniphier_pinctrl_get_group_name(struct udevice *dev, | |
32 | unsigned selector) | |
33 | { | |
34 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
35 | ||
64c1cc4c MY |
36 | if (!priv->socdata->groups[selector].name) |
37 | return uniphier_pinctrl_dummy_name; | |
38 | ||
5dc626f8 MY |
39 | return priv->socdata->groups[selector].name; |
40 | } | |
41 | ||
42 | static int uniphier_pinmux_get_functions_count(struct udevice *dev) | |
43 | { | |
44 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
45 | ||
46 | return priv->socdata->functions_count; | |
47 | } | |
48 | ||
49 | static const char *uniphier_pinmux_get_function_name(struct udevice *dev, | |
50 | unsigned selector) | |
51 | { | |
52 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
53 | ||
64c1cc4c MY |
54 | if (!priv->socdata->functions[selector]) |
55 | return uniphier_pinctrl_dummy_name; | |
56 | ||
5dc626f8 MY |
57 | return priv->socdata->functions[selector]; |
58 | } | |
59 | ||
6a6b9d5d MY |
60 | static int uniphier_pinconf_input_enable_perpin(struct udevice *dev, |
61 | unsigned int pin, int enable) | |
3b05b5f0 MY |
62 | { |
63 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
64 | unsigned reg; | |
65 | u32 mask, tmp; | |
66 | ||
67 | reg = UNIPHIER_PINCTRL_IECTRL + pin / 32 * 4; | |
68 | mask = BIT(pin % 32); | |
69 | ||
70 | tmp = readl(priv->base + reg); | |
6a6b9d5d MY |
71 | if (enable) |
72 | tmp |= mask; | |
73 | else | |
74 | tmp &= ~mask; | |
3b05b5f0 | 75 | writel(tmp, priv->base + reg); |
6a6b9d5d MY |
76 | |
77 | return 0; | |
3b05b5f0 MY |
78 | } |
79 | ||
6a6b9d5d MY |
80 | static int uniphier_pinconf_input_enable_legacy(struct udevice *dev, |
81 | unsigned int pin, int enable) | |
5dc626f8 MY |
82 | { |
83 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
84 | int pins_count = priv->socdata->pins_count; | |
85 | const struct uniphier_pinctrl_pin *pins = priv->socdata->pins; | |
86 | int i; | |
87 | ||
6a6b9d5d MY |
88 | /* |
89 | * Multiple pins share one input enable, per-pin disabling is | |
90 | * impossible. | |
91 | */ | |
92 | if (!enable) | |
93 | return -EINVAL; | |
94 | ||
5dc626f8 MY |
95 | for (i = 0; i < pins_count; i++) { |
96 | if (pins[i].number == pin) { | |
97 | unsigned int iectrl; | |
98 | u32 tmp; | |
99 | ||
100 | iectrl = uniphier_pin_get_iectrl(pins[i].data); | |
101 | tmp = readl(priv->base + UNIPHIER_PINCTRL_IECTRL); | |
102 | tmp |= 1 << iectrl; | |
103 | writel(tmp, priv->base + UNIPHIER_PINCTRL_IECTRL); | |
104 | } | |
105 | } | |
6a6b9d5d MY |
106 | |
107 | return 0; | |
5dc626f8 MY |
108 | } |
109 | ||
6a6b9d5d MY |
110 | static int uniphier_pinconf_input_enable(struct udevice *dev, |
111 | unsigned int pin, int enable) | |
3b05b5f0 MY |
112 | { |
113 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
114 | ||
115 | if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) | |
6a6b9d5d MY |
116 | return uniphier_pinconf_input_enable_perpin(dev, pin, enable); |
117 | else | |
118 | return uniphier_pinconf_input_enable_legacy(dev, pin, enable); | |
119 | } | |
120 | ||
121 | #if CONFIG_IS_ENABLED(PINCONF) | |
122 | ||
123 | static const struct pinconf_param uniphier_pinconf_params[] = { | |
124 | { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, | |
125 | { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, | |
126 | { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, | |
127 | { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, | |
128 | { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, | |
129 | { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, | |
130 | }; | |
131 | ||
132 | static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin, | |
133 | unsigned int param, unsigned int arg) | |
134 | { | |
135 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
136 | unsigned int enable = 1; | |
137 | unsigned int reg; | |
138 | u32 mask, tmp; | |
139 | ||
140 | if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE)) | |
141 | return -ENOTSUPP; | |
142 | ||
143 | switch (param) { | |
144 | case PIN_CONFIG_BIAS_DISABLE: | |
145 | enable = 0; | |
146 | break; | |
147 | case PIN_CONFIG_BIAS_PULL_UP: | |
148 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
149 | if (arg == 0) /* total bias is not supported */ | |
150 | return -EINVAL; | |
151 | break; | |
152 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | |
153 | if (arg == 0) /* configuration ignored */ | |
154 | return 0; | |
155 | default: | |
156 | BUG(); | |
157 | } | |
158 | ||
159 | reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pin / 32 * 4; | |
160 | mask = BIT(pin % 32); | |
161 | ||
162 | tmp = readl(priv->base + reg); | |
163 | if (enable) | |
164 | tmp |= mask; | |
3b05b5f0 | 165 | else |
6a6b9d5d MY |
166 | tmp &= ~mask; |
167 | writel(tmp, priv->base + reg); | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static int uniphier_pinconf_set_one(struct udevice *dev, unsigned int pin, | |
173 | unsigned int param, unsigned int arg) | |
174 | { | |
175 | int ret; | |
176 | ||
177 | switch (param) { | |
178 | case PIN_CONFIG_BIAS_DISABLE: | |
179 | case PIN_CONFIG_BIAS_PULL_UP: | |
180 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
181 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | |
182 | ret = uniphier_pinconf_bias_set(dev, pin, param, arg); | |
183 | break; | |
184 | case PIN_CONFIG_INPUT_ENABLE: | |
185 | ret = uniphier_pinconf_input_enable(dev, pin, arg); | |
186 | break; | |
187 | default: | |
188 | printf("unsupported configuration parameter %u\n", param); | |
189 | return -EINVAL; | |
190 | } | |
191 | ||
192 | return ret; | |
3b05b5f0 MY |
193 | } |
194 | ||
6a6b9d5d MY |
195 | static int uniphier_pinconf_group_set(struct udevice *dev, |
196 | unsigned int group_selector, | |
197 | unsigned int param, unsigned int arg) | |
198 | { | |
199 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
200 | const struct uniphier_pinctrl_group *grp = | |
201 | &priv->socdata->groups[group_selector]; | |
202 | int i, ret; | |
203 | ||
204 | for (i = 0; i < grp->num_pins; i++) { | |
205 | ret = uniphier_pinconf_set_one(dev, grp->pins[i], param, arg); | |
206 | if (ret) | |
207 | return ret; | |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | #endif /* CONFIG_IS_ENABLED(PINCONF) */ | |
214 | ||
5dc626f8 | 215 | static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin, |
5e25b9d5 | 216 | int muxval) |
5dc626f8 MY |
217 | { |
218 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
bbb11980 MY |
219 | unsigned reg, reg_end, shift, mask; |
220 | unsigned mux_bits = 8; | |
221 | unsigned reg_stride = 4; | |
222 | bool load_pinctrl = false; | |
5dc626f8 MY |
223 | u32 tmp; |
224 | ||
fdd15b6a | 225 | /* some pins need input-enabling */ |
6a6b9d5d | 226 | uniphier_pinconf_input_enable(dev, pin, 1); |
fdd15b6a | 227 | |
5e25b9d5 MY |
228 | if (muxval < 0) |
229 | return; /* dedicated pin; nothing to do for pin-mux */ | |
230 | ||
bbb11980 MY |
231 | if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_MUX_4BIT) |
232 | mux_bits = 4; | |
233 | ||
8cc92b99 MY |
234 | if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { |
235 | /* | |
236 | * Mode offset bit | |
237 | * Normal 4 * n shift+3:shift | |
238 | * Debug 4 * n shift+7:shift+4 | |
239 | */ | |
bbb11980 | 240 | mux_bits /= 2; |
8cc92b99 MY |
241 | reg_stride = 8; |
242 | load_pinctrl = true; | |
8cc92b99 MY |
243 | } |
244 | ||
5dc626f8 MY |
245 | reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride; |
246 | reg_end = reg + reg_stride; | |
247 | shift = pin * mux_bits % 32; | |
248 | mask = (1U << mux_bits) - 1; | |
249 | ||
250 | /* | |
251 | * If reg_stride is greater than 4, the MSB of each pinsel shall be | |
252 | * stored in the offset+4. | |
253 | */ | |
254 | for (; reg < reg_end; reg += 4) { | |
255 | tmp = readl(priv->base + reg); | |
256 | tmp &= ~(mask << shift); | |
257 | tmp |= (mask & muxval) << shift; | |
258 | writel(tmp, priv->base + reg); | |
259 | ||
260 | muxval >>= mux_bits; | |
261 | } | |
262 | ||
8cc92b99 | 263 | if (load_pinctrl) |
5dc626f8 | 264 | writel(1, priv->base + UNIPHIER_PINCTRL_LOAD_PINMUX); |
5dc626f8 MY |
265 | } |
266 | ||
267 | static int uniphier_pinmux_group_set(struct udevice *dev, | |
268 | unsigned group_selector, | |
269 | unsigned func_selector) | |
270 | { | |
271 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
272 | const struct uniphier_pinctrl_group *grp = | |
273 | &priv->socdata->groups[group_selector]; | |
274 | int i; | |
275 | ||
276 | for (i = 0; i < grp->num_pins; i++) | |
277 | uniphier_pinmux_set_one(dev, grp->pins[i], grp->muxvals[i]); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | const struct pinctrl_ops uniphier_pinctrl_ops = { | |
283 | .get_groups_count = uniphier_pinctrl_get_groups_count, | |
284 | .get_group_name = uniphier_pinctrl_get_group_name, | |
285 | .get_functions_count = uniphier_pinmux_get_functions_count, | |
286 | .get_function_name = uniphier_pinmux_get_function_name, | |
287 | .pinmux_group_set = uniphier_pinmux_group_set, | |
6a6b9d5d MY |
288 | #if CONFIG_IS_ENABLED(PINCONF) |
289 | .pinconf_num_params = ARRAY_SIZE(uniphier_pinconf_params), | |
290 | .pinconf_params = uniphier_pinconf_params, | |
291 | .pinconf_group_set = uniphier_pinconf_group_set, | |
292 | #endif | |
5dc626f8 MY |
293 | .set_state = pinctrl_generic_set_state, |
294 | }; | |
295 | ||
296 | int uniphier_pinctrl_probe(struct udevice *dev, | |
297 | struct uniphier_pinctrl_socdata *socdata) | |
298 | { | |
299 | struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); | |
300 | fdt_addr_t addr; | |
5dc626f8 | 301 | |
a821c4af | 302 | addr = devfdt_get_addr(dev->parent); |
5dc626f8 MY |
303 | if (addr == FDT_ADDR_T_NONE) |
304 | return -EINVAL; | |
305 | ||
4e3d8406 | 306 | priv->base = devm_ioremap(dev, addr, SZ_4K); |
5dc626f8 MY |
307 | if (!priv->base) |
308 | return -ENOMEM; | |
309 | ||
310 | priv->socdata = socdata; | |
311 | ||
312 | return 0; | |
313 | } |