Support transfers > 4-bytes long by reading and writing using the
controller's packet-based interface instead of non-packet ("normal") mode.
This commit is contained in:
parent
137a13ddd4
commit
df78cf4a7d
@ -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 <jmcneill@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_t self, void *aux)
|
||||
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 *sc)
|
||||
__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;
|
||||
int error, retry;
|
||||
uint32_t stat = 0;
|
||||
|
||||
getmicrotime(&tnow);
|
||||
timeradd(&tnow, &timeout, &tend);
|
||||
retry = (flags & I2C_F_POLL) ? 100000 : 100;
|
||||
|
||||
for (;;) {
|
||||
getmicrotime(&tnow);
|
||||
if (timercmp(&tnow, &tend, >=)) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
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;
|
||||
const uint8_t *p = buf;
|
||||
size_t n, resid = buflen;
|
||||
uint32_t data;
|
||||
int retry;
|
||||
|
||||
if (buflen > 4)
|
||||
return EINVAL;
|
||||
const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
|
||||
I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
|
||||
|
||||
I2C_WRITE(sc, I2C_CMD_ADDR0_REG, addr << 1);
|
||||
for (n = 0, data = 0; n < buflen; n++) {
|
||||
data |= (uint32_t)buf[n] << (n * 8);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
I2C_SET_CLEAR(sc, I2C_BUS_CONFIG_LOAD_REG,
|
||||
I2C_BUS_CONFIG_LOAD_MSTR_CONFIG_LOAD, 0);
|
||||
|
||||
I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0);
|
||||
|
||||
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;
|
||||
uint8_t *p = buf;
|
||||
size_t n, resid = buflen;
|
||||
uint32_t data;
|
||||
int error, retry;
|
||||
|
||||
if (buflen > 4)
|
||||
return EINVAL;
|
||||
const uint32_t istatus = I2C_READ(sc, I2C_INTERRUPT_STATUS_REG);
|
||||
I2C_WRITE(sc, I2C_INTERRUPT_STATUS_REG, istatus);
|
||||
|
||||
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);
|
||||
/* 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));
|
||||
|
||||
I2C_SET_CLEAR(sc, I2C_CNFG_REG, I2C_CNFG_SEND, 0);
|
||||
|
||||
error = tegra_i2c_wait(sc, flags);
|
||||
if (error)
|
||||
if ((error = tegra_i2c_wait(sc, flags)) != 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
data = I2C_READ(sc, I2C_CMD_DATA1_REG);
|
||||
for (n = 0; n < buflen; n++) {
|
||||
buf[n] = (data >> (n * 8)) & 0xff;
|
||||
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_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;
|
||||
|
@ -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 <jmcneill@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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user