Module Name: src Committed By: jmcneill Date: Sat May 16 21:31:39 UTC 2015
Modified Files: src/sys/arch/arm/nvidia: tegra_i2c.c tegra_i2creg.h Log Message: Support transfers > 4-bytes long by reading and writing using the controller's packet-based interface instead of non-packet ("normal") mode. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/nvidia/tegra_i2c.c \ src/sys/arch/arm/nvidia/tegra_i2creg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/nvidia/tegra_i2c.c diff -u src/sys/arch/arm/nvidia/tegra_i2c.c:1.1 src/sys/arch/arm/nvidia/tegra_i2c.c:1.2 --- src/sys/arch/arm/nvidia/tegra_i2c.c:1.1 Sun May 10 23:50:21 2015 +++ src/sys/arch/arm/nvidia/tegra_i2c.c Sat May 16 21:31:39 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_i2c.c,v 1.1 2015/05/10 23:50:21 jmcneill Exp $ */ +/* $NetBSD: tegra_i2c.c,v 1.2 2015/05/16 21:31:39 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "locators.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_i2c.c,v 1.1 2015/05/10 23:50:21 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_i2c.c,v 1.2 2015/05/16 21:31:39 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -52,6 +52,7 @@ struct tegra_i2c_softc { bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_ih; + u_int sc_port; struct i2c_controller sc_ic; kmutex_t sc_lock; @@ -107,6 +108,7 @@ tegra_i2c_attach(device_t parent, device sc->sc_bst = tio->tio_bst; bus_space_subregion(tio->tio_bst, tio->tio_bsh, loc->loc_offset, loc->loc_size, &sc->sc_bsh); + sc->sc_port = loc->loc_port; mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); cv_init(&sc->sc_cv, device_xname(self)); @@ -144,7 +146,8 @@ tegra_i2c_init(struct tegra_i2c_softc *s __SHIFTIN(0x1, I2C_CLK_DIVISOR_HSMODE)); I2C_WRITE(sc, I2C_INTERRUPT_MASK_REG, 0); - I2C_WRITE(sc, I2C_CNFG_REG, I2C_CNFG_NEW_MASTER_FSM); + I2C_WRITE(sc, I2C_CNFG_REG, + I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN); I2C_SET_CLEAR(sc, I2C_SL_CNFG_REG, I2C_SL_CNFG_NEWSL, 0); } @@ -242,43 +245,42 @@ done: static int tegra_i2c_wait(struct tegra_i2c_softc *sc, int flags) { - const struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 }; - struct timeval tnow, tend; - uint32_t stat; - int error; - - getmicrotime(&tnow); - timeradd(&tnow, &timeout, &tend); - - for (;;) { - getmicrotime(&tnow); - if (timercmp(&tnow, &tend, >=)) { - return ETIMEDOUT; - } + int error, retry; + uint32_t stat = 0; + + retry = (flags & I2C_F_POLL) ? 100000 : 100; + + while (--retry > 0) { if ((flags & I2C_F_POLL) == 0) { - struct timeval trem; - timersub(&tend, &tnow, &trem); - const u_int ms = (trem.tv_sec * 1000) + - (trem.tv_usec / 1000); - KASSERT(ms > 0); error = cv_timedwait_sig(&sc->sc_cv, &sc->sc_lock, - max(mstohz(ms), 1)); + max(mstohz(10), 1)); if (error) { return error; } } - stat = I2C_READ(sc, I2C_STATUS_REG); - if ((stat & I2C_STATUS_BUSY) == 0) { + stat = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG); + if (stat & I2C_INTERRUPT_STATUS_PACKET_XFER_COMPLETE) { break; } if (flags & I2C_F_POLL) { - delay(1); + delay(10); } } + if (retry == 0) { + stat = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG); + device_printf(sc->sc_dev, "timed out, status = %#x\n", stat); + return ETIMEDOUT; + } + const uint32_t err_mask = + I2C_INTERRUPT_STATUS_NOACK | + I2C_INTERRUPT_STATUS_ARB_LOST | + I2C_INTERRUPT_MASK_TIMEOUT; - if (__SHIFTOUT(stat, I2C_STATUS_CMD1_STAT) != 0) + if (stat & err_mask) { + device_printf(sc->sc_dev, "error, status = %#x\n", stat); return EIO; + } return 0; } @@ -287,33 +289,55 @@ static int tegra_i2c_write(struct tegra_i2c_softc *sc, i2c_addr_t addr, const uint8_t *buf, size_t buflen, int flags) { - uint32_t data, cnfg; - size_t n; - - if (buflen > 4) - return EINVAL; + const uint8_t *p = buf; + size_t n, resid = buflen; + uint32_t data; + int retry; - I2C_WRITE(sc, I2C_CMD_ADDR0_REG, addr << 1); - for (n = 0, data = 0; n < buflen; n++) { - data |= (uint32_t)buf[n] << (n * 8); - } - I2C_WRITE(sc, I2C_CMD_DATA1_REG, data); - - cnfg = I2C_READ(sc, I2C_CNFG_REG); - cnfg &= ~I2C_CNFG_DEBOUNCE_CNT; - cnfg |= __SHIFTIN(2, I2C_CNFG_DEBOUNCE_CNT); - cnfg &= ~I2C_CNFG_LENGTH; - cnfg |= __SHIFTIN(buflen - 1, I2C_CNFG_LENGTH); - cnfg &= ~I2C_CNFG_SLV2; - cnfg &= ~I2C_CNFG_CMD1; - cnfg &= ~I2C_CNFG_NOACK; - cnfg &= ~I2C_CNFG_A_MOD; - I2C_WRITE(sc, I2C_CNFG_REG, cnfg); + const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG); + I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus); - I2C_SET_CLEAR(sc, I2C_BUS_CONFIG_LOAD_REG, - I2C_BUS_CONFIG_LOAD_MSTR_CONFIG_LOAD, 0); + /* Generic Header 0 */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + __SHIFTIN(I2C_IOPACKET_WORD0_PROTHDRSZ_REQ, + I2C_IOPACKET_WORD0_PROTHDRSZ) | + __SHIFTIN(sc->sc_port, I2C_IOPACKET_WORD0_CONTROLLERID) | + __SHIFTIN(1, I2C_IOPACKET_WORD0_PKTID) | + __SHIFTIN(I2C_IOPACKET_WORD0_PROTOCOL_I2C, + I2C_IOPACKET_WORD0_PROTOCOL) | + __SHIFTIN(I2C_IOPACKET_WORD0_PKTTYPE_REQ, + I2C_IOPACKET_WORD0_PKTTYPE)); + /* Generic Header 1 */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + __SHIFTIN(buflen - 1, I2C_IOPACKET_WORD1_PAYLOADSIZE)); + /* I2C Master Transmit Packet Header */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + I2C_IOPACKET_XMITHDR_IE | + __SHIFTIN((addr << 1), I2C_IOPACKET_XMITHDR_SLAVE_ADDR)); + + /* Transmit data */ + while (resid > 0) { + retry = 10000; + while (--retry > 0) { + const uint32_t fs = I2C_READ(sc, I2C_FIFO_STATUS_REG); + const u_int cnt = + __SHIFTOUT(fs, I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT); + if (cnt > 0) + break; + delay(10); + } + if (retry == 0) { + device_printf(sc->sc_dev, "TX FIFO timeout\n"); + return ETIMEDOUT; + } - I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0); + for (n = 0, data = 0; n < min(resid, 4); n++) { + data |= (uint32_t)p[n] << (n * 8); + } + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, data); + resid -= min(resid, 4); + p += min(resid, 4); + } return tegra_i2c_wait(sc, flags); } @@ -322,30 +346,57 @@ static int tegra_i2c_read(struct tegra_i2c_softc *sc, i2c_addr_t addr, uint8_t *buf, size_t buflen, int flags) { - uint32_t data, cnfg; - int error; - size_t n; - - if (buflen > 4) - return EINVAL; - - I2C_WRITE(sc, I2C_CMD_ADDR0_REG, (addr << 1) | 1); - cnfg = I2C_READ(sc, I2C_CNFG_REG); - cnfg &= ~I2C_CNFG_SLV2; - cnfg |= I2C_CNFG_CMD1; - cnfg &= ~I2C_CNFG_LENGTH; - cnfg |= __SHIFTIN(buflen - 1, I2C_CNFG_LENGTH); - I2C_WRITE(sc, I2C_CNFG_REG, cnfg); + uint8_t *p = buf; + size_t n, resid = buflen; + uint32_t data; + int error, retry; + + const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG); + I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus); - I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0); + /* Generic Header 0 */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + __SHIFTIN(I2C_IOPACKET_WORD0_PROTHDRSZ_REQ, + I2C_IOPACKET_WORD0_PROTHDRSZ) | + __SHIFTIN(sc->sc_port, I2C_IOPACKET_WORD0_CONTROLLERID) | + __SHIFTIN(1, I2C_IOPACKET_WORD0_PKTID) | + __SHIFTIN(I2C_IOPACKET_WORD0_PROTOCOL_I2C, + I2C_IOPACKET_WORD0_PROTOCOL) | + __SHIFTIN(I2C_IOPACKET_WORD0_PKTTYPE_REQ, + I2C_IOPACKET_WORD0_PKTTYPE)); + /* Generic Header 1 */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + __SHIFTIN(buflen - 1, I2C_IOPACKET_WORD1_PAYLOADSIZE)); + /* I2C Master Transmit Packet Header */ + I2C_WRITE(sc, I2C_TX_PACKET_FIFO_REG, + I2C_IOPACKET_XMITHDR_IE | I2C_IOPACKET_XMITHDR_READ | + __SHIFTIN((addr << 1) | 1, I2C_IOPACKET_XMITHDR_SLAVE_ADDR)); - error = tegra_i2c_wait(sc, flags); - if (error) + if ((error = tegra_i2c_wait(sc, flags)) != 0) { return error; + } + + while (resid > 0) { + retry = 10000; + while (--retry > 0) { + const uint32_t fs = I2C_READ(sc, I2C_FIFO_STATUS_REG); + const u_int cnt = + __SHIFTOUT(fs, I2C_FIFO_STATUS_RX_FIFO_FULL_CNT); + if (cnt > 0) + break; + delay(10); + } + if (retry == 0) { + device_printf(sc->sc_dev, "RX FIFO timeout\n"); + return ETIMEDOUT; + } - data = I2C_READ(sc, I2C_CMD_DATA1_REG); - for (n = 0; n < buflen; n++) { - buf[n] = (data >> (n * 8)) & 0xff; + data = I2C_READ(sc, I2C_RX_FIFO_REG); + for (n = 0; n < min(resid, 4); n++) { + p[n] = (data >> (n * 8)) & 0xff; + } + resid -= min(resid, 4); + p += min(resid, 4); } return 0; Index: src/sys/arch/arm/nvidia/tegra_i2creg.h diff -u src/sys/arch/arm/nvidia/tegra_i2creg.h:1.1 src/sys/arch/arm/nvidia/tegra_i2creg.h:1.2 --- src/sys/arch/arm/nvidia/tegra_i2creg.h:1.1 Sun May 10 23:50:21 2015 +++ src/sys/arch/arm/nvidia/tegra_i2creg.h Sat May 16 21:31:39 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_i2creg.h,v 1.1 2015/05/10 23:50:21 jmcneill Exp $ */ +/* $NetBSD: tegra_i2creg.h,v 1.2 2015/05/16 21:31:39 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -76,7 +76,15 @@ #define I2C_SL_INT_SET_REG 0x48 #define I2C_TX_PACKET_FIFO_REG 0x50 #define I2C_RX_FIFO_REG 0x54 + #define I2C_PACKET_TRANSFER_STATUS_REG 0x58 +#define I2C_PACKET_TRANSFER_STATUS_TRANSFER_COMPLETE __BIT(24) +#define I2C_PACKET_TRANSFER_STATUS_TRANSFER_PKT_ID __BITS(23,16) +#define I2C_PACKET_TRANSFER_STATUS_TRANSFER_BYTENUM __BITS(15,4) +#define I2C_PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR __BIT(3) +#define I2C_PACKET_TRANSFER_STATUS_NOACK_FOR_DATA __BIT(2) +#define I2C_PACKET_TRANSFER_STATUS_ARB_LOST __BIT(1) +#define I2C_PACKET_TRANSFER_STATUS_CONTROLLER_BUSY __BIT(0) #define I2C_FIFO_CONTROL_REG 0x5c #define I2C_FIFO_CONTROL_SLV_TX_FIFO_TRIG __BITS(15,13) @@ -91,9 +99,9 @@ #define I2C_FIFO_STATUS_REG 0x60 #define I2C_FIFO_STATUS_SLV_XFER_ERR_REASON __BIT(25) #define I2C_FIFO_STATUS_SLV_TX_FIFO_EMPTY_CNT __BITS(23,20) -#define I2C_FIFO_STATUS_SLV_RX_FIFO_EMPTY_CNT __BITS(19,16) +#define I2C_FIFO_STATUS_SLV_RX_FIFO_FULL_CNT __BITS(19,16) #define I2C_FIFO_STATUS_TX_FIFO_EMPTY_CNT __BITS(7,4) -#define I2C_FIFO_STATUS_RX_FIFO_EMPTY_CNT __BITS(3,0) +#define I2C_FIFO_STATUS_RX_FIFO_FULL_CNT __BITS(3,0) #define I2C_INTERRUPT_MASK_REG 0x64 #define I2C_INTERRUPT_MASK_TIMEOUT __BIT(8) @@ -105,6 +113,13 @@ #define I2C_INTERRUPT_MASK_RFIFO_DATA_REQ __BIT(0) #define I2C_INTERRUPT_STATUS_REG 0x68 +#define I2C_INTERRUPT_STATUS_TIMEOUT __BIT(8) +#define I2C_INTERRUPT_STATUS_PACKET_XFER_COMPLETE __BIT(7) +#define I2C_INTERRUPT_STATUS_ALL_PACKETS_XFER_COMPLETE __BIT(6) +#define I2C_INTERRUPT_STATUS_NOACK __BIT(3) +#define I2C_INTERRUPT_STATUS_ARB_LOST __BIT(2) +#define I2C_INTERRUPT_STATUS_TFIFO_DATA_REQ __BIT(1) +#define I2C_INTERRUPT_STATUS_RFIFO_DATA_REQ __BIT(0) #define I2C_CLK_DIVISOR_REG 0x6c #define I2C_CLK_DIVISOR_STD_FAST_MODE __BITS(31,16) @@ -131,4 +146,45 @@ #define I2C_HS_INTERFACE_TIMING0_REG 0x9c #define I2C_HS_INTERFACE_TIMING1_REG 0xa0 +/* + * I/O Packets + */ +#define I2C_IOPACKET_WORD0_PROTHDRSZ __BITS(29,28) +#define I2C_IOPACKET_WORD0_PROTHDRSZ_REQ 0 +#define I2C_IOPACKET_WORD0_PROTHDRSZ_RES 1 +#define I2C_IOPACKET_WORD0_PKTID __BITS(23,16) +#define I2C_IOPACKET_WORD0_CONTROLLERID __BITS(15,12) +#define I2C_IOPACKET_WORD0_PROTOCOL __BITS(7,4) +#define I2C_IOPACKET_WORD0_PROTOCOL_I2C 1 +#define I2C_IOPACKET_WORD0_PKTTYPE __BITS(2,0) +#define I2C_IOPACKET_WORD0_PKTTYPE_REQ 0 +#define I2C_IOPACKET_WORD0_PKTTYPE_RES 1 +#define I2C_IOPACKET_WORD0_PKTTYPE_INT 2 +#define I2C_IOPACKET_WORD0_PKTTYPE_STOP 3 + +#define I2C_IOPACKET_WORD1_PAYLOADSIZE __BITS(11,0) + +#define I2C_IOPACKET_XMITHDR_RESP_PKT_FREQ __BIT(25) +#define I2C_IOPACKET_XMITHDR_RESP_PKT_ENABLE __BIT(24) +#define I2C_IOPACKET_XMITHDR_HS_MODE __BIT(22) +#define I2C_IOPACKET_XMITHDR_CONTINUE_ON_NACK __BIT(21) +#define I2C_IOPACKET_XMITHDR_SEND_START_BYTE __BIT(20) +#define I2C_IOPACKET_XMITHDR_READ __BIT(19) +#define I2C_IOPACKET_XMITHDR_ADDRESS_MODE __BIT(18) +#define I2C_IOPACKET_XMITHDR_IE __BIT(17) +#define I2C_IOPACKET_XMITHDR_REPEAT_STARTSTOP __BIT(16) +#define I2C_IOPACKET_XMITHDR_CONTINUE_XFER __BIT(15) +#define I2C_IOPACKET_XMITHDR_HS_MASTER_ADDR __BITS(14,12) +#define I2C_IOPACKET_XMITHDR_SLAVE_ADDR __BITS(9,0) + +#define I2C_IOPACKET_RESPHDR_RFIFO_OVF __BIT(26) +#define I2C_IOPACKET_RESPHDR_TFIFO_OVF __BIT(25) +#define I2C_IOPACKET_RESPHDR_TRANSFER_COMPLETE __BIT(24) +#define I2C_IOPACKET_RESPHDR_TRANSFER_PKT_ID __BITS(23,16) +#define I2C_IOPACKET_RESPHDR_TRANSFER_BYTENUM __BITS(15,4) +#define I2C_IOPACKET_RESPHDR_NOACK_FOR_ADDR __BIT(3) +#define I2C_IOPACKET_RESPHDR_NOACK_FOR_DATA __BIT(2) +#define I2C_IOPACKET_RESPHDR_ARB_LOST __BIT(1) +#define I2C_IOPACKET_RESPHDR_CONTROLLER_BUSY __BIT(0) + #endif /* _ARM_TEGRA_I2CREG_H */