Home | History | Annotate | Line # | Download | only in i2c
      1 /*	$NetBSD: sc16is7xxi2c.c,v 1.1 2025/10/24 23:16:11 brad Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2025 Brad Spencer <brad (at) anduin.eldar.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __KERNEL_RCSID(0, "$NetBSD: sc16is7xxi2c.c,v 1.1 2025/10/24 23:16:11 brad Exp $");
     21 
     22 /*
     23  * I2C frontend driver for the SC16IS7xx UART bridge.
     24  * The heavy lifting is done by the general sc16is7xx(4)
     25  * driver and the com(4) backend.
     26  */
     27 
     28 #include <sys/param.h>
     29 #include <sys/systm.h>
     30 #include <sys/kernel.h>
     31 #include <sys/device.h>
     32 #include <sys/module.h>
     33 #include <sys/conf.h>
     34 #include <sys/sysctl.h>
     35 #include <sys/mutex.h>
     36 #include <sys/condvar.h>
     37 #include <sys/pool.h>
     38 #include <sys/kmem.h>
     39 
     40 #include <dev/i2c/i2cvar.h>
     41 #include <dev/spi/spivar.h>
     42 #include <dev/ic/sc16is7xxreg.h>
     43 #include <dev/ic/sc16is7xxvar.h>
     44 
     45 struct sc16is7xx_i2c_softc {
     46 	struct sc16is7xx_sc sc_sc16is7xx;
     47 	i2c_tag_t sc_tag;
     48 	i2c_addr_t sc_addr;
     49 };
     50 
     51 #define	SC16IS7XX_TO_I2C(sc)	\
     52 	container_of((sc), struct sc16is7xx_i2c_softc, sc_sc16is7xx)
     53 
     54 static int sc16is7xxi2c_poke(i2c_tag_t, i2c_addr_t, bool);
     55 static int sc16is7xxi2c_match(device_t, cfdata_t, void *);
     56 static void sc16is7xxi2c_attach(device_t, device_t, void *);
     57 static int sc16is7xxi2c_detach(device_t, int);
     58 
     59 CFATTACH_DECL_NEW(sc16is7xxi2c, sizeof(struct sc16is7xx_sc),
     60     sc16is7xxi2c_match, sc16is7xxi2c_attach, sc16is7xxi2c_detach, NULL);
     61 
     62 static int
     63 sc16is7xxi2c_read_register_direct(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg,
     64     int channel, uint8_t *buf, size_t blen)
     65 {
     66 	int error;
     67 	uint8_t xreg;
     68 
     69 	xreg = (reg << 3) | (channel << 1);
     70 
     71 	error = iic_acquire_bus(tag, 0);
     72 	if (error == 0) {
     73 		error = iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &xreg, 1,
     74 		    buf, blen, 0);
     75 	}
     76 	iic_release_bus(tag, 0);
     77 
     78 	return error;
     79 }
     80 
     81 static int
     82 sc16is7xxi2c_write_register_direct(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg,
     83     int channel, uint8_t *buf, size_t blen)
     84 {
     85 	int error;
     86 	uint8_t xreg;
     87 
     88 	xreg = (reg << 3) | (channel << 1);
     89 
     90 	error = iic_acquire_bus(tag, 0);
     91 	if (error == 0) {
     92 		error = iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &xreg, 1,
     93 		    buf, blen, 0);
     94 	}
     95 	iic_release_bus(tag, 0);
     96 
     97 	return error;
     98 }
     99 /* Use these after the hand off to the general driver happens */
    100 
    101 static int
    102 sc16is7xxi2c_read_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel,
    103     uint8_t *buf, size_t blen)
    104 {
    105 	struct sc16is7xx_i2c_softc *isc = SC16IS7XX_TO_I2C(sc);
    106 	int error;
    107 
    108 	KASSERT(blen > 0);
    109 
    110 	error = sc16is7xxi2c_read_register_direct(isc->sc_tag, isc->sc_addr,
    111 	    reg, channel, buf, blen);
    112 
    113 	return error;
    114 }
    115 
    116 static int
    117 sc16is7xxi2c_write_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel,
    118     uint8_t *buf, size_t blen)
    119 {
    120 	struct sc16is7xx_i2c_softc *isc = SC16IS7XX_TO_I2C(sc);
    121 	int error;
    122 
    123 	KASSERT(blen > 0);
    124 
    125 	error = sc16is7xxi2c_write_register_direct(isc->sc_tag, isc->sc_addr,
    126 	    reg, channel, buf, blen);
    127 
    128 	return error;
    129 }
    130 
    131 static void
    132 sc16is7xxi2c_copy_handles(struct sc16is7xx_sc *sc, struct com_regs *regs)
    133 {
    134 	struct sc16is7xx_i2c_softc *isc = SC16IS7XX_TO_I2C(sc);
    135 
    136 	regs->cr_tag = isc->sc_tag;
    137 	regs->cr_addr = isc->sc_addr;
    138 }
    139 
    140 static const struct sc16is7xx_accessfuncs sc16is7xx_i2c_accessfuncs = {
    141 	.read_reg = sc16is7xxi2c_read_register,
    142 	.write_reg = sc16is7xxi2c_write_register,
    143 	.copy_handles = sc16is7xxi2c_copy_handles,
    144 };
    145 /* These will be used by dev/ic/com.c, conform to what is expected */
    146 
    147 static uint8_t
    148 sc16is7xx_i2c_com_read_1(struct com_regs *regs, u_int reg)
    149 {
    150 	uint8_t buf;
    151 	int error;
    152 
    153 	error = sc16is7xxi2c_read_register_direct(regs->cr_tag, regs->cr_addr,
    154 	    reg, regs->cr_channel, &buf, 1);
    155 
    156 	if (!error)
    157 		return buf;
    158 
    159 	return 0;
    160 }
    161 
    162 static void
    163 sc16is7xx_i2c_com_write_1(struct com_regs *regs, u_int reg, uint8_t val)
    164 {
    165 	sc16is7xxi2c_write_register_direct(regs->cr_tag, regs->cr_addr,
    166 	    reg, regs->cr_channel, &val, 1);
    167 }
    168 
    169 static void
    170 sc16is7xx_i2c_com_write_multi_1(struct com_regs *regs, u_int reg, const uint8_t *datap,
    171     bus_size_t count)
    172 {
    173 	sc16is7xxi2c_write_register_direct(regs->cr_tag, regs->cr_addr,
    174 	    reg, regs->cr_channel, __UNCONST(datap), count);
    175 }
    176 
    177 static const struct sc16is7xx_accessfuncs sc16is7xx_i2c_com_accessfuncs = {
    178 	.com_read_1 = sc16is7xx_i2c_com_read_1,
    179 	.com_write_1 = sc16is7xx_i2c_com_write_1,
    180 	.com_write_multi_1 = sc16is7xx_i2c_com_write_multi_1,
    181 };
    182 
    183 static int
    184 sc16is7xxi2c_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
    185 {
    186 	uint8_t reg = SC16IS7XX_REGISTER_SPR;
    187 	uint8_t buf[1];
    188 	int error;
    189 
    190 	error = sc16is7xxi2c_read_register_direct(tag, addr, reg, 0, buf, 1);
    191 	if (matchdebug) {
    192 		printf("poke addr=%02x, error=%d\n", addr, error);
    193 	}
    194 	return error;
    195 }
    196 
    197 static int
    198 sc16is7xxi2c_match(device_t parent, cfdata_t cf, void *aux)
    199 {
    200 	struct i2c_attach_args *ia = aux;
    201 	int error, match_result;
    202 	const bool matchdebug = false;
    203 	bool indirect_found = false;
    204 	int i;
    205 
    206 	if (iic_use_direct_match(ia, cf, sc16is7xx_compat_data, &match_result)) {
    207 		return match_result;
    208 	}
    209 	for (i = SC16IS7XX_LOW_I2C_ADDR; i <= SC16IS7XX_HIGH_I2C_ADDR && indirect_found == false; i++) {
    210 		if (ia->ia_addr == i) {
    211 			if (matchdebug) {
    212 				printf("sc16is7xxi2c_match possible indirect: ia_addr=%02x, i=%02x\n", ia->ia_addr, i);
    213 			}
    214 			indirect_found = true;
    215 		}
    216 	}
    217 
    218 	if (!indirect_found)
    219 		return 0;
    220 
    221 	/* Check to see if something is really at this i2c address. This will
    222 	 * keep phantom devices from appearing */
    223 
    224 	error = sc16is7xxi2c_poke(ia->ia_tag, ia->ia_addr, matchdebug);
    225 
    226 	return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
    227 }
    228 
    229 static void
    230 sc16is7xxi2c_attach(device_t parent, device_t self, void *aux)
    231 {
    232 	struct sc16is7xx_i2c_softc *isc = device_private(self);
    233 	struct sc16is7xx_sc *sc = &isc->sc_sc16is7xx;
    234 	struct i2c_attach_args *ia = aux;
    235 
    236 	sc->sc_dev = self;
    237 	sc->sc_funcs = &sc16is7xx_i2c_accessfuncs;
    238 	sc->sc_com_funcs = &sc16is7xx_i2c_com_accessfuncs;
    239 
    240 	isc->sc_tag = ia->ia_tag;
    241 	isc->sc_addr = ia->ia_addr;
    242 
    243 	sc16is7xx_attach(sc);
    244 }
    245 
    246 static int
    247 sc16is7xxi2c_detach(device_t self, int flags)
    248 {
    249 	struct sc16is7xx_i2c_softc *isc = device_private(self);
    250 
    251 	return sc16is7xx_detach(&isc->sc_sc16is7xx, flags);
    252 }
    253