/*      $NetBSD: ds28e17iic.c,v 1.1 2025/01/23 19:02:42 brad Exp $      */

/*
* Copyright (c) 2025 Brad Spencer <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/


/* Driver for the DS28E17 1-Wire to I2C bridge chip */

/* https://www.analog.com/en/products/DS28E17.html */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ds28e17iic.c,v 1.1 2025/01/23 19:02:42 brad Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysctl.h>

#include <dev/onewire/onewiredevs.h>
#include <dev/onewire/onewirereg.h>
#include <dev/onewire/onewirevar.h>

#include <dev/i2c/i2cvar.h>

#include <dev/onewire/ds28e17iicreg.h>
#include <dev/onewire/ds28e17iicvar.h>

static int      ds28e17iic_match(device_t, cfdata_t, void *);
static void     ds28e17iic_attach(device_t, device_t, void *);
static int      ds28e17iic_detach(device_t, int);
static int      ds28e17iic_activate(device_t, enum devact);
static int      ds28e17iic_verify_sysctl(SYSCTLFN_ARGS);

#define DS28E17IIC_DEBUG
#ifdef DS28E17IIC_DEBUG
#define DPRINTF(s, l, x) \
   do { \
       if (l <= s->sc_ds28e17iicdebug) \
           printf x; \
   } while (/*CONSTCOND*/0)
#else
#define DPRINTF(s, l, x)
#endif

CFATTACH_DECL_NEW(ds28e17iic, sizeof(struct ds28e17iic_softc),
       ds28e17iic_match, ds28e17iic_attach, ds28e17iic_detach, ds28e17iic_activate);

extern struct cfdriver ds28e17iic_cd;

static const struct onewire_matchfam ds28e17iic_fams[] = {
       { ONEWIRE_FAMILY_DS28E17 },
};


#define READY_DELAY(d) if (d > 0) delay(d)

/* The chip uses a 16 bit CRC on the 1-Wire bus when doing any I2C transaction.
* But it has some strangness to it..  the CRC can be made up from a number of
* parts of the 1-Wire transaction, some of which are only used for some
* transactions.  Then the result must be inverted and placed in the proper
* order.
*/

static uint16_t
ds28e17iic_crc16_bit(uint16_t icrc)
{
       for (size_t i = 0; i < 8; i++) {
               if (icrc & 0x01) {
                       icrc >>= 1;
                       icrc ^= 0xA001;
               } else {
                       icrc >>= 1;
               }
       }

       return(icrc);
}

static void
ds28e17iic_crc16(uint8_t crc16[], uint8_t cmd, uint8_t i2c_addr, uint8_t len, uint8_t *data, uint8_t len2)
{
       uint16_t crc = 0;

       crc ^= cmd;
       crc = ds28e17iic_crc16_bit(crc);

       /* This is a magic value which means that it should not be considered.
        * The address will never be 0xff, but could, in theory, be 0x00, so
        * don't use that.
        */

       if (i2c_addr != 0xff) {
               crc ^= i2c_addr;
               crc = ds28e17iic_crc16_bit(crc);
       }

       crc ^= len;
       crc = ds28e17iic_crc16_bit(crc);

       if (data != NULL) {
               for (size_t j = 0; j < len; j++) {
                       crc ^= data[j];
                       crc = ds28e17iic_crc16_bit(crc);
               }
       }

       if (len2 > 0) {
               crc ^= len2;
               crc = ds28e17iic_crc16_bit(crc);
       }

       crc = crc ^ 0xffff;

       crc16[1] = crc >> 8;
       crc16[0] = crc & 0xff;
}

int
ds28e17iic_verify_sysctl(SYSCTLFN_ARGS)
{
       int error, t;
       struct sysctlnode node;

       node = *rnode;
       t = *(int *)rnode->sysctl_data;
       node.sysctl_data = &t;
       error = sysctl_lookup(SYSCTLFN_CALL(&node));
       if (error || newp == NULL)
               return error;

       if (t < 0)
               return EINVAL;

       *(int *)rnode->sysctl_data = t;

       return 0;
}

