root/sys/dev/iicbus/controller/qcom/geni_iic.c
/*
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2025 Poul-Henning Kamp <phk@FreeBSD.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * QualComm GENI I2C controller
 *
 * The GENI is actually a multi-protocol serial controller, so a lot of
 * this can probably be shared if we ever get to those protocols.
 *
 * The best open "documentation" of the hardware is the Linux device driver
 * from which much was learned, and we tip our hat to the authors of it.
 */

#include <sys/cdefs.h>

#include "opt_acpi.h"

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/endian.h>
#include <sys/time.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/bus.h>

#include <machine/bus.h>
#include <sys/rman.h>

#include <dev/iicbus/iicbus.h>
#include <dev/iicbus/iiconf.h>

#include <dev/iicbus/controller/qcom/geni_iic_var.h>

#define GENI_ALL_REGISTERS(THIS_MACRO) \
        THIS_MACRO(GENI_FORCE_DEFAULT_REG,      0x020) \
        THIS_MACRO(GENI_OUTPUT_CTRL,            0x024) \
        THIS_MACRO(GENI_STATUS,                 0x040) \
        THIS_MACRO(GENI_SER_M_CLK_CFG,          0x048) \
        THIS_MACRO(GENI_SER_S_CLK_CFG,          0x04c) \
        THIS_MACRO(GENI_IF_DISABLE_RO,          0x064) \
        THIS_MACRO(GENI_FW_REVISION_RO,         0x068) \
        THIS_MACRO(GENI_CLK_SEL,                0x07c) \
        THIS_MACRO(GENI_CFG_SEQ_START,          0x084) \
        THIS_MACRO(GENI_BYTE_GRANULARITY,       0x254) \
        THIS_MACRO(GENI_DMA_MODE_EN,            0x258) \
        THIS_MACRO(GENI_TX_PACKING_CFG0,        0x260) \
        THIS_MACRO(GENI_TX_PACKING_CFG1,        0x264) \
        THIS_MACRO(GENI_I2C_TX_TRANS_LEN,       0x26c) \
        THIS_MACRO(GENI_I2C_RX_TRANS_LEN,       0x270) \
        THIS_MACRO(GENI_I2C_SCL_COUNTERS,       0x278) \
        THIS_MACRO(GENI_RX_PACKING_CFG0,        0x284) \
        THIS_MACRO(GENI_RX_PACKING_CFG1,        0x288) \
        THIS_MACRO(GENI_M_CMD0,                 0x600) \
        THIS_MACRO(GENI_M_CMD_CTRL_REG,         0x604) \
        THIS_MACRO(GENI_M_IRQ_STATUS,           0x610) \
        THIS_MACRO(GENI_M_IRQ_EN,               0x614) \
        THIS_MACRO(GENI_M_IRQ_CLEAR,            0x618) \
        THIS_MACRO(GENI_M_IRQ_EN_SET,           0x61c) \
        THIS_MACRO(GENI_M_IRQ_EN_CLEAR,         0x620) \
        THIS_MACRO(GENI_S_CMD0,                 0x630) \
        THIS_MACRO(GENI_S_CMD_CTRL_REG,         0x634) \
        THIS_MACRO(GENI_S_IRQ_STATUS,           0x640) \
        THIS_MACRO(GENI_S_IRQ_EN,               0x644) \
        THIS_MACRO(GENI_S_IRQ_CLEAR,            0x648) \
        THIS_MACRO(GENI_S_IRQ_EN_SET,           0x64c) \
        THIS_MACRO(GENI_S_IRQ_EN_CLEAR,         0x650) \
        THIS_MACRO(GENI_TX_FIFOn,               0x700) \
        THIS_MACRO(GENI_RX_FIFOn,               0x780) \
        THIS_MACRO(GENI_TX_FIFO_STATUS,         0x800) \
        THIS_MACRO(GENI_RX_FIFO_STATUS,         0x804) \
        THIS_MACRO(GENI_TX_WATERMARK_REG,       0x80c) \
        THIS_MACRO(GENI_RX_WATERMARK_REG,       0x810) \
        THIS_MACRO(GENI_RX_RFR_WATERMARK_REG,   0x814) \
        THIS_MACRO(GENI_IOS,                    0x908) \
        THIS_MACRO(GENI_M_GP_LENGTH,            0x910) \
        THIS_MACRO(GENI_S_GP_LENGTH,            0x914) \
        THIS_MACRO(GENI_DMA_TX_IRQ_STAT,        0xc40) \
        THIS_MACRO(GENI_DMA_TX_IRQ_CLR,         0xc44) \
        THIS_MACRO(GENI_DMA_TX_IRQ_EN,          0xc48) \
        THIS_MACRO(GENI_DMA_TX_IRQ_EN_CLR,      0xc4c) \
        THIS_MACRO(GENI_DMA_TX_IRQ_EN_SET,      0xc50) \
        THIS_MACRO(GENI_DMA_TX_FSM_RST,         0xc58) \
        THIS_MACRO(GENI_DMA_RX_IRQ_STAT,        0xd40) \
        THIS_MACRO(GENI_DMA_RX_IRQ_CLR,         0xd44) \
        THIS_MACRO(GENI_DMA_RX_IRQ_EN,          0xd48) \
        THIS_MACRO(GENI_DMA_RX_IRQ_EN_CLR,      0xd4c) \
        THIS_MACRO(GENI_DMA_RX_IRQ_EN_SET,      0xd50) \
        THIS_MACRO(GENI_DMA_RX_LEN_IN,          0xd54) \
        THIS_MACRO(GENI_DMA_RX_FSM_RST,         0xd58) \
        THIS_MACRO(GENI_IRQ_EN,                 0xe1c) \
        THIS_MACRO(GENI_HW_PARAM_0,             0xe24) \
        THIS_MACRO(GENI_HW_PARAM_1,             0xe28)

