xref: /openbsd/sys/dev/fdt/mvpinctrl.c (revision 771fbea0)
1 /* $OpenBSD: mvpinctrl.c,v 1.8 2021/05/07 01:54:17 jsg 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 	char *pin;
51 	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 	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 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 	struct mvpinctrl_pin *pins;
94 	int npins;
95 };
96 
97 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,armada-7k-pinctrl",
120 		armada_cp110_pins, nitems(armada_cp110_pins)
121 	},
122 	{
123 		"marvell,armada-8k-cpm-pinctrl",
124 		armada_cp110_pins, nitems(armada_cp110_pins)
125 	},
126 	{
127 		"marvell,armada-8k-cps-pinctrl",
128 		armada_cp110_pins, nitems(armada_cp110_pins)
129 	},
130 };
131 
132 int
133 mvpinctrl_match(struct device *parent, void *match, void *aux)
134 {
135 	struct fdt_attach_args *faa = aux;
136 	int i;
137 
138 	for (i = 0; i < nitems(mvpinctrl_pins); i++) {
139 		if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat))
140 			return 10;
141 	}
142 
143 	if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") ||
144 	    OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl"))
145 		return 10;
146 
147 	return 0;
148 }
149 
150 void
151 mvpinctrl_attach(struct device *parent, struct device *self, void *aux)
152 {
153 	struct mvpinctrl_softc *sc = (struct mvpinctrl_softc *)self;
154 	struct fdt_attach_args *faa = aux;
155 	int i, node;
156 
157 	if (faa->fa_nreg > 0) {
158 		sc->sc_iot = faa->fa_iot;
159 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
160 		    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
161 			printf(": can't map registers\n");
162 			return;
163 		}
164 
165 		regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
166 		    faa->fa_reg[0].size);
167 		sc->sc_rm = regmap_bynode(faa->fa_node);
168 	} else {
169 		/* No registers; use regmap provided by parent. */
170 		sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
171 	}
172 
173 	if (sc->sc_rm == NULL) {
174 		printf(": no registers\n");
175 		return;
176 	}
177 
178 	printf("\n");
179 
180 	if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl")) {
181 		for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
182 			if (OF_is_compatible(node, "marvell,armada-3700-xtal-clock"))
183 				break;
184 		}
185 		KASSERT(node != 0);
186 		sc->sc_cd_xtal.cd_node = node;
187 		sc->sc_cd_xtal.cd_cookie = sc;
188 		sc->sc_cd_xtal.cd_get_frequency = a3700_xtal_get_frequency;
189 		clock_register(&sc->sc_cd_xtal);
190 	}
191 
192 	if (OF_is_compatible(faa->fa_node, "marvell,armada3710-nb-pinctrl") ||
193 	    OF_is_compatible(faa->fa_node, "marvell,armada3710-sb-pinctrl")) {
194 		for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
195 			if (OF_getproplen(node, "gpio-controller") == 0)
196 				break;
197 		}
198 		KASSERT(node != 0);
199 		sc->sc_gc.gc_node = node;
200 		sc->sc_gc.gc_cookie = sc;
201 		sc->sc_gc.gc_config_pin = mvpinctrl_config_pin;
202 		sc->sc_gc.gc_get_pin = mvpinctrl_get_pin;
203 		sc->sc_gc.gc_set_pin = mvpinctrl_set_pin;
204 		gpio_controller_register(&sc->sc_gc);
205 		return;
206 	}
207 
208 	for (i = 0; i < nitems(mvpinctrl_pins); i++) {
209 		if (OF_is_compatible(faa->fa_node, mvpinctrl_pins[i].compat)) {
210 			sc->sc_pins = mvpinctrl_pins[i].pins;
211 			sc->sc_npins = mvpinctrl_pins[i].npins;
212 			break;
213 		}
214 	}
215 
216 	KASSERT(sc->sc_pins);
217 	pinctrl_register(faa->fa_node, mvpinctrl_pinctrl, sc);
218 }
219 
220 int
221 mvpinctrl_pinctrl(uint32_t phandle, void *cookie)
222 {
223 	struct mvpinctrl_softc *sc = cookie;
224 	char *pins, *pin, *func;
225 	int i, flen, plen, node;
226 
227 	node = OF_getnodebyphandle(phandle);
228 	if (node == 0)
229 		return -1;
230 
231 	flen = OF_getproplen(node, "marvell,function");
232 	if (flen <= 0)
233 		return -1;
234 
235 	func = malloc(flen, M_TEMP, M_WAITOK);
236 	OF_getprop(node, "marvell,function", func, flen);
237 
238 	plen = OF_getproplen(node, "marvell,pins");
239 	if (plen <= 0)
240 		return -1;
241 
242 	pin = pins = malloc(plen, M_TEMP, M_WAITOK);
243 	OF_getprop(node, "marvell,pins", pins, plen);
244 
245 	while (plen > 0) {
246 		for (i = 0; i < sc->sc_npins; i++) {
247 			uint32_t off, shift;
248 
249 			if (strcmp(sc->sc_pins[i].pin, pin))
250 				continue;
251 			if (strcmp(sc->sc_pins[i].function, func))
252 				continue;
253 
254 			off = (sc->sc_pins[i].pid / 8) * sizeof(uint32_t);
255 			shift = (sc->sc_pins[i].pid % 8) * 4;
256 
257 			HWRITE4(sc, off, (HREAD4(sc, off) & ~(0xf << shift)) |
258 			    (sc->sc_pins[i].value << shift));
259 			break;
260 		}
261 
262 		if (i == sc->sc_npins)
263 			printf("%s: unsupported pin %s function %s\n",
264 			    sc->sc_dev.dv_xname, pin, func);
265 
266 		plen -= strlen(pin) + 1;
267 		pin += strlen(pin) + 1;
268 	}
269 
270 	free(func, M_TEMP, flen);
271 	free(pins, M_TEMP, plen);
272 	return 0;
273 }
274 
275 void
276 mvpinctrl_config_pin(void *cookie, uint32_t *cells, int config)
277 {
278 	struct mvpinctrl_softc *sc = cookie;
279 	uint32_t pin = cells[0];
280 
281 	if (pin >= 32)
282 		return;
283 
284 	if (config & GPIO_CONFIG_OUTPUT)
285 		HSET4(sc, GPIO_DIRECTION, (1 << pin));
286 	else
287 		HCLR4(sc, GPIO_DIRECTION, (1 << pin));
288 }
289 
290 int
291 mvpinctrl_get_pin(void *cookie, uint32_t *cells)
292 {
293 	struct mvpinctrl_softc *sc = cookie;
294 	uint32_t pin = cells[0];
295 	uint32_t flags = cells[1];
296 	uint32_t reg;
297 	int val;
298 
299 	if (pin >= 32)
300 		return 0;
301 
302 	reg = HREAD4(sc, GPIO_INPUT);
303 	reg &= (1 << pin);
304 	val = (reg >> pin) & 1;
305 	if (flags & GPIO_ACTIVE_LOW)
306 		val = !val;
307 	return val;
308 }
309 
310 void
311 mvpinctrl_set_pin(void *cookie, uint32_t *cells, int val)
312 {
313 	struct mvpinctrl_softc *sc = cookie;
314 	uint32_t pin = cells[0];
315 	uint32_t flags = cells[1];
316 
317 	if (pin >= 32)
318 		return;
319 
320 	if (flags & GPIO_ACTIVE_LOW)
321 		val = !val;
322 	if (val)
323 		HSET4(sc, GPIO_OUTPUT, (1 << pin));
324 	else
325 		HCLR4(sc, GPIO_OUTPUT, (1 << pin));
326 }
327 
328 /* Armada 3700 XTAL block */
329 
330 #define XTAL			0xc
331 #define  XTAL_MODE			(1 << 31)
332 
333 uint32_t
334 a3700_xtal_get_frequency(void *cookie, uint32_t *cells)
335 {
336 	struct mvpinctrl_softc *sc = cookie;
337 
338 	if (regmap_read_4(sc->sc_rm, XTAL) & XTAL_MODE)
339 		return 40000000;
340 	else
341 		return 25000000;
342 }
343