xref: /freebsd/sys/powerpc/mpc85xx/mpc85xx_gpio.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
120d34e61SJustin Hibbits /*-
220d34e61SJustin Hibbits  * Copyright (c) 2015 Justin Hibbits
320d34e61SJustin Hibbits  * Copyright (c) 2013 Thomas Skibo
420d34e61SJustin Hibbits  * All rights reserved.
520d34e61SJustin Hibbits  *
620d34e61SJustin Hibbits  * Redistribution and use in source and binary forms, with or without
720d34e61SJustin Hibbits  * modification, are permitted provided that the following conditions
820d34e61SJustin Hibbits  * are met:
920d34e61SJustin Hibbits  * 1. Redistributions of source code must retain the above copyright
1020d34e61SJustin Hibbits  *    notice, this list of conditions and the following disclaimer.
1120d34e61SJustin Hibbits  * 2. Redistributions in binary form must reproduce the above copyright
1220d34e61SJustin Hibbits  *    notice, this list of conditions and the following disclaimer in the
1320d34e61SJustin Hibbits  *    documentation and/or other materials provided with the distribution.
1420d34e61SJustin Hibbits  *
1520d34e61SJustin Hibbits  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1620d34e61SJustin Hibbits  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1720d34e61SJustin Hibbits  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1820d34e61SJustin Hibbits  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1920d34e61SJustin Hibbits  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2020d34e61SJustin Hibbits  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2120d34e61SJustin Hibbits  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2220d34e61SJustin Hibbits  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2320d34e61SJustin Hibbits  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2420d34e61SJustin Hibbits  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2520d34e61SJustin Hibbits  * SUCH DAMAGE.
2620d34e61SJustin Hibbits  */
2720d34e61SJustin Hibbits 
2820d34e61SJustin Hibbits #include <sys/param.h>
2920d34e61SJustin Hibbits #include <sys/systm.h>
3020d34e61SJustin Hibbits #include <sys/conf.h>
3120d34e61SJustin Hibbits #include <sys/bus.h>
3220d34e61SJustin Hibbits #include <sys/kernel.h>
3320d34e61SJustin Hibbits #include <sys/module.h>
3420d34e61SJustin Hibbits #include <sys/lock.h>
3520d34e61SJustin Hibbits #include <sys/mutex.h>
3620d34e61SJustin Hibbits #include <sys/resource.h>
3720d34e61SJustin Hibbits #include <sys/rman.h>
3820d34e61SJustin Hibbits #include <sys/gpio.h>
3920d34e61SJustin Hibbits 
4020d34e61SJustin Hibbits #include <machine/bus.h>
4120d34e61SJustin Hibbits #include <machine/resource.h>
4220d34e61SJustin Hibbits #include <machine/stdarg.h>
4320d34e61SJustin Hibbits 
4420d34e61SJustin Hibbits #include <dev/fdt/fdt_common.h>
4520d34e61SJustin Hibbits #include <dev/gpio/gpiobusvar.h>
4620d34e61SJustin Hibbits #include <dev/ofw/ofw_bus.h>
4720d34e61SJustin Hibbits #include <dev/ofw/ofw_bus_subr.h>
4820d34e61SJustin Hibbits 
4920d34e61SJustin Hibbits #include "gpio_if.h"
5020d34e61SJustin Hibbits 
5120d34e61SJustin Hibbits #define MAXPIN		(7)
5220d34e61SJustin Hibbits 
5320d34e61SJustin Hibbits #define VALID_PIN(u)	((u) >= 0 && (u) <= MAXPIN)
5420d34e61SJustin Hibbits 
5520d34e61SJustin Hibbits #define GPIO_LOCK(sc)			mtx_lock(&(sc)->sc_mtx)
5620d34e61SJustin Hibbits #define	GPIO_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
5720d34e61SJustin Hibbits #define GPIO_LOCK_INIT(sc) \
5820d34e61SJustin Hibbits 	mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev),	\
5920d34e61SJustin Hibbits 	    "gpio", MTX_DEF)
6020d34e61SJustin Hibbits #define GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
6120d34e61SJustin Hibbits 
6220d34e61SJustin Hibbits struct mpc85xx_gpio_softc {
6320d34e61SJustin Hibbits 	device_t	dev;
6420d34e61SJustin Hibbits 	device_t	busdev;
6520d34e61SJustin Hibbits 	struct mtx	sc_mtx;
6620d34e61SJustin Hibbits 	struct resource *out_res;	/* Memory resource */
6720d34e61SJustin Hibbits 	struct resource *in_res;
6820d34e61SJustin Hibbits };
6920d34e61SJustin Hibbits 
7020d34e61SJustin Hibbits static device_t
mpc85xx_gpio_get_bus(device_t dev)7120d34e61SJustin Hibbits mpc85xx_gpio_get_bus(device_t dev)
7220d34e61SJustin Hibbits {
7320d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc;
7420d34e61SJustin Hibbits 
7520d34e61SJustin Hibbits 	sc = device_get_softc(dev);
7620d34e61SJustin Hibbits 
7720d34e61SJustin Hibbits 	return (sc->busdev);
7820d34e61SJustin Hibbits }
7920d34e61SJustin Hibbits 
8020d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_max(device_t dev,int * maxpin)8120d34e61SJustin Hibbits mpc85xx_gpio_pin_max(device_t dev, int *maxpin)
8220d34e61SJustin Hibbits {
8320d34e61SJustin Hibbits 
8420d34e61SJustin Hibbits 	*maxpin = MAXPIN;
8520d34e61SJustin Hibbits 	return (0);
8620d34e61SJustin Hibbits }
8720d34e61SJustin Hibbits 
8820d34e61SJustin Hibbits /* Get a specific pin's capabilities. */
8920d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)9020d34e61SJustin Hibbits mpc85xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
9120d34e61SJustin Hibbits {
9220d34e61SJustin Hibbits 
9320d34e61SJustin Hibbits 	if (!VALID_PIN(pin))
9420d34e61SJustin Hibbits 		return (EINVAL);
9520d34e61SJustin Hibbits 
9620d34e61SJustin Hibbits 	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
9720d34e61SJustin Hibbits 
9820d34e61SJustin Hibbits 	return (0);
9920d34e61SJustin Hibbits }
10020d34e61SJustin Hibbits 
10120d34e61SJustin Hibbits /* Get a specific pin's name. */
10220d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_getname(device_t dev,uint32_t pin,char * name)10320d34e61SJustin Hibbits mpc85xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
10420d34e61SJustin Hibbits {
10520d34e61SJustin Hibbits 
10620d34e61SJustin Hibbits 	if (!VALID_PIN(pin))
10720d34e61SJustin Hibbits 		return (EINVAL);
10820d34e61SJustin Hibbits 
10920d34e61SJustin Hibbits 	snprintf(name, GPIOMAXNAME, "GPIO%d", pin);
11020d34e61SJustin Hibbits 	name[GPIOMAXNAME-1] = '\0';
11120d34e61SJustin Hibbits 
11220d34e61SJustin Hibbits 	return (0);
11320d34e61SJustin Hibbits }
11420d34e61SJustin Hibbits 
11520d34e61SJustin Hibbits /* Set a specific output pin's value. */
11620d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_set(device_t dev,uint32_t pin,unsigned int value)11720d34e61SJustin Hibbits mpc85xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
11820d34e61SJustin Hibbits {
11920d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc = device_get_softc(dev);
12020d34e61SJustin Hibbits 	uint32_t outvals;
12120d34e61SJustin Hibbits 	uint8_t pinbit;
12220d34e61SJustin Hibbits 
12320d34e61SJustin Hibbits 	if (!VALID_PIN(pin) || value > 1)
12420d34e61SJustin Hibbits 		return (EINVAL);
12520d34e61SJustin Hibbits 
12620d34e61SJustin Hibbits 	GPIO_LOCK(sc);
12720d34e61SJustin Hibbits 	pinbit = 31 - pin;
12820d34e61SJustin Hibbits 
12920d34e61SJustin Hibbits 	outvals = bus_read_4(sc->out_res, 0);
13020d34e61SJustin Hibbits 	outvals &= ~(1 << pinbit);
13120d34e61SJustin Hibbits 	outvals |= (value << pinbit);
13220d34e61SJustin Hibbits 	bus_write_4(sc->out_res, 0, outvals);
13320d34e61SJustin Hibbits 
13420d34e61SJustin Hibbits 	GPIO_UNLOCK(sc);
13520d34e61SJustin Hibbits 
13620d34e61SJustin Hibbits 	return (0);
13720d34e61SJustin Hibbits }
13820d34e61SJustin Hibbits 
13920d34e61SJustin Hibbits /* Get a specific pin's input value. */
14020d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_get(device_t dev,uint32_t pin,unsigned int * value)14120d34e61SJustin Hibbits mpc85xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
14220d34e61SJustin Hibbits {
14320d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc = device_get_softc(dev);
14420d34e61SJustin Hibbits 
14520d34e61SJustin Hibbits 	if (!VALID_PIN(pin))
14620d34e61SJustin Hibbits 		return (EINVAL);
14720d34e61SJustin Hibbits 
14820d34e61SJustin Hibbits 	*value = (bus_read_4(sc->in_res, 0) >> (31 - pin)) & 1;
14920d34e61SJustin Hibbits 
15020d34e61SJustin Hibbits 	return (0);
15120d34e61SJustin Hibbits }
15220d34e61SJustin Hibbits 
15320d34e61SJustin Hibbits /* Toggle a pin's output value. */
15420d34e61SJustin Hibbits static int
mpc85xx_gpio_pin_toggle(device_t dev,uint32_t pin)15520d34e61SJustin Hibbits mpc85xx_gpio_pin_toggle(device_t dev, uint32_t pin)
15620d34e61SJustin Hibbits {
15720d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc = device_get_softc(dev);
15820d34e61SJustin Hibbits 	uint32_t val;
15920d34e61SJustin Hibbits 
16020d34e61SJustin Hibbits 	if (!VALID_PIN(pin))
16120d34e61SJustin Hibbits 		return (EINVAL);
16220d34e61SJustin Hibbits 
16320d34e61SJustin Hibbits 	GPIO_LOCK(sc);
16420d34e61SJustin Hibbits 
16520d34e61SJustin Hibbits 	val = bus_read_4(sc->out_res, 0);
16620d34e61SJustin Hibbits 	val ^= (1 << (31 - pin));
16720d34e61SJustin Hibbits 	bus_write_4(sc->out_res, 0, val);
16820d34e61SJustin Hibbits 
16920d34e61SJustin Hibbits 	GPIO_UNLOCK(sc);
17020d34e61SJustin Hibbits 
17120d34e61SJustin Hibbits 	return (0);
17220d34e61SJustin Hibbits }
17320d34e61SJustin Hibbits 
17420d34e61SJustin Hibbits static int
mpc85xx_gpio_probe(device_t dev)17520d34e61SJustin Hibbits mpc85xx_gpio_probe(device_t dev)
17620d34e61SJustin Hibbits {
17720d34e61SJustin Hibbits 	uint32_t svr;
17820d34e61SJustin Hibbits 
17920d34e61SJustin Hibbits 	if (!ofw_bus_status_okay(dev))
18020d34e61SJustin Hibbits 		return (ENXIO);
18120d34e61SJustin Hibbits 
18220d34e61SJustin Hibbits 	if (!ofw_bus_is_compatible(dev, "gpio"))
18320d34e61SJustin Hibbits 		return (ENXIO);
18420d34e61SJustin Hibbits 
18520d34e61SJustin Hibbits 	svr = mfspr(SPR_SVR);
18620d34e61SJustin Hibbits 	switch (SVR_VER(svr)) {
18720d34e61SJustin Hibbits 	case SVR_MPC8533:
18820d34e61SJustin Hibbits 	case SVR_MPC8533E:
18920d34e61SJustin Hibbits 		break;
19020d34e61SJustin Hibbits 	default:
19120d34e61SJustin Hibbits 		return (ENXIO);
19220d34e61SJustin Hibbits 	}
19320d34e61SJustin Hibbits 
19420d34e61SJustin Hibbits 	device_set_desc(dev, "MPC85xx GPIO driver");
19520d34e61SJustin Hibbits 	return (0);
19620d34e61SJustin Hibbits }
19720d34e61SJustin Hibbits 
19820d34e61SJustin Hibbits static int mpc85xx_gpio_detach(device_t dev);
19920d34e61SJustin Hibbits 
20020d34e61SJustin Hibbits static int
mpc85xx_gpio_attach(device_t dev)20120d34e61SJustin Hibbits mpc85xx_gpio_attach(device_t dev)
20220d34e61SJustin Hibbits {
20320d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc = device_get_softc(dev);
20420d34e61SJustin Hibbits 	int rid;
20520d34e61SJustin Hibbits 
20620d34e61SJustin Hibbits 	sc->dev = dev;
20720d34e61SJustin Hibbits 
20820d34e61SJustin Hibbits 	GPIO_LOCK_INIT(sc);
20920d34e61SJustin Hibbits 
21020d34e61SJustin Hibbits 	/* Allocate memory. */
21120d34e61SJustin Hibbits 	rid = 0;
21220d34e61SJustin Hibbits 	sc->out_res = bus_alloc_resource_any(dev,
21320d34e61SJustin Hibbits 		     SYS_RES_MEMORY, &rid, RF_ACTIVE);
21420d34e61SJustin Hibbits 	if (sc->out_res == NULL) {
21520d34e61SJustin Hibbits 		device_printf(dev, "Can't allocate memory for device output port");
21620d34e61SJustin Hibbits 		mpc85xx_gpio_detach(dev);
21720d34e61SJustin Hibbits 		return (ENOMEM);
21820d34e61SJustin Hibbits 	}
21920d34e61SJustin Hibbits 
22020d34e61SJustin Hibbits 	rid = 1;
22120d34e61SJustin Hibbits 	sc->in_res = bus_alloc_resource_any(dev,
22220d34e61SJustin Hibbits 		     SYS_RES_MEMORY, &rid, RF_ACTIVE);
22320d34e61SJustin Hibbits 	if (sc->in_res == NULL) {
22420d34e61SJustin Hibbits 		device_printf(dev, "Can't allocate memory for device input port");
22520d34e61SJustin Hibbits 		mpc85xx_gpio_detach(dev);
22620d34e61SJustin Hibbits 		return (ENOMEM);
22720d34e61SJustin Hibbits 	}
22820d34e61SJustin Hibbits 
22920d34e61SJustin Hibbits 	sc->busdev = gpiobus_attach_bus(dev);
23020d34e61SJustin Hibbits 	if (sc->busdev == NULL) {
23120d34e61SJustin Hibbits 		mpc85xx_gpio_detach(dev);
23220d34e61SJustin Hibbits 		return (ENOMEM);
23320d34e61SJustin Hibbits 	}
23420d34e61SJustin Hibbits 
23520d34e61SJustin Hibbits 	OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
23620d34e61SJustin Hibbits 
23720d34e61SJustin Hibbits 	return (0);
23820d34e61SJustin Hibbits }
23920d34e61SJustin Hibbits 
24020d34e61SJustin Hibbits static int
mpc85xx_gpio_detach(device_t dev)24120d34e61SJustin Hibbits mpc85xx_gpio_detach(device_t dev)
24220d34e61SJustin Hibbits {
24320d34e61SJustin Hibbits 	struct mpc85xx_gpio_softc *sc = device_get_softc(dev);
24420d34e61SJustin Hibbits 
24520d34e61SJustin Hibbits 	gpiobus_detach_bus(dev);
24620d34e61SJustin Hibbits 
24720d34e61SJustin Hibbits 	if (sc->out_res != NULL) {
24820d34e61SJustin Hibbits 		/* Release output port resource. */
24920d34e61SJustin Hibbits 		bus_release_resource(dev, SYS_RES_MEMORY,
25020d34e61SJustin Hibbits 				     rman_get_rid(sc->out_res), sc->out_res);
25120d34e61SJustin Hibbits 	}
25220d34e61SJustin Hibbits 
25320d34e61SJustin Hibbits 	if (sc->in_res != NULL) {
25420d34e61SJustin Hibbits 		/* Release input port resource. */
25520d34e61SJustin Hibbits 		bus_release_resource(dev, SYS_RES_MEMORY,
25620d34e61SJustin Hibbits 				     rman_get_rid(sc->in_res), sc->in_res);
25720d34e61SJustin Hibbits 	}
25820d34e61SJustin Hibbits 
25920d34e61SJustin Hibbits 	GPIO_LOCK_DESTROY(sc);
26020d34e61SJustin Hibbits 
26120d34e61SJustin Hibbits 	return (0);
26220d34e61SJustin Hibbits }
26320d34e61SJustin Hibbits 
26420d34e61SJustin Hibbits static device_method_t mpc85xx_gpio_methods[] = {
26520d34e61SJustin Hibbits 	/* device_if */
26620d34e61SJustin Hibbits 	DEVMETHOD(device_probe, 	mpc85xx_gpio_probe),
26720d34e61SJustin Hibbits 	DEVMETHOD(device_attach, 	mpc85xx_gpio_attach),
26820d34e61SJustin Hibbits 	DEVMETHOD(device_detach, 	mpc85xx_gpio_detach),
26920d34e61SJustin Hibbits 
27020d34e61SJustin Hibbits 	/* GPIO protocol */
27120d34e61SJustin Hibbits 	DEVMETHOD(gpio_get_bus, 	mpc85xx_gpio_get_bus),
27220d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_max, 	mpc85xx_gpio_pin_max),
27320d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_getname, 	mpc85xx_gpio_pin_getname),
27420d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_getcaps, 	mpc85xx_gpio_pin_getcaps),
27520d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_get, 	mpc85xx_gpio_pin_get),
27620d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_set, 	mpc85xx_gpio_pin_set),
27720d34e61SJustin Hibbits 	DEVMETHOD(gpio_pin_toggle, 	mpc85xx_gpio_pin_toggle),
27820d34e61SJustin Hibbits 
27920d34e61SJustin Hibbits 	DEVMETHOD_END
28020d34e61SJustin Hibbits };
28120d34e61SJustin Hibbits 
28220d34e61SJustin Hibbits static driver_t mpc85xx_gpio_driver = {
28320d34e61SJustin Hibbits 	"gpio",
28420d34e61SJustin Hibbits 	mpc85xx_gpio_methods,
28520d34e61SJustin Hibbits 	sizeof(struct mpc85xx_gpio_softc),
28620d34e61SJustin Hibbits };
28720d34e61SJustin Hibbits 
288*5d7d6129SJohn Baldwin EARLY_DRIVER_MODULE(mpc85xx_gpio, simplebus, mpc85xx_gpio_driver, NULL, NULL,
28920d34e61SJustin Hibbits     BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
290