xref: /openbsd/sys/dev/fdt/mvpinctrl.c (revision 5dea098c)
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
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
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
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
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
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
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
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