/* There isn't much required to acquire or release the I2C bus, but the man
* pages says these are needed
*/

static int
ds28e17iic_acquire_bus(void *v, int flags)
{
       return(0);
}

static void
ds28e17iic_release_bus(void *v, int flags)
{
       return;
}

/* Perform most of a I2C transaction.  Sometimes there there will be
* more to read from the 1-Wire bus.  That is device command dependent.
*/

static int
ds28e17iic_ow_i2c_transaction(struct ds28e17iic_softc *sc, const char *what, uint8_t device_cmd,
   uint8_t i2c_addr, uint8_t len1, uint8_t *buf1, uint8_t len2,
   uint8_t crc16[2], uint8_t *i2c_status)
{
       int err = 0;
       int readycount;
       uint8_t ready;

       if (onewire_reset(sc->sc_onewire) != 0) {
               err = EIO;
       } else {
               ds28e17iic_crc16(crc16,device_cmd,i2c_addr,len1,buf1,len2);
               onewire_matchrom(sc->sc_onewire, sc->sc_rom);
               onewire_write_byte(sc->sc_onewire,device_cmd);
               if (i2c_addr != 0xff)
                       onewire_write_byte(sc->sc_onewire,i2c_addr);
               onewire_write_byte(sc->sc_onewire,len1);
               if (buf1 != NULL)
                       onewire_write_block(sc->sc_onewire,buf1,len1);
               if (len2 > 0)
                       onewire_write_byte(sc->sc_onewire,len2);
               onewire_write_block(sc->sc_onewire,crc16,2);
               readycount=0;
               do {
                       READY_DELAY(sc->sc_readydelay);
                       ready = onewire_read_bit(sc->sc_onewire);
                       readycount++;
                       if (readycount > sc->sc_readycount)
                               err = EAGAIN;
               } while (ready != 0 && !err);
               DPRINTF(sc, 3, ("%s: readycount=%d, err=%d\n",
                   what, readycount, err));
               *i2c_status = onewire_read_byte(sc->sc_onewire);
       }

       return(err);
}

/* This needs to determine what sort of write is going on.  We will may make use
* of the fact that the chip can start a write transaction with one command and
* add data to it with a second command.
*/

