15f958b85SOleksandr Tymoshenko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 45f958b85SOleksandr Tymoshenko * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 55f958b85SOleksandr Tymoshenko * All rights reserved. 65f958b85SOleksandr Tymoshenko * 75f958b85SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 85f958b85SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 95f958b85SOleksandr Tymoshenko * are met: 105f958b85SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 115f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 125f958b85SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 135f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 145f958b85SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 155f958b85SOleksandr Tymoshenko * 165f958b85SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 175f958b85SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 185f958b85SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 195f958b85SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 205f958b85SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 215f958b85SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 225f958b85SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 235f958b85SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 245f958b85SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 255f958b85SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 265f958b85SOleksandr Tymoshenko * SUCH DAMAGE. 275f958b85SOleksandr Tymoshenko */ 285f958b85SOleksandr Tymoshenko 296b34b16eSOleksandr Tymoshenko #include <sys/cdefs.h> 306b34b16eSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 316b34b16eSOleksandr Tymoshenko 326b34b16eSOleksandr Tymoshenko #include <sys/param.h> 336b34b16eSOleksandr Tymoshenko #include <sys/systm.h> 346b34b16eSOleksandr Tymoshenko #include <sys/bus.h> 356b34b16eSOleksandr Tymoshenko #include <sys/conf.h> 36667357dcSLuiz Otavio O Souza #include <sys/gpio.h> 376b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h> 386b34b16eSOleksandr Tymoshenko #include <sys/kernel.h> 396b34b16eSOleksandr Tymoshenko #include <sys/malloc.h> 406b34b16eSOleksandr Tymoshenko #include <sys/module.h> 416b34b16eSOleksandr Tymoshenko 42667357dcSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 43667357dcSLuiz Otavio O Souza 446b34b16eSOleksandr Tymoshenko #include "gpio_if.h" 45d752f0f6SLuiz Otavio O Souza #include "gpiobus_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 { 834723c14fSLuiz Otavio O Souza int err; 844723c14fSLuiz Otavio O Souza struct gpioc_softc *sc; 854723c14fSLuiz Otavio O Souza struct make_dev_args devargs; 866b34b16eSOleksandr Tymoshenko 874723c14fSLuiz Otavio O Souza sc = device_get_softc(dev); 886b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 896b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 906b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 914723c14fSLuiz Otavio O Souza make_dev_args_init(&devargs); 924723c14fSLuiz Otavio O Souza devargs.mda_devsw = &gpioc_cdevsw; 934723c14fSLuiz Otavio O Souza devargs.mda_uid = UID_ROOT; 944723c14fSLuiz Otavio O Souza devargs.mda_gid = GID_WHEEL; 954723c14fSLuiz Otavio O Souza devargs.mda_mode = 0600; 964723c14fSLuiz Otavio O Souza devargs.mda_si_drv1 = sc; 974723c14fSLuiz Otavio O Souza err = make_dev_s(&devargs, &sc->sc_ctl_dev, "gpioc%d", sc->sc_unit); 984723c14fSLuiz Otavio O Souza if (err != 0) { 996b34b16eSOleksandr Tymoshenko printf("Failed to create gpioc%d", sc->sc_unit); 1006b34b16eSOleksandr Tymoshenko return (ENXIO); 1016b34b16eSOleksandr Tymoshenko } 1026b34b16eSOleksandr Tymoshenko 1036b34b16eSOleksandr Tymoshenko return (0); 1046b34b16eSOleksandr Tymoshenko } 1056b34b16eSOleksandr Tymoshenko 1066b34b16eSOleksandr Tymoshenko static int 1076b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 1086b34b16eSOleksandr Tymoshenko { 1096b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 1106b34b16eSOleksandr Tymoshenko int err; 1116b34b16eSOleksandr Tymoshenko 1126de0a4faSOleksandr Tymoshenko if (sc->sc_ctl_dev) 1136b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 1146b34b16eSOleksandr Tymoshenko 1156b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 1166b34b16eSOleksandr Tymoshenko return (err); 1176b34b16eSOleksandr Tymoshenko 1186b34b16eSOleksandr Tymoshenko return (0); 1196b34b16eSOleksandr Tymoshenko } 1206b34b16eSOleksandr Tymoshenko 1216b34b16eSOleksandr Tymoshenko static int 1226b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 1236b34b16eSOleksandr Tymoshenko struct thread *td) 1246b34b16eSOleksandr Tymoshenko { 125d752f0f6SLuiz Otavio O Souza device_t bus; 1266b34b16eSOleksandr Tymoshenko int max_pin, res; 1276b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 1286b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 1296b34b16eSOleksandr Tymoshenko struct gpio_req req; 130e1275c68SIan Lepore struct gpio_access_32 *a32; 131e1275c68SIan Lepore struct gpio_config_32 *c32; 132667357dcSLuiz Otavio O Souza uint32_t caps; 1336b34b16eSOleksandr Tymoshenko 134d752f0f6SLuiz Otavio O Souza bus = GPIO_GET_BUS(sc->sc_pdev); 135d752f0f6SLuiz Otavio O Souza if (bus == NULL) 136d752f0f6SLuiz Otavio O Souza return (EINVAL); 1376b34b16eSOleksandr Tymoshenko switch (cmd) { 1386b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 1396b34b16eSOleksandr Tymoshenko max_pin = -1; 1406b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 1416b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 1426b34b16eSOleksandr Tymoshenko break; 1436b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 1446b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1456b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 1466b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 1476b34b16eSOleksandr Tymoshenko &pin.gp_flags); 1486b34b16eSOleksandr Tymoshenko /* Fail early */ 1496b34b16eSOleksandr Tymoshenko if (res) 1506b34b16eSOleksandr Tymoshenko break; 1516b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 152d752f0f6SLuiz Otavio O Souza GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); 1536b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 1546b34b16eSOleksandr Tymoshenko break; 1556b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 1566b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 1576b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 158667357dcSLuiz Otavio O Souza res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); 159667357dcSLuiz Otavio O Souza if (res == 0) 160667357dcSLuiz Otavio O Souza res = gpio_check_flags(caps, pin.gp_flags); 161667357dcSLuiz Otavio O Souza if (res == 0) 1626b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 1636b34b16eSOleksandr Tymoshenko pin.gp_flags); 1646b34b16eSOleksandr Tymoshenko break; 1656b34b16eSOleksandr Tymoshenko case GPIOGET: 1666b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1676b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 1686b34b16eSOleksandr Tymoshenko &req.gp_value); 1696b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 1706b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1716b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 1726b34b16eSOleksandr Tymoshenko break; 1736b34b16eSOleksandr Tymoshenko case GPIOSET: 1746b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1756b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 1766b34b16eSOleksandr Tymoshenko req.gp_value); 1776b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 1786b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 1796b34b16eSOleksandr Tymoshenko break; 1806b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 1816b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 1826b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 1836b34b16eSOleksandr Tymoshenko req.gp_pin); 1846b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 1856b34b16eSOleksandr Tymoshenko break; 186d752f0f6SLuiz Otavio O Souza case GPIOSETNAME: 187d752f0f6SLuiz Otavio O Souza bcopy(arg, &pin, sizeof(pin)); 188d752f0f6SLuiz Otavio O Souza dprintf("set name on pin %d\n", pin.gp_pin); 189d752f0f6SLuiz Otavio O Souza res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, 190d752f0f6SLuiz Otavio O Souza pin.gp_name); 191d752f0f6SLuiz Otavio O Souza break; 192e1275c68SIan Lepore case GPIOACCESS32: 193e1275c68SIan Lepore a32 = (struct gpio_access_32 *)arg; 194e1275c68SIan Lepore res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin, 195*20105d31SIan Lepore a32->clear_pins, a32->change_pins, &a32->orig_pins); 196e1275c68SIan Lepore break; 197e1275c68SIan Lepore case GPIOCONFIG32: 198e1275c68SIan Lepore c32 = (struct gpio_config_32 *)arg; 199e1275c68SIan Lepore res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin, 200e1275c68SIan Lepore c32->num_pins, c32->pin_flags); 201e1275c68SIan Lepore break; 2026b34b16eSOleksandr Tymoshenko default: 2036b34b16eSOleksandr Tymoshenko return (ENOTTY); 2046b34b16eSOleksandr Tymoshenko break; 2056b34b16eSOleksandr Tymoshenko } 2066b34b16eSOleksandr Tymoshenko 2076b34b16eSOleksandr Tymoshenko return (res); 2086b34b16eSOleksandr Tymoshenko } 2096b34b16eSOleksandr Tymoshenko 2106b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 2116b34b16eSOleksandr Tymoshenko /* Device interface */ 2126b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 2136b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 2146b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 2156b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 2166b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 2176b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 2186b34b16eSOleksandr Tymoshenko 219e2a1919dSOleksandr Tymoshenko DEVMETHOD_END 2206b34b16eSOleksandr Tymoshenko }; 2216b34b16eSOleksandr Tymoshenko 22254873b4cSAndrew Thompson driver_t gpioc_driver = { 2236b34b16eSOleksandr Tymoshenko "gpioc", 2246b34b16eSOleksandr Tymoshenko gpioc_methods, 2256b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 2266b34b16eSOleksandr Tymoshenko }; 2276b34b16eSOleksandr Tymoshenko 2286b34b16eSOleksandr Tymoshenko devclass_t gpioc_devclass; 2296b34b16eSOleksandr Tymoshenko 2306b34b16eSOleksandr Tymoshenko DRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 2316b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 232