1 /* $OpenBSD: mvpinctrl.c,v 1.12 2023/08/15 08:27:30 miod Exp $ */
2 /*
3 * Copyright (c) 2013,2016 Patrick Wildt <patrick@blueri.se>
4 * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23
24 #include <machine/intr.h>
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_clock.h>
30 #include <dev/ofw/ofw_gpio.h>
31 #include <dev/ofw/ofw_misc.h>
32 #include <dev/ofw/ofw_pinctrl.h>
33 #include <dev/ofw/fdt.h>
34
35 /* Armada 3700 Registers */
36 #define GPIO_DIRECTION 0x00
37 #define GPIO_INPUT 0x10
38 #define GPIO_OUTPUT 0x18
39
40 #define HREAD4(sc, reg) \
41 (regmap_read_4((sc)->sc_rm, (reg)))
42 #define HWRITE4(sc, reg, val) \
43 regmap_write_4((sc)->sc_rm, (reg), (val))
44 #define HSET4(sc, reg, bits) \
45 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
46 #define HCLR4(sc, reg, bits) \
47 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
48
49 struct mvpinctrl_pin {
50 const char *pin;
51 const char *function;
52 int value;
53 int pid;
54 };
55
56 struct mvpinctrl_softc {
57 struct device sc_dev;
58 bus_space_tag_t sc_iot;
59 bus_space_handle_t sc_ioh;
60 struct regmap *sc_rm;
61 const struct mvpinctrl_pin *sc_pins;
62 int sc_npins;
63 struct gpio_controller sc_gc;
64 struct clock_device sc_cd_xtal;
65 };
66
67 int mvpinctrl_match(struct device *, void *, void *);
68 void mvpinctrl_attach(struct device *, struct device *, void *);
69 int mvpinctrl_pinctrl(uint32_t, void *);
70
71 void mvpinctrl_config_pin(void *, uint32_t *, int);
72 int mvpinctrl_get_pin(void *, uint32_t *);
73 void mvpinctrl_set_pin(void *, uint32_t *, int);
74
75 uint32_t a3700_xtal_get_frequency(void *, uint32_t *);
76
77 const struct cfattach mvpinctrl_ca = {
78 sizeof (struct mvpinctrl_softc), mvpinctrl_match, mvpinctrl_attach
79 };
80
81 struct cfdriver mvpinctrl_cd = {
82 NULL, "mvpinctrl", DV_DULL
83 };
84
85 #define STR_HELPER(x) #x
86 #define STR(x) STR_HELPER(x)
87 #define MPP(id, func, val) { STR(mpp ## id), func, val, id }
88
89 #include "mvpinctrl_pins.h"
90
91 struct mvpinctrl_pins {
92 const char *compat;
93 const struct mvpinctrl_pin *pins;
94 int npins;
95 };
96
97 const struct mvpinctrl_pins mvpinctrl_pins[] = {
98 {
99 "marvell,mv88f6810-pinctrl",
100 armada_38x_pins, nitems(armada_38x_pins)
101 },
102 {
103 "marvell,mv88f6820-pinctrl",
104 armada_38x_pins, nitems(armada_38x_pins)
105 },
106 {
107 "marvell,mv88f6828-pinctrl",
108 armada_38x_pins, nitems(armada_38x_pins)
109 },
110 {
111 "marvell,ap806-pinctrl",
112 armada_ap806_pins, nitems(armada_ap806_pins)
113 },
114 {
115 "marvell,cp110-pinctrl",
116 armada_cp110_pins, nitems(armada_cp110_pins)
117 },
118 {
119 "marvell,cp115-standalone-pinctrl",
120 armada_cp110_pins, nitems(armada_cp110_pins)
121 },
122 {
123 "marvell,armada-7k-pinctrl",
124 armada_cp110_pins, nitems(armada_cp110_pins)
125 },
126 {
127 "marvell,armada-8k-cpm-pinctrl",
128 armada_cp110_pins, nitems(armada_cp110_pins)
129 },
130 {
131 "marvell,armada-8k-cps-pinctrl",
132 armada_cp110_pins, nitems(armada_cp110_pins)
133 },
134 };
135
136 int
mvpinctrl_match(struct device * parent,void * match,void * aux)137 mvpinctrl_match(struct device *parent, void *match, void *aux)
138 {
139 struct fdt_attach_args *faa = aux;
140 int i;
141
142 for (i = 0; i < nitems(mvpinctrl_pins); i++) {
143 if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat))
144 return 10;
145 }
146
147 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") ||
148 OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl"))
149 return 10;
150
151 return 0;
152 }
153
154 void
mvpinctrl_attach(struct device * parent,struct device * self,void * aux)155 mvpinctrl_attach(struct device *parent, struct device *self, void *aux)
156 {
157 struct mvpinctrl_softc *sc = (struct mvpinctrl_softc *)self;
158 struct fdt_attach_args *faa = aux;
159 int i, node;
160
161 if (faa->fa_nreg > 0) {
162 sc->sc_iot = faa->fa_iot;
163 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
164 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
165 printf(": can't map registers\n");
166 return;
167 }
168
169 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
170 faa->fa_reg[0].size);
171 sc->sc_rm = regmap_bynode(faa->fa_node);
172 } else {
173 /* No registers; use regmap provided by parent. */
174 sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
175 }
176
177 if (sc->sc_rm == NULL) {
178 printf(": no registers\n");
179 return;
180 }
181
182 printf("\n");
183
184 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl")) {
185 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
186 if (OF_is_compatible(node, "marvell,armada-3700-xtal-clock"))
187 break;
188 }
189 KASSERT(node != 0);
190 sc->sc_cd_xtal.cd_node = node;
191 sc->sc_cd_xtal.cd_cookie = sc;
192 sc->sc_cd_xtal.cd_get_frequency = a3700_xtal_get_frequency;
193 clock_register(&sc->sc_cd_xtal);
194 }
195
196 if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") ||
197 OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl")) {
198 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
199 if (OF_getproplen(node, "gpio-controller") == 0)
200 break;
201 }
202 KASSERT(node != 0);
203 sc->sc_gc.gc_node = node;
204 sc->sc_gc.gc_cookie = sc;
205 sc->sc_gc.gc_config_pin = mvpinctrl_config_pin;
206 sc->sc_gc.gc_get_pin = mvpinctrl_get_pin;
207 sc->sc_gc.gc_set_pin = mvpinctrl_set_pin;
208 gpio_controller_register(&sc->sc_gc);
209 return;
210 }
211
212 for (i = 0; i < nitems(mvpinctrl_pins); i++) {
213 if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat)) {
214 sc->sc_pins = mvpinctrl_pins[i].pins;
215 sc->sc_npins = mvpinctrl_pins[i].npins;
216 break;
217 }
218 }
219
220 KASSERT(sc->sc_pins);
221 pinctrl_register(faa->fa_node, mvpinctrl_pinctrl, sc);
222 }
223
224 int
mvpinctrl_pinctrl(uint32_t phandle,void * cookie)225 mvpinctrl_pinctrl(uint32_t phandle, void *cookie)
226 {
227 struct mvpinctrl_softc *sc = cookie;
228 char *pins, *pin, *func;
229 int i, flen, plen, node;
230
231 node = OF_getnodebyphandle(phandle);
232 if (node == 0)
233 return -1;
234
235 flen = OF_getproplen(node, "marvell,function");
236 if (flen <= 0)
237 return -1;
238
239 func = malloc(flen, M_TEMP, M_WAITOK);
240 OF_getprop(node, "marvell,function", func, flen);
241
242 plen = OF_getproplen(node, "marvell,pins");
243 if (plen <= 0)
244 return -1;
245
246 pin = pins = malloc(plen, M_TEMP, M_WAITOK);
247 OF_getprop(node, "marvell,pins", pins, plen);
248
249 while (plen > 0) {
250 for (i = 0; i < sc->sc_npins; i++) {
251 uint32_t off, shift;
252
253 if (strcmp(sc->sc_pins[i].pin, pin))
254 continue;
255 if (strcmp(sc->sc_pins[i].function, func))
256 continue;
257
258 off = (sc->sc_pins[i].pid / 8) * sizeof(uint32_t);
259 shift = (sc->sc_pins[i].pid % 8) * 4;
260
261 HWRITE4(sc, off, (HREAD4(sc, off) & ~(0xf << shift)) |
262 (sc->sc_pins[i].value << shift));
263 break;
264 }
265
266 if (i == sc->sc_npins)
267 printf("%s: unsupported pin %s function %s\n",
268 sc->sc_dev.dv_xname, pin, func);
269
270 plen -= strlen(pin) + 1;
271 pin += strlen(pin) + 1;
272 }
273
274 free(func, M_TEMP, flen);
275 free(pins, M_TEMP, plen);
276 return 0;
277 }
278
279 void
mvpinctrl_config_pin(void * cookie,uint32_t * cells,int config)280 mvpinctrl_config_pin(void *cookie, uint32_t *cells, int config)
281 {
282 struct mvpinctrl_softc *sc = cookie;
283 uint32_t pin = cells[0];
284
285 if (pin >= 32)
286 return;
287
288 if (config & GPIO_CONFIG_OUTPUT)
289 HSET4(sc, GPIO_DIRECTION, (1 << pin));
290 else
291 HCLR4(sc, GPIO_DIRECTION, (1 << pin));
292 }
293
294 int
mvpinctrl_get_pin(void * cookie,uint32_t * cells)295 mvpinctrl_get_pin(void *cookie, uint32_t *cells)
296 {
297 struct mvpinctrl_softc *sc = cookie;
298 uint32_t pin = cells[0];
299 uint32_t flags = cells[1];
300 uint32_t reg;
301 int val;
302
303 if (pin >= 32)
304 return 0;
305
306 reg = HREAD4(sc, GPIO_INPUT);
307 reg &= (1 << pin);
308 val = (reg >> pin) & 1;
309 if (flags & GPIO_ACTIVE_LOW)
310 val = !val;
311 return val;
312 }
313
314 void
mvpinctrl_set_pin(void * cookie,uint32_t * cells,int val)315 mvpinctrl_set_pin(void *cookie, uint32_t *cells, int val)
316 {
317 struct mvpinctrl_softc *sc = cookie;
318 uint32_t pin = cells[0];
319 uint32_t flags = cells[1];
320
321 if (pin >= 32)
322 return;
323
324 if (flags & GPIO_ACTIVE_LOW)
325 val = !val;
326 if (val)
327 HSET4(sc, GPIO_OUTPUT, (1 << pin));
328 else
329 HCLR4(sc, GPIO_OUTPUT, (1 << pin));
330 }
331
332 /* Armada 3700 XTAL block */
333
334 #define XTAL 0xc
335 #define XTAL_MODE (1U << 31)
336
337 uint32_t
a3700_xtal_get_frequency(void * cookie,uint32_t * cells)338 a3700_xtal_get_frequency(void *cookie, uint32_t *cells)
339 {
340 struct mvpinctrl_softc *sc = cookie;
341
342 if (regmap_read_4(sc->sc_rm, XTAL) & XTAL_MODE)
343 return 40000000;
344 else
345 return 25000000;
346 }
347