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