1286908Sloos/*-
2286908Sloos * Copyright (c) 2015 Rubicon Communications, LLC (Netgate)
3286908Sloos * All rights reserved.
4286908Sloos *
5286908Sloos * Redistribution and use in source and binary forms, with or without
6286908Sloos * modification, are permitted provided that the following conditions
7286908Sloos * are met:
8286908Sloos * 1. Redistributions of source code must retain the above copyright
9286908Sloos *    notice, this list of conditions and the following disclaimer.
10286908Sloos * 2. Redistributions in binary form must reproduce the above copyright
11286908Sloos *    notice, this list of conditions and the following disclaimer in the
12286908Sloos *    documentation and/or other materials provided with the distribution.
13286908Sloos *
14286908Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15286908Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16286908Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17286908Sloos * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18286908Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19286908Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20286908Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21286908Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22286908Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23286908Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24286908Sloos * SUCH DAMAGE.
25286908Sloos */
26286908Sloos#include <sys/cdefs.h>
27286908Sloos__FBSDID("$FreeBSD: stable/11/sys/dev/rccgpio/rccgpio.c 311205 2017-01-04 00:25:22Z loos $");
28286908Sloos
29286908Sloos/*
30286908Sloos * GPIO driver for the ADI Engineering RCC-VE and RCC-DFF/DFFv2.
31286908Sloos */
32286908Sloos
33286908Sloos#include <sys/param.h>
34286908Sloos#include <sys/systm.h>
35286908Sloos#include <sys/bus.h>
36286908Sloos#include <sys/gpio.h>
37286908Sloos#include <sys/kernel.h>
38286908Sloos#include <sys/module.h>
39286908Sloos#include <sys/rman.h>
40286908Sloos
41286908Sloos#include <machine/bus.h>
42286908Sloos
43286908Sloos#include <dev/gpio/gpiobusvar.h>
44286908Sloos#include <isa/isavar.h>
45286908Sloos
46286908Sloos#include "gpio_if.h"
47286908Sloos
48286908Sloos#define	RCC_GPIO_BASE		0x500
49286908Sloos#define	RCC_GPIO_USE_SEL	0x00
50286908Sloos#define	RCC_GPIO_IO_SEL		0x04
51286908Sloos#define	RCC_GPIO_GP_LVL		0x08
52286908Sloos
53286908Sloosstruct rcc_gpio_pin {
54286908Sloos	uint32_t		pin;
55286908Sloos	const char		*name;
56286908Sloos	uint32_t		caps;
57286908Sloos};
58286908Sloos
59286908Sloosstatic struct rcc_gpio_pin rcc_pins[] = {
60311205Sloos	{ .pin = (1 << 11), .name = "reset switch", .caps = GPIO_PIN_INPUT },
61311205Sloos	{ .pin = (1 << 15), .name = "red LED", .caps = GPIO_PIN_OUTPUT },
62311205Sloos	{ .pin = (1 << 17), .name = "green LED", .caps = GPIO_PIN_OUTPUT },
63286908Sloos#if 0
64311205Sloos	{ .pin = (1 << 16), .name = "HD1 LED", .caps = GPIO_PIN_OUTPUT },
65311205Sloos	{ .pin = (1 << 18), .name = "HD2 LED", .caps = GPIO_PIN_OUTPUT },
66286908Sloos#endif
67286908Sloos};
68286908Sloos
69286908Sloosstruct rcc_gpio_softc {
70286908Sloos	device_t		sc_dev;
71286908Sloos	device_t		sc_busdev;
72286908Sloos	struct mtx		sc_mtx;
73286908Sloos	struct resource		*sc_io_res;
74286908Sloos	bus_space_tag_t		sc_bst;
75286908Sloos	bus_space_handle_t	sc_bsh;
76286908Sloos	uint32_t		sc_output;
77286908Sloos	int			sc_io_rid;
78286908Sloos	int			sc_gpio_npins;
79286908Sloos};
80286908Sloos
81286908Sloos#define	RCC_GPIO_LOCK(_sc)	mtx_lock(&(_sc)->sc_mtx)
82286908Sloos#define	RCC_GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
83286908Sloos#define	RCC_WRITE(_sc, _off, _val)				\
84286908Sloos	bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
85286908Sloos#define	RCC_READ(_sc, _off)					\
86286908Sloos	bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
87286908Sloos
88286908Sloosstatic void
89286908Sloosrcc_gpio_modify_bits(struct rcc_gpio_softc *sc, uint32_t reg, uint32_t mask,
90311205Sloos	uint32_t writebits)
91286908Sloos{
92286908Sloos	uint32_t value;
93286908Sloos
94286908Sloos	RCC_GPIO_LOCK(sc);
95286908Sloos	value = RCC_READ(sc, reg);
96311205Sloos	value &= ~mask;
97311205Sloos	value |= writebits;
98286908Sloos	RCC_WRITE(sc, reg, value);
99286908Sloos	RCC_GPIO_UNLOCK(sc);
100286908Sloos}
101286908Sloos
102286908Sloosstatic device_t
103286908Sloosrcc_gpio_get_bus(device_t dev)
104286908Sloos{
105286908Sloos	struct rcc_gpio_softc *sc;
106286908Sloos
107286908Sloos	sc = device_get_softc(dev);
108286908Sloos
109286908Sloos	return (sc->sc_busdev);
110286908Sloos}
111286908Sloos
112286908Sloosstatic int
113286908Sloosrcc_gpio_pin_max(device_t dev, int *maxpin)
114286908Sloos{
115286908Sloos	struct rcc_gpio_softc *sc;
116286908Sloos
117286908Sloos	sc = device_get_softc(dev);
118286908Sloos	*maxpin = sc->sc_gpio_npins - 1;
119286908Sloos
120286908Sloos	return (0);
121286908Sloos}
122286908Sloos
123286908Sloosstatic int
124286908Sloosrcc_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
125286908Sloos{
126286908Sloos	struct rcc_gpio_softc *sc;
127286908Sloos
128286908Sloos	sc = device_get_softc(dev);
129287542Sloos	if (pin >= sc->sc_gpio_npins)
130286908Sloos		return (EINVAL);
131286908Sloos
132286908Sloos	*caps = rcc_pins[pin].caps;
133286908Sloos
134286908Sloos	return (0);
135286908Sloos}
136286908Sloos
137286908Sloosstatic int
138286908Sloosrcc_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
139286908Sloos{
140286908Sloos	struct rcc_gpio_softc *sc;
141286908Sloos
142286908Sloos	sc = device_get_softc(dev);
143287542Sloos	if (pin >= sc->sc_gpio_npins)
144286908Sloos		return (EINVAL);
145286908Sloos
146286908Sloos	/* Flags cannot be changed. */
147286908Sloos	*flags = rcc_pins[pin].caps;
148286908Sloos
149286908Sloos	return (0);
150286908Sloos}
151286908Sloos
152286908Sloosstatic int
153286908Sloosrcc_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
154286908Sloos{
155286908Sloos	struct rcc_gpio_softc *sc;
156286908Sloos
157286908Sloos	sc = device_get_softc(dev);
158287542Sloos	if (pin >= sc->sc_gpio_npins)
159286908Sloos		return (EINVAL);
160286908Sloos
161286908Sloos	memcpy(name, rcc_pins[pin].name, GPIOMAXNAME);
162286908Sloos
163286908Sloos	return (0);
164286908Sloos}
165286908Sloos
166286908Sloosstatic int
167286908Sloosrcc_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
168286908Sloos{
169286908Sloos	struct rcc_gpio_softc *sc;
170286908Sloos
171286908Sloos	sc = device_get_softc(dev);
172287542Sloos	if (pin >= sc->sc_gpio_npins)
173286908Sloos		return (EINVAL);
174286908Sloos
175286908Sloos	/* Flags cannot be changed - risk of short-circuit!!! */
176286908Sloos
177286908Sloos	return (0);
178286908Sloos}
179286908Sloos
180286908Sloosstatic int
181286908Sloosrcc_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
182286908Sloos{
183286908Sloos	struct rcc_gpio_softc *sc;
184286908Sloos
185286908Sloos	sc = device_get_softc(dev);
186287542Sloos	if (pin >= sc->sc_gpio_npins)
187286908Sloos		return (EINVAL);
188286908Sloos
189287542Sloos	if ((rcc_pins[pin].caps & GPIO_PIN_OUTPUT) == 0)
190287542Sloos		return (EINVAL);
191287542Sloos
192286908Sloos	RCC_GPIO_LOCK(sc);
193286908Sloos	if (value)
194286908Sloos		sc->sc_output |= (1 << rcc_pins[pin].pin);
195286908Sloos	else
196286908Sloos		sc->sc_output &= ~(1 << rcc_pins[pin].pin);
197286908Sloos	RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
198286908Sloos	RCC_GPIO_UNLOCK(sc);
199286908Sloos
200286908Sloos	return (0);
201286908Sloos}
202286908Sloos
203286908Sloosstatic int
204286908Sloosrcc_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
205286908Sloos{
206286908Sloos	struct rcc_gpio_softc *sc;
207286908Sloos	uint32_t value;
208286908Sloos
209286908Sloos	sc = device_get_softc(dev);
210287542Sloos	if (pin >= sc->sc_gpio_npins)
211286908Sloos		return (EINVAL);
212286908Sloos
213286908Sloos	RCC_GPIO_LOCK(sc);
214286908Sloos	if (rcc_pins[pin].caps & GPIO_PIN_INPUT)
215286908Sloos		value = RCC_READ(sc, RCC_GPIO_GP_LVL);
216286908Sloos	else
217286908Sloos		value = sc->sc_output;
218286908Sloos	RCC_GPIO_UNLOCK(sc);
219286908Sloos	*val = (value & (1 << rcc_pins[pin].pin)) ? 1 : 0;
220286908Sloos
221286908Sloos	return (0);
222286908Sloos}
223286908Sloos
224286908Sloosstatic int
225286908Sloosrcc_gpio_pin_toggle(device_t dev, uint32_t pin)
226286908Sloos{
227286908Sloos	struct rcc_gpio_softc *sc;
228286908Sloos
229286908Sloos	sc = device_get_softc(dev);
230287542Sloos	if (pin >= sc->sc_gpio_npins)
231286908Sloos		return (EINVAL);
232286908Sloos
233287542Sloos	if ((rcc_pins[pin].caps & GPIO_PIN_OUTPUT) == 0)
234287542Sloos		return (EINVAL);
235287542Sloos
236286908Sloos	RCC_GPIO_LOCK(sc);
237286908Sloos	if ((sc->sc_output & (1 << rcc_pins[pin].pin)) == 0)
238286908Sloos		sc->sc_output |= (1 << rcc_pins[pin].pin);
239286908Sloos	else
240286908Sloos		sc->sc_output &= ~(1 << rcc_pins[pin].pin);
241286908Sloos	RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
242286908Sloos	RCC_GPIO_UNLOCK(sc);
243286908Sloos
244286908Sloos	return (0);
245286908Sloos}
246286908Sloos
247286908Sloosstatic int
248286908Sloosrcc_gpio_probe(device_t dev)
249286908Sloos{
250286908Sloos	char *prod;
251286908Sloos	int port;
252286908Sloos
253286908Sloos	/*
254286908Sloos	 * We don't know of any PnP ID's for this GPIO controller.
255286908Sloos	 */
256286908Sloos	if (isa_get_logicalid(dev) != 0)
257286908Sloos		return (ENXIO);
258286908Sloos
259286908Sloos	/*
260286908Sloos	 * We have to have an IO port hint that is valid.
261286908Sloos	 */
262286908Sloos	port = isa_get_port(dev);
263286908Sloos	if (port != RCC_GPIO_BASE)
264286908Sloos		return (ENXIO);
265286908Sloos
266286908Sloos	prod = kern_getenv("smbios.system.product");
267286908Sloos	if (prod == NULL ||
268286908Sloos	    (strcmp(prod, "RCC-VE") != 0 && strcmp(prod, "RCC-DFF") != 0))
269286908Sloos		return (ENXIO);
270286908Sloos
271286908Sloos	device_set_desc(dev, "RCC-VE/DFF GPIO controller");
272286908Sloos
273286908Sloos	return (BUS_PROBE_DEFAULT);
274286908Sloos}
275286908Sloos
276286908Sloosstatic int
277286908Sloosrcc_gpio_attach(device_t dev)
278286908Sloos{
279286908Sloos	int i;
280286908Sloos	struct rcc_gpio_softc *sc;
281286908Sloos
282286908Sloos	sc = device_get_softc(dev);
283286908Sloos 	sc->sc_dev = dev;
284286908Sloos
285286908Sloos	/* Allocate IO resources. */
286286908Sloos	sc->sc_io_rid = 0;
287286908Sloos	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
288286908Sloos	    &sc->sc_io_rid, RF_ACTIVE);
289286908Sloos	if (sc->sc_io_res == NULL) {
290286908Sloos		device_printf(dev, "cannot allocate memory window\n");
291286908Sloos		return (ENXIO);
292286908Sloos	}
293286908Sloos	sc->sc_bst = rman_get_bustag(sc->sc_io_res);
294286908Sloos	sc->sc_bsh = rman_get_bushandle(sc->sc_io_res);
295286908Sloos	mtx_init(&sc->sc_mtx, "rcc-gpio", "gpio", MTX_DEF);
296286908Sloos
297286908Sloos	/* Initialize the pins. */
298286908Sloos	sc->sc_gpio_npins = nitems(rcc_pins);
299286908Sloos	for (i = 0; i < sc->sc_gpio_npins; i++) {
300286908Sloos		/* Enable it for GPIO. */
301286908Sloos		rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, 0, rcc_pins[i].pin);
302286908Sloos		/* Set the pin as input or output. */
303286908Sloos		if (rcc_pins[i].caps & GPIO_PIN_OUTPUT)
304286908Sloos			rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL,
305286908Sloos			    rcc_pins[i].pin, 0);
306286908Sloos		else
307286908Sloos			rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL,
308286908Sloos			    0, rcc_pins[i].pin);
309286908Sloos	}
310286908Sloos	RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
311286908Sloos
312286908Sloos	/* Attach the gpiobus. */
313286908Sloos	sc->sc_busdev = gpiobus_attach_bus(dev);
314286908Sloos	if (sc->sc_busdev == NULL) {
315286908Sloos		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid,
316286908Sloos		    sc->sc_io_res);
317286908Sloos		mtx_destroy(&sc->sc_mtx);
318286908Sloos		return (ENXIO);
319286908Sloos	}
320286908Sloos
321286908Sloos	return (0);
322286908Sloos}
323286908Sloos
324286908Sloosstatic int
325286908Sloosrcc_gpio_detach(device_t dev)
326286908Sloos{
327286908Sloos	int i;
328286908Sloos	struct rcc_gpio_softc *sc;
329286908Sloos
330286908Sloos	sc = device_get_softc(dev);
331286908Sloos	gpiobus_detach_bus(dev);
332286908Sloos
333286908Sloos	/* Disable the GPIO function. */
334286908Sloos	for (i = 0; i < sc->sc_gpio_npins; i++)
335286908Sloos		rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, rcc_pins[i].pin, 0);
336286908Sloos
337286908Sloos	if (sc->sc_io_res != NULL)
338286908Sloos		bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid,
339286908Sloos		    sc->sc_io_res);
340286908Sloos	mtx_destroy(&sc->sc_mtx);
341286908Sloos
342286908Sloos	return (0);
343286908Sloos}
344286908Sloos
345286908Sloosstatic device_method_t rcc_gpio_methods[] = {
346286908Sloos	/* Device interface */
347286908Sloos	DEVMETHOD(device_probe,		rcc_gpio_probe),
348286908Sloos	DEVMETHOD(device_attach,	rcc_gpio_attach),
349286908Sloos	DEVMETHOD(device_detach,	rcc_gpio_detach),
350286908Sloos
351286908Sloos	/* GPIO protocol */
352286908Sloos	DEVMETHOD(gpio_get_bus,		rcc_gpio_get_bus),
353286908Sloos	DEVMETHOD(gpio_pin_max,		rcc_gpio_pin_max),
354286908Sloos	DEVMETHOD(gpio_pin_getname,	rcc_gpio_pin_getname),
355286908Sloos	DEVMETHOD(gpio_pin_getflags,	rcc_gpio_pin_getflags),
356286908Sloos	DEVMETHOD(gpio_pin_getcaps,	rcc_gpio_pin_getcaps),
357286908Sloos	DEVMETHOD(gpio_pin_setflags,	rcc_gpio_pin_setflags),
358286908Sloos	DEVMETHOD(gpio_pin_get,		rcc_gpio_pin_get),
359286908Sloos	DEVMETHOD(gpio_pin_set,		rcc_gpio_pin_set),
360286908Sloos	DEVMETHOD(gpio_pin_toggle,	rcc_gpio_pin_toggle),
361286908Sloos
362286908Sloos	DEVMETHOD_END
363286908Sloos};
364286908Sloos
365286908Sloosstatic devclass_t rcc_gpio_devclass;
366286908Sloos
367286908Sloosstatic driver_t rcc_gpio_driver = {
368286908Sloos	"gpio",
369286908Sloos	rcc_gpio_methods,
370286908Sloos	sizeof(struct rcc_gpio_softc),
371286908Sloos};
372286908Sloos
373286908SloosDRIVER_MODULE(rcc_gpio, isa, rcc_gpio_driver, rcc_gpio_devclass, 0, 0);
374286908SloosMODULE_DEPEND(rcc_gpio, gpiobus, 1, 1, 1);
375