static int
ds28e17iic_i2c_write(struct ds28e17iic_softc *sc, i2c_op_t op, i2c_addr_t addr,
   const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags)
{
       uint8_t crc16[2];
       uint8_t dcmd;
       uint8_t i2c_status;
       uint8_t i2c_write_status;
       uint8_t *buf;
       size_t len;
       uint8_t maddr;
       int err = 0;

       if (sc->sc_dying)
               return(EIO);

       /* It would be possible to support more than 256 bytes in a transfer by
        * breaking it up and using the chip's ability to add data to a write,
        * but that isn't currently done.
        */

       if (cmdbuf != NULL &&
           cmdlen > 256)
               return(ENOTSUP);

       if (databuf != NULL &&
           datalen > 256)
               return(ENOTSUP);

       /* Just null out any attempt to not actually do anything, might save us
        * a panic */

       if (cmdbuf == NULL &&
           databuf == NULL)
               return(err);

       maddr = addr << 1;

       onewire_lock(sc->sc_onewire);

       /* Consider two ways to do a basic write.  One is where there is a
        * cmdbuf and databuf which can use the chip's ability to add to a
        * ongoing transaction and the other case where there is just the
        * cmdbuf or the databuf, in which case, just figure out if a stop
        * is needed.
        */

       if (cmdbuf != NULL &&
           databuf != NULL) {
               /* This chip considers a zero length write an error */
               if (cmdlen == 0 ||
                   datalen == 0) {
                       if (sc->sc_reportzerolen)
                               device_printf(sc->sc_dv,"ds28e17iic_i2c_write: ************ called with zero length read: cmdlen: %zu, datalen: %zu ***************\n",
                                   cmdlen, datalen);
               }

               /* In this case, always start out with a write without stop. */

               err = ds28e17iic_ow_i2c_transaction(sc, "ds28e17iic_i2c_write cmd and data 1",
                   DS28E17IIC_DC_WD, maddr, cmdlen, __UNCONST(cmdbuf), 0, crc16, &i2c_status);
               if (! err) {
                       i2c_write_status = onewire_read_byte(sc->sc_onewire);
                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: both cmd and data 1: i2c_status=%02x, i2c_write_status=%02x\n",
                           i2c_status, i2c_write_status));
                       if (i2c_status == 0 &&
                           i2c_write_status == 0) {
                               /* Add data to the transaction and maybe do a stop as well */
                               if (I2C_OP_STOP_P(op))
                                       dcmd = DS28E17IIC_DC_WD_ONLY_WITH_STOP;
                               else
                                       dcmd = DS28E17IIC_DC_WD_ONLY;

                               err = ds28e17iic_ow_i2c_transaction(sc, "ds28e17iic_i2c_write cmd and data 2",
                                   dcmd, 0xff, datalen, databuf, 0, crc16, &i2c_status);
                               if (! err) {
                                       i2c_write_status = onewire_read_byte(sc->sc_onewire);
                                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: both cmd and data 2: dcmd=%02x, i2c_status=%02x, i2c_write_status=%02x\n",
                                           dcmd, i2c_status, i2c_write_status));
                                       if (i2c_status != 0 ||
                                           i2c_write_status != 0)
                                               err = EIO;
                               } else {
                                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: cmd and data 2: err=%d\n", err));
                               }
                       } else {
                               err = EIO;
                       }
               } else {
                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: cmd and data 1: err=%d\n", err));
               }
       } else {
               /* Either the cmdbuf or databuf has something, figure out which
                * and maybe perform a stop after the write.
                */
               if (cmdbuf != NULL) {
                       buf = __UNCONST(cmdbuf);
                       len = cmdlen;
               } else {
                       buf = databuf;
                       len = datalen;
               }

               if (I2C_OP_STOP_P(op))
                       dcmd = DS28E17IIC_DC_WD_WITH_STOP;
               else
                       dcmd = DS28E17IIC_DC_WD;

               if (len == 0) {
                       if (sc->sc_reportzerolen)
                               device_printf(sc->sc_dv,"ds28e17iic_i2c_write: ************ called with zero length read ***************\n");
               }

               err = ds28e17iic_ow_i2c_transaction(sc, "ds28e17iic_i2c_write cmd or data",
                   dcmd, maddr, len, buf, 0, crc16, &i2c_status);
               if (! err) {
                       i2c_write_status = onewire_read_byte(sc->sc_onewire);
                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: cmd or data: dcmd=%02x, i2c_status=%02x, i2c_write_status=%02x\n",
                           dcmd, i2c_status, i2c_write_status));
                       if (i2c_status != 0 ||
                           i2c_write_status != 0)
                               err = EIO;
               } else {
                       DPRINTF(sc, 2, ("ds28e17iic_i2c_write: cmd or data: err=%d\n", err));
               }
       }

       onewire_unlock(sc->sc_onewire);

       return(err);
}

/* This deals with the situation where the desire is just to read from
* the device.  The chip does not support Read without Stop, so turn
* that into a Read with Stop and hope for the best.
*/

static int
ds28e17iic_i2c_read(struct ds28e17iic_softc *sc, i2c_op_t op, i2c_addr_t addr,
   void *databuf, size_t datalen, int flags)
{
       uint8_t crc16[2];
       uint8_t i2c_status;
       uint8_t maddr;
       int err = 0;

       if (sc->sc_dying)
               return(EIO);

       /* It does not appear that it is possible to read more than 256 bytes */

       if (databuf != NULL &&
           datalen > 256)
               return(ENOTSUP);

       /* Just null out the attempt to not really read anything */

       if (databuf == NULL)
               return(err);

       maddr = (addr << 1) | 0x01;

       onewire_lock(sc->sc_onewire);

       /* Same thing as a write, a zero length read is considered an error */

       if (datalen == 0)
               if (sc->sc_reportzerolen)
                       device_printf(sc->sc_dv,"ds28e17iic_i2c_read: ************ called with zero length read ***************\n");

       if (!I2C_OP_STOP_P(op) &&
           sc->sc_reportreadnostop)
               device_printf(sc->sc_dv,"ds28e17iic_i2c_read: ************ called with READ without STOP ***************\n");

       err = ds28e17iic_ow_i2c_transaction(sc, "ds28e17iic_i2c_read",
           DS28E17IIC_DC_RD_WITH_STOP, maddr, datalen,
           NULL, 0, crc16, &i2c_status);
       if (! err) {
               DPRINTF(sc, 2, ("ds28e17iic_i2c_read: i2c_status=%02x\n", i2c_status));
               if (i2c_status == 0) {
                       onewire_read_block(sc->sc_onewire, databuf, datalen);
               } else {
                       err = EIO;
               }
       } else {
               DPRINTF(sc, 2, ("ds28e17iic_i2c_read: err=%d\n", err));
       }

       onewire_unlock(sc->sc_onewire);

       return(err);
}

