pl061gpio_fdt.c revision 1.2
1/* $NetBSD: pl061gpio_fdt.c,v 1.2 2018/09/03 23:19:01 jmcneill Exp $ */
2
3/*
4 * Copyright (c) 2018 Jonathan A. Kollasch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: pl061gpio_fdt.c,v 1.2 2018/09/03 23:19:01 jmcneill Exp $");
31
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/device.h>
35#include <sys/intr.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/kmem.h>
39#include <sys/gpio.h>
40
41#include <dev/gpio/gpiovar.h>
42#include "gpio.h"
43
44#include <dev/ic/pl061reg.h>
45
46#include <dev/fdt/fdtvar.h>
47
48static int	pl061_gpio_match(device_t, cfdata_t, void *);
49static void	pl061_gpio_attach(device_t, device_t, void *);
50
51static void *	pl061_gpio_fdt_acquire(device_t, const void *,
52		    size_t, int);
53static void	pl061_gpio_fdt_release(device_t, void *);
54static int	pl061_gpio_fdt_read(device_t, void *, bool);
55static void	pl061_gpio_fdt_write(device_t, void *, int, bool);
56
57struct fdtbus_gpio_controller_func pl061_gpio_funcs = {
58	.acquire = pl061_gpio_fdt_acquire,
59	.release = pl061_gpio_fdt_release,
60	.read = pl061_gpio_fdt_read,
61	.write = pl061_gpio_fdt_write
62};
63
64struct pl061_gpio_softc {
65	device_t		sc_dev;
66	bus_space_tag_t		sc_bst;
67	bus_space_handle_t	sc_bsh;
68
69	struct gpio_chipset_tag	sc_gc;
70	gpio_pin_t		sc_pins[8];
71};
72
73struct pl061_gpio_pin {
74	struct pl061_gpio_softc *pin_sc;
75	int			pin_no;
76	u_int			pin_flags;
77	bool			pin_actlo;
78};
79
80static int	pl061_gpio_pin_read(void *, int);
81static void	pl061_gpio_pin_write(void *, int, int);
82static void	pl061_gpio_pin_ctl(void *, int, int);
83
84CFATTACH_DECL_NEW(pl061gpio_fdt, sizeof(struct pl061_gpio_softc),
85	pl061_gpio_match, pl061_gpio_attach, NULL, NULL);
86
87#define PL061_WRITE(sc, reg, val) \
88	bus_space_write_1((sc)->sc_bst, (sc)->sc_bsh, (reg)/4, (val))
89#define PL061_READ(sc, reg) \
90	bus_space_read_1((sc)->sc_bst, (sc)->sc_bsh, (reg)/4)
91
92static int
93pl061_gpio_match(device_t parent, cfdata_t cf, void *aux)
94{
95	const char * const compatible[] = {
96		"arm,pl061",
97		NULL
98	};
99	struct fdt_attach_args * const faa = aux;
100
101	return of_match_compatible(faa->faa_phandle, compatible);
102}
103
104static void
105pl061_gpio_attach(device_t parent, device_t self, void *aux)
106{
107	struct pl061_gpio_softc * const sc = device_private(self);
108	struct fdt_attach_args * const faa = aux;
109	bus_addr_t addr;
110	bus_size_t size;
111	int error;
112	struct gpiobus_attach_args gba;
113	u_int pin;
114
115
116	if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
117		aprint_error(": couldn't get registers\n");
118		return;
119	}
120
121	sc->sc_dev = self;
122	sc->sc_bst = faa->faa_a4x_bst;
123	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
124	if (error) {
125		aprint_error(": couldn't map %#"PRIx64": %d", (uint64_t)addr, error);
126		return;
127	}
128
129	aprint_naive("\n");
130	aprint_normal(": GPIO\n");
131
132	sc->sc_gc.gp_cookie = sc;
133	sc->sc_gc.gp_pin_read = pl061_gpio_pin_read;
134	sc->sc_gc.gp_pin_write = pl061_gpio_pin_write;
135	sc->sc_gc.gp_pin_ctl = pl061_gpio_pin_ctl;
136
137	const uint32_t cnf = PL061_READ(sc, PL061_GPIOAFSEL_REG);
138
139	for (pin = 0; pin < 8; pin++) {
140		sc->sc_pins[pin].pin_num = pin;
141		/* skip pins in hardware control mode */
142		if ((cnf & __BIT(pin)) != 0)
143			continue;
144		sc->sc_pins[pin].pin_caps =
145		    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
146		    GPIO_PIN_TRISTATE;
147		sc->sc_pins[pin].pin_state =
148		    pl061_gpio_pin_read(sc, pin);
149	}
150
151	memset(&gba, 0, sizeof(gba));
152	gba.gba_gc = &sc->sc_gc;
153	gba.gba_pins = sc->sc_pins;
154	gba.gba_npins = 8;
155
156#if NGPIO > 0
157	(void)config_found_ia(sc->sc_dev, "gpiobus", &gba,
158	    gpiobus_print);
159#endif
160
161	fdtbus_register_gpio_controller(self, faa->faa_phandle,
162	    &pl061_gpio_funcs);
163
164}
165
166static int
167pl061_gpio_pin_read(void *priv, int pin)
168{
169	struct pl061_gpio_softc * const sc = priv;
170
171	const uint32_t v = PL061_READ(sc, PL061_GPIODATA_REG(1<<pin));
172
173	return (v >> pin) & 1;
174}
175
176static void
177pl061_gpio_pin_write(void *priv, int pin, int val)
178{
179	struct pl061_gpio_softc * const sc = priv;
180
181	PL061_WRITE(sc, PL061_GPIODATA_REG(1 << pin), val << pin);
182}
183
184static void
185pl061_gpio_pin_ctl(void *priv, int pin, int flags)
186{
187	struct pl061_gpio_softc * const sc = priv;
188	uint32_t v;
189
190	if (flags & GPIO_PIN_INPUT) {
191		v = PL061_READ(sc, PL061_GPIODIR_REG);
192		v &= ~(1 << pin);
193		PL061_WRITE(sc, PL061_GPIODIR_REG, v);
194	} else if (flags & GPIO_PIN_OUTPUT) {
195		v = PL061_READ(sc, PL061_GPIODIR_REG);
196		v |= (1 << pin);
197		PL061_WRITE(sc, PL061_GPIODIR_REG, v);
198	}
199}
200
201static void *
202pl061_gpio_fdt_acquire(device_t dev, const void *data, size_t len, int flags)
203{
204	struct pl061_gpio_softc * const sc = device_private(dev);
205	struct pl061_gpio_pin *gpin;
206	const u_int *gpio = data;
207
208	if (len != 12)
209		return NULL;
210
211	const u_int pin = be32toh(gpio[1]);
212	const bool actlo = be32toh(gpio[2]) & 1;
213
214	if (pin > 8)
215		return NULL;
216
217	const uint32_t cnf = PL061_READ(sc, PL061_GPIOAFSEL_REG);
218	if ((cnf & __BIT(pin)) != 0)
219		PL061_WRITE(sc, PL061_GPIOAFSEL_REG, cnf & ~__BIT(pin));
220
221	gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
222	gpin->pin_sc = sc;
223	gpin->pin_no = pin;
224	gpin->pin_flags = flags;
225	gpin->pin_actlo = actlo;
226
227	pl061_gpio_pin_ctl(gpin->pin_sc, gpin->pin_no, gpin->pin_flags);
228
229	return gpin;
230}
231
232static void
233pl061_gpio_fdt_release(device_t dev, void *priv)
234{
235	struct pl061_gpio_pin * const gpin = priv;
236
237	pl061_gpio_pin_ctl(gpin->pin_sc, gpin->pin_no, GPIO_PIN_INPUT);
238	kmem_free(gpin, sizeof(*gpin));
239}
240
241static int
242pl061_gpio_fdt_read(device_t dev, void *priv, bool raw)
243{
244	struct pl061_gpio_pin * const gpin = priv;
245	int val;
246
247	val = pl061_gpio_pin_read(gpin->pin_sc, gpin->pin_no);
248
249	if (!raw && gpin->pin_actlo)
250		val = !val;
251
252	return val;
253}
254
255static void
256pl061_gpio_fdt_write(device_t dev, void *priv, int val, bool raw)
257{
258	struct pl061_gpio_pin * const gpin = priv;
259
260	if (!raw && gpin->pin_actlo)
261		val = !val;
262
263	pl061_gpio_pin_write(gpin->pin_sc, gpin->pin_no, val);
264}
265