1*80bce6dbSderaadt /* $OpenBSD: dwiic.c,v 1.21 2024/08/17 02:35:00 deraadt Exp $ */ 2c6df0db7Sjcs /* 3c6df0db7Sjcs * Synopsys DesignWare I2C controller 4c6df0db7Sjcs * 5c6df0db7Sjcs * Copyright (c) 2015-2017 joshua stein <jcs@openbsd.org> 6c6df0db7Sjcs * 7c6df0db7Sjcs * Permission to use, copy, modify, and/or distribute this software for any 8c6df0db7Sjcs * purpose with or without fee is hereby granted, provided that the above 9c6df0db7Sjcs * copyright notice and this permission notice appear in all copies. 10c6df0db7Sjcs * 11c6df0db7Sjcs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12c6df0db7Sjcs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13c6df0db7Sjcs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14c6df0db7Sjcs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15c6df0db7Sjcs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16c6df0db7Sjcs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17c6df0db7Sjcs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18c6df0db7Sjcs */ 19c6df0db7Sjcs 20c6df0db7Sjcs #include <sys/param.h> 21c6df0db7Sjcs #include <sys/systm.h> 22c6df0db7Sjcs #include <sys/kernel.h> 23c6df0db7Sjcs 24c6df0db7Sjcs #include <dev/i2c/i2cvar.h> 25c6df0db7Sjcs 26c6df0db7Sjcs #include <dev/ic/dwiicvar.h> 27c6df0db7Sjcs 28c6df0db7Sjcs struct cfdriver dwiic_cd = { 29c6df0db7Sjcs NULL, "dwiic", DV_DULL 30c6df0db7Sjcs }; 31c6df0db7Sjcs 32c6df0db7Sjcs int 33c6df0db7Sjcs dwiic_activate(struct device *self, int act) 34c6df0db7Sjcs { 35c6df0db7Sjcs struct dwiic_softc *sc = (struct dwiic_softc *)self; 3699dfaab1Sderaadt int rv; 37c6df0db7Sjcs 38c6df0db7Sjcs switch (act) { 39c6df0db7Sjcs case DVACT_SUSPEND: 4099dfaab1Sderaadt rv = config_activate_children(self, act); 41c6df0db7Sjcs /* disable controller */ 42c6df0db7Sjcs dwiic_enable(sc, 0); 43c6df0db7Sjcs 44c6df0db7Sjcs /* disable interrupts */ 45c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, 0); 46c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_INTR); 47c6df0db7Sjcs break; 480fa92fe2Sderaadt case DVACT_RESUME: 49c6b110bcSderaadt /* if it became enabled for some reason, force it down */ 50c6b110bcSderaadt dwiic_enable(sc, 0); 51c6b110bcSderaadt 52c6b110bcSderaadt dwiic_write(sc, DW_IC_INTR_MASK, 0); 53c6b110bcSderaadt dwiic_read(sc, DW_IC_CLR_INTR); 54c6b110bcSderaadt 55c6b110bcSderaadt /* write standard-mode SCL timing parameters */ 56c6b110bcSderaadt dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt); 57c6b110bcSderaadt dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt); 58c6b110bcSderaadt 59c6b110bcSderaadt /* and fast-mode SCL timing parameters */ 60c6b110bcSderaadt dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt); 61c6b110bcSderaadt dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt); 62c6b110bcSderaadt 63c6b110bcSderaadt /* SDA hold time */ 64c6b110bcSderaadt dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time); 65c6b110bcSderaadt 66c6b110bcSderaadt dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2); 67c6b110bcSderaadt dwiic_write(sc, DW_IC_RX_TL, 0); 68c6b110bcSderaadt 69c6b110bcSderaadt /* configure as i2c master with fast speed */ 70c6b110bcSderaadt sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | 71c6b110bcSderaadt DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; 72c6b110bcSderaadt dwiic_write(sc, DW_IC_CON, sc->master_cfg); 7399dfaab1Sderaadt rv = config_activate_children(self, act); 7499dfaab1Sderaadt break; 7599dfaab1Sderaadt default: 7699dfaab1Sderaadt rv = config_activate_children(self, act); 77c6df0db7Sjcs break; 78c6df0db7Sjcs } 7999dfaab1Sderaadt return rv; 80c6df0db7Sjcs } 81c6df0db7Sjcs 82c6df0db7Sjcs int 83c6df0db7Sjcs dwiic_i2c_print(void *aux, const char *pnp) 84c6df0db7Sjcs { 85c6df0db7Sjcs struct i2c_attach_args *ia = aux; 86c6df0db7Sjcs 87c6df0db7Sjcs if (pnp != NULL) 883eb71d7dSkettenis printf("\"%s\" at %s", ia->ia_name, pnp); 89c6df0db7Sjcs 90c6df0db7Sjcs printf(" addr 0x%x", ia->ia_addr); 91c6df0db7Sjcs 92c6df0db7Sjcs return UNCONF; 93c6df0db7Sjcs } 94c6df0db7Sjcs 95c6df0db7Sjcs uint32_t 96c6df0db7Sjcs dwiic_read(struct dwiic_softc *sc, int offset) 97c6df0db7Sjcs { 98c6df0db7Sjcs u_int32_t b = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); 99c6df0db7Sjcs 100c6df0db7Sjcs DPRINTF(("%s: read at 0x%x = 0x%x\n", sc->sc_dev.dv_xname, offset, b)); 101c6df0db7Sjcs 102c6df0db7Sjcs return b; 103c6df0db7Sjcs } 104c6df0db7Sjcs 105c6df0db7Sjcs void 106c6df0db7Sjcs dwiic_write(struct dwiic_softc *sc, int offset, uint32_t val) 107c6df0db7Sjcs { 108c6df0db7Sjcs DPRINTF(("%s: write at 0x%x: 0x%x\n", sc->sc_dev.dv_xname, offset, 109c6df0db7Sjcs val)); 110c6df0db7Sjcs 111c6df0db7Sjcs bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val); 112c6df0db7Sjcs } 113c6df0db7Sjcs 114c6df0db7Sjcs int 115c6df0db7Sjcs dwiic_i2c_acquire_bus(void *cookie, int flags) 116c6df0db7Sjcs { 117c6df0db7Sjcs struct dwiic_softc *sc = cookie; 118c6df0db7Sjcs 119c6df0db7Sjcs if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 120c6df0db7Sjcs return (0); 121c6df0db7Sjcs 122c6df0db7Sjcs return rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR); 123c6df0db7Sjcs } 124c6df0db7Sjcs 125c6df0db7Sjcs void 126c6df0db7Sjcs dwiic_i2c_release_bus(void *cookie, int flags) 127c6df0db7Sjcs { 128c6df0db7Sjcs struct dwiic_softc *sc = cookie; 129c6df0db7Sjcs 130c6df0db7Sjcs if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 131c6df0db7Sjcs return; 132c6df0db7Sjcs 133c6df0db7Sjcs rw_exit(&sc->sc_i2c_lock); 134c6df0db7Sjcs } 135c6df0db7Sjcs 136c6df0db7Sjcs int 137c6df0db7Sjcs dwiic_init(struct dwiic_softc *sc) 138c6df0db7Sjcs { 139c6df0db7Sjcs uint32_t reg; 140a097e4f1Skettenis uint8_t tx_fifo_depth; 141a097e4f1Skettenis uint8_t rx_fifo_depth; 142c6df0db7Sjcs 143c6df0db7Sjcs /* make sure we're talking to a device we know */ 144c6df0db7Sjcs reg = dwiic_read(sc, DW_IC_COMP_TYPE); 145c6df0db7Sjcs if (reg != DW_IC_COMP_TYPE_VALUE) { 146c6df0db7Sjcs DPRINTF(("%s: invalid component type 0x%x\n", 147c6df0db7Sjcs sc->sc_dev.dv_xname, reg)); 148c6df0db7Sjcs return 1; 149c6df0db7Sjcs } 150c6df0db7Sjcs 1512567c03bSjcs /* fetch default timing parameters if not already specified */ 1522567c03bSjcs if (!sc->ss_hcnt) 1532567c03bSjcs sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT); 1542567c03bSjcs if (!sc->ss_lcnt) 1552567c03bSjcs sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT); 1562567c03bSjcs if (!sc->fs_hcnt) 1572567c03bSjcs sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT); 1582567c03bSjcs if (!sc->fs_lcnt) 1592567c03bSjcs sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT); 1602567c03bSjcs if (!sc->sda_hold_time) 1612567c03bSjcs sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD); 1622567c03bSjcs 163c6df0db7Sjcs /* disable the adapter */ 164c6df0db7Sjcs dwiic_enable(sc, 0); 165c6df0db7Sjcs 166c6df0db7Sjcs /* write standard-mode SCL timing parameters */ 167c6df0db7Sjcs dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt); 168c6df0db7Sjcs dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt); 169c6df0db7Sjcs 170c6df0db7Sjcs /* and fast-mode SCL timing parameters */ 171c6df0db7Sjcs dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt); 172c6df0db7Sjcs dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt); 173c6df0db7Sjcs 174c6df0db7Sjcs /* SDA hold time */ 175c6df0db7Sjcs reg = dwiic_read(sc, DW_IC_COMP_VERSION); 176c6df0db7Sjcs if (reg >= DW_IC_SDA_HOLD_MIN_VERS) 177c6df0db7Sjcs dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time); 178c6df0db7Sjcs 179c6df0db7Sjcs /* FIFO threshold levels */ 180c6df0db7Sjcs sc->tx_fifo_depth = 32; 181c6df0db7Sjcs sc->rx_fifo_depth = 32; 182a097e4f1Skettenis reg = dwiic_read(sc, DW_IC_COMP_PARAM_1); 183a097e4f1Skettenis tx_fifo_depth = DW_IC_TX_FIFO_DEPTH(reg); 184a097e4f1Skettenis rx_fifo_depth = DW_IC_RX_FIFO_DEPTH(reg); 185a097e4f1Skettenis if (tx_fifo_depth > 1 && tx_fifo_depth < sc->tx_fifo_depth) 186a097e4f1Skettenis sc->tx_fifo_depth = tx_fifo_depth; 187a097e4f1Skettenis if (rx_fifo_depth > 1 && rx_fifo_depth < sc->rx_fifo_depth) 188a097e4f1Skettenis sc->rx_fifo_depth = rx_fifo_depth; 189a097e4f1Skettenis 190c6df0db7Sjcs dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2); 191c6df0db7Sjcs dwiic_write(sc, DW_IC_RX_TL, 0); 192c6df0db7Sjcs 193c6df0db7Sjcs /* configure as i2c master with fast speed */ 194c6df0db7Sjcs sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | 195c6df0db7Sjcs DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; 196c6df0db7Sjcs dwiic_write(sc, DW_IC_CON, sc->master_cfg); 197c6df0db7Sjcs 198c6df0db7Sjcs return 0; 199c6df0db7Sjcs } 200c6df0db7Sjcs 201c6df0db7Sjcs void 202c6df0db7Sjcs dwiic_enable(struct dwiic_softc *sc, int enable) 203c6df0db7Sjcs { 204c6df0db7Sjcs int retries; 205c6df0db7Sjcs 206c6df0db7Sjcs for (retries = 100; retries > 0; retries--) { 207c6df0db7Sjcs dwiic_write(sc, DW_IC_ENABLE, enable); 208c6df0db7Sjcs if ((dwiic_read(sc, DW_IC_ENABLE_STATUS) & 1) == enable) 209c6df0db7Sjcs return; 210c6df0db7Sjcs 211c6df0db7Sjcs DELAY(25); 212c6df0db7Sjcs } 213c6df0db7Sjcs 214c6df0db7Sjcs printf("%s: failed to %sable\n", sc->sc_dev.dv_xname, 215c6df0db7Sjcs (enable ? "en" : "dis")); 216c6df0db7Sjcs } 217c6df0db7Sjcs 218c6df0db7Sjcs int 219c6df0db7Sjcs dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, 220c6df0db7Sjcs size_t cmdlen, void *buf, size_t len, int flags) 221c6df0db7Sjcs { 222c6df0db7Sjcs struct dwiic_softc *sc = cookie; 223c6df0db7Sjcs u_int32_t ic_con, st, cmd, resp; 224c6df0db7Sjcs int retries, tx_limit, rx_avail, x, readpos; 225c6df0db7Sjcs uint8_t *b; 22635d201e4Skettenis int s; 227c6df0db7Sjcs 228c6df0db7Sjcs if (sc->sc_busy) 229c6df0db7Sjcs return 1; 230c6df0db7Sjcs 231c6df0db7Sjcs sc->sc_busy++; 232c6df0db7Sjcs 233c6df0db7Sjcs DPRINTF(("%s: %s: op %d, addr 0x%02x, cmdlen %zu, len %zu, " 234c6df0db7Sjcs "flags 0x%02x\n", sc->sc_dev.dv_xname, __func__, op, addr, cmdlen, 235c6df0db7Sjcs len, flags)); 236c6df0db7Sjcs 237c6df0db7Sjcs /* setup transfer */ 238c6df0db7Sjcs sc->sc_i2c_xfer.op = op; 239c6df0db7Sjcs sc->sc_i2c_xfer.buf = buf; 240c6df0db7Sjcs sc->sc_i2c_xfer.len = len; 241c6df0db7Sjcs sc->sc_i2c_xfer.flags = flags; 242c6df0db7Sjcs sc->sc_i2c_xfer.error = 0; 243c6df0db7Sjcs 244c6df0db7Sjcs /* wait for bus to be idle */ 245c6df0db7Sjcs for (retries = 100; retries > 0; retries--) { 246c6df0db7Sjcs st = dwiic_read(sc, DW_IC_STATUS); 247c6df0db7Sjcs if (!(st & DW_IC_STATUS_ACTIVITY)) 248c6df0db7Sjcs break; 249c6df0db7Sjcs DELAY(1000); 250c6df0db7Sjcs } 251c6df0db7Sjcs DPRINTF(("%s: %s: status 0x%x\n", sc->sc_dev.dv_xname, __func__, st)); 252c6df0db7Sjcs if (st & DW_IC_STATUS_ACTIVITY) { 253c6df0db7Sjcs sc->sc_busy = 0; 254c6df0db7Sjcs return (1); 255c6df0db7Sjcs } 256c6df0db7Sjcs 257c6df0db7Sjcs if (cold || sc->sc_poll) 258c6df0db7Sjcs flags |= I2C_F_POLL; 259c6df0db7Sjcs 260c6df0db7Sjcs /* disable controller */ 261c6df0db7Sjcs dwiic_enable(sc, 0); 262c6df0db7Sjcs 263c6df0db7Sjcs /* set slave address */ 264c6df0db7Sjcs ic_con = dwiic_read(sc, DW_IC_CON); 265c6df0db7Sjcs ic_con &= ~DW_IC_CON_10BITADDR_MASTER; 266c6df0db7Sjcs dwiic_write(sc, DW_IC_CON, ic_con); 267c6df0db7Sjcs dwiic_write(sc, DW_IC_TAR, addr); 268c6df0db7Sjcs 269c6df0db7Sjcs /* disable interrupts */ 270c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, 0); 271c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_INTR); 272c6df0db7Sjcs 273c6df0db7Sjcs /* enable controller */ 274c6df0db7Sjcs dwiic_enable(sc, 1); 275c6df0db7Sjcs 276c6df0db7Sjcs /* wait until the controller is ready for commands */ 277c6df0db7Sjcs if (flags & I2C_F_POLL) 278c6df0db7Sjcs DELAY(200); 279c6df0db7Sjcs else { 28035d201e4Skettenis s = splbio(); 281c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_INTR); 282c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY); 283c6df0db7Sjcs 284c7da16f2Scheloha if (tsleep_nsec(&sc->sc_writewait, PRIBIO, "dwiic", 285c7da16f2Scheloha MSEC_TO_NSEC(500)) != 0) 286c6df0db7Sjcs printf("%s: timed out waiting for tx_empty intr\n", 287c6df0db7Sjcs sc->sc_dev.dv_xname); 28835d201e4Skettenis splx(s); 289c6df0db7Sjcs } 290c6df0db7Sjcs 291c6df0db7Sjcs /* send our command, one byte at a time */ 292c6df0db7Sjcs if (cmdlen > 0) { 293c6df0db7Sjcs b = (void *)cmdbuf; 294c6df0db7Sjcs 295c6df0db7Sjcs DPRINTF(("%s: %s: sending cmd (len %zu):", sc->sc_dev.dv_xname, 296c6df0db7Sjcs __func__, cmdlen)); 297c6df0db7Sjcs for (x = 0; x < cmdlen; x++) 298c6df0db7Sjcs DPRINTF((" %02x", b[x])); 299c6df0db7Sjcs DPRINTF(("\n")); 300c6df0db7Sjcs 301c6df0db7Sjcs tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); 302c6df0db7Sjcs if (cmdlen > tx_limit) { 303c6df0db7Sjcs /* TODO */ 304c6df0db7Sjcs printf("%s: can't write %zu (> %d)\n", 305c6df0db7Sjcs sc->sc_dev.dv_xname, cmdlen, tx_limit); 306c6df0db7Sjcs sc->sc_i2c_xfer.error = 1; 307c6df0db7Sjcs sc->sc_busy = 0; 308c6df0db7Sjcs return (1); 309c6df0db7Sjcs } 310c6df0db7Sjcs 311c6df0db7Sjcs for (x = 0; x < cmdlen; x++) { 312c6df0db7Sjcs cmd = b[x]; 313c6df0db7Sjcs /* 314c6df0db7Sjcs * Generate STOP condition if this is the last 315c6df0db7Sjcs * byte of the transfer. 316c6df0db7Sjcs */ 317c6df0db7Sjcs if (x == (cmdlen - 1) && len == 0 && I2C_OP_STOP_P(op)) 318c6df0db7Sjcs cmd |= DW_IC_DATA_CMD_STOP; 319c6df0db7Sjcs dwiic_write(sc, DW_IC_DATA_CMD, cmd); 320c6df0db7Sjcs } 321c6df0db7Sjcs } 322c6df0db7Sjcs 323c6df0db7Sjcs b = (void *)buf; 324c6df0db7Sjcs x = readpos = 0; 325c6df0db7Sjcs tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); 326c6df0db7Sjcs 327c6df0db7Sjcs DPRINTF(("%s: %s: need to read %zu bytes, can send %d read reqs\n", 328c6df0db7Sjcs sc->sc_dev.dv_xname, __func__, len, tx_limit)); 329c6df0db7Sjcs 330c6df0db7Sjcs while (x < len) { 331c6df0db7Sjcs if (I2C_OP_WRITE_P(op)) 332c6df0db7Sjcs cmd = b[x]; 333c6df0db7Sjcs else 334c6df0db7Sjcs cmd = DW_IC_DATA_CMD_READ; 335c6df0db7Sjcs 336c6df0db7Sjcs /* 337c6df0db7Sjcs * Generate RESTART condition if we're reversing 338c6df0db7Sjcs * direction. 339c6df0db7Sjcs */ 340c6df0db7Sjcs if (x == 0 && cmdlen > 0 && I2C_OP_READ_P(op)) 341c6df0db7Sjcs cmd |= DW_IC_DATA_CMD_RESTART; 342c6df0db7Sjcs /* 3434b1a56afSjsg * Generate STOP condition on the last byte of the 344c6df0db7Sjcs * transfer. 345c6df0db7Sjcs */ 346c6df0db7Sjcs if (x == (len - 1) && I2C_OP_STOP_P(op)) 347c6df0db7Sjcs cmd |= DW_IC_DATA_CMD_STOP; 348c6df0db7Sjcs 349c6df0db7Sjcs dwiic_write(sc, DW_IC_DATA_CMD, cmd); 350c6df0db7Sjcs 351e636b2a9Skettenis /* 352e636b2a9Skettenis * For a block read, get the byte count before 353e636b2a9Skettenis * continuing to read the data bytes. 354e636b2a9Skettenis */ 355e636b2a9Skettenis if (I2C_OP_READ_P(op) && I2C_OP_BLKMODE_P(op) && readpos == 0) 356e636b2a9Skettenis tx_limit = 1; 357e636b2a9Skettenis 358c6df0db7Sjcs tx_limit--; 359c6df0db7Sjcs x++; 360c6df0db7Sjcs 361c6df0db7Sjcs /* 362c6df0db7Sjcs * As TXFLR fills up, we need to clear it out by reading all 363c6df0db7Sjcs * available data. 364c6df0db7Sjcs */ 36535d201e4Skettenis while (I2C_OP_READ_P(op) && (tx_limit == 0 || x == len)) { 366c6df0db7Sjcs DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n", 367c6df0db7Sjcs sc->sc_dev.dv_xname, __func__, tx_limit, x)); 368c6df0db7Sjcs 369c6df0db7Sjcs if (flags & I2C_F_POLL) { 37066fc675eSkettenis for (retries = 1000; retries > 0; retries--) { 371c6df0db7Sjcs rx_avail = dwiic_read(sc, DW_IC_RXFLR); 372c6df0db7Sjcs if (rx_avail > 0) 373c6df0db7Sjcs break; 374c6df0db7Sjcs DELAY(50); 375c6df0db7Sjcs } 376c6df0db7Sjcs } else { 37735d201e4Skettenis s = splbio(); 378c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_INTR); 379c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, 380c6df0db7Sjcs DW_IC_INTR_RX_FULL); 381c6df0db7Sjcs 382c7da16f2Scheloha if (tsleep_nsec(&sc->sc_readwait, PRIBIO, 383c7da16f2Scheloha "dwiic", MSEC_TO_NSEC(500)) != 0) 384c6df0db7Sjcs printf("%s: timed out waiting for " 385c6df0db7Sjcs "rx_full intr\n", 386c6df0db7Sjcs sc->sc_dev.dv_xname); 38735d201e4Skettenis splx(s); 388c6df0db7Sjcs 389c6df0db7Sjcs rx_avail = dwiic_read(sc, DW_IC_RXFLR); 390c6df0db7Sjcs } 391c6df0db7Sjcs 392c6df0db7Sjcs if (rx_avail == 0) { 393c6df0db7Sjcs printf("%s: timed out reading remaining %d\n", 394e636b2a9Skettenis sc->sc_dev.dv_xname, (int)(len - readpos)); 395c6df0db7Sjcs sc->sc_i2c_xfer.error = 1; 396c6df0db7Sjcs sc->sc_busy = 0; 397c6df0db7Sjcs 398c6df0db7Sjcs return (1); 399c6df0db7Sjcs } 400c6df0db7Sjcs 401c6df0db7Sjcs DPRINTF(("%s: %s: %d avail to read (%zu remaining)\n", 402c6df0db7Sjcs sc->sc_dev.dv_xname, __func__, rx_avail, 403c6df0db7Sjcs len - readpos)); 404c6df0db7Sjcs 405c6df0db7Sjcs while (rx_avail > 0) { 406c6df0db7Sjcs resp = dwiic_read(sc, DW_IC_DATA_CMD); 407c6df0db7Sjcs if (readpos < len) { 408c6df0db7Sjcs b[readpos] = resp; 409c6df0db7Sjcs readpos++; 410c6df0db7Sjcs } 411c6df0db7Sjcs rx_avail--; 412c6df0db7Sjcs } 413c6df0db7Sjcs 414e636b2a9Skettenis /* 415e636b2a9Skettenis * Update the transfer length when doing a 416e636b2a9Skettenis * block read. 417e636b2a9Skettenis */ 418e636b2a9Skettenis if (I2C_OP_BLKMODE_P(op) && readpos > 0 && len > b[0]) 419e636b2a9Skettenis len = b[0] + 1; 420e636b2a9Skettenis 421c6df0db7Sjcs if (readpos >= len) 422c6df0db7Sjcs break; 423c6df0db7Sjcs 424c6df0db7Sjcs DPRINTF(("%s: still need to read %d bytes\n", 425c6df0db7Sjcs sc->sc_dev.dv_xname, (int)(len - readpos))); 426c6df0db7Sjcs tx_limit = sc->tx_fifo_depth - 427c6df0db7Sjcs dwiic_read(sc, DW_IC_TXFLR); 428c6df0db7Sjcs } 4298231eef2Spatrick 4308231eef2Spatrick if (I2C_OP_WRITE_P(op) && tx_limit == 0 && x < len) { 4318231eef2Spatrick if (flags & I2C_F_POLL) { 4328231eef2Spatrick for (retries = 1000; retries > 0; retries--) { 4338231eef2Spatrick tx_limit = sc->tx_fifo_depth - 4348231eef2Spatrick dwiic_read(sc, DW_IC_TXFLR); 4358231eef2Spatrick if (tx_limit > 0) 4368231eef2Spatrick break; 4378231eef2Spatrick DELAY(50); 4388231eef2Spatrick } 4398231eef2Spatrick } else { 4408231eef2Spatrick s = splbio(); 4418231eef2Spatrick dwiic_read(sc, DW_IC_CLR_INTR); 4428231eef2Spatrick dwiic_write(sc, DW_IC_INTR_MASK, 4438231eef2Spatrick DW_IC_INTR_TX_EMPTY); 4448231eef2Spatrick 4458231eef2Spatrick if (tsleep_nsec(&sc->sc_writewait, PRIBIO, 4468231eef2Spatrick "dwiic", MSEC_TO_NSEC(500)) != 0) 4478231eef2Spatrick printf("%s: timed out waiting for " 4488231eef2Spatrick "tx_empty intr\n", 4498231eef2Spatrick sc->sc_dev.dv_xname); 4508231eef2Spatrick splx(s); 4518231eef2Spatrick 4528231eef2Spatrick tx_limit = sc->tx_fifo_depth - 4538231eef2Spatrick dwiic_read(sc, DW_IC_TXFLR); 4548231eef2Spatrick } 4558231eef2Spatrick 4568231eef2Spatrick if (tx_limit == 0) { 4578231eef2Spatrick printf("%s: timed out writing remaining %d\n", 4588231eef2Spatrick sc->sc_dev.dv_xname, (int)(len - x)); 4598231eef2Spatrick sc->sc_i2c_xfer.error = 1; 4608231eef2Spatrick sc->sc_busy = 0; 4618231eef2Spatrick 4628231eef2Spatrick return (1); 4638231eef2Spatrick } 4648231eef2Spatrick } 465c6df0db7Sjcs } 466c6df0db7Sjcs 46735d201e4Skettenis if (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) { 46835d201e4Skettenis if (flags & I2C_F_POLL) { 46935d201e4Skettenis for (retries = 100; retries > 0; retries--) { 4702933375bSkettenis st = dwiic_read(sc, DW_IC_RAW_INTR_STAT); 4712933375bSkettenis if (st & DW_IC_INTR_STOP_DET) 47235d201e4Skettenis break; 47335d201e4Skettenis DELAY(1000); 47435d201e4Skettenis } 4752933375bSkettenis if (!(st & DW_IC_INTR_STOP_DET)) 47635d201e4Skettenis printf("%s: timed out waiting for bus idle\n", 47735d201e4Skettenis sc->sc_dev.dv_xname); 47835d201e4Skettenis } else { 47935d201e4Skettenis s = splbio(); 48035d201e4Skettenis while (sc->sc_busy) { 48135d201e4Skettenis dwiic_write(sc, DW_IC_INTR_MASK, 48235d201e4Skettenis DW_IC_INTR_STOP_DET); 483c7da16f2Scheloha if (tsleep_nsec(&sc->sc_busy, PRIBIO, "dwiic", 484c7da16f2Scheloha MSEC_TO_NSEC(500)) != 0) 48535d201e4Skettenis printf("%s: timed out waiting for " 48635d201e4Skettenis "stop intr\n", 48735d201e4Skettenis sc->sc_dev.dv_xname); 48835d201e4Skettenis } 48935d201e4Skettenis splx(s); 49035d201e4Skettenis } 49135d201e4Skettenis } 492c6df0db7Sjcs sc->sc_busy = 0; 493c6df0db7Sjcs 494c6df0db7Sjcs return 0; 495c6df0db7Sjcs } 496c6df0db7Sjcs 497c6df0db7Sjcs uint32_t 498c6df0db7Sjcs dwiic_read_clear_intrbits(struct dwiic_softc *sc) 499c6df0db7Sjcs { 500c6df0db7Sjcs uint32_t stat; 501c6df0db7Sjcs 502c6df0db7Sjcs stat = dwiic_read(sc, DW_IC_INTR_STAT); 503c6df0db7Sjcs 504c6df0db7Sjcs if (stat & DW_IC_INTR_RX_UNDER) 505c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_RX_UNDER); 506c6df0db7Sjcs if (stat & DW_IC_INTR_RX_OVER) 507c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_RX_OVER); 508c6df0db7Sjcs if (stat & DW_IC_INTR_TX_OVER) 509c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_TX_OVER); 510c6df0db7Sjcs if (stat & DW_IC_INTR_RD_REQ) 511c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_RD_REQ); 512c6df0db7Sjcs if (stat & DW_IC_INTR_TX_ABRT) 513c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_TX_ABRT); 514c6df0db7Sjcs if (stat & DW_IC_INTR_RX_DONE) 515c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_RX_DONE); 516c6df0db7Sjcs if (stat & DW_IC_INTR_ACTIVITY) 517c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_ACTIVITY); 518c6df0db7Sjcs if (stat & DW_IC_INTR_STOP_DET) 519c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_STOP_DET); 520c6df0db7Sjcs if (stat & DW_IC_INTR_START_DET) 521c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_START_DET); 522c6df0db7Sjcs if (stat & DW_IC_INTR_GEN_CALL) 523c6df0db7Sjcs dwiic_read(sc, DW_IC_CLR_GEN_CALL); 524c6df0db7Sjcs 525c6df0db7Sjcs return stat; 526c6df0db7Sjcs } 527c6df0db7Sjcs 528c6df0db7Sjcs int 529c6df0db7Sjcs dwiic_intr(void *arg) 530c6df0db7Sjcs { 531c6df0db7Sjcs struct dwiic_softc *sc = arg; 532c6df0db7Sjcs uint32_t en, stat; 533c6df0db7Sjcs 534c6df0db7Sjcs en = dwiic_read(sc, DW_IC_ENABLE); 535c6df0db7Sjcs /* probably for the other controller */ 536c6df0db7Sjcs if (!en) 537c6df0db7Sjcs return 0; 538c6df0db7Sjcs 539c6df0db7Sjcs stat = dwiic_read_clear_intrbits(sc); 540c6df0db7Sjcs DPRINTF(("%s: %s: enabled=0x%x stat=0x%x\n", sc->sc_dev.dv_xname, 541c6df0db7Sjcs __func__, en, stat)); 542c6df0db7Sjcs if (!(stat & ~DW_IC_INTR_ACTIVITY)) 543c0e86f1bSstsp return 0; 544c6df0db7Sjcs 545c6df0db7Sjcs if (stat & DW_IC_INTR_TX_ABRT) 546c6df0db7Sjcs sc->sc_i2c_xfer.error = 1; 547c6df0db7Sjcs 548c6df0db7Sjcs if (sc->sc_i2c_xfer.flags & I2C_F_POLL) 549c6df0db7Sjcs DPRINTF(("%s: %s: intr in poll mode?\n", sc->sc_dev.dv_xname, 550c6df0db7Sjcs __func__)); 551c6df0db7Sjcs else { 552c6df0db7Sjcs if (stat & DW_IC_INTR_RX_FULL) { 553c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, 0); 554c6df0db7Sjcs DPRINTF(("%s: %s: waking up reader\n", 555c6df0db7Sjcs sc->sc_dev.dv_xname, __func__)); 556c6df0db7Sjcs wakeup(&sc->sc_readwait); 557c6df0db7Sjcs } 558c6df0db7Sjcs if (stat & DW_IC_INTR_TX_EMPTY) { 559c6df0db7Sjcs dwiic_write(sc, DW_IC_INTR_MASK, 0); 560c6df0db7Sjcs DPRINTF(("%s: %s: waking up writer\n", 561c6df0db7Sjcs sc->sc_dev.dv_xname, __func__)); 562c6df0db7Sjcs wakeup(&sc->sc_writewait); 563c6df0db7Sjcs } 56435d201e4Skettenis if (stat & DW_IC_INTR_STOP_DET) { 56535d201e4Skettenis dwiic_write(sc, DW_IC_INTR_MASK, 0); 56635d201e4Skettenis sc->sc_busy = 0; 56735d201e4Skettenis wakeup(&sc->sc_busy); 56835d201e4Skettenis } 569c6df0db7Sjcs } 570c6df0db7Sjcs 571c6df0db7Sjcs return 1; 572c6df0db7Sjcs } 573