/* This deals with the situation where the desire is to write something to
* the device and then read some stuff back.  The chip does not support Read
* without Stop, so turn that into a Read with Stop and hope for the best.
*/

static int
ds28e17iic_i2c_write_read(struct ds28e17iic_softc *sc, i2c_op_t op, i2c_addr_t addr,
   const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags)
{
       uint8_t crc16[2];
       uint8_t i2c_status;
       uint8_t i2c_write_status;
       uint8_t maddr;
       int err = 0;

       if (sc->sc_dying)
               return(EIO);

       /* When using the write+read command of the chip, it does not appear to be
        * possible to read more than 256 bytes, or write more than 256 bytes in a
        * transaction.
        */

       if (cmdbuf != NULL &&
           cmdlen > 256)
               return(ENOTSUP);

       if (databuf != NULL &&
           datalen > 256)
               return(ENOTSUP);

       /* Just return if asked to not actually do anything */

       if (cmdbuf == NULL &&
           databuf == NULL)
               return(err);

       maddr = addr << 1;

       /* Same as a single read or write, a zero length anything here is
        * considered an error.
        */

       if (cmdlen == 0 ||
           datalen == 0) {
               if (sc->sc_reportzerolen)
                       device_printf(sc->sc_dv,"ds28e17iic_i2c_write_read: ************ called with zero length read: cmdlen: %zu, datalen: %zu ***************\n",
                           cmdlen, datalen);
       }

       if (!I2C_OP_STOP_P(op) &&
           sc->sc_reportreadnostop)
               device_printf(sc->sc_dv,"ds28e17iic_i2c_write_read: ************ called with READ without STOP ***************\n");

       onewire_lock(sc->sc_onewire);

       err = ds28e17iic_ow_i2c_transaction(sc, "ds28e17iic_i2c_write_read",
           DS28E17IIC_DC_WD_RD_WITH_STOP, maddr, cmdlen,
           __UNCONST(cmdbuf), datalen, crc16, &i2c_status);
       if (! err) {
               /* Like with the normal write, even if the i2c_status is not zero,
                * we have to read the write status too.  It will end up being
                * the value 0xff. */
               i2c_write_status = onewire_read_byte(sc->sc_onewire);
               DPRINTF(sc, 2, ("ds28e17iic_i2c_write_read: i2c_status=%02x, i2c_write_status=%02x\n",
                   i2c_status, i2c_write_status));
               /* However, don't bother trying to read the data block if there was an error */
               if (i2c_status == 0 &&
                   i2c_write_status == 0) {
                       onewire_read_block(sc->sc_onewire, databuf, datalen);
               } else {
                       err = EIO;
               }
       } else {
               DPRINTF(sc, 2, ("ds28e17iic_i2c_write_read: err=%d\n", err));
       }

       onewire_unlock(sc->sc_onewire);

       return(err);
}

/* This needs to figure out what sort of thing is being sent to the end device.
* The chip will help out with some of this.
*/

