1*6354154eSAndriy Gapon /*- 2*6354154eSAndriy Gapon * SPDX-License-Identifier: BSD-2-Clause 3*6354154eSAndriy Gapon * 4*6354154eSAndriy Gapon * Copyright (c) Andriy Gapon 5*6354154eSAndriy Gapon * 6*6354154eSAndriy Gapon * Redistribution and use in source and binary forms, with or without 7*6354154eSAndriy Gapon * modification, are permitted provided that the following conditions 8*6354154eSAndriy Gapon * are met: 9*6354154eSAndriy Gapon * 1. Redistributions of source code must retain the above copyright 10*6354154eSAndriy Gapon * notice, this list of conditions, and the following disclaimer. 11*6354154eSAndriy Gapon * 2. Redistributions in binary form must reproduce the above copyright 12*6354154eSAndriy Gapon * notice, this list of conditions and the following disclaimer in the 13*6354154eSAndriy Gapon * documentation and/or other materials provided with the distribution. 14*6354154eSAndriy Gapon * 15*6354154eSAndriy Gapon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*6354154eSAndriy Gapon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*6354154eSAndriy Gapon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*6354154eSAndriy Gapon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19*6354154eSAndriy Gapon * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*6354154eSAndriy Gapon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*6354154eSAndriy Gapon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*6354154eSAndriy Gapon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*6354154eSAndriy Gapon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*6354154eSAndriy Gapon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*6354154eSAndriy Gapon * SUCH DAMAGE. 26*6354154eSAndriy Gapon * 27*6354154eSAndriy Gapon */ 28*6354154eSAndriy Gapon 29*6354154eSAndriy Gapon /* 30*6354154eSAndriy Gapon * Driver for PCF8574 / PCF8574A 8-bit I/O expander 31*6354154eSAndriy Gapon * with quasi-bidirectional I/O. 32*6354154eSAndriy Gapon * There is no separate mode / configuration register. 33*6354154eSAndriy Gapon * Pins are set and queried via a single register. 34*6354154eSAndriy Gapon * Because of that we have to maintain the state in the driver 35*6354154eSAndriy Gapon * and assume that there is no outside meddling with the device. 36*6354154eSAndriy Gapon * See the datasheet for details. 37*6354154eSAndriy Gapon */ 38*6354154eSAndriy Gapon 39*6354154eSAndriy Gapon #include <sys/cdefs.h> 40*6354154eSAndriy Gapon __FBSDID("$FreeBSD$"); 41*6354154eSAndriy Gapon 42*6354154eSAndriy Gapon #include "opt_platform.h" 43*6354154eSAndriy Gapon 44*6354154eSAndriy Gapon #include <sys/param.h> 45*6354154eSAndriy Gapon #include <sys/bus.h> 46*6354154eSAndriy Gapon #include <sys/gpio.h> 47*6354154eSAndriy Gapon #include <sys/kernel.h> 48*6354154eSAndriy Gapon #include <sys/module.h> 49*6354154eSAndriy Gapon #include <sys/systm.h> 50*6354154eSAndriy Gapon #include <sys/sx.h> 51*6354154eSAndriy Gapon 52*6354154eSAndriy Gapon #ifdef FDT 53*6354154eSAndriy Gapon #include <dev/ofw/openfirm.h> 54*6354154eSAndriy Gapon #include <dev/ofw/ofw_bus.h> 55*6354154eSAndriy Gapon #include <dev/ofw/ofw_bus_subr.h> 56*6354154eSAndriy Gapon #endif 57*6354154eSAndriy Gapon 58*6354154eSAndriy Gapon #include <dev/iicbus/iicbus.h> 59*6354154eSAndriy Gapon #include <dev/iicbus/iiconf.h> 60*6354154eSAndriy Gapon 61*6354154eSAndriy Gapon #include <dev/gpio/gpiobusvar.h> 62*6354154eSAndriy Gapon 63*6354154eSAndriy Gapon #include "gpio_if.h" 64*6354154eSAndriy Gapon 65*6354154eSAndriy Gapon #define NUM_PINS 8 66*6354154eSAndriy Gapon #define PIN_CAPS (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT) 67*6354154eSAndriy Gapon 68*6354154eSAndriy Gapon #define dbg_dev_printf(dev, fmt, args...) \ 69*6354154eSAndriy Gapon if (bootverbose) device_printf(dev, fmt, ##args) 70*6354154eSAndriy Gapon 71*6354154eSAndriy Gapon struct pcf8574_softc { 72*6354154eSAndriy Gapon device_t dev; 73*6354154eSAndriy Gapon device_t busdev; 74*6354154eSAndriy Gapon struct sx lock; 75*6354154eSAndriy Gapon uint8_t addr; 76*6354154eSAndriy Gapon uint8_t output_mask; 77*6354154eSAndriy Gapon uint8_t output_state; 78*6354154eSAndriy Gapon }; 79*6354154eSAndriy Gapon 80*6354154eSAndriy Gapon #ifdef FDT 81*6354154eSAndriy Gapon static struct ofw_compat_data compat_data[] = { 82*6354154eSAndriy Gapon { "nxp,pcf8574", 1 }, 83*6354154eSAndriy Gapon { "nxp,pcf8574a", 1 }, 84*6354154eSAndriy Gapon { NULL, 0 } 85*6354154eSAndriy Gapon }; 86*6354154eSAndriy Gapon #endif 87*6354154eSAndriy Gapon 88*6354154eSAndriy Gapon static int 89*6354154eSAndriy Gapon pcf8574_read(struct pcf8574_softc *sc, uint8_t *val) 90*6354154eSAndriy Gapon { 91*6354154eSAndriy Gapon struct iic_msg msg; 92*6354154eSAndriy Gapon int error; 93*6354154eSAndriy Gapon 94*6354154eSAndriy Gapon msg.slave = sc->addr; 95*6354154eSAndriy Gapon msg.flags = IIC_M_RD; 96*6354154eSAndriy Gapon msg.len = 1; 97*6354154eSAndriy Gapon msg.buf = val; 98*6354154eSAndriy Gapon 99*6354154eSAndriy Gapon error = iicbus_transfer_excl(sc->dev, &msg, 1, IIC_WAIT); 100*6354154eSAndriy Gapon return (iic2errno(error)); 101*6354154eSAndriy Gapon } 102*6354154eSAndriy Gapon 103*6354154eSAndriy Gapon static int 104*6354154eSAndriy Gapon pcf8574_write(struct pcf8574_softc *sc, uint8_t val) 105*6354154eSAndriy Gapon { 106*6354154eSAndriy Gapon struct iic_msg msg; 107*6354154eSAndriy Gapon int error; 108*6354154eSAndriy Gapon 109*6354154eSAndriy Gapon msg.slave = sc->addr; 110*6354154eSAndriy Gapon msg.flags = IIC_M_WR; 111*6354154eSAndriy Gapon msg.len = 1; 112*6354154eSAndriy Gapon msg.buf = &val; 113*6354154eSAndriy Gapon 114*6354154eSAndriy Gapon error = iicbus_transfer_excl(sc->dev, &msg, 1, IIC_WAIT); 115*6354154eSAndriy Gapon return (iic2errno(error)); 116*6354154eSAndriy Gapon } 117*6354154eSAndriy Gapon 118*6354154eSAndriy Gapon static int 119*6354154eSAndriy Gapon pcf8574_probe(device_t dev) 120*6354154eSAndriy Gapon { 121*6354154eSAndriy Gapon 122*6354154eSAndriy Gapon #ifdef FDT 123*6354154eSAndriy Gapon if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 124*6354154eSAndriy Gapon return (ENXIO); 125*6354154eSAndriy Gapon #endif 126*6354154eSAndriy Gapon device_set_desc(dev, "PCF8574 I/O expander"); 127*6354154eSAndriy Gapon return (BUS_PROBE_DEFAULT); 128*6354154eSAndriy Gapon } 129*6354154eSAndriy Gapon 130*6354154eSAndriy Gapon static int 131*6354154eSAndriy Gapon pcf8574_attach(device_t dev) 132*6354154eSAndriy Gapon { 133*6354154eSAndriy Gapon struct pcf8574_softc *sc; 134*6354154eSAndriy Gapon 135*6354154eSAndriy Gapon sc = device_get_softc(dev); 136*6354154eSAndriy Gapon sc->dev = dev; 137*6354154eSAndriy Gapon sc->addr = iicbus_get_addr(dev); 138*6354154eSAndriy Gapon 139*6354154eSAndriy Gapon /* Treat everything as input because there is no way to tell. */ 140*6354154eSAndriy Gapon sc->output_mask = 0; 141*6354154eSAndriy Gapon sc->output_state = 0xff; 142*6354154eSAndriy Gapon 143*6354154eSAndriy Gapon /* Put the device to a safe, known state. */ 144*6354154eSAndriy Gapon (void)pcf8574_write(sc, 0xff); 145*6354154eSAndriy Gapon 146*6354154eSAndriy Gapon sx_init(&sc->lock, "pcf8574"); 147*6354154eSAndriy Gapon sc->busdev = gpiobus_attach_bus(dev); 148*6354154eSAndriy Gapon if (sc->busdev == NULL) { 149*6354154eSAndriy Gapon device_printf(dev, "Could not create busdev child\n"); 150*6354154eSAndriy Gapon sx_destroy(&sc->lock); 151*6354154eSAndriy Gapon return (ENXIO); 152*6354154eSAndriy Gapon } 153*6354154eSAndriy Gapon return (0); 154*6354154eSAndriy Gapon } 155*6354154eSAndriy Gapon 156*6354154eSAndriy Gapon static int 157*6354154eSAndriy Gapon pcf8574_detach(device_t dev) 158*6354154eSAndriy Gapon { 159*6354154eSAndriy Gapon struct pcf8574_softc *sc; 160*6354154eSAndriy Gapon 161*6354154eSAndriy Gapon sc = device_get_softc(dev); 162*6354154eSAndriy Gapon 163*6354154eSAndriy Gapon if (sc->busdev != NULL) 164*6354154eSAndriy Gapon gpiobus_detach_bus(sc->busdev); 165*6354154eSAndriy Gapon 166*6354154eSAndriy Gapon sx_destroy(&sc->lock); 167*6354154eSAndriy Gapon return (0); 168*6354154eSAndriy Gapon } 169*6354154eSAndriy Gapon 170*6354154eSAndriy Gapon static device_t 171*6354154eSAndriy Gapon pcf8574_get_bus(device_t dev) 172*6354154eSAndriy Gapon { 173*6354154eSAndriy Gapon struct pcf8574_softc *sc; 174*6354154eSAndriy Gapon 175*6354154eSAndriy Gapon sc = device_get_softc(dev); 176*6354154eSAndriy Gapon return (sc->busdev); 177*6354154eSAndriy Gapon } 178*6354154eSAndriy Gapon 179*6354154eSAndriy Gapon static int 180*6354154eSAndriy Gapon pcf8574_pin_max(device_t dev __unused, int *maxpin) 181*6354154eSAndriy Gapon { 182*6354154eSAndriy Gapon *maxpin = NUM_PINS - 1; 183*6354154eSAndriy Gapon return (0); 184*6354154eSAndriy Gapon } 185*6354154eSAndriy Gapon 186*6354154eSAndriy Gapon static int 187*6354154eSAndriy Gapon pcf8574_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 188*6354154eSAndriy Gapon { 189*6354154eSAndriy Gapon 190*6354154eSAndriy Gapon if (pin >= NUM_PINS) 191*6354154eSAndriy Gapon return (EINVAL); 192*6354154eSAndriy Gapon *caps = PIN_CAPS; 193*6354154eSAndriy Gapon return (0); 194*6354154eSAndriy Gapon } 195*6354154eSAndriy Gapon 196*6354154eSAndriy Gapon static int 197*6354154eSAndriy Gapon pcf8574_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags) 198*6354154eSAndriy Gapon { 199*6354154eSAndriy Gapon struct pcf8574_softc *sc; 200*6354154eSAndriy Gapon uint8_t val, stale; 201*6354154eSAndriy Gapon int error; 202*6354154eSAndriy Gapon 203*6354154eSAndriy Gapon sc = device_get_softc(dev); 204*6354154eSAndriy Gapon 205*6354154eSAndriy Gapon if (pin >= NUM_PINS) 206*6354154eSAndriy Gapon return (EINVAL); 207*6354154eSAndriy Gapon 208*6354154eSAndriy Gapon sx_xlock(&sc->lock); 209*6354154eSAndriy Gapon error = pcf8574_read(sc, &val); 210*6354154eSAndriy Gapon if (error != 0) { 211*6354154eSAndriy Gapon dbg_dev_printf(dev, "failed to read from device: %d\n", 212*6354154eSAndriy Gapon error); 213*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 214*6354154eSAndriy Gapon return (error); 215*6354154eSAndriy Gapon } 216*6354154eSAndriy Gapon 217*6354154eSAndriy Gapon /* 218*6354154eSAndriy Gapon * Check for pins whose read value is one, but they are configured 219*6354154eSAndriy Gapon * as outputs with low signal. This is an impossible combination, 220*6354154eSAndriy Gapon * so change their view to be inputs. 221*6354154eSAndriy Gapon */ 222*6354154eSAndriy Gapon stale = val & sc->output_mask & ~sc->output_state; 223*6354154eSAndriy Gapon sc->output_mask &= ~stale; 224*6354154eSAndriy Gapon sc->output_state |= stale; 225*6354154eSAndriy Gapon 226*6354154eSAndriy Gapon if ((sc->output_mask & (1 << pin)) != 0) 227*6354154eSAndriy Gapon *pflags = GPIO_PIN_OUTPUT; 228*6354154eSAndriy Gapon else 229*6354154eSAndriy Gapon *pflags = GPIO_PIN_INPUT; 230*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 231*6354154eSAndriy Gapon 232*6354154eSAndriy Gapon return (0); 233*6354154eSAndriy Gapon } 234*6354154eSAndriy Gapon 235*6354154eSAndriy Gapon static int 236*6354154eSAndriy Gapon pcf8574_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 237*6354154eSAndriy Gapon { 238*6354154eSAndriy Gapon struct pcf8574_softc *sc; 239*6354154eSAndriy Gapon int error; 240*6354154eSAndriy Gapon uint8_t val; 241*6354154eSAndriy Gapon bool update_needed; 242*6354154eSAndriy Gapon 243*6354154eSAndriy Gapon sc = device_get_softc(dev); 244*6354154eSAndriy Gapon 245*6354154eSAndriy Gapon if (pin >= NUM_PINS) 246*6354154eSAndriy Gapon return (EINVAL); 247*6354154eSAndriy Gapon if ((flags & ~PIN_CAPS) != 0) 248*6354154eSAndriy Gapon return (EINVAL); 249*6354154eSAndriy Gapon 250*6354154eSAndriy Gapon sx_xlock(&sc->lock); 251*6354154eSAndriy Gapon if ((flags & GPIO_PIN_OUTPUT) != 0) { 252*6354154eSAndriy Gapon sc->output_mask |= 1 << pin; 253*6354154eSAndriy Gapon update_needed = false; 254*6354154eSAndriy Gapon } else if ((flags & GPIO_PIN_INPUT) != 0) { 255*6354154eSAndriy Gapon sc->output_mask &= ~(1 << pin); 256*6354154eSAndriy Gapon sc->output_state |= 1 << pin; 257*6354154eSAndriy Gapon update_needed = true; 258*6354154eSAndriy Gapon } else { 259*6354154eSAndriy Gapon KASSERT(false, ("both input and output modes requested")); 260*6354154eSAndriy Gapon update_needed = false; 261*6354154eSAndriy Gapon } 262*6354154eSAndriy Gapon 263*6354154eSAndriy Gapon if (update_needed) { 264*6354154eSAndriy Gapon val = sc->output_state | ~sc->output_mask; 265*6354154eSAndriy Gapon error = pcf8574_write(sc, val); 266*6354154eSAndriy Gapon if (error != 0) 267*6354154eSAndriy Gapon dbg_dev_printf(dev, "failed to write to device: %d\n", 268*6354154eSAndriy Gapon error); 269*6354154eSAndriy Gapon } 270*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 271*6354154eSAndriy Gapon 272*6354154eSAndriy Gapon return (0); 273*6354154eSAndriy Gapon } 274*6354154eSAndriy Gapon 275*6354154eSAndriy Gapon static int 276*6354154eSAndriy Gapon pcf8574_pin_getname(device_t dev, uint32_t pin, char *name) 277*6354154eSAndriy Gapon { 278*6354154eSAndriy Gapon 279*6354154eSAndriy Gapon if (pin >= NUM_PINS) 280*6354154eSAndriy Gapon return (EINVAL); 281*6354154eSAndriy Gapon snprintf(name, GPIOMAXNAME, "P%d", pin); 282*6354154eSAndriy Gapon return (0); 283*6354154eSAndriy Gapon } 284*6354154eSAndriy Gapon 285*6354154eSAndriy Gapon static int 286*6354154eSAndriy Gapon pcf8574_pin_get(device_t dev, uint32_t pin, unsigned int *on) 287*6354154eSAndriy Gapon { 288*6354154eSAndriy Gapon struct pcf8574_softc *sc; 289*6354154eSAndriy Gapon uint8_t val; 290*6354154eSAndriy Gapon int error; 291*6354154eSAndriy Gapon 292*6354154eSAndriy Gapon sc = device_get_softc(dev); 293*6354154eSAndriy Gapon 294*6354154eSAndriy Gapon sx_xlock(&sc->lock); 295*6354154eSAndriy Gapon if ((sc->output_mask & (1 << pin)) != 0) { 296*6354154eSAndriy Gapon *on = (sc->output_state & (1 << pin)) != 0; 297*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 298*6354154eSAndriy Gapon return (0); 299*6354154eSAndriy Gapon } 300*6354154eSAndriy Gapon 301*6354154eSAndriy Gapon error = pcf8574_read(sc, &val); 302*6354154eSAndriy Gapon if (error != 0) { 303*6354154eSAndriy Gapon dbg_dev_printf(dev, "failed to read from device: %d\n", error); 304*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 305*6354154eSAndriy Gapon return (error); 306*6354154eSAndriy Gapon } 307*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 308*6354154eSAndriy Gapon 309*6354154eSAndriy Gapon *on = (val & (1 << pin)) != 0; 310*6354154eSAndriy Gapon return (0); 311*6354154eSAndriy Gapon } 312*6354154eSAndriy Gapon 313*6354154eSAndriy Gapon static int 314*6354154eSAndriy Gapon pcf8574_pin_set(device_t dev, uint32_t pin, unsigned int on) 315*6354154eSAndriy Gapon { 316*6354154eSAndriy Gapon struct pcf8574_softc *sc; 317*6354154eSAndriy Gapon uint8_t val; 318*6354154eSAndriy Gapon int error; 319*6354154eSAndriy Gapon 320*6354154eSAndriy Gapon sc = device_get_softc(dev); 321*6354154eSAndriy Gapon 322*6354154eSAndriy Gapon if (pin >= NUM_PINS) 323*6354154eSAndriy Gapon return (EINVAL); 324*6354154eSAndriy Gapon 325*6354154eSAndriy Gapon sx_xlock(&sc->lock); 326*6354154eSAndriy Gapon 327*6354154eSAndriy Gapon if ((sc->output_mask & (1 << pin)) == 0) { 328*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 329*6354154eSAndriy Gapon return (EINVAL); 330*6354154eSAndriy Gapon } 331*6354154eSAndriy Gapon 332*6354154eSAndriy Gapon /* 333*6354154eSAndriy Gapon * Algorithm: 334*6354154eSAndriy Gapon * - set all outputs to their recorded state; 335*6354154eSAndriy Gapon * - set all inputs to the high state; 336*6354154eSAndriy Gapon * - apply the requested change. 337*6354154eSAndriy Gapon */ 338*6354154eSAndriy Gapon val = sc->output_state | ~sc->output_mask; 339*6354154eSAndriy Gapon val &= ~(1 << pin); 340*6354154eSAndriy Gapon val |= (on != 0) << pin; 341*6354154eSAndriy Gapon 342*6354154eSAndriy Gapon error = pcf8574_write(sc, val); 343*6354154eSAndriy Gapon if (error != 0) { 344*6354154eSAndriy Gapon dbg_dev_printf(dev, "failed to write to device: %d\n", error); 345*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 346*6354154eSAndriy Gapon return (error); 347*6354154eSAndriy Gapon } 348*6354154eSAndriy Gapon 349*6354154eSAndriy Gapon /* 350*6354154eSAndriy Gapon * NB: we can record anything as "output" state of input pins. 351*6354154eSAndriy Gapon * By convention and for convenience it will be recorded as 1. 352*6354154eSAndriy Gapon */ 353*6354154eSAndriy Gapon sc->output_state = val; 354*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 355*6354154eSAndriy Gapon return (0); 356*6354154eSAndriy Gapon } 357*6354154eSAndriy Gapon 358*6354154eSAndriy Gapon static int 359*6354154eSAndriy Gapon pcf8574_pin_toggle(device_t dev, uint32_t pin) 360*6354154eSAndriy Gapon { 361*6354154eSAndriy Gapon struct pcf8574_softc *sc; 362*6354154eSAndriy Gapon uint8_t val; 363*6354154eSAndriy Gapon int error; 364*6354154eSAndriy Gapon 365*6354154eSAndriy Gapon sc = device_get_softc(dev); 366*6354154eSAndriy Gapon 367*6354154eSAndriy Gapon if (pin >= NUM_PINS) 368*6354154eSAndriy Gapon return (EINVAL); 369*6354154eSAndriy Gapon 370*6354154eSAndriy Gapon sx_xlock(&sc->lock); 371*6354154eSAndriy Gapon 372*6354154eSAndriy Gapon if ((sc->output_mask & (1 << pin)) == 0) { 373*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 374*6354154eSAndriy Gapon return (EINVAL); 375*6354154eSAndriy Gapon } 376*6354154eSAndriy Gapon 377*6354154eSAndriy Gapon val = sc->output_state | ~sc->output_mask; 378*6354154eSAndriy Gapon val ^= 1 << pin; 379*6354154eSAndriy Gapon 380*6354154eSAndriy Gapon error = pcf8574_write(sc, val); 381*6354154eSAndriy Gapon if (error != 0) { 382*6354154eSAndriy Gapon dbg_dev_printf(dev, "failed to write to device: %d\n", error); 383*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 384*6354154eSAndriy Gapon return (error); 385*6354154eSAndriy Gapon } 386*6354154eSAndriy Gapon 387*6354154eSAndriy Gapon sc->output_state = val; 388*6354154eSAndriy Gapon sx_xunlock(&sc->lock); 389*6354154eSAndriy Gapon return (0); 390*6354154eSAndriy Gapon } 391*6354154eSAndriy Gapon 392*6354154eSAndriy Gapon static device_method_t pcf8574_methods[] = { 393*6354154eSAndriy Gapon DEVMETHOD(device_probe, pcf8574_probe), 394*6354154eSAndriy Gapon DEVMETHOD(device_attach, pcf8574_attach), 395*6354154eSAndriy Gapon DEVMETHOD(device_detach, pcf8574_detach), 396*6354154eSAndriy Gapon 397*6354154eSAndriy Gapon /* GPIO methods */ 398*6354154eSAndriy Gapon DEVMETHOD(gpio_get_bus, pcf8574_get_bus), 399*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_max, pcf8574_pin_max), 400*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_getcaps, pcf8574_pin_getcaps), 401*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_getflags, pcf8574_pin_getflags), 402*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_setflags, pcf8574_pin_setflags), 403*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_getname, pcf8574_pin_getname), 404*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_get, pcf8574_pin_get), 405*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_set, pcf8574_pin_set), 406*6354154eSAndriy Gapon DEVMETHOD(gpio_pin_toggle, pcf8574_pin_toggle), 407*6354154eSAndriy Gapon 408*6354154eSAndriy Gapon DEVMETHOD_END 409*6354154eSAndriy Gapon }; 410*6354154eSAndriy Gapon 411*6354154eSAndriy Gapon static driver_t pcf8574_driver = { 412*6354154eSAndriy Gapon "gpio", 413*6354154eSAndriy Gapon pcf8574_methods, 414*6354154eSAndriy Gapon sizeof(struct pcf8574_softc) 415*6354154eSAndriy Gapon }; 416*6354154eSAndriy Gapon 417*6354154eSAndriy Gapon static devclass_t pcf8574_devclass; 418*6354154eSAndriy Gapon 419*6354154eSAndriy Gapon DRIVER_MODULE(pcf8574, iicbus, pcf8574_driver, pcf8574_devclass, 0, 0); 420*6354154eSAndriy Gapon MODULE_DEPEND(pcf8574, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 421*6354154eSAndriy Gapon MODULE_DEPEND(pcf8574, gpiobus, 1, 1, 1); 422*6354154eSAndriy Gapon MODULE_VERSION(pcf8574, 1); 423*6354154eSAndriy Gapon #ifdef FDT 424*6354154eSAndriy Gapon IICBUS_FDT_PNP_INFO(compat_data); 425*6354154eSAndriy Gapon #endif 426