enum geni_registers {
#define ITER_MACRO(name, offset) name = offset,
        GENI_ALL_REGISTERS(ITER_MACRO)
#undef ITER_MACRO
};

#define RD(sc, reg) bus_read_4((sc)->regs_res, reg)
#define WR(sc, reg, val) bus_write_4((sc)->regs_res, reg, val)

static void
geni_dump_regs(geniiic_softc_t *sc)
{
        device_printf(sc->dev, "Register Dump\n");
#define DUMP_MACRO(name, offset) \
        device_printf(sc->dev, \
            "    %08x %04x " #name "\n", \
            RD(sc, offset), offset);
        GENI_ALL_REGISTERS(DUMP_MACRO)
#undef DUMP_MACRO
}

static unsigned geniiic_debug_units = 0;

static SYSCTL_NODE(_hw, OID_AUTO, geniiic, CTLFLAG_RW, 0, "GENI I2C");
SYSCTL_INT(_hw_geniiic, OID_AUTO, debug_units, CTLFLAG_RWTUN,
    &geniiic_debug_units, 1, "Bitmask of units to debug");


static driver_filter_t geniiic_intr;

static int
geniiic_intr(void *cookie)
{
        uint32_t m_status, rx_fifo_status;
        int retval = FILTER_STRAY;
        geniiic_softc_t *sc = cookie;

        mtx_lock_spin(&sc->intr_lock);
        m_status = RD(sc, GENI_M_IRQ_STATUS);

        rx_fifo_status = RD(sc, GENI_RX_FIFO_STATUS);
        if (sc->rx_buf != NULL && rx_fifo_status & 0x3f) {

                // Number of whole FIFO words, each 4 bytes
                unsigned gotlen = (((rx_fifo_status & 0x3f) << 2)-1) * 4;

                // Valid bytes in the last FIFO word
                // (Field is 3 bits, we'll only ever see 0…3)
                gotlen +=  (rx_fifo_status >> 28) & 0x7;

                unsigned cnt;
                for (cnt = 0; cnt < (rx_fifo_status & 0x3f); cnt++) {
                        uint32_t data = RD(sc, GENI_RX_FIFOn);
                        unsigned u;
                        for (u = 0; u < 4 && sc->rx_len && gotlen; u++) {
                                *sc->rx_buf++ = data & 0xff;
                                data >>= 8;
                                sc->rx_len--;
                                gotlen--;
                        }
                }
        }
        if (m_status & (1<<26)) {
                WR(sc, GENI_M_IRQ_CLEAR, (1<<26));
                retval = FILTER_HANDLED;
        }

        if (m_status & (1<<0)) {
                sc->rx_complete = true;
                WR(sc, GENI_M_IRQ_EN_CLEAR, (1<<0));
                WR(sc, GENI_M_IRQ_EN_CLEAR, (1<<26));
                WR(sc, GENI_M_IRQ_CLEAR, (1<<0));
                wakeup(sc);
                retval = FILTER_HANDLED;
        }
        sc->cmd_status = m_status;

        if (sc->rx_buf == NULL) {
                device_printf(sc->dev,
                    "Interrupt m_stat %x rx_fifo_status %x retval %d\n",
                    m_status, rx_fifo_status, retval);
                WR(sc, GENI_M_IRQ_EN, 0);
                WR(sc, GENI_M_IRQ_CLEAR, m_status);
                device_printf(sc->dev,
                    "Interrupt M_IRQ_STATUS 0x%x M_IRQ_EN 0x%x\n",
                    RD(sc, GENI_M_IRQ_STATUS), RD(sc, GENI_M_IRQ_EN));
                device_printf(sc->dev,
                    "Interrupt S_IRQ_STATUS 0x%x S_IRQ_EN 0x%x\n",
                    RD(sc, GENI_S_IRQ_STATUS), RD(sc, GENI_S_IRQ_EN));
                device_printf(sc->dev,
                    "Interrupt DMA_TX_IRQ_STAT 0x%x DMA_RX_IRQ_STAT 0x%x\n",
                    RD(sc, GENI_DMA_TX_IRQ_STAT), RD(sc, GENI_DMA_RX_IRQ_STAT));
                device_printf(sc->dev,
                    "Interrupt DMA_TX_IRQ_EN 0x%x DMA_RX_IRQ_EN 0x%x\n",
                    RD(sc, GENI_DMA_TX_IRQ_EN), RD(sc, GENI_DMA_RX_IRQ_EN));
                WR(sc, GENI_DMA_TX_IRQ_EN_CLR, RD(sc, GENI_DMA_TX_IRQ_STAT));
                WR(sc, GENI_DMA_TX_IRQ_CLR, RD(sc, GENI_DMA_TX_IRQ_STAT));
                WR(sc, GENI_DMA_RX_IRQ_EN_CLR, RD(sc, GENI_DMA_RX_IRQ_STAT));
                WR(sc, GENI_DMA_RX_IRQ_CLR, RD(sc, GENI_DMA_RX_IRQ_STAT));
        }
        mtx_unlock_spin(&sc->intr_lock);
        return(retval);
}