static int
ds28e17iic_i2c_exec(void *v, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
   size_t cmdlen, void *databuf, size_t datalen, int flags)
{
       struct ds28e17iic_softc *sc = v;
       int err = 0;

       if (sc->sc_dying)
               return(EIO);

       /* The chip only supports 7 bit addressing */

       if (addr > 0x7f)
               return (ENOTSUP);

       /* XXX - this driver could support setting the speed for this I2C
        * transaction as that information is in the flags, but nothing really
        * asks to do that, so just ignore that for now.
        *
        * If it did support setting speed, do it here.
        *
        */

       if (I2C_OP_WRITE_P(op)) {
               /* A write may include the cmdbuf and/or the databuf */
               err = ds28e17iic_i2c_write(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags);

               DPRINTF(sc, 2, ("ds28e17iic_exec: I2C WRITE: addr=%02x, err=%d\n", addr, err));
       } else {
               /* If a read includes a cmdbuf, then this is a write+read operation */
               if (cmdbuf != NULL)
                       err = ds28e17iic_i2c_write_read(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags);
               else
                       err = ds28e17iic_i2c_read(sc, op, addr, databuf, datalen, flags);

               DPRINTF(sc, 2, ("ds28e17iic_exec: I2C %s addr=%02x, err=%d\n",
                   cmdbuf != NULL ? "WRITE-READ" : "READ", addr, err));
       }

       return(err);
}

static int
ds28e17iic_sysctl_init(struct ds28e17iic_softc *sc)
{
       int error;
       const struct sysctlnode *cnode;
       int sysctlroot_num;

       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           0, CTLTYPE_NODE, device_xname(sc->sc_dv),
           SYSCTL_DESCR("DS28E17IIC controls"), NULL, 0, NULL, 0, CTL_HW,
           CTL_CREATE, CTL_EOL)) != 0)
               return error;

       sysctlroot_num = cnode->sysctl_num;

#ifdef DS28E17IIC_DEBUG
       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
           SYSCTL_DESCR("Debug level"), ds28e17iic_verify_sysctl, 0,
           &sc->sc_ds28e17iicdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
           CTL_EOL)) != 0)
               return error;
#endif

       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           CTLFLAG_READWRITE, CTLTYPE_INT, "readycount",
           SYSCTL_DESCR("How many times to wait for the I2C transaction to finish"), ds28e17iic_verify_sysctl, 0,
           &sc->sc_readycount, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
           CTL_EOL)) != 0)
               return error;

       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           CTLFLAG_READWRITE, CTLTYPE_INT, "readydelay",
           SYSCTL_DESCR("Delay in microseconds before checking the I2C transaction"), ds28e17iic_verify_sysctl, 0,
           &sc->sc_readydelay, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
           CTL_EOL)) != 0)
               return error;

       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           CTLFLAG_READWRITE, CTLTYPE_BOOL, "reportreadnostop",
           SYSCTL_DESCR("Report that a READ without STOP was attempted by a device"),
           NULL, 0, &sc->sc_reportreadnostop, 0, CTL_HW, sysctlroot_num,
           CTL_CREATE, CTL_EOL)) != 0)
               return error;

       if ((error = sysctl_createv(&sc->sc_ds28e17iiclog, 0, NULL, &cnode,
           CTLFLAG_READWRITE, CTLTYPE_BOOL, "reportzerolen",
           SYSCTL_DESCR("Report that an attempt to read or write zero length was attempted"),
           NULL, 0, &sc->sc_reportzerolen, 0, CTL_HW, sysctlroot_num,
           CTL_CREATE, CTL_EOL)) != 0)
               return error;

       return 0;
}

static int
ds28e17iic_match(device_t parent, cfdata_t match, void *aux)
{
       return (onewire_matchbyfam(aux, ds28e17iic_fams,
           __arraycount(ds28e17iic_fams)));
}

