xref: /openbsd/sys/arch/armv7/exynos/exgpio.c (revision a4a50d96)
1 /* $OpenBSD: exgpio.c,v 1.7 2021/03/25 04:12:01 jsg Exp $ */
2 /*
3  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
5  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_gpio.h>
30 #include <dev/ofw/ofw_pinctrl.h>
31 #include <dev/ofw/fdt.h>
32 
33 #define GPXCON(x)	((x) + 0x0000)
34 #define  GPXCON_INPUT	0
35 #define  GPXCON_OUTPUT	1
36 #define GPXDAT(x)	((x) + 0x0004)
37 #define GPXPUD(x)	((x) + 0x0008)
38 #define GPXDRV(x)	((x) + 0x000c)
39 
40 #define GPX_NUM_PINS	8
41 
42 #define HREAD4(sc, reg)							\
43 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
44 #define HWRITE4(sc, reg, val)						\
45 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
46 
47 struct exgpio_bank {
48 	const char name[8];
49 	bus_addr_t addr;
50 };
51 
52 struct exgpio_controller {
53 	struct gpio_controller ec_gc;
54 	struct exgpio_bank *ec_bank;
55 	struct exgpio_softc *ec_sc;
56 };
57 
58 struct exgpio_softc {
59 	struct device		sc_dev;
60 	bus_space_tag_t		sc_iot;
61 	bus_space_handle_t	sc_ioh;
62 
63 	struct exgpio_bank	*sc_banks;
64 	int			sc_nbanks;
65 };
66 
67 int exgpio_match(struct device *, void *, void *);
68 void exgpio_attach(struct device *, struct device *, void *);
69 
70 struct cfattach	exgpio_ca = {
71 	sizeof (struct exgpio_softc), exgpio_match, exgpio_attach
72 };
73 
74 struct cfdriver exgpio_cd = {
75 	NULL, "exgpio", DV_DULL
76 };
77 
78 /* Exynos 5420/5422 */
79 struct exgpio_bank exynos5420_banks[] = {
80 	/* Controller 0 */
81 	{ "gpy7", 0x0000 },
82 	{ "gpx0", 0x0c00 },
83 	{ "gpx1", 0x0c20 },
84 	{ "gpx2", 0x0c40 },
85 	{ "gpx3", 0x0c60 },
86 
87 	/* Controller 1 */
88 	{ "gpc0", 0x0000 },
89 	{ "gpc1", 0x0020 },
90 	{ "gpc2", 0x0040 },
91 	{ "gpc3", 0x0060 },
92 	{ "gpc4", 0x0080 },
93 	{ "gpd1", 0x00a0 },
94 	{ "gpy0", 0x00c0 },
95 	{ "gpy1", 0x00e0 },
96 	{ "gpy2", 0x0100 },
97 	{ "gpy3", 0x0120 },
98 	{ "gpy4", 0x0140 },
99 	{ "gpy5", 0x0160 },
100 	{ "gpy6", 0x0180 },
101 
102 	/* Controller 2 */
103 	{ "gpe0", 0x0000 },
104 	{ "gpe1", 0x0020 },
105 	{ "gpf0", 0x0040 },
106 	{ "gpf1", 0x0060 },
107 	{ "gpg0", 0x0080 },
108 	{ "gpg1", 0x00a0 },
109 	{ "gpg2", 0x00c0 },
110 	{ "gpj4", 0x00e0 },
111 
112 	/* Controller 3 */
113 	{ "gpa0", 0x0000 },
114 	{ "gpa1", 0x0020 },
115 	{ "gpa2", 0x0040 },
116 	{ "gpb0", 0x0060 },
117 	{ "gpb1", 0x0080 },
118 	{ "gpb2", 0x00a0 },
119 	{ "gpb3", 0x00c0 },
120 	{ "gpb4", 0x00e0 },
121 	{ "gph0", 0x0100 },
122 
123 	/* Controller 4 */
124 	{ "gpz", 0x0000 },
125 };
126 
127 struct exgpio_bank *exgpio_bank(struct exgpio_softc *, const char *);
128 int	exgpio_pinctrl(uint32_t, void *);
129 void	exgpio_config_pin(void *, uint32_t *, int);
130 int	exgpio_get_pin(void *, uint32_t *);
131 void	exgpio_set_pin(void *, uint32_t *, int);
132 
133 int
134 exgpio_match(struct device *parent, void *match, void *aux)
135 {
136 	struct fdt_attach_args *faa = aux;
137 
138 	return OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl");
139 }
140 
141 void
142 exgpio_attach(struct device *parent, struct device *self, void *aux)
143 {
144 	struct exgpio_softc *sc = (struct exgpio_softc *)self;
145 	struct fdt_attach_args *faa = aux;
146 	struct exgpio_controller *ec;
147 	struct exgpio_bank *bank;
148 	char name[8];
149 	int node;
150 	int len;
151 
152 	sc->sc_iot = faa->fa_iot;
153 
154 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
155 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
156 		panic("%s: bus_space_map failed!", __func__);
157 
158 	if (OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl")) {
159 		sc->sc_banks = exynos5420_banks;
160 		sc->sc_nbanks = nitems(exynos5420_banks);
161 	}
162 
163 	KASSERT(sc->sc_banks);
164 	pinctrl_register(faa->fa_node, exgpio_pinctrl, sc);
165 
166 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
167 		if (OF_getproplen(node, "gpio-controller") < 0)
168 			continue;
169 
170 		len = OF_getprop(node, "name", &name, sizeof(name));
171 		if (len <= 0 || len >= sizeof(name))
172 			continue;
173 
174 		bank = exgpio_bank(sc, name);
175 		if (bank == NULL)
176 			continue;
177 
178 		ec = malloc(sizeof(*ec), M_DEVBUF, M_WAITOK);
179 		ec->ec_bank = exgpio_bank(sc, name);
180 		ec->ec_sc = sc;
181 		ec->ec_gc.gc_node = node;
182 		ec->ec_gc.gc_cookie = ec;
183 		ec->ec_gc.gc_config_pin = exgpio_config_pin;
184 		ec->ec_gc.gc_get_pin = exgpio_get_pin;
185 		ec->ec_gc.gc_set_pin = exgpio_set_pin;
186 		gpio_controller_register(&ec->ec_gc);
187 	}
188 
189 	printf("\n");
190 }
191 
192 struct exgpio_bank *
193 exgpio_bank(struct exgpio_softc *sc, const char *name)
194 {
195 	int i;
196 
197 	for (i = 0; i < sc->sc_nbanks; i++) {
198 		if (strcmp(name, sc->sc_banks[i].name) == 0)
199 			return &sc->sc_banks[i];
200 	}
201 
202 	return NULL;
203 }
204 
205 int
206 exgpio_pinctrl(uint32_t phandle, void *cookie)
207 {
208 	struct exgpio_softc *sc = cookie;
209 	char *pins, *bank_name, *pin_name;
210 	struct exgpio_bank *bank;
211 	uint32_t func, val, pud, drv;
212 	uint32_t reg;
213 	int node;
214 	int len;
215 	int pin;
216 
217 	node = OF_getnodebyphandle(phandle);
218 	if (node == 0)
219 		return -1;
220 
221 	len = OF_getproplen(node, "samsung,pins");
222 	if (len <= 0)
223 		return -1;
224 
225 	pins = malloc(len, M_TEMP, M_WAITOK);
226 	OF_getprop(node, "samsung,pins", pins, len);
227 
228 	func = OF_getpropint(node, "samsung,pin-function", 0);
229 	val = OF_getpropint(node, "samsung,pin-val", 0);
230 	pud = OF_getpropint(node, "samsung,pin-pud", 1);
231 	drv = OF_getpropint(node, "samsung,pin-drv", 0);
232 
233 	bank_name = pins;
234 	while (bank_name < pins + len) {
235 		pin_name = strchr(bank_name, '-');
236 		if (pin_name == NULL)
237 			goto fail;
238 		*pin_name++ = 0;
239 		pin = *pin_name - '0';
240 		if (pin < 0 || pin >= GPX_NUM_PINS)
241 			goto fail;
242 
243 		bank = exgpio_bank(sc, bank_name);
244 		if (bank == NULL)
245 			goto fail;
246 
247 		reg = HREAD4(sc, GPXCON(bank->addr));
248 		reg &= ~(0xf << (pin * 4));
249 		reg |= (func << (pin * 4));
250 		HWRITE4(sc, GPXCON(bank->addr), reg);
251 
252 		reg = HREAD4(sc, GPXDAT(bank->addr));
253 		if (val)
254 			reg |= (1 << pin);
255 		else
256 			reg &= ~(1 << pin);
257 		HWRITE4(sc, GPXDAT(bank->addr), reg);
258 
259 		reg = HREAD4(sc, GPXPUD(bank->addr));
260 		reg &= ~(0x3 << (pin * 2));
261 		reg |= (pud << (pin * 2));
262 		HWRITE4(sc, GPXPUD(bank->addr), reg);
263 
264 		reg = HREAD4(sc, GPXDRV(bank->addr));
265 		reg &= ~(0x3 << (pin * 2));
266 		reg |= (drv << (pin * 2));
267 		HWRITE4(sc, GPXDRV(bank->addr), reg);
268 
269 		bank_name = pin_name + 2;
270 	}
271 
272 	free(pins, M_TEMP, len);
273 	return 0;
274 
275 fail:
276 	free(pins, M_TEMP, len);
277 	return -1;
278 }
279 
280 void
281 exgpio_config_pin(void *cookie, uint32_t *cells, int config)
282 {
283 	struct exgpio_controller *ec = cookie;
284 	uint32_t pin = cells[0];
285 	uint32_t val;
286 	int func;
287 
288 	if (pin >= GPX_NUM_PINS)
289 		return;
290 
291 	func = (config & GPIO_CONFIG_OUTPUT) ? GPXCON_OUTPUT : GPXCON_INPUT;
292 	val = HREAD4(ec->ec_sc, GPXCON(ec->ec_bank->addr));
293 	val &= ~(0xf << (pin * 4));
294 	val |= (func << (pin * 4));
295 	HWRITE4(ec->ec_sc, GPXCON(ec->ec_bank->addr), val);
296 }
297 
298 int
299 exgpio_get_pin(void *cookie, uint32_t *cells)
300 {
301 	struct exgpio_controller *ec = cookie;
302 	uint32_t pin = cells[0];
303 	uint32_t flags = cells[1];
304 	uint32_t reg;
305 	int val;
306 
307 	if (pin >= GPX_NUM_PINS)
308 		return 0;
309 
310 	reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr));
311 	reg &= (1 << pin);
312 	val = (reg >> pin) & 1;
313 	if (flags & GPIO_ACTIVE_LOW)
314 		val = !val;
315 	return val;
316 }
317 
318 void
319 exgpio_set_pin(void *cookie, uint32_t *cells, int val)
320 {
321 	struct exgpio_controller *ec = cookie;
322 	uint32_t pin = cells[0];
323 	uint32_t flags = cells[1];
324 	uint32_t reg;
325 
326 	if (pin >= GPX_NUM_PINS)
327 		return;
328 
329 	reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr));
330 	if (flags & GPIO_ACTIVE_LOW)
331 		val = !val;
332 	if (val)
333 		reg |= (1 << pin);
334 	else
335 		reg &= ~(1 << pin);
336 	HWRITE4(ec->ec_sc, GPXDAT(ec->ec_bank->addr), reg);
337 }
338