static int
geniiic_wait_m_ireq(geniiic_softc_t *sc, uint32_t bits)
{
        uint32_t status;
        int timeout;

        for (timeout = 0; timeout < 10000; timeout++) {
                status = RD(sc, GENI_M_IRQ_STATUS);
                if (status & bits) {
                        return (0);
                }
                DELAY(10);
        }
        return (IIC_ETIMEOUT);
}

static int
geniiic_read(geniiic_softc_t *sc,
    uint8_t slave, uint8_t *buf, uint16_t len, bool nonfinal)
{
        uint32_t cmd, istatus;

        istatus = RD(sc, GENI_M_IRQ_STATUS);
        WR(sc, GENI_M_IRQ_CLEAR, istatus);

        sc->rx_complete = false;
        sc->rx_fifo = false;
        sc->rx_buf = buf;
        sc->rx_len = len;
        WR(sc, GENI_I2C_RX_TRANS_LEN, len);

        // GENI_M_CMD0_OPCODE_I2C_READ << M_OPCODE_SHFT
        cmd = (0x2 << 27);

        // GENI_M_CMD0_SLV_ADDR_SHIFT
        cmd |= slave << 9;

        if (nonfinal) {
                // GENI_M_CMD0_STOP_STRETCH
                cmd |= (1<<2);
        }
        WR(sc, GENI_RX_WATERMARK_REG, sc->rx_fifo_size - 4);

        // CMD_DONE, RX_FIFO_WATERMARK
        WR(sc, GENI_M_IRQ_EN, (1<<0) | (1<<26));

        // M_IRQ
        WR(sc, GENI_IRQ_EN, (1<<2));

        WR(sc, GENI_M_CMD0, cmd);

        mtx_lock_spin(&sc->intr_lock);
        sc->rx_fifo = false;
        unsigned msec;
        for (msec = 0; msec < 100; msec++) {
                msleep_spin_sbt(sc, &sc->intr_lock,
                    "geniwait", SBT_1MS, SBT_1MS / 10, 0);
                if (sc->rx_complete)
                        break;
        }
        if (msec > sc->worst) {
                device_printf(sc->dev,
                    "Tworst from %u to %u\n", sc->worst, msec);
                if (msec != 100)
                    sc->worst = msec;
        }

        if (!sc->rx_complete) {
                // S_GENI_CMD_CANCEL
                WR(sc, GENI_M_CMD_CTRL_REG, (1<<2));

                WR(sc, GENI_IRQ_EN, 0);
                device_printf(sc->dev,
                    "Incomplete read (residual %x)\n", sc->rx_len);
        }

        sc->rx_buf = NULL;
        len = sc->rx_len;
        sc->rx_len = 0;

        mtx_unlock_spin(&sc->intr_lock);

#define COMPLAIN(about) \
        device_printf(sc->dev, \
            "read " about " slave=0x%x len=0x%x, cmd=0x%x cmd_status=0x%x\n", \
            slave, len, cmd, sc->cmd_status \
        )

        if (geniiic_debug_units) {
                unsigned unit = device_get_unit(sc->dev);
                if (unit < 32 && geniiic_debug_units & (1<<unit) && len == 0) {
                        COMPLAIN("OK");
                        return(IIC_NOERR);
                }
        }
        if (len == 0)
                return(IIC_NOERR);

        if (sc->cmd_status & (1<<10)) {
                COMPLAIN("ESTATUS");
                return(IIC_ESTATUS);
        }
        if (len) {
                COMPLAIN("EUNDERFLOW");
                return(IIC_EUNDERFLOW);
        }
        COMPLAIN("EBUSERR");
        return (IIC_EBUSERR);
#undef COMPLAIN
}