static void
ds28e17iic_attach(device_t parent, device_t self, void *aux)
{
       struct ds28e17iic_softc *sc = device_private(self);
       struct onewire_attach_args *oa = aux;
       struct i2cbus_attach_args iba;
       uint8_t hardware_rev;
       uint8_t i2c_speed[2];
       int err = 0;

       aprint_normal("\n");

       sc->sc_dv = self;
       sc->sc_onewire = oa->oa_onewire;
       sc->sc_rom = oa->oa_rom;
       sc->sc_reportreadnostop = true;
       sc->sc_reportzerolen = true;
       sc->sc_ds28e17iicdebug = 0;
       sc->sc_readycount=20;
       sc->sc_readydelay = 10;

       if ((err = ds28e17iic_sysctl_init(sc)) != 0) {
               aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", err);
               return;
       }

       onewire_lock(sc->sc_onewire);

       if (onewire_reset(sc->sc_onewire) != 0) {
               aprint_error_dev(sc->sc_dv, "Could not reset the onewire bus for hardware version\n");
               onewire_unlock(sc->sc_onewire);
               return;
       }
       onewire_matchrom(sc->sc_onewire, sc->sc_rom);
       onewire_write_byte(sc->sc_onewire, DS28E17IIC_DC_DEV_REVISION);
       hardware_rev = onewire_read_byte(sc->sc_onewire);

       aprint_normal_dev(sc->sc_dv, "Hardware revision: %d\n",
           hardware_rev);

       /* The default for the chip is 400Khz, but some devices may not be able
        * to deal with that, so set the speed to 100Khz which is standard I2C
        * speed.
        */

       if (onewire_reset(sc->sc_onewire) != 0) {
               aprint_error_dev(sc->sc_dv, "Could not reset the onewire bus to set I2C speed\n");
               onewire_unlock(sc->sc_onewire);
               return;
       }
       onewire_matchrom(sc->sc_onewire, sc->sc_rom);
       i2c_speed[0] = DS28E17IIC_DC_WRITE_CONFIG;
       i2c_speed[1] = DS28E17IIC_SPEED_100KHZ;
       onewire_write_block(sc->sc_onewire, i2c_speed, 2);

       onewire_unlock(sc->sc_onewire);

       iic_tag_init(&sc->sc_i2c_tag);
       sc->sc_i2c_tag.ic_cookie = sc;
       sc->sc_i2c_tag.ic_acquire_bus = ds28e17iic_acquire_bus;
       sc->sc_i2c_tag.ic_release_bus = ds28e17iic_release_bus;
       sc->sc_i2c_tag.ic_exec = ds28e17iic_i2c_exec;

       memset(&iba, 0, sizeof(iba));
       iba.iba_tag = &sc->sc_i2c_tag;
       sc->sc_i2c_dev = config_found(self, &iba, iicbus_print, CFARGS(.iattr = "i2cbus"));
}

static int
ds28e17iic_detach(device_t self, int flags)
{
       struct ds28e17iic_softc *sc = device_private(self);
       int err = 0;

       sc->sc_dying = 1;

       err = config_detach_children(self, flags);
       if (err)
               return err;

       sysctl_teardown(&sc->sc_ds28e17iiclog);

       return 0;
}

static int
ds28e17iic_activate(device_t self, enum devact act)
{
       struct ds28e17iic_softc *sc = device_private(self);

       switch (act) {
       case DVACT_DEACTIVATE:
               sc->sc_dying = 1;
               return 0;
       default:
               return EOPNOTSUPP;
       }
}


MODULE(MODULE_CLASS_DRIVER, ds28e17iic, "onewire,iic");

#ifdef _MODULE
#include "ioconf.c"
#endif

static int
ds28e17iic_modcmd(modcmd_t cmd, void *opaque)
{
       int error;

       error = 0;
       switch (cmd) {
       case MODULE_CMD_INIT:
#ifdef _MODULE
               error = config_init_component(cfdriver_ioconf_ds28e17iic,
                   cfattach_ioconf_ds28e17iic, cfdata_ioconf_ds28e17iic);
               if (error)
                       aprint_error("%s: unable to init component\n",
                           ds28e17iic_cd.cd_name);
#endif
               break;
       case MODULE_CMD_FINI:
#ifdef _MODULE
               config_fini_component(cfdriver_ioconf_ds28e17iic,
                   cfattach_ioconf_ds28e17iic, cfdata_ioconf_ds28e17iic);
#endif
               break;
       default:
               error = ENOTTY;
       }
       return error;
}