1 /* $Id: at91twi.c,v 1.11 2025/09/15 13:23:00 thorpej Exp $ */ 2 /* $NetBSD: at91twi.c,v 1.11 2025/09/15 13:23:00 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 2007 Embedtronics Oy. All rights reserved. 6 * 7 * Based on arch/macppc/dev/ki2c.c, 8 * Copyright (c) 2001 Tsubai Masanari. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: at91twi.c,v 1.11 2025/09/15 13:23:00 thorpej Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/device.h> 38 #include <sys/systm.h> 39 40 #include <sys/bus.h> 41 #include <sys/lock.h> 42 43 #include <arm/at91/at91var.h> 44 #include <arm/at91/at91reg.h> 45 46 #include <dev/i2c/i2cvar.h> 47 #include <arm/at91/at91twivar.h> 48 #include <arm/at91/at91twireg.h> 49 50 int at91twi_match(device_t, cfdata_t, void *); 51 void at91twi_attach(device_t, device_t, void *); 52 inline u_int at91twi_readreg(struct at91twi_softc *, int); 53 inline void at91twi_writereg(struct at91twi_softc *, int, u_int); 54 int at91twi_intr(void *); 55 int at91twi_poll(struct at91twi_softc *, int, int); 56 int at91twi_start(struct at91twi_softc *, int, void *, int, int); 57 int at91twi_read(struct at91twi_softc *, int, void *, int, int); 58 int at91twi_write(struct at91twi_softc *, int, void *, int, int); 59 60 /* I2C glue */ 61 static int at91twi_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 62 void *, size_t, int); 63 64 65 CFATTACH_DECL_NEW(at91twi, sizeof(struct at91twi_softc), 66 at91twi_match, at91twi_attach, NULL, NULL); 67 68 int 69 at91twi_match(device_t parent, cfdata_t match, void *aux) 70 { 71 if (strcmp(match->cf_name, "at91twi") == 0) 72 return 2; 73 return 0; 74 } 75 76 void 77 at91twi_attach(device_t parent, device_t self, void *aux) 78 { 79 struct at91twi_softc *sc = device_private(self); 80 struct at91bus_attach_args *sa = aux; 81 unsigned ckdiv, cxdiv; 82 83 // gather attach data: 84 sc->sc_dev = self; 85 sc->sc_iot = sa->sa_iot; 86 sc->sc_pid = sa->sa_pid; 87 88 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0, &sc->sc_ioh)) 89 panic("%s: Cannot map registers", device_xname(self)); 90 91 printf(": I2C controller\n"); 92 93 /* initialize I2C controller */ 94 at91_peripheral_clock(sc->sc_pid, 1); 95 96 at91twi_writereg(sc, TWI_CR, TWI_CR_SWRST); 97 delay(1000); 98 #if 1 99 // target to 100 kHz 100 for (ckdiv = 0; ckdiv < 8; ckdiv++) { 101 if ((cxdiv = (AT91_MSTCLK / (1U << ckdiv)) / (2 * 50000U)) < 256) { 102 goto found_ckdiv; 103 } 104 } 105 panic("%s: Cannot calculate clock divider!", __FUNCTION__); 106 107 found_ckdiv: 108 #else 109 ckdiv = 5; cxdiv = 0xFF; 110 #endif 111 at91twi_writereg(sc, TWI_CWGR, (ckdiv << 16) | (cxdiv << 8) | cxdiv); 112 at91twi_writereg(sc, TWI_CR, TWI_CR_MSEN); 113 114 //#ifdef AT91TWI_DEBUG 115 printf("%s: ckdiv=%d cxdiv=%d CWGR=0x%08X SR=0x%08X\n", device_xname(self), ckdiv, cxdiv, at91twi_readreg(sc, TWI_CWGR), at91twi_readreg(sc, TWI_SR)); 116 //#endif 117 118 /* initialize rest */ 119 sc->sc_ih = at91_intr_establish(sc->sc_pid, IPL_SERIAL, INTR_HIGH_LEVEL, 120 at91twi_intr, sc); 121 122 /* fill in the i2c tag */ 123 iic_tag_init(&sc->sc_i2c); 124 sc->sc_i2c.ic_cookie = sc; 125 sc->sc_i2c.ic_exec = at91twi_i2c_exec; 126 127 iicbus_attach(sc->sc_dev, &sc->sc_i2c); 128 } 129 130 u_int 131 at91twi_readreg(struct at91twi_softc *sc, int reg) 132 { 133 return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg); 134 } 135 136 void 137 at91twi_writereg(struct at91twi_softc *sc, int reg, u_int val) 138 { 139 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); 140 } 141 142 int 143 at91twi_intr(void *arg) 144 { 145 struct at91twi_softc *sc = arg; 146 u_int sr, isr, imr; 147 148 sr = at91twi_readreg(sc, TWI_SR); 149 imr = at91twi_readreg(sc, TWI_IMR); 150 isr = sr & imr; 151 152 if (!isr) { 153 #ifdef AT91TWI_DEBUG 154 // printf("%s(%s): interrupts are disabled (sr=%08X imr=%08X)\n", __FUNCTION__, device_xname(sc->sc_dev), sr, imr); 155 #endif 156 return 0; 157 } 158 159 if (isr & TWI_SR_TXCOMP) { 160 // transmission has completed! 161 if (sr & (TWI_SR_NACK | TWI_SR_UNRE | TWI_SR_OVRE)) { 162 // failed! 163 #ifdef AT91TWI_DEBUG 164 printf("%s(%s): FAILED (sr=%08X)\n", __FUNCTION__, 165 device_xname(sc->sc_dev), sr); 166 #endif 167 sc->sc_flags |= I2C_ERROR; 168 } else { 169 #ifdef AT91TWI_DEBUG 170 printf("%s(%s): SUCCESS (sr=%08X)\n", __FUNCTION__, 171 device_xname(sc->sc_dev), sr); 172 #endif 173 } 174 if (sc->sc_flags & I2C_READING && sr & TWI_SR_RXRDY) { 175 *sc->sc_data++ = at91twi_readreg(sc, TWI_RHR); 176 sc->sc_resid--; 177 } 178 sc->sc_flags &= ~I2C_BUSY; 179 at91twi_writereg(sc, TWI_IDR, -1); 180 goto out; 181 } 182 183 if (isr & TWI_SR_TXRDY) { 184 if (--sc->sc_resid > 0) 185 at91twi_writereg(sc, TWI_THR, *sc->sc_data++); 186 } 187 188 if (isr & TWI_SR_RXRDY) { 189 // data has been received 190 *sc->sc_data++ = at91twi_readreg(sc, TWI_RHR); 191 sc->sc_resid--; 192 } 193 194 if (isr & (TWI_SR_TXRDY | TWI_SR_RXRDY) && sc->sc_resid <= 0) { 195 // all bytes have been transmitted, send stop condition 196 at91twi_writereg(sc, TWI_IDR, TWI_SR_RXRDY | TWI_SR_TXRDY); 197 at91twi_writereg(sc, TWI_CR, TWI_CR_STOP); 198 } 199 out: 200 return 1; 201 } 202 203 int 204 at91twi_poll(struct at91twi_softc *sc, int timo, int flags) 205 { 206 207 timo = 1000000U; 208 209 while (sc->sc_flags & I2C_BUSY) { 210 if (timo < 0) { 211 printf("i2c_poll: timeout\n"); 212 return -1; 213 } 214 if (flags & I2C_F_POLL) { 215 at91_intr_poll(sc->sc_ih, 1); 216 delay(1); 217 timo--; 218 } else { 219 delay(100); // @@@ sleep!? 220 timo -= 100; 221 } 222 } 223 return 0; 224 } 225 226 int 227 at91twi_start(struct at91twi_softc *sc, int addr, void *data, int len, 228 int flags) 229 { 230 int rd = (sc->sc_flags & I2C_READING); 231 int timo, s; 232 233 KASSERT((addr & 1) == 0); 234 235 sc->sc_data = data; 236 sc->sc_resid = len; 237 sc->sc_flags |= I2C_BUSY; 238 239 timo = 1000 + len * 200; 240 241 s = splserial(); 242 // if writing, queue first byte immediately 243 if (!rd) 244 at91twi_writereg(sc, TWI_THR, *sc->sc_data++); 245 // if there's just one byte to transmit, we must set STOP-bit too 246 if (sc->sc_resid == 1) { 247 at91twi_writereg(sc, TWI_IER, TWI_SR_TXCOMP); 248 at91twi_writereg(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP); 249 } else { 250 at91twi_writereg(sc, TWI_IER, TWI_SR_TXCOMP 251 | (rd ? TWI_SR_RXRDY : TWI_SR_TXRDY)); 252 at91twi_writereg(sc, TWI_CR, TWI_CR_START); 253 } 254 splx(s); 255 256 if (at91twi_poll(sc, timo, flags)) 257 return -1; 258 if (sc->sc_flags & I2C_ERROR) { 259 printf("I2C_ERROR\n"); 260 return -1; 261 } 262 return 0; 263 } 264 265 int 266 at91twi_read(struct at91twi_softc *sc, int addr, void *data, int len, int flags) 267 { 268 sc->sc_flags = I2C_READING; 269 #ifdef AT91TWI_DEBUG 270 printf("at91twi_read: %02x %d\n", addr, len); 271 #endif 272 return at91twi_start(sc, addr, data, len, flags); 273 } 274 275 int 276 at91twi_write(struct at91twi_softc *sc, int addr, void *data, int len, int flags) 277 { 278 sc->sc_flags = 0; 279 #ifdef AT91TWI_DEBUG 280 printf("at91twi_write: %02x %d\n", addr, len); 281 #endif 282 return at91twi_start(sc, addr, data, len, flags); 283 } 284 285 int 286 at91twi_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd, 287 size_t cmdlen, void *vbuf, size_t buflen, int flags) 288 { 289 struct at91twi_softc *sc = cookie; 290 291 if (I2C_OP_READ_P(op)) 292 { 293 u_int iadr = 0; 294 if (vcmd) { 295 const uint8_t *cmd = (const uint8_t *)vcmd; 296 if (cmdlen > 3) { 297 // we're in trouble.. 298 return -1; 299 } 300 iadr = cmd[0]; 301 if (cmdlen > 1) { 302 iadr <<= 8; 303 iadr |= cmd[1]; 304 } 305 if (cmdlen > 2) { 306 iadr <<= 8; 307 iadr |= cmd[2]; 308 } 309 } 310 at91twi_writereg(sc, TWI_MMR, (addr << 16) | TWI_MMR_MREAD | (cmdlen << 8)); 311 if (cmdlen > 0) { 312 #ifdef AT91TWI_DEBUG 313 printf("at91twi_read: %02x iadr=%08X mmr=%08X\n", 314 addr, iadr, at91twi_readreg(sc, TWI_MMR)); 315 #endif 316 at91twi_writereg(sc, TWI_IADR, iadr); 317 } 318 if (at91twi_read(sc, addr, vbuf, buflen, flags) != 0) 319 return -1; 320 } else if (vcmd) { 321 at91twi_writereg(sc, TWI_MMR, addr << 16); 322 if (at91twi_write(sc, addr, __UNCONST(vcmd), cmdlen, flags) !=0) 323 return -1; 324 } 325 return 0; 326 } 327