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