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/types.h> 33213237Sgonzo 34213237Sgonzo#include <sys/bus.h> 35213237Sgonzo#include <sys/conf.h> 36213237Sgonzo#include <sys/ioccom.h> 37213237Sgonzo#include <sys/kernel.h> 38213237Sgonzo#include <sys/malloc.h> 39213237Sgonzo#include <sys/module.h> 40213237Sgonzo#include <sys/queue.h> 41213237Sgonzo#include <machine/bus.h> 42213237Sgonzo#include <machine/resource.h> 43213237Sgonzo 44213237Sgonzo#include <sys/gpio.h> 45213237Sgonzo#include "gpio_if.h" 46213237Sgonzo 47213237Sgonzo#undef GPIOC_DEBUG 48213237Sgonzo#ifdef GPIOC_DEBUG 49213237Sgonzo#define dprintf printf 50213237Sgonzo#else 51213237Sgonzo#define dprintf(x, arg...) 52213237Sgonzo#endif 53213237Sgonzo 54213237Sgonzostatic int gpioc_probe(device_t dev); 55213237Sgonzostatic int gpioc_attach(device_t dev); 56213237Sgonzostatic int gpioc_detach(device_t dev); 57213237Sgonzo 58213237Sgonzostatic d_ioctl_t gpioc_ioctl; 59213237Sgonzo 60213237Sgonzostatic struct cdevsw gpioc_cdevsw = { 61213237Sgonzo .d_version = D_VERSION, 62213237Sgonzo .d_ioctl = gpioc_ioctl, 63213237Sgonzo .d_name = "gpioc", 64213237Sgonzo#if __FreeBSD_version >= 800039 65213237Sgonzo .d_flags = D_PSEUDO | D_NEEDMINOR 66213237Sgonzo#endif 67213237Sgonzo}; 68213237Sgonzo 69213237Sgonzostruct gpioc_softc { 70213237Sgonzo device_t sc_dev; /* gpiocX dev */ 71213237Sgonzo device_t sc_pdev; /* gpioX dev */ 72213237Sgonzo struct cdev *sc_ctl_dev; /* controller device */ 73213237Sgonzo int sc_unit; 74213237Sgonzo}; 75213237Sgonzo 76213237Sgonzostatic int 77213237Sgonzogpioc_probe(device_t dev) 78213237Sgonzo{ 79213237Sgonzo device_set_desc(dev, "GPIO controller"); 80213237Sgonzo return (0); 81213237Sgonzo} 82213237Sgonzo 83213237Sgonzostatic int 84213237Sgonzogpioc_attach(device_t dev) 85213237Sgonzo{ 86213237Sgonzo struct gpioc_softc *sc = device_get_softc(dev); 87213237Sgonzo 88213237Sgonzo sc->sc_dev = dev; 89213237Sgonzo sc->sc_pdev = device_get_parent(dev); 90213237Sgonzo sc->sc_unit = device_get_unit(dev); 91213237Sgonzo sc->sc_ctl_dev = make_dev(&gpioc_cdevsw, sc->sc_unit, 92213237Sgonzo UID_ROOT, GID_WHEEL, 0600, "gpioc%d", sc->sc_unit); 93213237Sgonzo if (!sc->sc_ctl_dev) { 94213237Sgonzo printf("Failed to create gpioc%d", sc->sc_unit); 95213237Sgonzo return (ENXIO); 96213237Sgonzo } 97213237Sgonzo sc->sc_ctl_dev->si_drv1 = sc; 98213237Sgonzo 99213237Sgonzo return (0); 100213237Sgonzo} 101213237Sgonzo 102213237Sgonzostatic int 103213237Sgonzogpioc_detach(device_t dev) 104213237Sgonzo{ 105213237Sgonzo struct gpioc_softc *sc = device_get_softc(dev); 106213237Sgonzo int err; 107213237Sgonzo 108213237Sgonzo if (sc->sc_ctl_dev); 109213237Sgonzo destroy_dev(sc->sc_ctl_dev); 110213237Sgonzo 111213237Sgonzo if ((err = bus_generic_detach(dev)) != 0) 112213237Sgonzo return (err); 113213237Sgonzo 114213237Sgonzo return (0); 115213237Sgonzo} 116213237Sgonzo 117213237Sgonzostatic int 118213237Sgonzogpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 119213237Sgonzo struct thread *td) 120213237Sgonzo{ 121213237Sgonzo int max_pin, res; 122213237Sgonzo struct gpioc_softc *sc = cdev->si_drv1; 123213237Sgonzo struct gpio_pin pin; 124213237Sgonzo struct gpio_req req; 125213237Sgonzo 126213237Sgonzo switch (cmd) { 127213237Sgonzo case GPIOMAXPIN: 128213237Sgonzo max_pin = -1; 129213237Sgonzo res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 130213237Sgonzo bcopy(&max_pin, arg, sizeof(max_pin)); 131213237Sgonzo break; 132213237Sgonzo case GPIOGETCONFIG: 133213237Sgonzo bcopy(arg, &pin, sizeof(pin)); 134213237Sgonzo dprintf("get config pin %d\n", pin.gp_pin); 135213237Sgonzo res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 136213237Sgonzo &pin.gp_flags); 137213237Sgonzo /* Fail early */ 138213237Sgonzo if (res) 139213237Sgonzo break; 140213237Sgonzo GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 141213237Sgonzo GPIO_PIN_GETNAME(sc->sc_pdev, pin.gp_pin, pin.gp_name); 142213237Sgonzo bcopy(&pin, arg, sizeof(pin)); 143213237Sgonzo break; 144213237Sgonzo case GPIOSETCONFIG: 145213237Sgonzo bcopy(arg, &pin, sizeof(pin)); 146213237Sgonzo dprintf("set config pin %d\n", pin.gp_pin); 147213237Sgonzo res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 148213237Sgonzo pin.gp_flags); 149213237Sgonzo break; 150213237Sgonzo case GPIOGET: 151213237Sgonzo bcopy(arg, &req, sizeof(req)); 152213237Sgonzo res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 153213237Sgonzo &req.gp_value); 154213237Sgonzo dprintf("read pin %d -> %d\n", 155213237Sgonzo req.gp_pin, req.gp_value); 156213237Sgonzo bcopy(&req, arg, sizeof(req)); 157213237Sgonzo break; 158213237Sgonzo case GPIOSET: 159213237Sgonzo bcopy(arg, &req, sizeof(req)); 160213237Sgonzo res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 161213237Sgonzo req.gp_value); 162213237Sgonzo dprintf("write pin %d -> %d\n", 163213237Sgonzo req.gp_pin, req.gp_value); 164213237Sgonzo break; 165213237Sgonzo case GPIOTOGGLE: 166213237Sgonzo bcopy(arg, &req, sizeof(req)); 167213237Sgonzo dprintf("toggle pin %d\n", 168213237Sgonzo req.gp_pin); 169213237Sgonzo res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 170213237Sgonzo break; 171213237Sgonzo default: 172213237Sgonzo return (ENOTTY); 173213237Sgonzo break; 174213237Sgonzo } 175213237Sgonzo 176213237Sgonzo return (res); 177213237Sgonzo} 178213237Sgonzo 179213237Sgonzostatic device_method_t gpioc_methods[] = { 180213237Sgonzo /* Device interface */ 181213237Sgonzo DEVMETHOD(device_probe, gpioc_probe), 182213237Sgonzo DEVMETHOD(device_attach, gpioc_attach), 183213237Sgonzo DEVMETHOD(device_detach, gpioc_detach), 184213237Sgonzo DEVMETHOD(device_shutdown, bus_generic_shutdown), 185213237Sgonzo DEVMETHOD(device_suspend, bus_generic_suspend), 186213237Sgonzo DEVMETHOD(device_resume, bus_generic_resume), 187213237Sgonzo 188213237Sgonzo { 0, 0 } 189213237Sgonzo}; 190213237Sgonzo 191215142Sthompsadriver_t gpioc_driver = { 192213237Sgonzo "gpioc", 193213237Sgonzo gpioc_methods, 194213237Sgonzo sizeof(struct gpioc_softc) 195213237Sgonzo}; 196213237Sgonzo 197213237Sgonzodevclass_t gpioc_devclass; 198213237Sgonzo 199213237SgonzoDRIVER_MODULE(gpioc, gpio, gpioc_driver, gpioc_devclass, 0, 0); 200213237SgonzoMODULE_VERSION(gpioc, 1); 201