static int
geniiic_write(geniiic_softc_t *sc,
    uint8_t slave, uint8_t *buf, uint16_t len, bool nonfinal)
{
        uint32_t status, data, cmd;
        int timeout, error;

        status = RD(sc, GENI_M_IRQ_STATUS);
        WR(sc, GENI_M_IRQ_CLEAR, status);

        WR(sc, GENI_I2C_TX_TRANS_LEN, len);

        // GENI_M_CMD0_OPCODE_I2C_WRITE << M_OPCODE_SHFT
        cmd = (0x1 << 27);

        // GENI_M_CMD0_SLV_ADDR_SHIFT
        cmd |= slave << 9;

        if (nonfinal) {
                // GENI_M_CMD0_STOP_STRETCH
                cmd |= (1<<2);
        }
        WR(sc, GENI_M_CMD0, cmd);
        for(timeout = 0; len > 0 && timeout < 100; timeout++) {
                status = RD(sc, GENI_TX_FIFO_STATUS);
                if (status < 16) {
                        data = 0;
                        if (len) { data |= *buf <<  0; buf++; len--; }
                        if (len) { data |= *buf <<  8; buf++; len--; }
                        if (len) { data |= *buf << 16; buf++; len--; }
                        if (len) { data |= *buf << 24; buf++; len--; }
                        WR(sc, GENI_TX_FIFOn, data);
                } else {
                        DELAY(10);
                }
        }

        // GENI_M_IRQ_CMD_DONE
        error = geniiic_wait_m_ireq(sc, 1);

        if (len == 0 && error == 0)
                return(IIC_NOERR);
        device_printf(sc->dev,
            "write ERR len=%d, error=%d cmd=0x%x\n", len, error, cmd);
        return (IIC_EBUSERR);
}

