static int piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) { struct piixpm_smbus *smbus = cookie; struct piixpm_softc *sc = smbus->softc; const u_int8_t *b; u_int8_t ctl = 0, st; int retries; DPRINTF(("%s: exec: op %d, addr 0x%x, cmdlen %zu, len %zu, flags 0x%x\n", device_xname(sc->sc_dev), op, addr, cmdlen, len, flags)); /* Clear status bits */ bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, PIIX_SMB_HS_INTR | PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR | PIIX_SMB_HS_FAILED); bus_space_barrier(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, 1, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); /* Wait for bus to be idle */ for (retries = 100; retries > 0; retries--) { st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS); if (!(st & PIIX_SMB_HS_BUSY)) break; DELAY(PIIXPM_DELAY); } DPRINTF(("%s: exec: st 0x%d\n", device_xname(sc->sc_dev), st & 0xff)); if (st & PIIX_SMB_HS_BUSY) return (1); if (cold || sc->sc_poll) flags |= I2C_F_POLL; if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 || (cmdlen == 0 && len > 1)) return (1); /* Setup transfer */ sc->sc_i2c_xfer.op = op; sc->sc_i2c_xfer.buf = buf; sc->sc_i2c_xfer.len = len; sc->sc_i2c_xfer.flags = flags; sc->sc_i2c_xfer.error = 0; /* Set slave address and transfer direction */ bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_TXSLVA, PIIX_SMB_TXSLVA_ADDR(addr) | (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0)); b = cmdbuf; if (cmdlen > 0) /* Set command byte */ bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HCMD, b[0]); if (I2C_OP_WRITE_P(op)) { /* Write data */ b = buf; if (cmdlen == 0 && len == 1) bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HCMD, b[0]); else if (len > 0) bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HD0, b[0]); if (len > 1) bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HD1, b[1]); } /* Set SMBus command */ if (cmdlen == 0) { if (len == 0) ctl = PIIX_SMB_HC_CMD_QUICK; else ctl = PIIX_SMB_HC_CMD_BYTE; } else if (len == 1) ctl = PIIX_SMB_HC_CMD_BDATA; else if (len == 2) ctl = PIIX_SMB_HC_CMD_WDATA; if ((flags & I2C_F_POLL) == 0) ctl |= PIIX_SMB_HC_INTREN; /* Start transaction */ ctl |= PIIX_SMB_HC_START; bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HC, ctl); if (flags & I2C_F_POLL) { /* Poll for completion */ if (PIIXPM_IS_CSB5(sc->sc_id)) DELAY(2*PIIXPM_DELAY); else DELAY(PIIXPM_DELAY); for (retries = 1000; retries > 0; retries--) { st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS); if ((st & PIIX_SMB_HS_BUSY) == 0) break; DELAY(PIIXPM_DELAY); } if (st & PIIX_SMB_HS_BUSY) goto timeout; piixpm_intr(smbus); } else { /* Wait for interrupt */ if (tsleep(sc, PRIBIO, "iicexec", PIIXPM_TIMEOUT * hz)) goto timeout; } if (sc->sc_i2c_xfer.error) return (1); return (0); timeout: /* * Transfer timeout. Kill the transaction and clear status bits. */ aprint_error_dev(sc->sc_dev, "timeout, status 0x%x\n", st); bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HC, PIIX_SMB_HC_KILL); DELAY(PIIXPM_DELAY); st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS); if ((st & PIIX_SMB_HS_FAILED) == 0) aprint_error_dev(sc->sc_dev, "transaction abort failed, status 0x%x\n", st); bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, PIIX_SMB_HS, st); /* * CSB5 needs hard reset to unlock the smbus after timeout. */ if (PIIXPM_IS_CSB5(sc->sc_id)) piixpm_csb5_reset(sc); return (1); }
int piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) { struct piixpm_softc *sc = cookie; u_int8_t *b; u_int8_t ctl, st; int retries; DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, " "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags)); /* Wait for bus to be idle */ for (retries = 100; retries > 0; retries--) { st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS); if (!(st & PIIX_SMB_HS_BUSY)) break; DELAY(PIIXPM_DELAY); } DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS)); if (st & PIIX_SMB_HS_BUSY) return (1); if (cold || sc->sc_poll) flags |= I2C_F_POLL; if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) return (1); /* Setup transfer */ sc->sc_i2c_xfer.op = op; sc->sc_i2c_xfer.buf = buf; sc->sc_i2c_xfer.len = len; sc->sc_i2c_xfer.flags = flags; sc->sc_i2c_xfer.error = 0; /* Set slave address and transfer direction */ bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_TXSLVA, PIIX_SMB_TXSLVA_ADDR(addr) | (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0)); b = (void *)cmdbuf; if (cmdlen > 0) /* Set command byte */ bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HCMD, b[0]); if (I2C_OP_WRITE_P(op)) { /* Write data */ b = buf; if (len > 0) bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HD0, b[0]); if (len > 1) bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HD1, b[1]); } /* Set SMBus command */ if (len == 0) ctl = PIIX_SMB_HC_CMD_BYTE; else if (len == 1) ctl = PIIX_SMB_HC_CMD_BDATA; else if (len == 2) ctl = PIIX_SMB_HC_CMD_WDATA; if ((flags & I2C_F_POLL) == 0) ctl |= PIIX_SMB_HC_INTREN; /* Start transaction */ ctl |= PIIX_SMB_HC_START; bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, ctl); if (flags & I2C_F_POLL) { /* Poll for completion */ DELAY(PIIXPM_DELAY); for (retries = 1000; retries > 0; retries--) { st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS); if ((st & PIIX_SMB_HS_BUSY) == 0) break; DELAY(PIIXPM_DELAY); } if (st & PIIX_SMB_HS_BUSY) goto timeout; piixpm_intr(sc); } else { /* Wait for interrupt */ if (tsleep(sc, PRIBIO, "iicexec", PIIXPM_TIMEOUT * hz)) goto timeout; } if (sc->sc_i2c_xfer.error) return (1); return (0); timeout: /* * Transfer timeout. Kill the transaction and clear status bits. */ printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, " "flags 0x%02x: timeout, status 0x%b\n", sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags, st, PIIX_SMB_HS_BITS); bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, PIIX_SMB_HC_KILL); DELAY(PIIXPM_DELAY); st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS); if ((st & PIIX_SMB_HS_FAILED) == 0) printf("%s: abort failed, status 0x%b\n", sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS); bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st); return (1); }