1 1.19 thorpej /* $NetBSD: gttwsi_core.c,v 1.19 2025/09/15 13:23:03 thorpej Exp $ */ 2 1.1 matt /* 3 1.1 matt * Copyright (c) 2008 Eiji Kawauchi. 4 1.1 matt * All rights reserved. 5 1.1 matt * 6 1.1 matt * Redistribution and use in source and binary forms, with or without 7 1.1 matt * modification, are permitted provided that the following conditions 8 1.1 matt * are met: 9 1.1 matt * 1. Redistributions of source code must retain the above copyright 10 1.1 matt * notice, this list of conditions and the following disclaimer. 11 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 matt * notice, this list of conditions and the following disclaimer in the 13 1.1 matt * documentation and/or other materials provided with the distribution. 14 1.1 matt * 3. All advertising materials mentioning features or use of this software 15 1.1 matt * must display the following acknowledgement: 16 1.1 matt * This product includes software developed for the NetBSD Project by 17 1.1 matt * Eiji Kawauchi. 18 1.1 matt * 4. The name of the author may not be used to endorse or promote products 19 1.1 matt * derived from this software without specific prior written permission 20 1.1 matt * 21 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 1.1 matt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 1.1 matt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 1.1 matt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 1.1 matt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 1.1 matt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 1.1 matt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 1.1 matt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 1.1 matt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 1.1 matt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 1.1 matt */ 32 1.1 matt /* 33 1.1 matt * Copyright (c) 2005 Brocade Communcations, inc. 34 1.1 matt * All rights reserved. 35 1.1 matt * 36 1.1 matt * Written by Matt Thomas for Brocade Communcations, Inc. 37 1.1 matt * 38 1.1 matt * Redistribution and use in source and binary forms, with or without 39 1.1 matt * modification, are permitted provided that the following conditions 40 1.1 matt * are met: 41 1.1 matt * 1. Redistributions of source code must retain the above copyright 42 1.1 matt * notice, this list of conditions and the following disclaimer. 43 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 44 1.1 matt * notice, this list of conditions and the following disclaimer in the 45 1.1 matt * documentation and/or other materials provided with the distribution. 46 1.1 matt * 3. The name of Brocade Communications, Inc. may not be used to endorse 47 1.1 matt * or promote products derived from this software without specific prior 48 1.1 matt * written permission. 49 1.1 matt * 50 1.1 matt * THIS SOFTWARE IS PROVIDED BY BROCADE COMMUNICATIONS, INC. ``AS IS'' AND 51 1.1 matt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 1.1 matt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 1.1 matt * ARE DISCLAIMED. IN NO EVENT SHALL EITHER BROCADE COMMUNICATIONS, INC. BE 54 1.1 matt * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 60 1.1 matt * OF THE POSSIBILITY OF SUCH DAMAGE. 61 1.1 matt */ 62 1.1 matt //#define TWSI_DEBUG 63 1.1 matt 64 1.1 matt /* 65 1.1 matt * Marvell Two-Wire Serial Interface (aka I2C) master driver 66 1.1 matt */ 67 1.1 matt 68 1.1 matt #include <sys/cdefs.h> 69 1.19 thorpej __KERNEL_RCSID(0, "$NetBSD: gttwsi_core.c,v 1.19 2025/09/15 13:23:03 thorpej Exp $"); 70 1.1 matt #include "locators.h" 71 1.1 matt 72 1.1 matt #include <sys/param.h> 73 1.1 matt #include <sys/bus.h> 74 1.1 matt #include <sys/condvar.h> 75 1.1 matt #include <sys/device.h> 76 1.1 matt #include <sys/errno.h> 77 1.1 matt #include <sys/kernel.h> 78 1.1 matt #include <sys/mutex.h> 79 1.1 matt #include <sys/systm.h> 80 1.1 matt 81 1.1 matt #include <dev/i2c/i2cvar.h> 82 1.1 matt 83 1.1 matt #include <dev/i2c/gttwsireg.h> 84 1.1 matt #include <dev/i2c/gttwsivar.h> 85 1.1 matt 86 1.1 matt static int gttwsi_send_start(void *v, int flags); 87 1.1 matt static int gttwsi_send_stop(void *v, int flags); 88 1.1 matt static int gttwsi_initiate_xfer(void *v, i2c_addr_t addr, int flags); 89 1.1 matt static int gttwsi_read_byte(void *v, uint8_t *valp, int flags); 90 1.1 matt static int gttwsi_write_byte(void *v, uint8_t val, int flags); 91 1.1 matt 92 1.6 thorpej static int gttwsi_wait(struct gttwsi_softc *, uint32_t, uint32_t, 93 1.13 thorpej uint32_t, int, const char *); 94 1.1 matt 95 1.12 thorpej uint32_t 96 1.12 thorpej gttwsi_read_4(struct gttwsi_softc *sc, uint32_t reg) 97 1.1 matt { 98 1.12 thorpej const uint32_t val = bus_space_read_4(sc->sc_bust, sc->sc_bush, 99 1.12 thorpej sc->sc_regmap[reg]); 100 1.1 matt #ifdef TWSI_DEBUG 101 1.14 martin printf("I2C:R:[%" PRIu32 "]%02" PRIxBUSSIZE ":%02" PRIx32 "\n", reg, sc->sc_regmap[reg], val); 102 1.1 matt #else 103 1.1 matt DELAY(TWSI_READ_DELAY); 104 1.1 matt #endif 105 1.1 matt return val; 106 1.1 matt } 107 1.1 matt 108 1.12 thorpej void 109 1.12 thorpej gttwsi_write_4(struct gttwsi_softc *sc, uint32_t reg, uint32_t val) 110 1.1 matt { 111 1.12 thorpej 112 1.15 thorpej bus_space_write_4(sc->sc_bust, sc->sc_bush, sc->sc_regmap[reg], val); 113 1.1 matt #ifdef TWSI_DEBUG 114 1.14 martin printf("I2C:W:[%" PRIu32 "]%02" PRIxBUSSIZE ":%02" PRIx32 "\n", reg, sc->sc_regmap[reg], val); 115 1.1 matt #else 116 1.1 matt DELAY(TWSI_WRITE_DELAY); 117 1.1 matt #endif 118 1.3 jmcneill } 119 1.1 matt 120 1.1 matt /* ARGSUSED */ 121 1.1 matt void 122 1.12 thorpej gttwsi_attach_subr(device_t self, bus_space_tag_t iot, bus_space_handle_t ioh, 123 1.12 thorpej const bus_size_t *regmap) 124 1.1 matt { 125 1.1 matt struct gttwsi_softc * const sc = device_private(self); 126 1.2 jmcneill prop_dictionary_t cfg = device_properties(self); 127 1.1 matt 128 1.1 matt aprint_naive("\n"); 129 1.1 matt aprint_normal(": Marvell TWSI controller\n"); 130 1.1 matt 131 1.1 matt sc->sc_dev = self; 132 1.1 matt sc->sc_bust = iot; 133 1.1 matt sc->sc_bush = ioh; 134 1.12 thorpej sc->sc_regmap = regmap; 135 1.3 jmcneill 136 1.1 matt mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_BIO); 137 1.1 matt cv_init(&sc->sc_cv, device_xname(self)); 138 1.1 matt 139 1.2 jmcneill prop_dictionary_get_bool(cfg, "iflg-rwc", &sc->sc_iflg_rwc); 140 1.2 jmcneill 141 1.1 matt sc->sc_started = false; 142 1.9 thorpej iic_tag_init(&sc->sc_i2c); 143 1.1 matt sc->sc_i2c.ic_cookie = sc; 144 1.1 matt sc->sc_i2c.ic_send_start = gttwsi_send_start; 145 1.1 matt sc->sc_i2c.ic_send_stop = gttwsi_send_stop; 146 1.1 matt sc->sc_i2c.ic_initiate_xfer = gttwsi_initiate_xfer; 147 1.1 matt sc->sc_i2c.ic_read_byte = gttwsi_read_byte; 148 1.1 matt sc->sc_i2c.ic_write_byte = gttwsi_write_byte; 149 1.1 matt 150 1.1 matt /* 151 1.1 matt * Put the controller into Soft Reset. 152 1.1 matt */ 153 1.1 matt /* reset */ 154 1.1 matt gttwsi_write_4(sc, TWSI_SOFTRESET, SOFTRESET_VAL); 155 1.1 matt } 156 1.1 matt 157 1.1 matt void 158 1.1 matt gttwsi_config_children(device_t self) 159 1.1 matt { 160 1.1 matt struct gttwsi_softc * const sc = device_private(self); 161 1.1 matt 162 1.19 thorpej iicbus_attach(self, &sc->sc_i2c); 163 1.1 matt } 164 1.1 matt 165 1.1 matt int 166 1.1 matt gttwsi_intr(void *arg) 167 1.1 matt { 168 1.1 matt struct gttwsi_softc *sc = arg; 169 1.1 matt uint32_t val; 170 1.1 matt 171 1.8 bouyer mutex_enter(&sc->sc_mtx); 172 1.1 matt val = gttwsi_read_4(sc, TWSI_CONTROL); 173 1.1 matt if (val & CONTROL_IFLG) { 174 1.1 matt gttwsi_write_4(sc, TWSI_CONTROL, val & ~CONTROL_INTEN); 175 1.5 jmcneill cv_broadcast(&sc->sc_cv); 176 1.1 matt mutex_exit(&sc->sc_mtx); 177 1.1 matt return 1; /* handled */ 178 1.1 matt } 179 1.8 bouyer mutex_exit(&sc->sc_mtx); 180 1.1 matt return 0; 181 1.1 matt } 182 1.1 matt 183 1.1 matt static int 184 1.1 matt gttwsi_send_start(void *v, int flags) 185 1.1 matt { 186 1.1 matt struct gttwsi_softc *sc = v; 187 1.1 matt int expect; 188 1.1 matt 189 1.1 matt if (sc->sc_started) 190 1.1 matt expect = STAT_RSCT; 191 1.1 matt else 192 1.1 matt expect = STAT_SCT; 193 1.1 matt sc->sc_started = true; 194 1.13 thorpej return gttwsi_wait(sc, CONTROL_START, expect, 0, flags, "send-start"); 195 1.1 matt } 196 1.1 matt 197 1.1 matt static int 198 1.1 matt gttwsi_send_stop(void *v, int flags) 199 1.1 matt { 200 1.1 matt struct gttwsi_softc *sc = v; 201 1.1 matt int retry = TWSI_RETRY_COUNT; 202 1.13 thorpej uint32_t control, status; 203 1.1 matt 204 1.1 matt sc->sc_started = false; 205 1.1 matt 206 1.1 matt /* Interrupt is not generated for STAT_NRS. */ 207 1.2 jmcneill control = CONTROL_STOP | CONTROL_TWSIEN; 208 1.2 jmcneill if (sc->sc_iflg_rwc) 209 1.2 jmcneill control |= CONTROL_IFLG; 210 1.2 jmcneill gttwsi_write_4(sc, TWSI_CONTROL, control); 211 1.1 matt while (retry > 0) { 212 1.13 thorpej if ((status = gttwsi_read_4(sc, TWSI_STATUS)) == STAT_NRS) 213 1.1 matt return 0; 214 1.1 matt retry--; 215 1.1 matt DELAY(TWSI_STAT_DELAY); 216 1.1 matt } 217 1.1 matt 218 1.13 thorpej aprint_error_dev(sc->sc_dev, "send STOP failed, status=0x%02x\n", 219 1.13 thorpej status); 220 1.13 thorpej return EWOULDBLOCK; 221 1.1 matt } 222 1.1 matt 223 1.1 matt static int 224 1.1 matt gttwsi_initiate_xfer(void *v, i2c_addr_t addr, int flags) 225 1.1 matt { 226 1.1 matt struct gttwsi_softc *sc = v; 227 1.6 thorpej uint32_t data, expect, alt; 228 1.1 matt int error, read; 229 1.1 matt 230 1.7 jakllsch error = gttwsi_send_start(v, flags); 231 1.7 jakllsch if (error) 232 1.7 jakllsch return error; 233 1.1 matt 234 1.1 matt read = (flags & I2C_F_READ) != 0; 235 1.6 thorpej if (read) { 236 1.1 matt expect = STAT_ARBT_AR; 237 1.6 thorpej alt = STAT_ARBT_ANR; 238 1.6 thorpej } else { 239 1.1 matt expect = STAT_AWBT_AR; 240 1.6 thorpej alt = STAT_AWBT_ANR; 241 1.6 thorpej } 242 1.1 matt 243 1.1 matt /* 244 1.1 matt * First byte contains whether this xfer is a read or write. 245 1.1 matt */ 246 1.1 matt data = read; 247 1.1 matt if (addr > 0x7f) { 248 1.1 matt /* 249 1.1 matt * If this is a 10bit request, the first address byte is 250 1.1 matt * 0b11110<b9><b8><r/w>. 251 1.1 matt */ 252 1.1 matt data |= 0xf0 | ((addr & 0x300) >> 7); 253 1.1 matt gttwsi_write_4(sc, TWSI_DATA, data); 254 1.13 thorpej error = gttwsi_wait(sc, 0, expect, alt, flags, "send-addr-10"); 255 1.1 matt if (error) 256 1.1 matt return error; 257 1.1 matt /* 258 1.1 matt * The first address byte has been sent, now to send 259 1.1 matt * the second one. 260 1.1 matt */ 261 1.6 thorpej if (read) { 262 1.1 matt expect = STAT_SARBT_AR; 263 1.6 thorpej alt = STAT_SARBT_ANR; 264 1.6 thorpej } else { 265 1.1 matt expect = STAT_SAWBT_AR; 266 1.6 thorpej alt = STAT_SAWBT_ANR; 267 1.6 thorpej } 268 1.1 matt data = (uint8_t)addr; 269 1.1 matt } else 270 1.1 matt data |= (addr << 1); 271 1.1 matt 272 1.1 matt gttwsi_write_4(sc, TWSI_DATA, data); 273 1.13 thorpej return gttwsi_wait(sc, 0, expect, alt, flags, "send-addr"); 274 1.1 matt } 275 1.1 matt 276 1.1 matt static int 277 1.1 matt gttwsi_read_byte(void *v, uint8_t *valp, int flags) 278 1.1 matt { 279 1.1 matt struct gttwsi_softc *sc = v; 280 1.1 matt int error; 281 1.1 matt 282 1.13 thorpej if (flags & I2C_F_LAST) { 283 1.13 thorpej error = gttwsi_wait(sc, 0, STAT_MRRD_ANT, 0, flags, 284 1.13 thorpej "read-last-byte"); 285 1.13 thorpej } else { 286 1.13 thorpej error = gttwsi_wait(sc, CONTROL_ACK, STAT_MRRD_AT, 0, flags, 287 1.13 thorpej "read-byte"); 288 1.13 thorpej } 289 1.1 matt if (!error) 290 1.1 matt *valp = gttwsi_read_4(sc, TWSI_DATA); 291 1.1 matt if ((flags & (I2C_F_LAST | I2C_F_STOP)) == (I2C_F_LAST | I2C_F_STOP)) 292 1.1 matt error = gttwsi_send_stop(sc, flags); 293 1.1 matt return error; 294 1.1 matt } 295 1.1 matt 296 1.1 matt static int 297 1.1 matt gttwsi_write_byte(void *v, uint8_t val, int flags) 298 1.1 matt { 299 1.1 matt struct gttwsi_softc *sc = v; 300 1.1 matt int error; 301 1.1 matt 302 1.1 matt gttwsi_write_4(sc, TWSI_DATA, val); 303 1.13 thorpej error = gttwsi_wait(sc, 0, STAT_MTDB_AR, 0, flags, "write-byte"); 304 1.1 matt if (flags & I2C_F_STOP) 305 1.1 matt gttwsi_send_stop(sc, flags); 306 1.1 matt return error; 307 1.1 matt } 308 1.1 matt 309 1.1 matt static int 310 1.1 matt gttwsi_wait(struct gttwsi_softc *sc, uint32_t control, uint32_t expect, 311 1.13 thorpej uint32_t alt, int flags, const char *what) 312 1.1 matt { 313 1.1 matt uint32_t status; 314 1.1 matt int timo, error = 0; 315 1.1 matt 316 1.16 thorpej /* 317 1.16 thorpej * XXX Interrupt-driven mode seems to be horribly broken, 318 1.16 thorpej * XXX at least on AllWinner implementations. Force polled 319 1.16 thorpej * XXX mode for now. 320 1.16 thorpej */ 321 1.16 thorpej flags |= I2C_F_POLL; 322 1.16 thorpej 323 1.1 matt DELAY(5); 324 1.1 matt if (!(flags & I2C_F_POLL)) 325 1.1 matt control |= CONTROL_INTEN; 326 1.2 jmcneill if (sc->sc_iflg_rwc) 327 1.2 jmcneill control |= CONTROL_IFLG; 328 1.8 bouyer mutex_enter(&sc->sc_mtx); 329 1.1 matt gttwsi_write_4(sc, TWSI_CONTROL, control | CONTROL_TWSIEN); 330 1.1 matt 331 1.1 matt timo = 0; 332 1.1 matt for (;;) { 333 1.1 matt control = gttwsi_read_4(sc, TWSI_CONTROL); 334 1.1 matt if (control & CONTROL_IFLG) 335 1.1 matt break; 336 1.1 matt if (!(flags & I2C_F_POLL)) { 337 1.11 thorpej error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz); 338 1.13 thorpej if (error) { 339 1.8 bouyer break; 340 1.13 thorpej } 341 1.8 bouyer } else { 342 1.8 bouyer DELAY(TWSI_RETRY_DELAY); 343 1.8 bouyer if (timo++ > 1000000) /* 1sec */ 344 1.8 bouyer break; 345 1.1 matt } 346 1.1 matt } 347 1.8 bouyer if ((control & CONTROL_IFLG) == 0) { 348 1.13 thorpej /* 349 1.13 thorpej * error is set by the cv_timedwait() call above in the 350 1.13 thorpej * non-polled case. 351 1.13 thorpej */ 352 1.13 thorpej if (flags & I2C_F_POLL) { 353 1.13 thorpej error = EWOULDBLOCK; 354 1.13 thorpej } else { 355 1.13 thorpej KASSERT(error != 0); 356 1.13 thorpej } 357 1.8 bouyer aprint_error_dev(sc->sc_dev, 358 1.13 thorpej "gttwsi_wait(): %s timeout%s, control=0x%x, error=%d\n", 359 1.13 thorpej what, (flags & I2C_F_POLL) ? " (polled)" : "", 360 1.13 thorpej control, error); 361 1.8 bouyer goto end; 362 1.8 bouyer } 363 1.1 matt status = gttwsi_read_4(sc, TWSI_STATUS); 364 1.1 matt if (status != expect) { 365 1.6 thorpej /* 366 1.6 thorpej * In the case of probing for a device, we are expecting 367 1.6 thorpej * 2 different status codes: the ACK case (device exists), 368 1.6 thorpej * or the NACK case (device does not exist). We don't 369 1.6 thorpej * need to report an error in the later case. 370 1.6 thorpej */ 371 1.6 thorpej if (alt != 0 && status != alt) 372 1.6 thorpej aprint_error_dev(sc->sc_dev, 373 1.6 thorpej "unexpected status 0x%x: expect 0x%x\n", status, 374 1.6 thorpej expect); 375 1.8 bouyer error = EIO; 376 1.1 matt } 377 1.13 thorpej end: 378 1.8 bouyer mutex_exit(&sc->sc_mtx); 379 1.1 matt return error; 380 1.1 matt } 381