static void
geniiic_dumpmsg(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{
        unsigned u;

        device_printf(dev, "transfer:\n");
        for (u = 0; u < nmsgs; u++) {
                device_printf(dev,
                    "  [%d] slave=0x%x, flags=0x%x len=0x%x buf=%p\n",
                    u, msgs[u].slave, msgs[u].flags, msgs[u].len, msgs[u].buf
                );
        }
}

int
geniiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{
        geniiic_softc_t *sc = device_get_softc(dev);
        unsigned u;
        int error;

        if (sc->nfail > 4) {
                pause_sbt("geniic_fail", SBT_1S * 5, SBT_1S, 0);
                return (IIC_ERESOURCE);
        }

        sx_xlock(&sc->real_bus_lock);

        if (geniiic_debug_units) {
                unsigned unit = device_get_unit(dev);
                if (unit < 32 && geniiic_debug_units & (1<<unit)) {
                        geniiic_dumpmsg(dev, msgs, nmsgs);
                }
        }

        error = 0;
        for (u = 0; u < nmsgs; u++) {
                bool nonfinal =
                    (u < nmsgs - 1) && (msgs[u].flags & IIC_M_NOSTOP);
                unsigned slave = msgs[u].slave >> 1;
                if (msgs[u].flags & IIC_M_RD) {
                        error = geniiic_read(sc,
                            slave, msgs[u].buf, msgs[u].len, nonfinal);
                } else {
                        error = geniiic_write(sc,
                            slave, msgs[u].buf, msgs[u].len, nonfinal);
                }
        }
        if (error) {
                device_printf(dev, "transfer error %d\n", error);
                geniiic_dumpmsg(dev, msgs, nmsgs);
        }
        if (error) {
                geniiic_reset(dev, 0, 0, NULL);
        }
        if (error)
                sc->nfail++;
        else
                sc->nfail = 0;
        sx_xunlock(&sc->real_bus_lock);
        return (error);
}

int
geniiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{
        geniiic_softc_t *sc = device_get_softc(dev);
        unsigned u;

        device_printf(dev, "reset\n");
        WR(sc, GENI_M_IRQ_EN, 0);
        WR(sc, GENI_M_IRQ_CLEAR, ~0);
        WR(sc, GENI_DMA_TX_IRQ_EN_CLR, ~0);
        WR(sc, GENI_DMA_TX_IRQ_CLR, ~0);
        WR(sc, GENI_DMA_RX_IRQ_EN_CLR, ~0);
        WR(sc, GENI_DMA_RX_IRQ_CLR, ~0);

        // S_GENI_CMD_ABORT
        WR(sc, GENI_M_CMD_CTRL_REG, (1<<1));

        WR(sc, GENI_DMA_RX_FSM_RST, 1);
        for (u = 0; u < 1000; u++) {
                if (RD(sc, GENI_DMA_RX_IRQ_STAT) & 0x8)
                        break;
                DELAY(10);
        }
        if (u > 0)
                device_printf(dev, "RXRESET time %u\n", u);
        WR(sc, GENI_DMA_TX_FSM_RST, 1);
        for (u = 0; u < 1000; u++) {
                if (RD(sc, GENI_DMA_TX_IRQ_STAT) & 0x8)
                        break;
                DELAY(10);
        }
        if (u > 0)
                device_printf(dev, "TXRESET time %u\n", u);
        return (0);
}

int
geniiic_callback(device_t dev, int index, caddr_t data)
{
        geniiic_softc_t *sc = device_get_softc(dev);
        int error = 0;

        return(0);
        switch (index) {
        case IIC_REQUEST_BUS:
                if (sx_try_xlock(&sc->bus_lock) == 0)
                        error = IIC_EBUSBSY;
                else
                        sc->bus_locked = true;
                break;

        case IIC_RELEASE_BUS:
                if (!sc->bus_locked) {
                        device_printf(dev, "Unlocking unlocked bus\n");
                }
                sc->bus_locked = false;
                sx_xunlock(&sc->bus_lock);
                break;

        default:
                device_printf(dev, "callback unknown %d\n", index);
                error = errno2iic(EINVAL);
        }

        return (error);
}

int
geniiic_attach(geniiic_softc_t *sc)
{
        int error = 0;

        if (bootverbose)
                geni_dump_regs(sc);
        mtx_init(&sc->intr_lock, "geniiic intr lock", NULL, MTX_SPIN);
        sx_init(&sc->real_bus_lock, "geniiic real bus lock");
        sx_init(&sc->bus_lock, "geniiic bus lock");

        sc->rx_fifo_size = (RD(sc, GENI_HW_PARAM_1) >> 16) & 0x3f;
        device_printf(sc->dev, "  RX fifo size= 0x%x\n", sc->rx_fifo_size);

        // We might want to set/check the following registers:
        //      GENI_BYTE_GRANULARITY   (0x00000000)
        //      GENI_TX_PACKING_CFG0    (0x0007f8fe)
        //      GENI_TX_PACKING_CFG1    (000ffefe)
        //      GENI_RX_PACKING_CFG0    (0x0007f8fe)
        //      GENI_RX_PACKING_CFG1    (000ffefe)

        sc->iicbus = device_add_child(sc->dev, "iicbus", DEVICE_UNIT_ANY);
        if (sc->iicbus == NULL) {
                device_printf(sc->dev, "iicbus driver not found\n");
                return(ENXIO);
        }

        error = bus_setup_intr(sc->dev,
            sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
            geniiic_intr, NULL, sc, &sc->intr_handle);
        if (error) {
                device_printf(sc->dev,
                    "Unable to setup irq: error %d\n", error);
        }

        bus_attach_children(sc->dev);
        return (error);
}

int
geniiic_detach(geniiic_softc_t *sc)
{
        int error = 0;

        error = bus_generic_detach(sc->dev);
        if (error)
                return (error);

        WR(sc, GENI_M_IRQ_EN, 0);

        if (sc->intr_handle) {
                bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
        }

        sx_xlock(&sc->bus_lock);
        sx_xlock(&sc->real_bus_lock);

        geniiic_reset(sc->dev, 0, 0, NULL);
        sc->iicbus = NULL;
        sc->intr_handle = NULL;

        sx_xunlock(&sc->real_bus_lock);
        sx_xunlock(&sc->bus_lock);

        sx_destroy(&sc->real_bus_lock);
        sx_destroy(&sc->bus_lock);

        mtx_destroy(&sc->intr_lock);
        return (error);
}

int
geniiic_suspend(geniiic_softc_t *sc)
{
        int error;

        device_printf(sc->dev, "suspend method is NO-OP (good luck!)\n");

        error = bus_generic_suspend(sc->dev);

        return (error);
}

int geniiic_resume(geniiic_softc_t *sc)
{
        int error;

        device_printf(sc->dev, "resume method is NO-OP (good luck!)\n");

        error = bus_generic_resume(sc->dev);

        return (error);
}

DRIVER_MODULE(iicbus, geniiic, iicbus_driver, NULL, NULL);
DRIVER_MODULE(acpi_iicbus, geniiic, acpi_iicbus_driver, NULL, NULL);
MODULE_DEPEND(geniiic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
MODULE_VERSION(geniiic, 1);