1 1.1 brad /* $NetBSD: sc16is7xxspi.c,v 1.1 2025/10/24 23:16:11 brad Exp $ */ 2 1.1 brad 3 1.1 brad /* 4 1.1 brad * Copyright (c) 2025 Brad Spencer <brad (at) anduin.eldar.org> 5 1.1 brad * 6 1.1 brad * Permission to use, copy, modify, and distribute this software for any 7 1.1 brad * purpose with or without fee is hereby granted, provided that the above 8 1.1 brad * copyright notice and this permission notice appear in all copies. 9 1.1 brad * 10 1.1 brad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 1.1 brad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 1.1 brad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 1.1 brad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 1.1 brad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 1.1 brad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 1.1 brad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 1.1 brad */ 18 1.1 brad 19 1.1 brad #include <sys/cdefs.h> 20 1.1 brad __KERNEL_RCSID(0, "$NetBSD: sc16is7xxspi.c,v 1.1 2025/10/24 23:16:11 brad Exp $"); 21 1.1 brad 22 1.1 brad /* 23 1.1 brad * SPI frontend driver for the SC16IS7xx UART bridge. 24 1.1 brad * The heavy lifting is done by the general sc16is7xx(4) 25 1.1 brad * driver and the com(4) backend. 26 1.1 brad */ 27 1.1 brad 28 1.1 brad #include <sys/param.h> 29 1.1 brad #include <sys/systm.h> 30 1.1 brad #include <sys/kernel.h> 31 1.1 brad #include <sys/device.h> 32 1.1 brad #include <sys/module.h> 33 1.1 brad #include <sys/conf.h> 34 1.1 brad #include <sys/sysctl.h> 35 1.1 brad #include <sys/mutex.h> 36 1.1 brad #include <sys/condvar.h> 37 1.1 brad #include <sys/pool.h> 38 1.1 brad #include <sys/kmem.h> 39 1.1 brad 40 1.1 brad #include <dev/i2c/i2cvar.h> 41 1.1 brad #include <dev/spi/spivar.h> 42 1.1 brad #include <dev/ic/sc16is7xxreg.h> 43 1.1 brad #include <dev/ic/sc16is7xxvar.h> 44 1.1 brad 45 1.1 brad #include "opt_sc16is7xx.h" 46 1.1 brad 47 1.1 brad struct sc16is7xx_spi_softc { 48 1.1 brad struct sc16is7xx_sc sc_sc16is7xx; 49 1.1 brad spi_handle_t sc_sh; 50 1.1 brad }; 51 1.1 brad 52 1.1 brad #define SC16IS7XX_TO_SPI(sc) \ 53 1.1 brad container_of((sc), struct sc16is7xx_spi_softc, sc_sc16is7xx) 54 1.1 brad 55 1.1 brad static int sc16is7xxspi_match(device_t, cfdata_t, void *); 56 1.1 brad static void sc16is7xxspi_attach(device_t, device_t, void *); 57 1.1 brad static int sc16is7xxspi_detach(device_t, int); 58 1.1 brad 59 1.1 brad CFATTACH_DECL_NEW(sc16is7xxspi, sizeof(struct sc16is7xx_sc), 60 1.1 brad sc16is7xxspi_match, sc16is7xxspi_attach, sc16is7xxspi_detach, NULL); 61 1.1 brad 62 1.1 brad static int 63 1.1 brad sc16is7xxspi_read_register_direct(spi_handle_t sh, 64 1.1 brad uint8_t reg, int channel, uint8_t *buf, size_t blen) 65 1.1 brad { 66 1.1 brad int error; 67 1.1 brad uint8_t xreg; 68 1.1 brad 69 1.1 brad xreg = ((reg << 3) | (channel << 1)) | 0x80; 70 1.1 brad 71 1.1 brad error = spi_send_recv(sh, 1, &xreg, blen, buf); 72 1.1 brad return error; 73 1.1 brad } 74 1.1 brad 75 1.1 brad static int 76 1.1 brad sc16is7xxspi_write_register_direct(spi_handle_t sh, 77 1.1 brad uint8_t reg, int channel, uint8_t *buf, size_t blen) 78 1.1 brad { 79 1.1 brad int error; 80 1.1 brad uint8_t xreg; 81 1.1 brad struct iovec iov[2]; 82 1.1 brad 83 1.1 brad xreg = (reg << 3) | (channel << 1); 84 1.1 brad 85 1.1 brad KASSERTMSG(!(xreg & 0x80), "xreg=%02x", xreg); /* panic if this ends up 86 1.1 brad * trying to be a read */ 87 1.1 brad 88 1.1 brad iov[0].iov_len = 1; 89 1.1 brad iov[0].iov_base = &xreg; 90 1.1 brad iov[1].iov_len = blen; 91 1.1 brad iov[1].iov_base = buf; 92 1.1 brad error = spi_sendv(sh, &iov[0], 2); 93 1.1 brad 94 1.1 brad return error; 95 1.1 brad } 96 1.1 brad /* Use these after the hand off to the general driver happens */ 97 1.1 brad 98 1.1 brad static int 99 1.1 brad sc16is7xxspi_read_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel, 100 1.1 brad uint8_t *buf, size_t blen) 101 1.1 brad { 102 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc); 103 1.1 brad int error; 104 1.1 brad 105 1.1 brad KASSERT(blen > 0); 106 1.1 brad 107 1.1 brad error = sc16is7xxspi_read_register_direct(ssc->sc_sh, 108 1.1 brad reg, channel, buf, blen); 109 1.1 brad 110 1.1 brad return error; 111 1.1 brad } 112 1.1 brad 113 1.1 brad static int 114 1.1 brad sc16is7xxspi_write_register(struct sc16is7xx_sc *sc, uint8_t reg, int channel, 115 1.1 brad uint8_t *buf, size_t blen) 116 1.1 brad { 117 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc); 118 1.1 brad int error; 119 1.1 brad 120 1.1 brad KASSERT(blen > 0); 121 1.1 brad 122 1.1 brad error = sc16is7xxspi_write_register_direct(ssc->sc_sh, 123 1.1 brad reg, channel, buf, blen); 124 1.1 brad 125 1.1 brad return error; 126 1.1 brad } 127 1.1 brad 128 1.1 brad static void 129 1.1 brad sc16is7xxspi_copy_handles(struct sc16is7xx_sc *sc, struct com_regs *regs) 130 1.1 brad { 131 1.1 brad struct sc16is7xx_spi_softc *ssc = SC16IS7XX_TO_SPI(sc); 132 1.1 brad 133 1.1 brad regs->cr_sh = ssc->sc_sh; 134 1.1 brad } 135 1.1 brad 136 1.1 brad static const struct sc16is7xx_accessfuncs sc16is7xx_spi_accessfuncs = { 137 1.1 brad .read_reg = sc16is7xxspi_read_register, 138 1.1 brad .write_reg = sc16is7xxspi_write_register, 139 1.1 brad .copy_handles = sc16is7xxspi_copy_handles, 140 1.1 brad }; 141 1.1 brad /* These will be used by dev/ic/com.c, conform to what is expected */ 142 1.1 brad 143 1.1 brad static uint8_t 144 1.1 brad sc16is7xx_spi_com_read_1(struct com_regs *regs, u_int reg) 145 1.1 brad { 146 1.1 brad uint8_t buf; 147 1.1 brad int error; 148 1.1 brad 149 1.1 brad error = sc16is7xxspi_read_register_direct(regs->cr_sh, 150 1.1 brad reg, regs->cr_channel, &buf, 1); 151 1.1 brad 152 1.1 brad if (!error) 153 1.1 brad return buf; 154 1.1 brad 155 1.1 brad return 0; 156 1.1 brad } 157 1.1 brad 158 1.1 brad static void 159 1.1 brad sc16is7xx_spi_com_write_1(struct com_regs *regs, u_int reg, uint8_t val) 160 1.1 brad { 161 1.1 brad sc16is7xxspi_write_register_direct(regs->cr_sh, 162 1.1 brad reg, regs->cr_channel, &val, 1); 163 1.1 brad } 164 1.1 brad 165 1.1 brad static void 166 1.1 brad sc16is7xx_spi_com_write_multi_1(struct com_regs *regs, u_int reg, const uint8_t *datap, 167 1.1 brad bus_size_t count) 168 1.1 brad { 169 1.1 brad sc16is7xxspi_write_register_direct(regs->cr_sh, 170 1.1 brad reg, regs->cr_channel, __UNCONST(datap), count); 171 1.1 brad } 172 1.1 brad 173 1.1 brad static const struct sc16is7xx_accessfuncs sc16is7xx_spi_com_accessfuncs = { 174 1.1 brad .com_read_1 = sc16is7xx_spi_com_read_1, 175 1.1 brad .com_write_1 = sc16is7xx_spi_com_write_1, 176 1.1 brad .com_write_multi_1 = sc16is7xx_spi_com_write_multi_1, 177 1.1 brad }; 178 1.1 brad 179 1.1 brad static int 180 1.1 brad sc16is7xxspi_match(device_t parent, cfdata_t match, void *aux) 181 1.1 brad { 182 1.1 brad struct spi_attach_args *sa = aux; 183 1.1 brad int match_result; 184 1.1 brad 185 1.1 brad if (spi_use_direct_match(sa, sc16is7xx_compat_data, &match_result)) { 186 1.1 brad return match_result; 187 1.1 brad } 188 1.1 brad return SPI_MATCH_DEFAULT; 189 1.1 brad } 190 1.1 brad 191 1.1 brad #ifndef SC16IS7XX_SPI_FREQUENCY 192 1.1 brad #define SC16IS7XX_SPI_FREQUENCY 1 193 1.1 brad #endif 194 1.1 brad 195 1.1 brad static void 196 1.1 brad sc16is7xxspi_attach(device_t parent, device_t self, void *aux) 197 1.1 brad { 198 1.1 brad struct sc16is7xx_spi_softc *ssc = device_private(self); 199 1.1 brad struct sc16is7xx_sc *sc = &ssc->sc_sc16is7xx; 200 1.1 brad struct spi_attach_args *sa = aux; 201 1.1 brad int error; 202 1.1 brad 203 1.1 brad sc->sc_dev = self; 204 1.1 brad sc->sc_funcs = &sc16is7xx_spi_accessfuncs; 205 1.1 brad sc->sc_com_funcs = &sc16is7xx_spi_com_accessfuncs; 206 1.1 brad 207 1.1 brad ssc->sc_sh = sa->sa_handle; 208 1.1 brad 209 1.1 brad aprint_normal("\n"); 210 1.1 brad aprint_normal_dev(sc->sc_dev, "SPI frequency %dMhz", SC16IS7XX_SPI_FREQUENCY); 211 1.1 brad 212 1.1 brad /* Configure for SPI mode 0 according to the data sheet. The chip will 213 1.1 brad * do up to 4Mhz or 15Mhz depending on the varient and does support 214 1.1 brad * other modes. */ 215 1.1 brad error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(SC16IS7XX_SPI_FREQUENCY)); 216 1.1 brad if (error) { 217 1.1 brad return; 218 1.1 brad } 219 1.1 brad sc16is7xx_attach(sc); 220 1.1 brad } 221 1.1 brad 222 1.1 brad static int 223 1.1 brad sc16is7xxspi_detach(device_t self, int flags) 224 1.1 brad { 225 1.1 brad struct sc16is7xx_spi_softc *ssc = device_private(self); 226 1.1 brad 227 1.1 brad return sc16is7xx_detach(&ssc->sc_sc16is7xx, flags); 228 1.1 brad } 229