Module Name: src Committed By: brad Date: Thu Jan 23 19:02:43 UTC 2025
Modified Files: src/distrib/sets/lists/debug: module.mi src/distrib/sets/lists/man: mi src/distrib/sets/lists/manhtml: mi src/distrib/sets/lists/modules: mi src/share/man/man4: Makefile src/sys/dev/onewire: files.onewire src/sys/modules: Makefile Added Files: src/share/man/man4: ds28e17iic.4 src/sys/dev/onewire: ds28e17iic.c ds28e17iicreg.h ds28e17iicvar.h src/sys/modules/ds28e17iic: Makefile ds28e17iic.ioconf Log Message: A driver for the DS28E17 1-Wire to I2C bridge chip. This chip acts like a 1-Wire slave device and provides a iic(4) master at the end of the 1-Wire bus. More or less it is the polar opposite of the DS2482 [ds2482ow(4)] chip. This device couples well with ds2482ow(4) and can be used to provide a I2C bus at very great lengths from the controlling computer. All features of the chip are supported, except for 1-Wire overdrive support, which requires more work from the onewire(4) infrastructure. The chip does not support Read without Stop. Attempts to do this will get turned into a Read with Stop and one will have to hope for the best. The chip also does not support zero length I2C reads or zero length I2C writes. This has the side effect of making the default mode, a zero length I2C write, for i2scan(8) return false positives. The alternative mode that i2cscan(8) can use, the single byte read, should work as expected. The chip has automatic support for end devices that do I2C clock stretching. It was noticed that this chip does not work with the gpioow(4) driver. That might be an interesting thing to debug if one has a good logic analyzer on hand. While the presence pulse is detected, the gpioow(4) driver is not able to complete the initial ROM enumeration. The DS28E17 works flawlessly when driven by a DS2482 [ds2482ow(4)] driver chip. Poke me if you want any more details. The chip is pretty inexpensive and only requires a single cap to get it hooked up. However, the package it comes in is only a 16-QFN package, so it could provide to be hard to solider onto a board for some. There are side tabs, so it was possible with a very small iron and lots of flux. There is a slightly expensive breakout board sold by Mikroe that probably works well -> https://www.mikroe.com/1-wire-i2c-click To generate a diff of this commit: cvs rdiff -u -r1.30 -r1.31 src/distrib/sets/lists/debug/module.mi cvs rdiff -u -r1.1794 -r1.1795 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.16 -r1.17 src/distrib/sets/lists/manhtml/mi cvs rdiff -u -r1.162 -r1.163 src/distrib/sets/lists/modules/mi cvs rdiff -u -r1.739 -r1.740 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man4/ds28e17iic.4 cvs rdiff -u -r0 -r1.1 src/sys/dev/onewire/ds28e17iic.c \ src/sys/dev/onewire/ds28e17iicreg.h src/sys/dev/onewire/ds28e17iicvar.h cvs rdiff -u -r1.5 -r1.6 src/sys/dev/onewire/files.onewire cvs rdiff -u -r1.294 -r1.295 src/sys/modules/Makefile cvs rdiff -u -r0 -r1.1 src/sys/modules/ds28e17iic/Makefile \ src/sys/modules/ds28e17iic/ds28e17iic.ioconf Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/debug/module.mi diff -u src/distrib/sets/lists/debug/module.mi:1.30 src/distrib/sets/lists/debug/module.mi:1.31 --- src/distrib/sets/lists/debug/module.mi:1.30 Mon Jan 20 13:54:54 2025 +++ src/distrib/sets/lists/debug/module.mi Thu Jan 23 19:02:43 2025 @@ -1,4 +1,4 @@ -# $NetBSD: module.mi,v 1.30 2025/01/20 13:54:54 maya Exp $ +# $NetBSD: module.mi,v 1.31 2025/01/23 19:02:43 brad Exp $ # ./usr/libdata/debug/@MODULEDIR@ modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/accf_dataready modules-base-kernel kmod,debug @@ -127,6 +127,8 @@ ./usr/libdata/debug/@MODULEDIR@/drvctl/drvctl.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/ds2482ow modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/ds2482ow/ds2482ow.kmod.debug modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/ds28e17iic modules-base-kernel kmod,debug +./usr/libdata/debug/@MODULEDIR@/ds28e17iic/ds28e17iic.kmod.debug modules-base-kernel kmod,debug ./usr/libdata/debug/@MODULEDIR@/dtrace modules-base-kernel kmod,dtrace,debug ./usr/libdata/debug/@MODULEDIR@/dtrace/dtrace.kmod.debug modules-base-kernel kmod,dtrace,debug ./usr/libdata/debug/@MODULEDIR@/dtrace_fbt modules-base-kernel kmod,dtrace,debug Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1794 src/distrib/sets/lists/man/mi:1.1795 --- src/distrib/sets/lists/man/mi:1.1794 Mon Jan 20 13:54:54 2025 +++ src/distrib/sets/lists/man/mi Thu Jan 23 19:02:43 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1794 2025/01/20 13:54:54 maya Exp $ +# $NetBSD: mi,v 1.1795 2025/01/23 19:02:43 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. ./etc/mtree/set.man man-sys-root @@ -1152,6 +1152,7 @@ ./usr/share/man/cat4/drum.0 man-sys-catman .cat ./usr/share/man/cat4/drvctl.0 man-sys-catman .cat ./usr/share/man/cat4/ds2482ow.0 man-sys-catman .cat +./usr/share/man/cat4/ds28e17iic.0 man-sys-catman .cat ./usr/share/man/cat4/dse.0 man-sys-catman .cat ./usr/share/man/cat4/dtide.0 man-sys-catman .cat ./usr/share/man/cat4/dtv.0 man-sys-catman .cat @@ -4724,6 +4725,7 @@ ./usr/share/man/man4/drum.4 man-sys-man .man ./usr/share/man/man4/drvctl.4 man-sys-man .man ./usr/share/man/man4/ds2482ow.4 man-sys-man .man +./usr/share/man/man4/ds28e17iic.4 man-sys-man .man ./usr/share/man/man4/dse.4 man-sys-man .man ./usr/share/man/man4/dtide.4 man-sys-man .man ./usr/share/man/man4/dtv.4 man-sys-man .man Index: src/distrib/sets/lists/manhtml/mi diff -u src/distrib/sets/lists/manhtml/mi:1.16 src/distrib/sets/lists/manhtml/mi:1.17 --- src/distrib/sets/lists/manhtml/mi:1.16 Mon Jan 20 13:54:54 2025 +++ src/distrib/sets/lists/manhtml/mi Thu Jan 23 19:02:43 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.16 2025/01/20 13:54:54 maya Exp $ +# $NetBSD: mi,v 1.17 2025/01/23 19:02:43 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -1035,6 +1035,7 @@ ./usr/share/man/html4/drum.html man-sys-htmlman html ./usr/share/man/html4/drvctl.html man-sys-htmlman html ./usr/share/man/html4/ds2482ow.html man-sys-htmlman html +./usr/share/man/html4/ds28e17iic.html man-sys-htmlman html ./usr/share/man/html4/dse.html man-sys-htmlman html ./usr/share/man/html4/dtide.html man-sys-htmlman html ./usr/share/man/html4/dtv.html man-sys-htmlman html Index: src/distrib/sets/lists/modules/mi diff -u src/distrib/sets/lists/modules/mi:1.162 src/distrib/sets/lists/modules/mi:1.163 --- src/distrib/sets/lists/modules/mi:1.162 Mon Jan 20 13:54:54 2025 +++ src/distrib/sets/lists/modules/mi Thu Jan 23 19:02:43 2025 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.162 2025/01/20 13:54:54 maya Exp $ +# $NetBSD: mi,v 1.163 2025/01/23 19:02:43 brad Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -145,6 +145,8 @@ ./@MODULEDIR@/drvctl/drvctl.kmod modules-base-kernel kmod ./@MODULEDIR@/ds2482ow modules-base-kernel kmod ./@MODULEDIR@/ds2482ow/ds2482ow.kmod modules-base-kernel kmod +./@MODULEDIR@/ds28e17iic modules-base-kernel kmod +./@MODULEDIR@/ds28e17iic/ds28e17iic.kmod modules-base-kernel kmod ./@MODULEDIR@/dtrace modules-base-kernel kmod,dtrace ./@MODULEDIR@/dtrace/dtrace.kmod modules-base-kernel kmod,dtrace ./@MODULEDIR@/dtrace_fbt modules-base-kernel kmod,dtrace Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.739 src/share/man/man4/Makefile:1.740 --- src/share/man/man4/Makefile:1.739 Mon Jan 20 13:54:54 2025 +++ src/share/man/man4/Makefile Thu Jan 23 19:02:42 2025 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.739 2025/01/20 13:54:54 maya Exp $ +# $NetBSD: Makefile,v 1.740 2025/01/23 19:02:42 brad Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -20,8 +20,8 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a clockctl.4 cmdide.4 cmpci.4 cms.4 cnw.4 \ com.4 coram.4 crypto.4 cs80bus.4 cuda.4 cypide.4 cxdtv.4 \ ddb.4 ddc.4 dge.4 dk.4 dm.4 dmoverio.4 \ - dmphy.4 dpt.4 dpti.4 drm.4 drum.4 drvctl.4 ds2482ow.4 dse.4 dtv.4 \ - dtviic.4 dwctwo.4 \ + dmphy.4 dpt.4 dpti.4 drm.4 drum.4 drvctl.4 ds2482ow.4 ds28e17iic.4 \ + dse.4 dtv.4 dtviic.4 dwctwo.4 \ eap.4 ebus.4 edc.4 elmc.4 emuxki.4 ena.4 envsys.4 ep.4 \ eqos.4 esa.4 esiop.4 esm.4 eso.4 et.4 etphy.4 exphy.4 \ fd.4 finsio.4 flash.4 fms.4 fss.4 \ Index: src/sys/dev/onewire/files.onewire diff -u src/sys/dev/onewire/files.onewire:1.5 src/sys/dev/onewire/files.onewire:1.6 --- src/sys/dev/onewire/files.onewire:1.5 Tue Apr 14 13:36:51 2020 +++ src/sys/dev/onewire/files.onewire Thu Jan 23 19:02:42 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.onewire,v 1.5 2020/04/14 13:36:51 macallan Exp $ +# $NetBSD: files.onewire,v 1.6 2025/01/23 19:02:42 brad Exp $ define onewire {} defflag opt_onewire.h ONEWIRE_DEBUG @@ -19,3 +19,8 @@ file dev/onewire/owtemp.c owtemp device oweeprom attach oweeprom at onewire file dev/onewire/oweeprom.c oweeprom + +# I2C bridge +device ds28e17iic: i2cbus, i2cexec +attach ds28e17iic at onewire +file dev/onewire/ds28e17iic.c ds28e17iic Index: src/sys/modules/Makefile diff -u src/sys/modules/Makefile:1.294 src/sys/modules/Makefile:1.295 --- src/sys/modules/Makefile:1.294 Mon Jan 20 13:54:55 2025 +++ src/sys/modules/Makefile Thu Jan 23 19:02:42 2025 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.294 2025/01/20 13:54:55 maya Exp $ +# $NetBSD: Makefile,v 1.295 2025/01/23 19:02:42 brad Exp $ .include <bsd.own.mk> @@ -61,6 +61,7 @@ SUBDIR+= des SUBDIR+= dk_subr SUBDIR+= drvctl SUBDIR+= ds2482ow +SUBDIR+= ds28e17iic SUBDIR+= efs SUBDIR+= ext2fs SUBDIR+= exec_script Added files: Index: src/share/man/man4/ds28e17iic.4 diff -u /dev/null src/share/man/man4/ds28e17iic.4:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/share/man/man4/ds28e17iic.4 Thu Jan 23 19:02:42 2025 @@ -0,0 +1,92 @@ +.\" $NetBSD: ds28e17iic.4,v 1.1 2025/01/23 19:02:42 brad Exp $ +.\" +.\" Copyright (c) 2025 Brad Spencer <b...@anduin.eldar.org> +.\" +.\" 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. +.\" +.Dd January 12, 2025 +.Dt DS28E17IIC 4 +.Os +.Sh NAME +.Nm ds28e17iic +.Nd Driver for Maxim DS28E17 1-Wire to I2C bridge +.Sh SYNOPSIS +.Cd "ds28e17iic* at onewire?" +.Sh DESCRIPTION +The +.Nm +driver provides a 1-Wire to I2C bridge with a +.Xr iic 4 +bus at the far end using the DS28E17 bridge chip. +.Pp +The DS28E17 will automatically detect and deal with a end device that +uses I2C clock stretching. +.Sh SYSCTL VARIABLES +The following +.Xr sysctl 3 +variables are provided: +.Bl -tag -width indent +.It Li hw.ds28e17iic0.readycount +.It Li hw.ds28e17iic0.readydelay +When the driver sends a transaction down the 1-Wire bus, these +variables are how long to delay before reading the status on whether +or not the conversion is done and the number of times to perform this +read back. In general, these values should be as short as possible, +but if there are too short, a EAGAIN timeout will occur when the end +device is just taking a longer than expect amount of time to respond. +This may be particularly noticed if end device is doing clock +stretching. +.It Li hw.ds28e17iic0.reportreadnostop +If set to 1, report that an attempt to do a Read without a Stop +occured. The chip does not support that operation. Read without Stop +will be treated as a Read with a Stop with the hope that the end +device will be able to deal with that. +.It Li hw.ds28e17iic0.reportzerolen +If set to 1, report that an attempt to perform a zero length read or +zero length write occured. The chip does not support zero length +reads or writes. +.It Li hw.ds28e17iic0.debug +If the driver is compiled with +.Dv DS28E17IIC_DEBUG , +this node will appear and can be used to set the debugging level. +.El +.Sh SEE ALSO +.Xr onewire 4 , +.Xr iic 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Nx 11.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Brad Spencer Aq Mt b...@anduin.eldar.org . +.Sh BUGS +While this may not be considered a bug, the DS28E17 chip will detach +itself from the +.Xr onewire 4 +bus if there is not a device connected to its SDA and SCL pins. +.Pp +The +.Xr i2cscan 8 +command will not function entirely correctly when run against a +DS28E17 chip. The default mode of doing a I2C Write with Stop that is +zero length is not supported by the DS28E17 chip. When the +.Xr i2cscan 8 +command is used with its one byte read mode it will find devices as +long as the device does not NACK on a I2C read. + Index: src/sys/dev/onewire/ds28e17iic.c diff -u /dev/null src/sys/dev/onewire/ds28e17iic.c:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/sys/dev/onewire/ds28e17iic.c Thu Jan 23 19:02:42 2025 @@ -0,0 +1,734 @@ +/* $NetBSD: ds28e17iic.c,v 1.1 2025/01/23 19:02:42 brad Exp $ */ + +/* + * Copyright (c) 2025 Brad Spencer <b...@anduin.eldar.org> + * + * 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; +} Index: src/sys/dev/onewire/ds28e17iicreg.h diff -u /dev/null src/sys/dev/onewire/ds28e17iicreg.h:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/sys/dev/onewire/ds28e17iicreg.h Thu Jan 23 19:02:42 2025 @@ -0,0 +1,36 @@ +/* $NetBSD: ds28e17iicreg.h,v 1.1 2025/01/23 19:02:42 brad Exp $ */ + +/* + * Copyright (c) 2025 Brad Spencer <b...@anduin.eldar.org> + * + * 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. + */ + +#ifndef _DEV_I2C_DS28E17IICREG_H_ +#define _DEV_I2C_DS28E17IICREG_H_ + +#define DS28E17IIC_DC_WD_WITH_STOP 0x4B +#define DS28E17IIC_DC_WD 0x5A +#define DS28E17IIC_DC_WD_ONLY 0x69 +#define DS28E17IIC_DC_WD_ONLY_WITH_STOP 0x78 +#define DS28E17IIC_DC_RD_WITH_STOP 0x87 +#define DS28E17IIC_DC_WD_RD_WITH_STOP 0x2D +#define DS28E17IIC_DC_WRITE_CONFIG 0xD2 +#define DS28E17IIC_DC_READ_CONFIG 0xE1 +#define DS28E17IIC_SPEED_100KHZ 0x00 +#define DS28E17IIC_SPEED_400KHZ 0x01 +#define DS28E17IIC_SPEED_900KHZ 0x02 +#define DS28E17IIC_DC_ENTER_SLEEP 0x1E +#define DS28E17IIC_DC_DEV_REVISION 0xC3 + +#endif Index: src/sys/dev/onewire/ds28e17iicvar.h diff -u /dev/null src/sys/dev/onewire/ds28e17iicvar.h:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/sys/dev/onewire/ds28e17iicvar.h Thu Jan 23 19:02:42 2025 @@ -0,0 +1,46 @@ +/* $NetBSD: ds28e17iicvar.h,v 1.1 2025/01/23 19:02:42 brad Exp $ */ + +/* + * Copyright (c) 2025 Brad Spencer <b...@anduin.eldar.org> + * + * 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. + */ + +#ifndef _DEV_I2C_DS28E17IICVAR_H_ +#define _DEV_I2C_DS28E17IICVAR_H_ + +#include <sys/sysctl.h> +#include <sys/types.h> + +#include <dev/i2c/i2cvar.h> + + +struct ds28e17iic_softc { + device_t sc_dv; + void *sc_onewire; + u_int64_t sc_rom; + int sc_dying; + int sc_ds28e17iicdebug; + + struct i2c_controller sc_i2c_tag; + device_t sc_i2c_dev; + + struct sysctllog *sc_ds28e17iiclog; + bool sc_reportreadnostop; + bool sc_reportzerolen; + + int sc_readycount; + int sc_readydelay; +}; + +#endif Index: src/sys/modules/ds28e17iic/Makefile diff -u /dev/null src/sys/modules/ds28e17iic/Makefile:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/sys/modules/ds28e17iic/Makefile Thu Jan 23 19:02:42 2025 @@ -0,0 +1,15 @@ +# $NetBSD: Makefile,v 1.1 2025/01/23 19:02:42 brad Exp $ + +.include "../Makefile.inc" + +.PATH: ${S}/dev/onewire + +KMOD= ds28e17iic +IOCONF= ds28e17iic.ioconf +SRCS= ds28e17iic.c + +CPPFLAGS+= -I${S}/onewire + +WARNS= 3 + +.include <bsd.kmodule.mk> Index: src/sys/modules/ds28e17iic/ds28e17iic.ioconf diff -u /dev/null src/sys/modules/ds28e17iic/ds28e17iic.ioconf:1.1 --- /dev/null Thu Jan 23 19:02:43 2025 +++ src/sys/modules/ds28e17iic/ds28e17iic.ioconf Thu Jan 23 19:02:42 2025 @@ -0,0 +1,8 @@ +# $NetBSD: ds28e17iic.ioconf,v 1.1 2025/01/23 19:02:42 brad Exp $ + +ioconf ds28e17iic + +include "conf/files" + +pseudo-root onewire* +ds28e17iic* at onewire?