16b34b16eSOleksandr Tymoshenko #include <sys/cdefs.h> 26b34b16eSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 36b34b16eSOleksandr Tymoshenko 46b34b16eSOleksandr Tymoshenko #include <sys/param.h> 56b34b16eSOleksandr Tymoshenko #include <sys/systm.h> 66b34b16eSOleksandr Tymoshenko #include <sys/types.h> 76b34b16eSOleksandr Tymoshenko 86b34b16eSOleksandr Tymoshenko #include <sys/bus.h> 96b34b16eSOleksandr Tymoshenko #include <sys/conf.h> 106b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h> 116b34b16eSOleksandr Tymoshenko #include <sys/kernel.h> 126b34b16eSOleksandr Tymoshenko #include <sys/malloc.h> 136b34b16eSOleksandr Tymoshenko #include <sys/module.h> 146b34b16eSOleksandr Tymoshenko #include <sys/queue.h> 156b34b16eSOleksandr Tymoshenko #include <machine/bus.h> 166b34b16eSOleksandr Tymoshenko #include <machine/resource.h> 176b34b16eSOleksandr Tymoshenko 186b34b16eSOleksandr Tymoshenko #include <sys/gpio.h> 196b34b16eSOleksandr Tymoshenko #include "gpio_if.h" 206b34b16eSOleksandr Tymoshenko 216b34b16eSOleksandr Tymoshenko #undef GPIOC_DEBUG 226b34b16eSOleksandr Tymoshenko #ifdef GPIOC_DEBUG 236b34b16eSOleksandr Tymoshenko #define dprintf printf 246b34b16eSOleksandr Tymoshenko #else 256b34b16eSOleksandr Tymoshenko #define dprintf(x, arg...) 266b34b16eSOleksandr Tymoshenko #endif 276b34b16eSOleksandr Tymoshenko 286b34b16eSOleksandr Tymoshenko static int gpioc_probe(device_t dev); 296b34b16eSOleksandr Tymoshenko static int gpioc_attach(device_t dev); 306b34b16eSOleksandr Tymoshenko static int gpioc_detach(device_t dev); 316b34b16eSOleksandr Tymoshenko 326b34b16eSOleksandr Tymoshenko static d_ioctl_t gpioc_ioctl; 336b34b16eSOleksandr Tymoshenko 346b34b16eSOleksandr Tymoshenko static struct cdevsw gpioc_cdevsw = { 356b34b16eSOleksandr Tymoshenko .d_version = D_VERSION, 366b34b16eSOleksandr Tymoshenko .d_ioctl = gpioc_ioctl, 376b34b16eSOleksandr Tymoshenko .d_name = "gpioc", 386b34b16eSOleksandr Tymoshenko #if __FreeBSD_version >= 800039 396b34b16eSOleksandr Tymoshenko .d_flags = D_PSEUDO | D_NEEDMINOR 406b34b16eSOleksandr Tymoshenko #endif 416b34b16eSOleksandr Tymoshenko }; 426b34b16eSOleksandr Tymoshenko 436b34b16eSOleksandr Tymoshenko struct gpioc_softc { 446b34b16eSOleksandr Tymoshenko device_t sc_dev; /* gpiocX dev */ 456b34b16eSOleksandr Tymoshenko device_t sc_pdev; /* gpioX dev */ 466b34b16eSOleksandr Tymoshenko struct cdev *sc_ctl_dev; /* controller device */ 476b34b16eSOleksandr Tymoshenko int sc_unit; 486b34b16eSOleksandr Tymoshenko }; 496b34b16eSOleksandr Tymoshenko 506b34b16eSOleksandr Tymoshenko static int 516b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev) 526b34b16eSOleksandr Tymoshenko { 536b34b16eSOleksandr Tymoshenko device_set_desc(dev, "GPIO controller"); 546b34b16eSOleksandr Tymoshenko return (0); 556b34b16eSOleksandr Tymoshenko } 566b34b16eSOleksandr Tymoshenko 576b34b16eSOleksandr Tymoshenko static int 586b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev) 596b34b16eSOleksandr Tymoshenko { 606b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 616b34b16eSOleksandr Tymoshenko 626b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 636b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 646b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 656b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, 666b34b16eSOleksandr Tymoshenko UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); 676b34b16eSOleksandr Tymoshenko if (!sc->sc_ctl_dev) { 686b34b16eSOleksandr Tymoshenko printf("Failed to create gpioc%d", sc->sc_unit); 696b34b16eSOleksandr Tymoshenko return (ENXIO); 706b34b16eSOleksandr Tymoshenko } 716b34b16eSOleksandr Tymoshenko sc->sc_ctl_dev->si_drv1 = sc; 726b34b16eSOleksandr Tymoshenko 736b34b16eSOleksandr Tymoshenko return (0); 746b34b16eSOleksandr Tymoshenko } 756b34b16eSOleksandr Tymoshenko 766b34b16eSOleksandr Tymoshenko static int 776b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 786b34b16eSOleksandr Tymoshenko { 796b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 806b34b16eSOleksandr Tymoshenko int err; 816b34b16eSOleksandr Tymoshenko 826b34b16eSOleksandr Tymoshenko if (sc->sc_ctl_dev); 836b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 846b34b16eSOleksandr Tymoshenko 856b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 866b34b16eSOleksandr Tymoshenko return (err); 876b34b16eSOleksandr Tymoshenko 886b34b16eSOleksandr Tymoshenko return (0); 896b34b16eSOleksandr Tymoshenko } 906b34b16eSOleksandr Tymoshenko 916b34b16eSOleksandr Tymoshenko static int 926b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 936b34b16eSOleksandr Tymoshenko struct thread *td) 946b34b16eSOleksandr Tymoshenko { 956b34b16eSOleksandr Tymoshenko int max_pin, res; 966b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 976b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 986b34b16eSOleksandr Tymoshenko struct gpio_req req; 996b34b16eSOleksandr Tymoshenko 1006b34b16eSOleksandr Tymoshenko switch (cmd) { 1016b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 1026b34b16eSOleksandr Tymoshenko max_pin = -1; 1036b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 1046b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 1056b34b16eSOleksandr Tymoshenko break; 1066b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 1076b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1086b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 1096b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 1106b34b16eSOleksandr Tymoshenko &pin.gp_flags); 1116b34b16eSOleksandr Tymoshenko /* Fail early */ 1126b34b16eSOleksandr Tymoshenko if (res) 1136b34b16eSOleksandr Tymoshenko break; 1146b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 1156b34b16eSOleksandr Tymoshenko GPIO_PIN_GETNAME(sc->sc_pdev, pin.gp_pin, pin.gp_name); 1166b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 1176b34b16eSOleksandr Tymoshenko break; 1186b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 1196b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1206b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 1216b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 1226b34b16eSOleksandr Tymoshenko pin.gp_flags); 1236b34b16eSOleksandr Tymoshenko break; 1246b34b16eSOleksandr Tymoshenko case GPIOGET: 1256b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1266b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 1276b34b16eSOleksandr Tymoshenko &req.gp_value); 1286b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 1296b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1306b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 1316b34b16eSOleksandr Tymoshenko break; 1326b34b16eSOleksandr Tymoshenko case GPIOSET: 1336b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1346b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 1356b34b16eSOleksandr Tymoshenko req.gp_value); 1366b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 1376b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1386b34b16eSOleksandr Tymoshenko break; 1396b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 1406b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1416b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 1426b34b16eSOleksandr Tymoshenko req.gp_pin); 1436b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 1446b34b16eSOleksandr Tymoshenko break; 1456b34b16eSOleksandr Tymoshenko default: 1466b34b16eSOleksandr Tymoshenko return (ENOTTY); 1476b34b16eSOleksandr Tymoshenko break; 1486b34b16eSOleksandr Tymoshenko } 1496b34b16eSOleksandr Tymoshenko 1506b34b16eSOleksandr Tymoshenko return (res); 1516b34b16eSOleksandr Tymoshenko } 1526b34b16eSOleksandr Tymoshenko 1536b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 1546b34b16eSOleksandr Tymoshenko /* Device interface */ 1556b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 1566b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 1576b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 1586b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 1596b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 1606b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 1616b34b16eSOleksandr Tymoshenko 1626b34b16eSOleksandr Tymoshenko { 0, 0 } 1636b34b16eSOleksandr Tymoshenko }; 1646b34b16eSOleksandr Tymoshenko 1656b34b16eSOleksandr Tymoshenko static driver_t gpioc_driver = { 1666b34b16eSOleksandr Tymoshenko "gpioc", 1676b34b16eSOleksandr Tymoshenko gpioc_methods, 1686b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 1696b34b16eSOleksandr Tymoshenko }; 1706b34b16eSOleksandr Tymoshenko 1716b34b16eSOleksandr Tymoshenko devclass_t gpioc_devclass; 1726b34b16eSOleksandr Tymoshenko 1736b34b16eSOleksandr Tymoshenko DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 1746b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 175