15f958b85SOleksandr Tymoshenko /*- 25f958b85SOleksandr Tymoshenko * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 35f958b85SOleksandr Tymoshenko * All rights reserved. 45f958b85SOleksandr Tymoshenko * 55f958b85SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 65f958b85SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 75f958b85SOleksandr Tymoshenko * are met: 85f958b85SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 95f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 105f958b85SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 115f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 125f958b85SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 135f958b85SOleksandr Tymoshenko * 145f958b85SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 155f958b85SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 165f958b85SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 175f958b85SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 185f958b85SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 195f958b85SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 205f958b85SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 215f958b85SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 225f958b85SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 235f958b85SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 245f958b85SOleksandr Tymoshenko * SUCH DAMAGE. 255f958b85SOleksandr Tymoshenko */ 265f958b85SOleksandr Tymoshenko 276b34b16eSOleksandr Tymoshenko #include <sys/cdefs.h> 286b34b16eSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 296b34b16eSOleksandr Tymoshenko 306b34b16eSOleksandr Tymoshenko #include <sys/param.h> 316b34b16eSOleksandr Tymoshenko #include <sys/systm.h> 326b34b16eSOleksandr Tymoshenko #include <sys/bus.h> 336b34b16eSOleksandr Tymoshenko #include <sys/conf.h> 34667357dcSLuiz Otavio O Souza #include <sys/gpio.h> 356b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h> 366b34b16eSOleksandr Tymoshenko #include <sys/kernel.h> 376b34b16eSOleksandr Tymoshenko #include <sys/malloc.h> 386b34b16eSOleksandr Tymoshenko #include <sys/module.h> 396b34b16eSOleksandr Tymoshenko 40667357dcSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 41667357dcSLuiz Otavio O Souza 426b34b16eSOleksandr Tymoshenko #include "gpio_if.h" 43d752f0f6SLuiz Otavio O Souza #include "gpiobus_if.h" 446b34b16eSOleksandr Tymoshenko 456b34b16eSOleksandr Tymoshenko #undef GPIOC_DEBUG 466b34b16eSOleksandr Tymoshenko #ifdef GPIOC_DEBUG 476b34b16eSOleksandr Tymoshenko #define dprintf printf 486b34b16eSOleksandr Tymoshenko #else 496b34b16eSOleksandr Tymoshenko #define dprintf(x, arg...) 506b34b16eSOleksandr Tymoshenko #endif 516b34b16eSOleksandr Tymoshenko 526b34b16eSOleksandr Tymoshenko static int gpioc_probe(device_t dev); 536b34b16eSOleksandr Tymoshenko static int gpioc_attach(device_t dev); 546b34b16eSOleksandr Tymoshenko static int gpioc_detach(device_t dev); 556b34b16eSOleksandr Tymoshenko 566b34b16eSOleksandr Tymoshenko static d_ioctl_t gpioc_ioctl; 576b34b16eSOleksandr Tymoshenko 586b34b16eSOleksandr Tymoshenko static struct cdevsw gpioc_cdevsw = { 596b34b16eSOleksandr Tymoshenko .d_version = D_VERSION, 606b34b16eSOleksandr Tymoshenko .d_ioctl = gpioc_ioctl, 616b34b16eSOleksandr Tymoshenko .d_name = "gpioc", 626b34b16eSOleksandr Tymoshenko }; 636b34b16eSOleksandr Tymoshenko 646b34b16eSOleksandr Tymoshenko struct gpioc_softc { 656b34b16eSOleksandr Tymoshenko device_t sc_dev; /* gpiocX dev */ 666b34b16eSOleksandr Tymoshenko device_t sc_pdev; /* gpioX dev */ 676b34b16eSOleksandr Tymoshenko struct cdev *sc_ctl_dev; /* controller device */ 686b34b16eSOleksandr Tymoshenko int sc_unit; 696b34b16eSOleksandr Tymoshenko }; 706b34b16eSOleksandr Tymoshenko 716b34b16eSOleksandr Tymoshenko static int 726b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev) 736b34b16eSOleksandr Tymoshenko { 746b34b16eSOleksandr Tymoshenko device_set_desc(dev, "GPIO controller"); 756b34b16eSOleksandr Tymoshenko return (0); 766b34b16eSOleksandr Tymoshenko } 776b34b16eSOleksandr Tymoshenko 786b34b16eSOleksandr Tymoshenko static int 796b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev) 806b34b16eSOleksandr Tymoshenko { 816b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 826b34b16eSOleksandr Tymoshenko 836b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 846b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 856b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 866b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, 876b34b16eSOleksandr Tymoshenko UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); 886b34b16eSOleksandr Tymoshenko if (!sc->sc_ctl_dev) { 896b34b16eSOleksandr Tymoshenko printf("Failed to create gpioc%d", sc->sc_unit); 906b34b16eSOleksandr Tymoshenko return (ENXIO); 916b34b16eSOleksandr Tymoshenko } 926b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev->si_drv1 = sc; 936b34b16eSOleksandr Tymoshenko 946b34b16eSOleksandr Tymoshenko return (0); 956b34b16eSOleksandr Tymoshenko } 966b34b16eSOleksandr Tymoshenko 976b34b16eSOleksandr Tymoshenko static int 986b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 996b34b16eSOleksandr Tymoshenko { 1006b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 1016b34b16eSOleksandr Tymoshenko int err; 1026b34b16eSOleksandr Tymoshenko 1036de0a4faSOleksandr Tymoshenko if (sc->sc_ctl_dev) 1046b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 1056b34b16eSOleksandr Tymoshenko 1066b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 1076b34b16eSOleksandr Tymoshenko return (err); 1086b34b16eSOleksandr Tymoshenko 1096b34b16eSOleksandr Tymoshenko return (0); 1106b34b16eSOleksandr Tymoshenko } 1116b34b16eSOleksandr Tymoshenko 1126b34b16eSOleksandr Tymoshenko static int 1136b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 1146b34b16eSOleksandr Tymoshenko struct thread *td) 1156b34b16eSOleksandr Tymoshenko { 116d752f0f6SLuiz Otavio O Souza device_t bus; 1176b34b16eSOleksandr Tymoshenko int max_pin, res; 1186b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 1196b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 1206b34b16eSOleksandr Tymoshenko struct gpio_req req; 121667357dcSLuiz Otavio O Souza uint32_t caps; 1226b34b16eSOleksandr Tymoshenko 123d752f0f6SLuiz Otavio O Souza bus = GPIO_GET_BUS(sc->sc_pdev); 124d752f0f6SLuiz Otavio O Souza if (bus == NULL) 125d752f0f6SLuiz Otavio O Souza return (EINVAL); 1266b34b16eSOleksandr Tymoshenko switch (cmd) { 1276b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 1286b34b16eSOleksandr Tymoshenko max_pin = -1; 1296b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 1306b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 1316b34b16eSOleksandr Tymoshenko break; 1326b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 1336b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1346b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 1356b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 1366b34b16eSOleksandr Tymoshenko &pin.gp_flags); 1376b34b16eSOleksandr Tymoshenko /* Fail early */ 1386b34b16eSOleksandr Tymoshenko if (res) 1396b34b16eSOleksandr Tymoshenko break; 1406b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 141d752f0f6SLuiz Otavio O Souza GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); 1426b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 1436b34b16eSOleksandr Tymoshenko break; 1446b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 1456b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1466b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 147667357dcSLuiz Otavio O Souza res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); 148667357dcSLuiz Otavio O Souza if (res == 0) 149667357dcSLuiz Otavio O Souza res = gpio_check_flags(caps, pin.gp_flags); 150667357dcSLuiz Otavio O Souza if (res == 0) 1516b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 1526b34b16eSOleksandr Tymoshenko pin.gp_flags); 1536b34b16eSOleksandr Tymoshenko break; 1546b34b16eSOleksandr Tymoshenko case GPIOGET: 1556b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1566b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 1576b34b16eSOleksandr Tymoshenko &req.gp_value); 1586b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 1596b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1606b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 1616b34b16eSOleksandr Tymoshenko break; 1626b34b16eSOleksandr Tymoshenko case GPIOSET: 1636b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1646b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 1656b34b16eSOleksandr Tymoshenko req.gp_value); 1666b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 1676b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1686b34b16eSOleksandr Tymoshenko break; 1696b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 1706b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1716b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 1726b34b16eSOleksandr Tymoshenko req.gp_pin); 1736b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 1746b34b16eSOleksandr Tymoshenko break; 175d752f0f6SLuiz Otavio O Souza case GPIOSETNAME: 176d752f0f6SLuiz Otavio O Souza bcopy(arg, &pin, sizeof(pin)); 177d752f0f6SLuiz Otavio O Souza dprintf("set name on pin %d\n", pin.gp_pin); 178d752f0f6SLuiz Otavio O Souza res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, 179d752f0f6SLuiz Otavio O Souza pin.gp_name); 180d752f0f6SLuiz Otavio O Souza break; 1816b34b16eSOleksandr Tymoshenko default: 1826b34b16eSOleksandr Tymoshenko return (ENOTTY); 1836b34b16eSOleksandr Tymoshenko break; 1846b34b16eSOleksandr Tymoshenko } 1856b34b16eSOleksandr Tymoshenko 1866b34b16eSOleksandr Tymoshenko return (res); 1876b34b16eSOleksandr Tymoshenko } 1886b34b16eSOleksandr Tymoshenko 1896b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 1906b34b16eSOleksandr Tymoshenko /* Device interface */ 1916b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 1926b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 1936b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 1946b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 1956b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 1966b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 1976b34b16eSOleksandr Tymoshenko 198e2a1919dSOleksandr Tymoshenko DEVMETHOD_END 1996b34b16eSOleksandr Tymoshenko }; 2006b34b16eSOleksandr Tymoshenko 20154873b4cSAndrew Thompson driver_t gpioc_driver = { 2026b34b16eSOleksandr Tymoshenko "gpioc", 2036b34b16eSOleksandr Tymoshenko gpioc_methods, 2046b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 2056b34b16eSOleksandr Tymoshenko }; 2066b34b16eSOleksandr Tymoshenko 2076b34b16eSOleksandr Tymoshenko devclass_t gpioc_devclass; 2086b34b16eSOleksandr Tymoshenko 2096b34b16eSOleksandr Tymoshenko DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 2106b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 211