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/types.h> 336b34b16eSOleksandr Tymoshenko 346b34b16eSOleksandr Tymoshenko #include <sys/bus.h> 356b34b16eSOleksandr Tymoshenko #include <sys/conf.h> 366b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h> 376b34b16eSOleksandr Tymoshenko #include <sys/kernel.h> 386b34b16eSOleksandr Tymoshenko #include <sys/malloc.h> 396b34b16eSOleksandr Tymoshenko #include <sys/module.h> 406b34b16eSOleksandr Tymoshenko #include <sys/queue.h> 416b34b16eSOleksandr Tymoshenko #include <machine/bus.h> 426b34b16eSOleksandr Tymoshenko #include <machine/resource.h> 436b34b16eSOleksandr Tymoshenko 446b34b16eSOleksandr Tymoshenko #include <sys/gpio.h> 456b34b16eSOleksandr Tymoshenko #include "gpio_if.h" 466b34b16eSOleksandr Tymoshenko 476b34b16eSOleksandr Tymoshenko #undef GPIOC_DEBUG 486b34b16eSOleksandr Tymoshenko #ifdef GPIOC_DEBUG 496b34b16eSOleksandr Tymoshenko #define dprintf printf 506b34b16eSOleksandr Tymoshenko #else 516b34b16eSOleksandr Tymoshenko #define dprintf(x, arg...) 526b34b16eSOleksandr Tymoshenko #endif 536b34b16eSOleksandr Tymoshenko 546b34b16eSOleksandr Tymoshenko static int gpioc_probe(device_t dev); 556b34b16eSOleksandr Tymoshenko static int gpioc_attach(device_t dev); 566b34b16eSOleksandr Tymoshenko static int gpioc_detach(device_t dev); 576b34b16eSOleksandr Tymoshenko 586b34b16eSOleksandr Tymoshenko static d_ioctl_t gpioc_ioctl; 596b34b16eSOleksandr Tymoshenko 606b34b16eSOleksandr Tymoshenko static struct cdevsw gpioc_cdevsw = { 616b34b16eSOleksandr Tymoshenko .d_version = D_VERSION, 626b34b16eSOleksandr Tymoshenko .d_ioctl = gpioc_ioctl, 636b34b16eSOleksandr Tymoshenko .d_name = "gpioc", 646b34b16eSOleksandr Tymoshenko }; 656b34b16eSOleksandr Tymoshenko 666b34b16eSOleksandr Tymoshenko struct gpioc_softc { 676b34b16eSOleksandr Tymoshenko device_t sc_dev; /* gpiocX dev */ 686b34b16eSOleksandr Tymoshenko device_t sc_pdev; /* gpioX dev */ 696b34b16eSOleksandr Tymoshenko struct cdev *sc_ctl_dev; /* controller device */ 706b34b16eSOleksandr Tymoshenko int sc_unit; 716b34b16eSOleksandr Tymoshenko }; 726b34b16eSOleksandr Tymoshenko 736b34b16eSOleksandr Tymoshenko static int 746b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev) 756b34b16eSOleksandr Tymoshenko { 766b34b16eSOleksandr Tymoshenko device_set_desc(dev, "GPIO controller"); 776b34b16eSOleksandr Tymoshenko return (0); 786b34b16eSOleksandr Tymoshenko } 796b34b16eSOleksandr Tymoshenko 806b34b16eSOleksandr Tymoshenko static int 816b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev) 826b34b16eSOleksandr Tymoshenko { 836b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 846b34b16eSOleksandr Tymoshenko 856b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 866b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 876b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 886b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, 896b34b16eSOleksandr Tymoshenko UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); 906b34b16eSOleksandr Tymoshenko if (!sc->sc_ctl_dev) { 916b34b16eSOleksandr Tymoshenko printf("Failed to create gpioc%d", sc->sc_unit); 926b34b16eSOleksandr Tymoshenko return (ENXIO); 936b34b16eSOleksandr Tymoshenko } 946b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev->si_drv1 = sc; 956b34b16eSOleksandr Tymoshenko 966b34b16eSOleksandr Tymoshenko return (0); 976b34b16eSOleksandr Tymoshenko } 986b34b16eSOleksandr Tymoshenko 996b34b16eSOleksandr Tymoshenko static int 1006b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 1016b34b16eSOleksandr Tymoshenko { 1026b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 1036b34b16eSOleksandr Tymoshenko int err; 1046b34b16eSOleksandr Tymoshenko 105*6de0a4faSOleksandr Tymoshenko if (sc->sc_ctl_dev) 1066b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 1076b34b16eSOleksandr Tymoshenko 1086b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 1096b34b16eSOleksandr Tymoshenko return (err); 1106b34b16eSOleksandr Tymoshenko 1116b34b16eSOleksandr Tymoshenko return (0); 1126b34b16eSOleksandr Tymoshenko } 1136b34b16eSOleksandr Tymoshenko 1146b34b16eSOleksandr Tymoshenko static int 1156b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 1166b34b16eSOleksandr Tymoshenko struct thread *td) 1176b34b16eSOleksandr Tymoshenko { 1186b34b16eSOleksandr Tymoshenko int max_pin, res; 1196b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 1206b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 1216b34b16eSOleksandr Tymoshenko struct gpio_req req; 1226b34b16eSOleksandr Tymoshenko 1236b34b16eSOleksandr Tymoshenko switch (cmd) { 1246b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 1256b34b16eSOleksandr Tymoshenko max_pin = -1; 1266b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 1276b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 1286b34b16eSOleksandr Tymoshenko break; 1296b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 1306b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1316b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 1326b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 1336b34b16eSOleksandr Tymoshenko &pin.gp_flags); 1346b34b16eSOleksandr Tymoshenko /* Fail early */ 1356b34b16eSOleksandr Tymoshenko if (res) 1366b34b16eSOleksandr Tymoshenko break; 1376b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 1386b34b16eSOleksandr Tymoshenko GPIO_PIN_GETNAME(sc->sc_pdev, pin.gp_pin, pin.gp_name); 1396b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 1406b34b16eSOleksandr Tymoshenko break; 1416b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 1426b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1436b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 1446b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 1456b34b16eSOleksandr Tymoshenko pin.gp_flags); 1466b34b16eSOleksandr Tymoshenko break; 1476b34b16eSOleksandr Tymoshenko case GPIOGET: 1486b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1496b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 1506b34b16eSOleksandr Tymoshenko &req.gp_value); 1516b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 1526b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1536b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 1546b34b16eSOleksandr Tymoshenko break; 1556b34b16eSOleksandr Tymoshenko case GPIOSET: 1566b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1576b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 1586b34b16eSOleksandr Tymoshenko req.gp_value); 1596b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 1606b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1616b34b16eSOleksandr Tymoshenko break; 1626b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 1636b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1646b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 1656b34b16eSOleksandr Tymoshenko req.gp_pin); 1666b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 1676b34b16eSOleksandr Tymoshenko break; 1686b34b16eSOleksandr Tymoshenko default: 1696b34b16eSOleksandr Tymoshenko return (ENOTTY); 1706b34b16eSOleksandr Tymoshenko break; 1716b34b16eSOleksandr Tymoshenko } 1726b34b16eSOleksandr Tymoshenko 1736b34b16eSOleksandr Tymoshenko return (res); 1746b34b16eSOleksandr Tymoshenko } 1756b34b16eSOleksandr Tymoshenko 1766b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 1776b34b16eSOleksandr Tymoshenko /* Device interface */ 1786b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 1796b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 1806b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 1816b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 1826b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 1836b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 1846b34b16eSOleksandr Tymoshenko 1856b34b16eSOleksandr Tymoshenko { 0, 0 } 1866b34b16eSOleksandr Tymoshenko }; 1876b34b16eSOleksandr Tymoshenko 18854873b4cSAndrew Thompson driver_t gpioc_driver = { 1896b34b16eSOleksandr Tymoshenko "gpioc", 1906b34b16eSOleksandr Tymoshenko gpioc_methods, 1916b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 1926b34b16eSOleksandr Tymoshenko }; 1936b34b16eSOleksandr Tymoshenko 1946b34b16eSOleksandr Tymoshenko devclass_t gpioc_devclass; 1956b34b16eSOleksandr Tymoshenko 1966b34b16eSOleksandr Tymoshenko DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 1976b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 198