gpioiic.c revision 266105
1213237Sgonzo/*- 2213237Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 3213237Sgonzo * Copyright (c) 2010 Luiz Otavio O Souza 4213237Sgonzo * All rights reserved. 5213237Sgonzo * 6213237Sgonzo * Redistribution and use in source and binary forms, with or without 7213237Sgonzo * modification, are permitted provided that the following conditions 8213237Sgonzo * are met: 9213237Sgonzo * 1. Redistributions of source code must retain the above copyright 10213237Sgonzo * notice, this list of conditions and the following disclaimer. 11213237Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12213237Sgonzo * notice, this list of conditions and the following disclaimer in the 13213237Sgonzo * documentation and/or other materials provided with the distribution. 14213237Sgonzo * 15213277Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16213277Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17213277Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18213277Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19213277Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20213277Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21213277Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22213277Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23213277Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24213277Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25213277Sgonzo * SUCH DAMAGE. 26213237Sgonzo */ 27213237Sgonzo 28213237Sgonzo#include <sys/cdefs.h> 29213237Sgonzo__FBSDID("$FreeBSD: stable/10/sys/dev/gpio/gpioiic.c 266105 2014-05-15 01:27:53Z loos $"); 30213237Sgonzo 31266105Sloos#include "opt_platform.h" 32266105Sloos 33213237Sgonzo#include <sys/param.h> 34213237Sgonzo#include <sys/systm.h> 35213237Sgonzo#include <sys/bus.h> 36213237Sgonzo#include <sys/conf.h> 37213237Sgonzo#include <sys/kernel.h> 38213237Sgonzo#include <sys/module.h> 39213237Sgonzo 40213237Sgonzo#include <sys/gpio.h> 41213237Sgonzo#include "gpiobus_if.h" 42213237Sgonzo 43266105Sloos#ifdef FDT 44266105Sloos#include <dev/ofw/ofw_bus.h> 45266105Sloos#include <dev/ofw/ofw_bus_subr.h> 46266105Sloos#include <dev/fdt/fdt_common.h> 47266105Sloos#endif 48266105Sloos 49213237Sgonzo#include <dev/iicbus/iiconf.h> 50213237Sgonzo#include <dev/iicbus/iicbus.h> 51213237Sgonzo 52213237Sgonzo#include "iicbb_if.h" 53213237Sgonzo 54228258Sadrian#define SCL_PIN_DEFAULT 0 /* default index of SCL pin on gpiobus */ 55228258Sadrian#define SDA_PIN_DEFAULT 1 56213237Sgonzo 57213237Sgonzostruct gpioiic_softc 58213237Sgonzo{ 59213237Sgonzo device_t sc_dev; 60213237Sgonzo device_t sc_busdev; 61228258Sadrian int scl_pin; 62228258Sadrian int sda_pin; 63213237Sgonzo}; 64213237Sgonzo 65213237Sgonzostatic int gpioiic_probe(device_t); 66213237Sgonzostatic int gpioiic_attach(device_t); 67213237Sgonzo 68213237Sgonzo/* iicbb interface */ 69213237Sgonzostatic void gpioiic_reset_bus(device_t); 70213237Sgonzostatic int gpioiic_callback(device_t, int, caddr_t); 71213237Sgonzostatic void gpioiic_setsda(device_t, int); 72213237Sgonzostatic void gpioiic_setscl(device_t, int); 73213237Sgonzostatic int gpioiic_getsda(device_t); 74213237Sgonzostatic int gpioiic_getscl(device_t); 75213237Sgonzostatic int gpioiic_reset(device_t, u_char, u_char, u_char *); 76213237Sgonzo 77213237Sgonzo 78213237Sgonzostatic int 79213237Sgonzogpioiic_probe(device_t dev) 80213237Sgonzo{ 81213237Sgonzo 82266105Sloos#ifdef FDT 83266105Sloos if (!ofw_bus_is_compatible(dev, "gpioiic")) 84266105Sloos return (ENXIO); 85266105Sloos#endif 86213237Sgonzo device_set_desc(dev, "GPIO I2C bit-banging driver"); 87266105Sloos 88213237Sgonzo return (0); 89213237Sgonzo} 90213237Sgonzo 91213237Sgonzostatic int 92213237Sgonzogpioiic_attach(device_t dev) 93213237Sgonzo{ 94213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 95213237Sgonzo device_t bitbang; 96266105Sloos#ifdef FDT 97266105Sloos phandle_t node; 98266105Sloos pcell_t pin; 99266105Sloos#endif 100213237Sgonzo 101213237Sgonzo sc->sc_dev = dev; 102213237Sgonzo sc->sc_busdev = device_get_parent(dev); 103228258Sadrian if (resource_int_value(device_get_name(dev), 104228258Sadrian device_get_unit(dev), "scl", &sc->scl_pin)) 105228258Sadrian sc->scl_pin = SCL_PIN_DEFAULT; 106228258Sadrian if (resource_int_value(device_get_name(dev), 107228258Sadrian device_get_unit(dev), "sda", &sc->sda_pin)) 108228258Sadrian sc->sda_pin = SDA_PIN_DEFAULT; 109213237Sgonzo 110266105Sloos#ifdef FDT 111266105Sloos if ((node = ofw_bus_get_node(dev)) == -1) 112266105Sloos return (ENXIO); 113266105Sloos if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0) 114266105Sloos sc->scl_pin = (int)pin; 115266105Sloos if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0) 116266105Sloos sc->sda_pin = (int)pin; 117266105Sloos#endif 118266105Sloos 119213237Sgonzo /* add generic bit-banging code */ 120213237Sgonzo bitbang = device_add_child(dev, "iicbb", -1); 121213237Sgonzo device_probe_and_attach(bitbang); 122213237Sgonzo 123213237Sgonzo return (0); 124213237Sgonzo} 125213237Sgonzo 126213237Sgonzo/* 127213237Sgonzo * Reset bus by setting SDA first and then SCL. 128213237Sgonzo * Must always be called with gpio bus locked. 129213237Sgonzo */ 130213237Sgonzostatic void 131213237Sgonzogpioiic_reset_bus(device_t dev) 132213237Sgonzo{ 133213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 134213237Sgonzo 135228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 136213237Sgonzo GPIO_PIN_INPUT); 137228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 138213237Sgonzo GPIO_PIN_INPUT); 139213237Sgonzo} 140213237Sgonzo 141213237Sgonzostatic int 142213237Sgonzogpioiic_callback(device_t dev, int index, caddr_t data) 143213237Sgonzo{ 144213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 145213237Sgonzo int error = 0; 146213237Sgonzo 147213237Sgonzo switch (index) { 148213237Sgonzo case IIC_REQUEST_BUS: 149213237Sgonzo GPIOBUS_LOCK_BUS(sc->sc_busdev); 150213237Sgonzo GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); 151213237Sgonzo GPIOBUS_UNLOCK_BUS(sc->sc_busdev); 152213237Sgonzo break; 153213237Sgonzo case IIC_RELEASE_BUS: 154213237Sgonzo GPIOBUS_LOCK_BUS(sc->sc_busdev); 155213237Sgonzo GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); 156213237Sgonzo GPIOBUS_UNLOCK_BUS(sc->sc_busdev); 157213237Sgonzo break; 158213237Sgonzo default: 159213237Sgonzo error = EINVAL; 160213237Sgonzo } 161213237Sgonzo 162266105Sloos return (error); 163213237Sgonzo} 164213237Sgonzo 165213237Sgonzostatic void 166213237Sgonzogpioiic_setsda(device_t dev, int val) 167213237Sgonzo{ 168213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 169213237Sgonzo 170213237Sgonzo if (val == 0) { 171228258Sadrian GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0); 172228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 173213237Sgonzo GPIO_PIN_OUTPUT); 174213237Sgonzo } else { 175228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 176213237Sgonzo GPIO_PIN_INPUT); 177213237Sgonzo } 178213237Sgonzo} 179213237Sgonzo 180213237Sgonzostatic void 181213237Sgonzogpioiic_setscl(device_t dev, int val) 182213237Sgonzo{ 183213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 184213237Sgonzo 185213237Sgonzo if (val == 0) { 186228258Sadrian GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0); 187228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 188213237Sgonzo GPIO_PIN_OUTPUT); 189213237Sgonzo } else { 190228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 191213237Sgonzo GPIO_PIN_INPUT); 192213237Sgonzo } 193213237Sgonzo} 194213237Sgonzo 195213237Sgonzostatic int 196213237Sgonzogpioiic_getscl(device_t dev) 197213237Sgonzo{ 198213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 199213237Sgonzo unsigned int val; 200213237Sgonzo 201228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 202213237Sgonzo GPIO_PIN_INPUT); 203228258Sadrian GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val); 204213237Sgonzo 205213237Sgonzo return ((int)val); 206213237Sgonzo} 207213237Sgonzo 208213237Sgonzostatic int 209213237Sgonzogpioiic_getsda(device_t dev) 210213237Sgonzo{ 211213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 212213237Sgonzo unsigned int val; 213213237Sgonzo 214228258Sadrian GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 215213237Sgonzo GPIO_PIN_INPUT); 216228258Sadrian GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val); 217213237Sgonzo 218213237Sgonzo return ((int)val); 219213237Sgonzo} 220213237Sgonzo 221213237Sgonzostatic int 222213237Sgonzogpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 223213237Sgonzo{ 224213237Sgonzo struct gpioiic_softc *sc = device_get_softc(dev); 225213237Sgonzo 226228729Sadrian GPIOBUS_LOCK_BUS(sc->sc_busdev); 227213237Sgonzo GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev); 228213237Sgonzo 229213237Sgonzo gpioiic_reset_bus(sc->sc_dev); 230213237Sgonzo 231213237Sgonzo GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); 232228729Sadrian GPIOBUS_UNLOCK_BUS(sc->sc_busdev); 233213237Sgonzo 234213237Sgonzo return (IIC_ENOADDR); 235213237Sgonzo} 236213237Sgonzo 237266105Sloos#ifdef FDT 238266105Sloosstatic phandle_t 239266105Sloosgpioiic_get_node(device_t bus, device_t dev) 240266105Sloos{ 241266105Sloos 242266105Sloos /* We only have one child, the iicbb, which needs our own node. */ 243266105Sloos return (ofw_bus_get_node(bus)); 244266105Sloos} 245266105Sloos#endif 246266105Sloos 247213237Sgonzostatic devclass_t gpioiic_devclass; 248213237Sgonzo 249213237Sgonzostatic device_method_t gpioiic_methods[] = { 250213237Sgonzo /* Device interface */ 251213237Sgonzo DEVMETHOD(device_probe, gpioiic_probe), 252213237Sgonzo DEVMETHOD(device_attach, gpioiic_attach), 253213237Sgonzo DEVMETHOD(device_detach, bus_generic_detach), 254213237Sgonzo 255213237Sgonzo /* iicbb interface */ 256213237Sgonzo DEVMETHOD(iicbb_callback, gpioiic_callback), 257213237Sgonzo DEVMETHOD(iicbb_setsda, gpioiic_setsda), 258213237Sgonzo DEVMETHOD(iicbb_setscl, gpioiic_setscl), 259213237Sgonzo DEVMETHOD(iicbb_getsda, gpioiic_getsda), 260213237Sgonzo DEVMETHOD(iicbb_getscl, gpioiic_getscl), 261213237Sgonzo DEVMETHOD(iicbb_reset, gpioiic_reset), 262213237Sgonzo 263266105Sloos#ifdef FDT 264266105Sloos /* OFW bus interface */ 265266105Sloos DEVMETHOD(ofw_bus_get_node, gpioiic_get_node), 266266105Sloos#endif 267266105Sloos 268213237Sgonzo { 0, 0 } 269213237Sgonzo}; 270213237Sgonzo 271213237Sgonzostatic driver_t gpioiic_driver = { 272213237Sgonzo "gpioiic", 273213237Sgonzo gpioiic_methods, 274213237Sgonzo sizeof(struct gpioiic_softc), 275213237Sgonzo}; 276213237Sgonzo 277213237SgonzoDRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0); 278213237SgonzoDRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0); 279213237SgonzoMODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 280213237SgonzoMODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1); 281