Index: sys/dev/iicbus/twsi/twsi.c =================================================================== --- sys/dev/iicbus/twsi/twsi.c +++ sys/dev/iicbus/twsi/twsi.c @@ -44,22 +44,19 @@ #include #include #include +#include +#include #include #include +#include +#include #include #include #include -#include - -#include -#include - #include #include -#include -#include #include @@ -72,12 +69,14 @@ #define TWSI_CONTROL_TWSIEN (1 << 6) #define TWSI_CONTROL_INTEN (1 << 7) +#define TWSI_STATUS_BUS_ERROR 0x00 #define TWSI_STATUS_START 0x08 #define TWSI_STATUS_RPTD_START 0x10 #define TWSI_STATUS_ADDR_W_ACK 0x18 #define TWSI_STATUS_ADDR_W_NACK 0x20 #define TWSI_STATUS_DATA_WR_ACK 0x28 #define TWSI_STATUS_DATA_WR_NACK 0x30 +#define TWSI_STATUS_ARBITRATION_LOST 0x38 #define TWSI_STATUS_ADDR_R_ACK 0x40 #define TWSI_STATUS_ADDR_R_NACK 0x48 #define TWSI_STATUS_DATA_RD_ACK 0x50 @@ -86,12 +85,18 @@ #define TWSI_DEBUG #undef TWSI_DEBUG +#define debugf(dev, fmt, args...) if (twsi_debug) device_printf(dev, "%s: " fmt, __func__, ##args) + #ifdef TWSI_DEBUG -#define debugf(dev, fmt, args...) device_printf(dev, "%s: " fmt, __func__, ##args) +static int twsi_debug = 1; #else -#define debugf(dev, fmt, args...) +static int twsi_debug = 0; #endif +SYSCTL_DECL(_hw_i2c); +SYSCTL_INT(_hw_i2c, OID_AUTO, twsi_debug, CTLFLAG_RWTUN, + &twsi_debug, 0, "Enable twsi driver debug"); + static struct resource_spec res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, @@ -104,7 +109,8 @@ uint32_t val; val = bus_read_4(sc->res[0], off); - debugf(sc->dev, "read %x from %lx\n", val, off); + if (twsi_debug > 1) + debugf(sc->dev, "read %x from %lx\n", val, off); return (val); } @@ -112,7 +118,8 @@ TWSI_WRITE(struct twsi_softc *sc, bus_size_t off, uint32_t val) { - debugf(sc->dev, "Writing %x to %lx\n", val, off); + if (twsi_debug > 1) + debugf(sc->dev, "Writing %x to %lx\n", val, off); bus_write_4(sc->res[0], off, val); } @@ -485,71 +492,105 @@ twsi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct twsi_softc *sc; + int i; + int error; sc = device_get_softc(dev); if (!sc->have_intr) return (iicbus_transfer_gen(dev, msgs, nmsgs)); - sc->error = 0; - - sc->control_val = TWSI_CONTROL_TWSIEN | - TWSI_CONTROL_INTEN | TWSI_CONTROL_ACK; - TWSI_WRITE(sc, sc->reg_control, sc->control_val); debugf(dev, "transmitting %d messages\n", nmsgs); debugf(sc->dev, "status=%x\n", TWSI_READ(sc, sc->reg_status)); + + mtx_lock(&sc->mutex); + KASSERT(sc->transfer == 0, + ("starting a transfer while another is active")); sc->nmsgs = nmsgs; sc->msgs = msgs; sc->msg_idx = 0; sc->transfer = 1; + sc->error = 0; #ifdef TWSI_DEBUG for (int i = 0; i < nmsgs; i++) debugf(sc->dev, "msg %d is %d bytes long\n", i, msgs[i].len); #endif + /* Send start and re-enable interrupts */ - sc->control_val = TWSI_CONTROL_TWSIEN | - TWSI_CONTROL_INTEN | TWSI_CONTROL_ACK; - if (sc->msgs[0].len == 1) - sc->control_val &= ~TWSI_CONTROL_ACK; + sc->control_val = TWSI_CONTROL_TWSIEN | TWSI_CONTROL_INTEN; TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START); - while (sc->error == 0 && sc->transfer != 0) { - tsleep_sbt(sc, 0, "twsi", SBT_1MS * 30, SBT_1MS, 0); + for (i = 0; i < 100 && sc->error == 0 && sc->transfer != 0; i++) { + msleep_sbt(sc, &sc->mutex, 0, "twsi", SBT_1MS * 30, SBT_1MS, 0); } + if (sc->error == 0 && sc->transfer != 0) { + device_printf(sc->dev, "transfer timeout\n"); + sc->error = IIC_ETIMEOUT; + sc->transfer = 0; + } debugf(sc->dev, "pause finish\n"); - if (sc->error) { - debugf(sc->dev, "Error, aborting (%d)\n", sc->error); - TWSI_WRITE(sc, sc->reg_control, 0); - } + if (sc->error != 0) + debugf(sc->dev, "Error: %d\n", sc->error); /* Disable module and interrupts */ debugf(sc->dev, "status=%x\n", TWSI_READ(sc, sc->reg_status)); TWSI_WRITE(sc, sc->reg_control, 0); debugf(sc->dev, "status=%x\n", TWSI_READ(sc, sc->reg_status)); + error = sc->error; + mtx_unlock(&sc->mutex); - return (sc->error); + return (error); } static void +twsi_error(struct twsi_softc *sc, int err) +{ + /* + * Must send stop condition to abort the current transfer and + * signal the caller. + */ + debugf(sc->dev, "Sending STOP condition for error %d\n", err); + sc->transfer = 0; + sc->error = err; + sc->control_val = 0; + TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_STOP); +} + +static void twsi_intr(void *arg) { struct twsi_softc *sc; uint32_t status; - int transfer_done = 0; + bool message_done; + bool send_start; sc = arg; + message_done = false; + send_start = false; - debugf(sc->dev, "Got interrupt Current msg=%x\n", sc->msg_idx); + mtx_lock(&sc->mutex); + debugf(sc->dev, "Got interrupt, current msg=%u\n", sc->msg_idx); status = TWSI_READ(sc, sc->reg_status); - debugf(sc->dev, "reg control=%x\n", TWSI_READ(sc, sc->reg_control)); + debugf(sc->dev, "reg control = 0x%x, status = 0x%x\n", + TWSI_READ(sc, sc->reg_control), status); + if (sc->transfer == 0) { + device_printf(sc->dev, "interrupt without active transfer, " + "status = 0x%x\n", status); + TWSI_WRITE(sc, sc->reg_control, sc->control_val | + TWSI_CONTROL_STOP); + goto end; + } + +restart: switch (status) { case TWSI_STATUS_START: case TWSI_STATUS_RPTD_START: /* Transmit the address */ - debugf(sc->dev, "Send the address (%x)", sc->msgs[sc->msg_idx].slave); + debugf(sc->dev, "Send address 0x%x\n", + sc->msgs[sc->msg_idx].slave); if (sc->msgs[sc->msg_idx].flags & IIC_M_RD) TWSI_WRITE(sc, sc->reg_data, @@ -557,131 +598,191 @@ else TWSI_WRITE(sc, sc->reg_data, sc->msgs[sc->msg_idx].slave & ~LSB); - TWSI_WRITE(sc, sc->reg_control, sc->control_val); break; case TWSI_STATUS_ADDR_W_ACK: - debugf(sc->dev, "Ack received after transmitting the address (write)\n"); - /* Directly send the first byte */ - sc->sent_bytes = 1; - debugf(sc->dev, "Sending byte 0 (of %d) = %x\n", - sc->msgs[sc->msg_idx].len, - sc->msgs[sc->msg_idx].buf[0]); - TWSI_WRITE(sc, sc->reg_data, sc->msgs[sc->msg_idx].buf[0]); + debugf(sc->dev, "Address ACK-ed (write)\n"); - TWSI_WRITE(sc, sc->reg_control, sc->control_val); + if (sc->msgs[sc->msg_idx].len > 0) { + /* Directly send the first byte */ + sc->sent_bytes = 1; + debugf(sc->dev, "Sending byte 0 (of %d) = %x\n", + sc->msgs[sc->msg_idx].len, + sc->msgs[sc->msg_idx].buf[0]); + TWSI_WRITE(sc, sc->reg_data, + sc->msgs[sc->msg_idx].buf[0]); + } else { + debugf(sc->dev, "Zero-length write, sending STOP\n"); + TWSI_WRITE(sc, sc->reg_control, + sc->control_val | TWSI_CONTROL_STOP); + } + break; case TWSI_STATUS_ADDR_R_ACK: - debugf(sc->dev, "Ack received after transmitting the address (read)\n"); + debugf(sc->dev, "Address ACK-ed (read)\n"); sc->recv_bytes = 0; - TWSI_WRITE(sc, sc->reg_control, sc->control_val); + if (sc->msgs[sc->msg_idx].len == 0) { + debugf(sc->dev, "Zero-length read, sending STOP\n"); + TWSI_WRITE(sc, sc->reg_control, + sc->control_val | TWSI_CONTROL_STOP); + } else if (sc->msgs[sc->msg_idx].len == 1) { + sc->control_val &= ~TWSI_CONTROL_ACK; + } else { + sc->control_val |= TWSI_CONTROL_ACK; + } break; case TWSI_STATUS_ADDR_W_NACK: case TWSI_STATUS_ADDR_R_NACK: - debugf(sc->dev, "No ack received after transmitting the address\n"); - sc->transfer = 0; - sc->error = IIC_ENOACK; - sc->control_val = 0; + debugf(sc->dev, "Address NACK-ed\n"); + twsi_error(sc, IIC_ENOACK); + break; + case TWSI_STATUS_DATA_WR_NACK: + debugf(sc->dev, "Data byte NACK-ed\n"); + twsi_error(sc, IIC_ENOACK); wakeup(sc); break; - case TWSI_STATUS_DATA_WR_ACK: - debugf(sc->dev, "Ack received after transmitting data\n"); + KASSERT(sc->sent_bytes <= sc->msgs[sc->msg_idx].len, + ("sent_bytes beyond message length")); + debugf(sc->dev, "ACK received after transmitting data\n"); if (sc->sent_bytes == sc->msgs[sc->msg_idx].len) { - debugf(sc->dev, "Done sending all the bytes for msg %d\n", sc->msg_idx); + debugf(sc->dev, "Done TX data\n"); + /* Send stop, no interrupts on stop */ if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) { - debugf(sc->dev, "Done TX data, send stop\n"); + debugf(sc->dev, "Send STOP\n"); TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_STOP); } else { - debugf(sc->dev, "Done TX data with NO_STOP\n"); - TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START); + debugf(sc->dev, "NOSTOP flag\n"); } - sc->msg_idx++; - if (sc->msg_idx == sc->nmsgs) { - debugf(sc->dev, "transfer_done=1\n"); - transfer_done = 1; - sc->error = 0; - } else { - debugf(sc->dev, "Send repeated start\n"); - TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START); - } - } else { - debugf(sc->dev, "Sending byte %d (of %d) = %x\n", - sc->sent_bytes, - sc->msgs[sc->msg_idx].len, - sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); - TWSI_WRITE(sc, sc->reg_data, - sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); - TWSI_WRITE(sc, sc->reg_control, - sc->control_val); - sc->sent_bytes++; + message_done = true; + break; } + + debugf(sc->dev, "Sending byte %d (of %d) = 0x%x\n", + sc->sent_bytes, + sc->msgs[sc->msg_idx].len, + sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); + TWSI_WRITE(sc, sc->reg_data, + sc->msgs[sc->msg_idx].buf[sc->sent_bytes]); + sc->sent_bytes++; break; case TWSI_STATUS_DATA_RD_ACK: - debugf(sc->dev, "Ack received after receiving data\n"); - sc->msgs[sc->msg_idx].buf[sc->recv_bytes++] = TWSI_READ(sc, sc->reg_data); - debugf(sc->dev, "msg_len=%d recv_bytes=%d\n", sc->msgs[sc->msg_idx].len, sc->recv_bytes); + debugf(sc->dev, "Received and ACK-ed data\n"); + KASSERT(sc->recv_bytes < sc->msgs[sc->msg_idx].len, + ("receiving beyond the end of buffer")); + sc->msgs[sc->msg_idx].buf[sc->recv_bytes] = + TWSI_READ(sc, sc->reg_data); + debugf(sc->dev, "Received byte %d (of %d) = 0x%x\n", + sc->recv_bytes, + sc->msgs[sc->msg_idx].len, + sc->msgs[sc->msg_idx].buf[sc->recv_bytes]); + sc->recv_bytes++; + /* If we only have one byte left, disable ACK */ - if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1) + if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1) { sc->control_val &= ~TWSI_CONTROL_ACK; - if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) { - debugf(sc->dev, "Done with msg %d\n", sc->msg_idx); - sc->msg_idx++; - if (sc->msg_idx == sc->nmsgs - 1) { - debugf(sc->dev, "No more msgs\n"); - transfer_done = 1; - sc->error = 0; - } + } else if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) { + /* + * We should not have ACK-ed the last byte. + * The protocol state machine is in invalid state. + */ + debugf(sc->dev, "RX all but asked for more?\n"); + twsi_error(sc, IIC_ESTATUS); } - TWSI_WRITE(sc, sc->reg_control, sc->control_val); break; case TWSI_STATUS_DATA_RD_NOACK: - if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1) { - sc->msgs[sc->msg_idx].buf[sc->recv_bytes++] = TWSI_READ(sc, sc->reg_data); - debugf(sc->dev, "Done RX data, send stop (2)\n"); - if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) + debugf(sc->dev, "Received and NACK-ed data\n"); + KASSERT(sc->recv_bytes == sc->msgs[sc->msg_idx].len - 1, + ("sent NACK before receiving all requested data")); + sc->msgs[sc->msg_idx].buf[sc->recv_bytes] = + TWSI_READ(sc, sc->reg_data); + debugf(sc->dev, "Received byte %d (of %d) = 0x%x\n", + sc->recv_bytes, + sc->msgs[sc->msg_idx].len, + sc->msgs[sc->msg_idx].buf[sc->recv_bytes]); + sc->recv_bytes++; + + if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) { + debugf(sc->dev, "Done RX data\n"); + if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) { + debugf(sc->dev, "Send STOP\n"); TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_STOP); + } + message_done = true; } else { - debugf(sc->dev, "No ack when receiving data, sending stop anyway\n"); - if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) - TWSI_WRITE(sc, sc->reg_control, - sc->control_val | TWSI_CONTROL_STOP); + /* + * We should not have NACK-ed yet. + * The protocol state machine is in invalid state. + */ + debugf(sc->dev, "NACK-ed before receving all bytes?\n"); + twsi_error(sc, IIC_ESTATUS); } - sc->transfer = 0; - transfer_done = 1; - sc->error = 0; break; + case TWSI_STATUS_BUS_ERROR: + debugf(sc->dev, "Bus error\n"); + twsi_error(sc, IIC_EBUSERR); + break; + case TWSI_STATUS_ARBITRATION_LOST: + debugf(sc->dev, "Arbitration lost\n"); + twsi_error(sc, IIC_EBUSBSY); + break; default: - debugf(sc->dev, "status=%x hot handled\n", status); - sc->transfer = 0; - sc->error = IIC_EBUSERR; - sc->control_val = 0; - wakeup(sc); + debugf(sc->dev, "unexpected status 0x%x\n", status); + twsi_error(sc, IIC_ESTATUS); break; } - debugf(sc->dev, "Refresh reg_control\n"); + if (message_done) { + sc->msg_idx++; + if (sc->msg_idx == sc->nmsgs) { + debugf(sc->dev, "All messages transmitted\n"); + sc->transfer = 0; + sc->error = 0; + } else if ((sc->msgs[sc->msg_idx].flags & IIC_M_NOSTART) == 0) { + debugf(sc->dev, "Send (repeated) start\n"); + send_start = true; + } else { + /* Just keep transmitting data. */ + KASSERT((sc->msgs[sc->msg_idx - 1].flags & IIC_M_NOSTOP) != 0, + ("NOSTART message after STOP")); + KASSERT((sc->msgs[sc->msg_idx].flags & IIC_M_RD) == + (sc->msgs[sc->msg_idx - 1].flags & IIC_M_RD), + ("change of transfer direction without a START")); + debugf(sc->dev, "NOSTART message after NOSTOP\n"); + sc->sent_bytes = 0; + sc->recv_bytes = 0; + if ((sc->msgs[sc->msg_idx].flags & IIC_M_RD) == 0) { + status = TWSI_STATUS_ADDR_W_ACK; + goto restart; + } else { + debugf(sc->dev, "Read+NOSTART unsupported\n"); + twsi_error(sc, IIC_ESTATUS); + } + } + } +end: /* * Newer Allwinner chips clear IFLG after writing 1 to it. */ + debugf(sc->dev, "Refresh reg_control\n"); TWSI_WRITE(sc, sc->reg_control, sc->control_val | - (sc->iflag_w1c ? TWSI_CONTROL_IFLG : 0)); + (sc->iflag_w1c ? TWSI_CONTROL_IFLG : 0) | + (send_start ? TWSI_CONTROL_START : 0)); - debugf(sc->dev, "Done with interrupts\n\n"); - if (transfer_done == 1) { - sc->transfer = 0; + debugf(sc->dev, "Done with interrupt, transfer = %d\n", sc->transfer); + if (sc->transfer == 0) wakeup(sc); - } + mtx_unlock(&sc->mutex); } static void