1/*	$OpenBSD: mpfgpio.c,v 1.1 2022/02/18 10:51:43 visa Exp $	*/
2
3/*
4 * Copyright (c) 2022 Visa Hankala
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/*
20 * Driver for PolarFire SoC MSS GPIO controller.
21 */
22
23#include <sys/param.h>
24#include <sys/systm.h>
25#include <sys/device.h>
26#include <sys/gpio.h>
27
28#include <machine/bus.h>
29#include <machine/fdt.h>
30
31#include <dev/gpio/gpiovar.h>
32
33#include <dev/ofw/fdt.h>
34#include <dev/ofw/openfirm.h>
35#include <dev/ofw/ofw_clock.h>
36#include <dev/ofw/ofw_gpio.h>
37
38#include "gpio.h"
39
40#define MPFGPIO_CONFIG(i)	(0x0000 + (i) * 4)
41#define  MPFGPIO_CONFIG_EN_INT		(1 << 3)
42#define  MPFGPIO_CONFIG_EN_OE_BUF	(1 << 2)
43#define  MPFGPIO_CONFIG_EN_IN		(1 << 1)
44#define  MPFGPIO_CONFIG_EN_OUT		(1 << 0)
45#define MPFGPIO_GPIN		0x0084
46#define MPFGPIO_GPOUT		0x0088
47#define MPFGPIO_CLEAR_BITS	0x00a0
48#define MPFGPIO_SET_BITS	0x00a4
49
50#define MPFGPIO_MAX_PINS	32
51
52struct mpfgpio_softc {
53	struct device		sc_dev;
54	bus_space_tag_t		sc_iot;
55	bus_space_handle_t	sc_ioh;
56	uint32_t		sc_npins;
57
58	struct gpio_controller	sc_gc;
59
60	struct gpio_chipset_tag	sc_gpio_tag;
61	gpio_pin_t		sc_gpio_pins[MPFGPIO_MAX_PINS];
62	uint8_t			sc_gpio_claimed[MPFGPIO_MAX_PINS];
63};
64
65#define HREAD4(sc, reg) \
66	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
67#define HWRITE4(sc, reg, val) \
68	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
69
70int	mpfgpio_match(struct device *, void *, void*);
71void	mpfgpio_attach(struct device *, struct device *, void *);
72
73void	mpfgpio_config_pin(void *, uint32_t *, int);
74int	mpfgpio_get_pin(void *, uint32_t *);
75void	mpfgpio_set_pin(void *, uint32_t *, int);
76
77int	mpfgpio_pin_read(void *, int);
78void	mpfgpio_pin_write(void *, int, int);
79void	mpfgpio_pin_ctl(void *, int, int);
80void	mpfgpio_attach_gpio(struct device *);
81
82const struct cfattach mpfgpio_ca = {
83	sizeof(struct mpfgpio_softc), mpfgpio_match, mpfgpio_attach
84};
85
86struct cfdriver mpfgpio_cd = {
87	NULL, "mpfgpio", DV_DULL
88};
89
90int
91mpfgpio_match(struct device *parent, void *match, void *aux)
92{
93	struct fdt_attach_args *faa = aux;
94
95	if (faa->fa_nreg < 1)
96		return 0;
97	return OF_is_compatible(faa->fa_node, "microchip,mpfs-gpio");
98}
99
100void
101mpfgpio_attach(struct device *parent, struct device *self, void *aux)
102{
103	struct fdt_attach_args *faa = aux;
104	struct mpfgpio_softc *sc = (struct mpfgpio_softc *)self;
105	unsigned int unit;
106
107	sc->sc_iot = faa->fa_iot;
108
109	unit = (faa->fa_reg[0].addr >> 12) & 0x3;
110	switch (unit) {
111	case 0:
112		sc->sc_npins = 14;
113		break;
114	case 1:
115		sc->sc_npins = 24;
116		break;
117	case 2:
118		sc->sc_npins = 32;
119		break;
120	default:
121		printf(": unexpected GPIO unit %u\n", unit);
122		return;
123	}
124	KASSERT(sc->sc_npins <= MPFGPIO_MAX_PINS);
125
126	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
127	    0, &sc->sc_ioh) != 0) {
128		printf(": can't map registers\n");
129		return;
130	}
131
132	clock_enable_all(faa->fa_node);
133
134	printf(": unit %u\n", unit);
135
136	sc->sc_gc.gc_node = faa->fa_node;
137	sc->sc_gc.gc_cookie = sc;
138	sc->sc_gc.gc_config_pin = mpfgpio_config_pin;
139	sc->sc_gc.gc_get_pin = mpfgpio_get_pin;
140	sc->sc_gc.gc_set_pin = mpfgpio_set_pin;
141	gpio_controller_register(&sc->sc_gc);
142
143#if NGPIO > 0
144	config_mountroot(self, mpfgpio_attach_gpio);
145#endif
146}
147
148void
149mpfgpio_config_pin(void *cookie, uint32_t *cells, int config)
150{
151	struct mpfgpio_softc *sc = cookie;
152	uint32_t pin = cells[0];
153	uint32_t val;
154
155	if (pin >= sc->sc_npins)
156		return;
157
158	val = HREAD4(sc, MPFGPIO_CONFIG(pin));
159	if (config & GPIO_CONFIG_OUTPUT) {
160		val &= ~MPFGPIO_CONFIG_EN_IN;
161		val |= MPFGPIO_CONFIG_EN_OUT;
162	} else {
163		val |= MPFGPIO_CONFIG_EN_IN;
164		val &= ~MPFGPIO_CONFIG_EN_OUT;
165	}
166	val &= ~MPFGPIO_CONFIG_EN_INT;
167	HWRITE4(sc, MPFGPIO_CONFIG(pin), val);
168
169	sc->sc_gpio_claimed[pin] = 1;
170}
171
172int
173mpfgpio_get_pin(void *cookie, uint32_t *cells)
174{
175	struct mpfgpio_softc *sc = cookie;
176	uint32_t pin = cells[0];
177	uint32_t flags = cells[1];
178	int val;
179
180	if (pin >= sc->sc_npins)
181		return 0;
182
183	val = (HREAD4(sc, MPFGPIO_GPIN) >> pin) & 1;
184	if (flags & GPIO_ACTIVE_LOW)
185		val = !val;
186	return val;
187}
188
189void
190mpfgpio_set_pin(void *cookie, uint32_t *cells, int val)
191{
192	struct mpfgpio_softc *sc = cookie;
193	uint32_t pin = cells[0];
194	uint32_t flags = cells[1];
195
196	if (pin >= sc->sc_npins)
197		return;
198
199	if (flags & GPIO_ACTIVE_LOW)
200		val = !val;
201	if (val)
202		HWRITE4(sc, MPFGPIO_SET_BITS, (1U << (pin % 32)));
203	else
204		HWRITE4(sc, MPFGPIO_CLEAR_BITS, (1U << (pin % 32)));
205}
206
207#if NGPIO > 0
208int
209mpfgpio_pin_read(void *cookie, int pin)
210{
211	struct mpfgpio_softc *sc = cookie;
212	uint32_t cells[2];
213
214	cells[0] = pin;
215	cells[1] = 0;
216
217	return mpfgpio_get_pin(sc, cells) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
218}
219
220void
221mpfgpio_pin_write(void *cookie, int pin, int val)
222{
223	struct mpfgpio_softc *sc = cookie;
224	uint32_t cells[2];
225
226	cells[0] = pin;
227	cells[1] = 0;
228
229	mpfgpio_set_pin(sc, cells, val);
230}
231
232void
233mpfgpio_pin_ctl(void *cookie, int pin, int flags)
234{
235	struct mpfgpio_softc *sc = cookie;
236	uint32_t cells[2];
237	uint32_t config = 0;
238
239	cells[0] = pin;
240	cells[1] = 0;
241
242	if (flags & GPIO_PIN_OUTPUT)
243		config |= GPIO_CONFIG_OUTPUT;
244
245	mpfgpio_config_pin(sc, cells, config);
246}
247
248static const struct gpio_chipset_tag mpfgpio_gpio_tag = {
249	.gp_pin_read	= mpfgpio_pin_read,
250	.gp_pin_write	= mpfgpio_pin_write,
251	.gp_pin_ctl	= mpfgpio_pin_ctl,
252};
253
254void
255mpfgpio_attach_gpio(struct device *parent)
256{
257	struct gpiobus_attach_args gba;
258	struct mpfgpio_softc *sc = (struct mpfgpio_softc *)parent;
259	uint32_t cfgreg, pin;
260	int flags, state;
261
262	for (pin = 0; pin < sc->sc_npins; pin++) {
263		/* Skip pins claimed by other devices. */
264		if (sc->sc_gpio_claimed[pin])
265			continue;
266
267		cfgreg = HREAD4(sc, MPFGPIO_CONFIG(pin));
268		if (cfgreg & MPFGPIO_CONFIG_EN_OUT)
269			flags = GPIO_PIN_SET | GPIO_PIN_OUTPUT;
270		else if (cfgreg & MPFGPIO_CONFIG_EN_IN)
271			flags = GPIO_PIN_SET | GPIO_PIN_INPUT;
272		else
273			flags = GPIO_PIN_SET;
274
275		state = (HREAD4(sc, MPFGPIO_GPIN) >> pin) & 1;
276
277		sc->sc_gpio_pins[pin].pin_caps =
278		    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
279		sc->sc_gpio_pins[pin].pin_flags = flags;
280		sc->sc_gpio_pins[pin].pin_state = state;
281		sc->sc_gpio_pins[pin].pin_num = pin;
282	}
283
284	sc->sc_gpio_tag = mpfgpio_gpio_tag;
285	sc->sc_gpio_tag.gp_cookie = sc;
286
287	gba.gba_name = "gpio";
288	gba.gba_gc = &sc->sc_gpio_tag;
289	gba.gba_pins = sc->sc_gpio_pins;
290	gba.gba_npins = sc->sc_npins;
291
292	config_found(&sc->sc_dev, &gba, gpiobus_print);
293}
294#endif
295