1213277Sgonzo/*- 2213277Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 3213277Sgonzo * All rights reserved. 4213277Sgonzo * 5213277Sgonzo * Redistribution and use in source and binary forms, with or without 6213277Sgonzo * modification, are permitted provided that the following conditions 7213277Sgonzo * are met: 8213277Sgonzo * 1. Redistributions of source code must retain the above copyright 9213277Sgonzo * notice, this list of conditions and the following disclaimer. 10213277Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11213277Sgonzo * notice, this list of conditions and the following disclaimer in the 12213277Sgonzo * documentation and/or other materials provided with the distribution. 13213277Sgonzo * 14213277Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15213277Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213277Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213277Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18213277Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213277Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213277Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213277Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213277Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213277Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213277Sgonzo * SUCH DAMAGE. 25213277Sgonzo */ 26213277Sgonzo 27213237Sgonzo#include <sys/cdefs.h> 28213237Sgonzo__FBSDID("$FreeBSD$"); 29213237Sgonzo 30213237Sgonzo#include <sys/param.h> 31213237Sgonzo#include <sys/systm.h> 32213237Sgonzo#include <sys/bus.h> 33213237Sgonzo#include <sys/conf.h> 34278786Sloos#include <sys/gpio.h> 35213237Sgonzo#include <sys/ioccom.h> 36213237Sgonzo#include <sys/kernel.h> 37213237Sgonzo#include <sys/malloc.h> 38213237Sgonzo#include <sys/module.h> 39213237Sgonzo 40278786Sloos#include <dev/gpio/gpiobusvar.h> 41278786Sloos 42213237Sgonzo#include "gpio_if.h" 43213237Sgonzo 44213237Sgonzo#undef GPIOC_DEBUG 45213237Sgonzo#ifdef GPIOC_DEBUG 46213237Sgonzo#define dprintf printf 47213237Sgonzo#else 48213237Sgonzo#define dprintf(x, arg...) 49213237Sgonzo#endif 50213237Sgonzo 51213237Sgonzostatic int gpioc_probe(device_t dev); 52213237Sgonzostatic int gpioc_attach(device_t dev); 53213237Sgonzostatic int gpioc_detach(device_t dev); 54213237Sgonzo 55213237Sgonzostatic d_ioctl_t gpioc_ioctl; 56213237Sgonzo 57213237Sgonzostatic struct cdevsw gpioc_cdevsw = { 58213237Sgonzo .d_version = D_VERSION, 59213237Sgonzo .d_ioctl = gpioc_ioctl, 60213237Sgonzo .d_name = "gpioc", 61213237Sgonzo}; 62213237Sgonzo 63213237Sgonzostruct gpioc_softc { 64213237Sgonzo device_t sc_dev; /* gpiocX dev */ 65213237Sgonzo device_t sc_pdev; /* gpioX dev */ 66213237Sgonzo struct cdev *sc_ctl_dev; /* controller device */ 67213237Sgonzo int sc_unit; 68213237Sgonzo}; 69213237Sgonzo 70213237Sgonzostatic int 71213237Sgonzogpioc_probe(device_t dev) 72213237Sgonzo{ 73213237Sgonzo device_set_desc(dev, "GPIO controller"); 74213237Sgonzo return (0); 75213237Sgonzo} 76213237Sgonzo 77213237Sgonzostatic int 78213237Sgonzogpioc_attach(device_t dev) 79213237Sgonzo{ 80213237Sgonzo struct gpioc_softc *sc = device_get_softc(dev); 81213237Sgonzo 82213237Sgonzo sc->sc_dev = dev; 83213237Sgonzo sc->sc_pdev = device_get_parent(dev); 84213237Sgonzo sc->sc_unit = device_get_unit(dev); 85213237Sgonzo sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, 86213237Sgonzo UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); 87213237Sgonzo if (!sc->sc_ctl_dev) { 88213237Sgonzo printf("Failed to create gpioc%d", sc->sc_unit); 89213237Sgonzo return (ENXIO); 90213237Sgonzo } 91213237Sgonzo sc->sc_ctl_dev->si_drv1 = sc; 92213237Sgonzo 93213237Sgonzo return (0); 94213237Sgonzo} 95213237Sgonzo 96213237Sgonzostatic int 97213237Sgonzogpioc_detach(device_t dev) 98213237Sgonzo{ 99213237Sgonzo struct gpioc_softc *sc = device_get_softc(dev); 100213237Sgonzo int err; 101213237Sgonzo 102233767Sgonzo if (sc->sc_ctl_dev) 103213237Sgonzo destroy_dev(sc->sc_ctl_dev); 104213237Sgonzo 105213237Sgonzo if ((err = bus_generic_detach(dev)) != 0) 106213237Sgonzo return (err); 107213237Sgonzo 108213237Sgonzo return (0); 109213237Sgonzo} 110213237Sgonzo 111213237Sgonzostatic int 112213237Sgonzogpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 113213237Sgonzo struct thread *td) 114213237Sgonzo{ 115213237Sgonzo int max_pin, res; 116213237Sgonzo struct gpioc_softc *sc = cdev->si_drv1; 117213237Sgonzo struct gpio_pin pin; 118213237Sgonzo struct gpio_req req; 119278786Sloos uint32_t caps; 120213237Sgonzo 121213237Sgonzo switch (cmd) { 122213237Sgonzo case GPIOMAXPIN: 123213237Sgonzo max_pin = -1; 124213237Sgonzo res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 125213237Sgonzo bcopy(&max_pin, arg, sizeof(max_pin)); 126213237Sgonzo break; 127213237Sgonzo case GPIOGETCONFIG: 128213237Sgonzo bcopy(arg, &pin, sizeof(pin)); 129213237Sgonzo dprintf("get config pin %d\n", pin.gp_pin); 130213237Sgonzo res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 131213237Sgonzo &pin.gp_flags); 132213237Sgonzo /* Fail early */ 133213237Sgonzo if (res) 134213237Sgonzo break; 135213237Sgonzo GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 136213237Sgonzo GPIO_PIN_GETNAME(sc->sc_pdev, pin.gp_pin, pin.gp_name); 137213237Sgonzo bcopy(&pin, arg, sizeof(pin)); 138213237Sgonzo break; 139213237Sgonzo case GPIOSETCONFIG: 140213237Sgonzo bcopy(arg, &pin, sizeof(pin)); 141213237Sgonzo dprintf("set config pin %d\n", pin.gp_pin); 142278786Sloos res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); 143278786Sloos if (res == 0) 144278786Sloos res = gpio_check_flags(caps, pin.gp_flags); 145278786Sloos if (res == 0) 146278786Sloos res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 147278786Sloos pin.gp_flags); 148213237Sgonzo break; 149213237Sgonzo case GPIOGET: 150213237Sgonzo bcopy(arg, &req, sizeof(req)); 151213237Sgonzo res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 152213237Sgonzo &req.gp_value); 153213237Sgonzo dprintf("read pin %d -> %d\n", 154213237Sgonzo req.gp_pin, req.gp_value); 155213237Sgonzo bcopy(&req, arg, sizeof(req)); 156213237Sgonzo break; 157213237Sgonzo case GPIOSET: 158213237Sgonzo bcopy(arg, &req, sizeof(req)); 159213237Sgonzo res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 160213237Sgonzo req.gp_value); 161213237Sgonzo dprintf("write pin %d -> %d\n", 162213237Sgonzo req.gp_pin, req.gp_value); 163213237Sgonzo break; 164213237Sgonzo case GPIOTOGGLE: 165213237Sgonzo bcopy(arg, &req, sizeof(req)); 166213237Sgonzo dprintf("toggle pin %d\n", 167213237Sgonzo req.gp_pin); 168213237Sgonzo res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 169213237Sgonzo break; 170213237Sgonzo default: 171213237Sgonzo return (ENOTTY); 172213237Sgonzo break; 173213237Sgonzo } 174213237Sgonzo 175213237Sgonzo return (res); 176213237Sgonzo} 177213237Sgonzo 178213237Sgonzostatic device_method_t gpioc_methods[] = { 179213237Sgonzo /* Device interface */ 180213237Sgonzo DEVMETHOD(device_probe, gpioc_probe), 181213237Sgonzo DEVMETHOD(device_attach, gpioc_attach), 182213237Sgonzo DEVMETHOD(device_detach, gpioc_detach), 183213237Sgonzo DEVMETHOD(device_shutdown, bus_generic_shutdown), 184213237Sgonzo DEVMETHOD(device_suspend, bus_generic_suspend), 185213237Sgonzo DEVMETHOD(device_resume, bus_generic_resume), 186213237Sgonzo 187213237Sgonzo { 0, 0 } 188213237Sgonzo}; 189213237Sgonzo 190215142Sthompsadriver_t gpioc_driver = { 191213237Sgonzo "gpioc", 192213237Sgonzo gpioc_methods, 193213237Sgonzo sizeof(struct gpioc_softc) 194213237Sgonzo}; 195213237Sgonzo 196213237Sgonzodevclass_t gpioc_devclass; 197213237Sgonzo 198213237SgonzoDRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 199213237SgonzoMODULE_VERSION(gpioc, 1); 200