i2c: make i2c_transfer() really generic

Make the i2c_transfer() to transact messages through the I2C bus.

It is useful for I2C storage devices, as now we can send one message
containing the destination byte/block address, then send the data
in another message. There is no need to construct one continuous
data buffer to send both address and data anymore.

The drivers and sample apps have been updated to utilize updated
API when appropriate. For i2c_dw, only master mode has been updated.
Slave mode will be updated once we can adequately test it.

Change-Id: I0a811d60567367817fcc8d15f5454e5c933722e2
Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2016-01-11 15:35:39 -08:00 committed by Anas Nashif
commit dff5f6a038
7 changed files with 376 additions and 261 deletions

View file

@ -102,8 +102,17 @@ static int _read_port_regs(struct device *dev, uint8_t reg,
uint16_t i2c_addr = config->i2c_slave_addr; uint16_t i2c_addr = config->i2c_slave_addr;
int ret; int ret;
uint8_t cmd[] = { reg }; uint8_t cmd[] = { reg };
struct i2c_msg msgs[2];
ret = i2c_transfer(i2c_master, cmd, 1, buf->byte, 2, i2c_addr, 0); msgs[0].buf = cmd;
msgs[0].len = 1;
msgs[0].flags = I2C_MSG_WRITE;
msgs[1].buf = buf->byte;
msgs[1].len = 2;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_RESTART;
ret = i2c_transfer(i2c_master, msgs, 2, i2c_addr);
if (ret) { if (ret) {
DBG("PCAL9535A[0x%X]: error reading register 0x%X (%d)\n", DBG("PCAL9535A[0x%X]: error reading register 0x%X (%d)\n",
i2c_addr, reg, ret); i2c_addr, reg, ret);

View file

@ -52,7 +52,7 @@
#endif /* CONFIG_STDOUT_CONSOLE */ #endif /* CONFIG_STDOUT_CONSOLE */
#endif /* CONFIG_I2C_DEBUG */ #endif /* CONFIG_I2C_DEBUG */
static inline void _i2c_dw_data_ask(struct device *dev, uint8_t restart) static inline void _i2c_dw_data_ask(struct device *dev)
{ {
struct i2c_dw_rom_config const * const rom = dev->config->config_info; struct i2c_dw_rom_config const * const rom = dev->config->config_info;
struct i2c_dw_dev_config * const dw = dev->driver_data; struct i2c_dw_dev_config * const dw = dev->driver_data;
@ -61,21 +61,23 @@ static inline void _i2c_dw_data_ask(struct device *dev, uint8_t restart)
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
/* No more bytes to request */ /* No more bytes to request, so command queue is no longer needed */
if (dw->request_bytes == 0) { if (dw->request_bytes == 0) {
regs->ic_intr_mask.bits.tx_empty = 0;
return; return;
} }
/* Tell controller to get another byte */ /* Tell controller to get another byte */
data = IC_DATA_CMD_CMD; data = IC_DATA_CMD_CMD;
/* Send restart if needed) */ /* Send RESTART if needed */
if (restart) { if (dw->xfr_flags & I2C_MSG_RESTART) {
data |= IC_DATA_CMD_RESTART; data |= IC_DATA_CMD_RESTART;
dw->xfr_flags &= ~(I2C_MSG_RESTART);
} }
/* After receiving the last byte, send STOP */ /* After receiving the last byte, send STOP if needed */
if (dw->request_bytes == 1) { if ((dw->xfr_flags & I2C_MSG_STOP) && (dw->request_bytes == 1)) {
data |= IC_DATA_CMD_STOP; data |= IC_DATA_CMD_STOP;
} }
@ -92,21 +94,21 @@ static void _i2c_dw_data_read(struct device *dev)
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
while (regs->ic_status.bits.rfne && (dw->rx_len > 0)) { while (regs->ic_status.bits.rfne && (dw->xfr_len > 0)) {
dw->rx_buffer[0] = regs->ic_data_cmd.raw; dw->xfr_buf[0] = regs->ic_data_cmd.raw;
dw->rx_buffer += 1; dw->xfr_buf++;
dw->rx_len -= 1; dw->xfr_len--;
if (dw->rx_len == 0) { if (dw->xfr_len == 0) {
break; break;
} }
_i2c_dw_data_ask(dev, 0); _i2c_dw_data_ask(dev);
} }
/* Nothing to receive anymore */ /* Nothing to receive anymore */
if (dw->rx_len == 0) { if (dw->xfr_len == 0) {
dw->state &= ~I2C_DW_CMD_RECV; dw->state &= ~I2C_DW_CMD_RECV;
return; return;
} }
@ -123,41 +125,33 @@ static void _i2c_dw_data_send(struct device *dev)
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
/* Nothing to send anymore, mask the interrupt */ /* Nothing to send anymore, mask the interrupt */
if (dw->tx_len == 0) { if (dw->xfr_len == 0) {
regs->ic_intr_mask.bits.tx_empty = 0; regs->ic_intr_mask.bits.tx_empty = 0;
if (dw->rx_len > 0) {
/* Tell controller to grab a byte.
* RESTART if something has ben sent.
*/
_i2c_dw_data_ask(dev, (dw->state & I2C_DW_CMD_SEND));
/* QUIRK:
* If requesting more than one byte, the process has
* to be jump-started by requesting two bytes first.
*/
_i2c_dw_data_ask(dev, 0);
}
dw->state &= ~I2C_DW_CMD_SEND; dw->state &= ~I2C_DW_CMD_SEND;
return; return;
} }
while (regs->ic_status.bits.tfnf && (dw->tx_len > 0)) { while (regs->ic_status.bits.tfnf && (dw->xfr_len > 0)) {
/* We have something to transmit to a specific host */ /* We have something to transmit to a specific host */
data = dw->tx_buffer[0]; data = dw->xfr_buf[0];
/* If this is the last byte to write /* Send RESTART if needed */
* and nothing to receive, send STOP. if (dw->xfr_flags & I2C_MSG_RESTART) {
*/ data |= IC_DATA_CMD_RESTART;
if ((dw->tx_len == 1) && (dw->rx_len == 0)) { dw->xfr_flags &= ~(I2C_MSG_RESTART);
}
/* Send STOP if needed */
if ((dw->xfr_len == 1) && (dw->xfr_flags & I2C_MSG_STOP)) {
data |= IC_DATA_CMD_STOP; data |= IC_DATA_CMD_STOP;
} }
regs->ic_data_cmd.raw = data; regs->ic_data_cmd.raw = data;
dw->tx_len -= 1;
dw->tx_buffer += 1; dw->xfr_len--;
dw->xfr_buf++;
} }
} }
@ -165,44 +159,38 @@ static inline void _i2c_dw_transfer_complete(struct device *dev)
{ {
struct i2c_dw_rom_config const * const rom = dev->config->config_info; struct i2c_dw_rom_config const * const rom = dev->config->config_info;
struct i2c_dw_dev_config * const dw = dev->driver_data; struct i2c_dw_dev_config * const dw = dev->driver_data;
bool done = false;
uint32_t value; uint32_t value;
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
if ((dw->state == I2C_DW_CMD_ERROR) || regs->ic_intr_mask.raw = DW_DISABLE_ALL_I2C_INT;
(!dw->tx_len && !dw->rx_len) || value = regs->ic_clr_intr;
(dw->tx_buffer && !dw->tx_len && !dw->rx_buffer) ||
(dw->rx_buffer && !dw->rx_len && !dw->tx_buffer)) {
done = true;
}
if (done) { synchronous_call_complete(&dw->sync);
regs->ic_intr_mask.raw = DW_DISABLE_ALL_I2C_INT;
value = regs->ic_clr_intr;
synchronous_call_complete(&dw->sync);
}
dw->state &= ~I2C_DW_BUSY;
} }
void i2c_dw_isr(struct device *port) void i2c_dw_isr(struct device *port)
{ {
struct i2c_dw_rom_config const * const rom = port->config->config_info; struct i2c_dw_rom_config const * const rom = port->config->config_info;
struct i2c_dw_dev_config * const dw = port->driver_data; struct i2c_dw_dev_config * const dw = port->driver_data;
uint32_t value = 0; union ic_interrupt_register intr_stat;
uint32_t value;
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
/* Cache ic_intr_stat for processing, so there is no need to read
* the register multiple times.
*/
intr_stat.raw = regs->ic_intr_stat.raw;
#if CONFIG_SHARED_IRQ #if CONFIG_SHARED_IRQ
/* If using with shared IRQ, this function will be called /* If using with shared IRQ, this function will be called
* by the shared IRQ driver. So check here if the interrupt * by the shared IRQ driver. So check here if the interrupt
* is coming from the I2C controller (or somewhere else). * is coming from the I2C controller (or somewhere else).
*/ */
if (!regs->ic_intr_stat.raw) { if (!intr_stat.raw) {
return; return;
} }
#endif #endif
@ -222,41 +210,51 @@ void i2c_dw_isr(struct device *port)
DBG("I2C: interrupt received\n"); DBG("I2C: interrupt received\n");
/*
* We got a STOP_DET, this means stop right after this byte has been
* handled.
*/
if (regs->ic_intr_stat.bits.stop_det) {
value = regs->ic_clr_stop_det;
_i2c_dw_transfer_complete(port);
}
/* Check if we are configured as a master device */ /* Check if we are configured as a master device */
if (regs->ic_con.bits.master_mode) { if (regs->ic_con.bits.master_mode) {
/* Check if the Master TX is ready for sending */ /* Bail early if there is any error. */
if (regs->ic_intr_stat.bits.tx_empty) { if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER |
_i2c_dw_data_send(port); DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_UNDER) &
intr_stat.raw) {
dw->state = I2C_DW_CMD_ERROR;
_i2c_dw_transfer_complete(port);
return;
} }
/* Check if the RX FIFO reached threshold */ /* Check if the RX FIFO reached threshold */
if (regs->ic_intr_stat.bits.rx_full) { if (intr_stat.bits.rx_full) {
_i2c_dw_data_read(port); _i2c_dw_data_read(port);
} }
if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER | /* Check if the TX FIFO is ready for commands.
DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_UNDER) & * TX FIFO also serves as command queue where read requests
regs->ic_intr_stat.raw) { * are written to TX FIFO.
dw->state = I2C_DW_CMD_ERROR; */
_i2c_dw_transfer_complete(port); if (intr_stat.bits.tx_empty) {
if ((dw->xfr_flags & I2C_MSG_RW_MASK)
== I2C_MSG_WRITE) {
_i2c_dw_data_send(port);
} else {
_i2c_dw_data_ask(port);
}
/* If STOP is not expected, finish processing this
* message if there is nothing left to do anymore.
*/
if ((dw->xfr_len == 0)
&& !(dw->xfr_flags & I2C_MSG_STOP)) {
_i2c_dw_transfer_complete(port);
return;
}
} }
} else { /* we must be configured as a slave device */ } else { /* we must be configured as a slave device */
/* We have a read requested by the master device */ /* We have a read requested by the master device */
if (regs->ic_intr_stat.bits.rd_req && if (intr_stat.bits.rd_req &&
(!dw->app_config.bits.is_slave_read)) { (!dw->app_config.bits.is_slave_read)) {
/* data is not ready to send */ /* data is not ready to send */
if (regs->ic_intr_stat.bits.tx_abrt) { if (intr_stat.bits.tx_abrt) {
/* clear the TX_ABRT interrupt */ /* clear the TX_ABRT interrupt */
value = regs->ic_clr_tx_abrt; value = regs->ic_clr_tx_abrt;
} }
@ -266,11 +264,18 @@ void i2c_dw_isr(struct device *port)
} }
/* The slave device is ready to receive */ /* The slave device is ready to receive */
if (regs->ic_intr_stat.bits.rx_full && if (intr_stat.bits.rx_full &&
dw->app_config.bits.is_slave_read) { dw->app_config.bits.is_slave_read) {
_i2c_dw_data_read(port); _i2c_dw_data_read(port);
} }
} }
/* STOP detected: finish processing this message */
if (intr_stat.bits.stop_det) {
value = regs->ic_clr_stop_det;
_i2c_dw_transfer_complete(port);
return;
}
} }
@ -375,33 +380,15 @@ static int _i2c_dw_setup(struct device *dev)
} }
static int _i2c_dw_transfer_init(struct device *dev, static int _i2c_dw_transfer_init(struct device *dev, uint16_t slave_address)
uint8_t *write_buf, uint32_t write_len,
uint8_t *read_buf, uint32_t read_len,
uint16_t slave_address)
{ {
struct i2c_dw_rom_config const * const rom = dev->config->config_info; struct i2c_dw_rom_config const * const rom = dev->config->config_info;
struct i2c_dw_dev_config * const dw = dev->driver_data; uint32_t value;
uint32_t value = 0; int ret;
int ret = DEV_OK;
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
dw->state |= I2C_DW_BUSY;
if (write_len > 0) {
dw->state |= I2C_DW_CMD_SEND;
}
if (read_len > 0) {
dw->state |= I2C_DW_CMD_RECV;
}
dw->rx_len = read_len;
dw->rx_buffer = read_buf;
dw->tx_len = write_len;
dw->tx_buffer = write_buf;
dw->request_bytes = read_len;
/* Disable the device controller to be able set TAR */ /* Disable the device controller to be able set TAR */
regs->ic_enable.bits.enable = 0; regs->ic_enable.bits.enable = 0;
@ -428,46 +415,95 @@ static int _i2c_dw_transfer_init(struct device *dev,
} }
static int i2c_dw_transfer(struct device *dev, static int i2c_dw_transfer(struct device *dev,
uint8_t *write_buf, uint32_t write_len, struct i2c_msg *msgs, uint8_t num_msgs,
uint8_t *read_buf, uint32_t read_len, uint16_t slave_address)
uint16_t slave_address, uint32_t flags)
{ {
struct i2c_dw_rom_config const * const rom = dev->config->config_info; struct i2c_dw_rom_config const * const rom = dev->config->config_info;
struct i2c_dw_dev_config * const dw = dev->driver_data; struct i2c_dw_dev_config * const dw = dev->driver_data;
struct i2c_msg *cur_msg = msgs;
uint8_t msg_left = num_msgs;
uint8_t pflags;
int ret; int ret;
volatile struct i2c_dw_registers * const regs = volatile struct i2c_dw_registers * const regs =
(struct i2c_dw_registers *)rom->base_address; (struct i2c_dw_registers *)rom->base_address;
/* Why bother processing no messages */
if (!msgs || !num_msgs) {
return DEV_INVALID_OP;
}
/* First step, check if there is current activity */ /* First step, check if there is current activity */
if (regs->ic_status.bits.activity) { if ((regs->ic_status.bits.activity) || (dw->state & I2C_DW_BUSY)) {
return DEV_FAIL; return DEV_FAIL;
} }
ret = _i2c_dw_transfer_init(dev, write_buf, write_len, dw->state |= I2C_DW_BUSY;
read_buf, read_len, slave_address);
ret = _i2c_dw_transfer_init(dev, slave_address);
if (ret) { if (ret) {
dw->state = I2C_DW_STATE_READY;
return ret; return ret;
} }
/* Trigger IRQ when TX_EMPTY */
regs->ic_con.bits.tx_empty_ctl = 1;
if (regs->ic_con.bits.master_mode) {
/* Enable necessary interrupts */
regs->ic_intr_mask.raw = (DW_ENABLE_TX_INT_I2C_MASTER |
DW_ENABLE_RX_INT_I2C_MASTER);
} else {
/* Enable necessary interrupts */
regs->ic_intr_mask.raw = DW_ENABLE_TX_INT_I2C_SLAVE;
}
/* Enable controller */ /* Enable controller */
regs->ic_enable.bits.enable = 1; regs->ic_enable.bits.enable = 1;
synchronous_call_wait(&dw->sync); /* Process all the messages */
if (dw->state == I2C_DW_CMD_ERROR) { while (msg_left > 0) {
ret = DEV_FAIL; pflags = dw->xfr_flags;
dw->xfr_buf = cur_msg->buf;
dw->xfr_len = cur_msg->len;
dw->xfr_flags = cur_msg->flags;
/* Need to RESTART if changing transfer direction */
if ((pflags & I2C_MSG_RW_MASK)
!= (dw->xfr_flags & I2C_MSG_RW_MASK)) {
dw->xfr_flags |= I2C_MSG_RESTART;
}
/* Send STOP if this is the last message */
if (msg_left == 1) {
dw->xfr_flags |= I2C_MSG_STOP;
}
dw->state &= ~(I2C_DW_CMD_SEND | I2C_DW_CMD_RECV);
if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
dw->state |= I2C_DW_CMD_SEND;
dw->request_bytes = 0;
} else {
dw->state |= I2C_DW_CMD_RECV;
dw->request_bytes = dw->xfr_len;
}
/* Enable interrupts to trigger ISR */
if (regs->ic_con.bits.master_mode) {
/* Enable necessary interrupts */
regs->ic_intr_mask.raw = (DW_ENABLE_TX_INT_I2C_MASTER |
DW_ENABLE_RX_INT_I2C_MASTER);
} else {
/* Enable necessary interrupts */
regs->ic_intr_mask.raw = DW_ENABLE_TX_INT_I2C_SLAVE;
}
/* Wait for transfer to be done */
synchronous_call_wait(&dw->sync);
if (dw->state & I2C_DW_CMD_ERROR) {
ret = DEV_FAIL;
break;
}
/* Something wrong if there is something left to do */
if (dw->xfr_len > 0) {
ret = DEV_FAIL;
break;
}
cur_msg++;
msg_left--;
} }
dw->state = I2C_DW_STATE_READY; dw->state = I2C_DW_STATE_READY;
@ -739,7 +775,7 @@ IRQ_CONNECT_STATIC(i2c_dw_0,
CONFIG_I2C_DW_0_IRQ, CONFIG_I2C_DW_0_IRQ,
CONFIG_I2C_DW_0_INT_PRIORITY, CONFIG_I2C_DW_0_INT_PRIORITY,
i2c_dw_isr, i2c_dw_isr,
0, SYS_GET_DEVICE(i2c_0),
I2C_DW_IRQ_FLAGS); I2C_DW_IRQ_FLAGS);
#endif #endif
@ -801,7 +837,7 @@ IRQ_CONNECT_STATIC(i2c_dw_1,
CONFIG_I2C_DW_1_IRQ, CONFIG_I2C_DW_1_IRQ,
CONFIG_I2C_DW_1_INT_PRIORITY, CONFIG_I2C_DW_1_INT_PRIORITY,
i2c_dw_isr, i2c_dw_isr,
0, SYS_GET_DEVICE(i2c_1),
I2C_DW_IRQ_FLAGS); I2C_DW_IRQ_FLAGS);
void i2c_config_1(struct device *port) void i2c_config_1(struct device *port)

View file

@ -112,10 +112,10 @@ struct i2c_dw_dev_config {
volatile uint8_t state; /* last direction of transfer */ volatile uint8_t state; /* last direction of transfer */
uint8_t request_bytes; uint8_t request_bytes;
uint8_t rx_len; uint8_t xfr_flags;
uint8_t *rx_buffer;
uint8_t tx_len; uint8_t *xfr_buf;
uint8_t *tx_buffer; uint32_t xfr_len;
bool support_hs_mode; bool support_hs_mode;
uint16_t hcnt; uint16_t hcnt;

View file

@ -127,26 +127,30 @@ static inline bool _i2c_qse_ss_check_irq(struct device *dev, uint32_t mask)
return _i2c_qse_ss_reg_check_bit(dev, REG_INTR_STAT, mask); return _i2c_qse_ss_reg_check_bit(dev, REG_INTR_STAT, mask);
} }
static inline void _i2c_qse_ss_data_ask(struct device *dev, uint8_t restart) static inline void _i2c_qse_ss_data_ask(struct device *dev)
{ {
struct i2c_qse_ss_dev_config * const dw = dev->driver_data; struct i2c_qse_ss_dev_config * const dw = dev->driver_data;
uint32_t data; uint32_t data;
/* No more bytes to request */ /* No more bytes to request, so command queue is no longer needed */
if (dw->request_bytes == 0) { if (dw->request_bytes == 0) {
_i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK,
~(IC_INTR_TX_EMPTY));
return; return;
} }
/* Tell controller to get another byte */ /* Tell controller to get another byte */
data = IC_DATA_CMD_CMD | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP; data = IC_DATA_CMD_CMD | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP;
/* Send restart if needed) */ /* Send RESTART if needed */
if (restart) { if (dw->xfr_flags & I2C_MSG_RESTART) {
data |= IC_DATA_CMD_RESTART; data |= IC_DATA_CMD_RESTART;
dw->xfr_flags &= ~(I2C_MSG_RESTART);
} }
/* After receiving the last byte, send STOP */ /* After receiving the last byte, send STOP if needed */
if (dw->request_bytes == 1) { if ((dw->xfr_flags & I2C_MSG_STOP) && (dw->request_bytes == 1)) {
data |= IC_DATA_CMD_STOP; data |= IC_DATA_CMD_STOP;
} }
@ -159,28 +163,28 @@ static void _i2c_qse_ss_data_read(struct device *dev)
{ {
struct i2c_qse_ss_dev_config * const dw = dev->driver_data; struct i2c_qse_ss_dev_config * const dw = dev->driver_data;
while (_i2c_qse_ss_is_rfne(dev) && (dw->rx_len > 0)) { while (_i2c_qse_ss_is_rfne(dev) && (dw->xfr_len > 0)) {
/* Need to write 0 to POP bit to /* Need to write 0 to POP bit to
* "pop" one byte from RX FIFO. * "pop" one byte from RX FIFO.
*/ */
_i2c_qse_ss_reg_write(dev, REG_DATA_CMD, _i2c_qse_ss_reg_write(dev, REG_DATA_CMD,
IC_DATA_CMD_STROBE); IC_DATA_CMD_STROBE);
dw->rx_buffer[0] = _i2c_qse_ss_reg_read(dev, REG_DATA_CMD) dw->xfr_buf[0] = _i2c_qse_ss_reg_read(dev, REG_DATA_CMD)
& IC_DATA_CMD_DATA_MASK; & IC_DATA_CMD_DATA_MASK;
dw->rx_buffer += 1; dw->xfr_buf++;
dw->rx_len -= 1; dw->xfr_len--;
if (dw->rx_len == 0) { if (dw->xfr_len == 0) {
break; break;
} }
_i2c_qse_ss_data_ask(dev, 0); _i2c_qse_ss_data_ask(dev);
} }
/* Nothing to receive anymore */ /* Nothing to receive anymore */
if (dw->rx_len == 0) { if (dw->xfr_len == 0) {
dw->state &= ~I2C_QSE_SS_CMD_RECV; dw->state &= ~I2C_QSE_SS_CMD_RECV;
return; return;
} }
@ -189,51 +193,42 @@ static void _i2c_qse_ss_data_read(struct device *dev)
static int _i2c_qse_ss_data_send(struct device *dev) static int _i2c_qse_ss_data_send(struct device *dev)
{ {
struct i2c_qse_ss_dev_config * const dw = dev->driver_data; struct i2c_qse_ss_dev_config * const dw = dev->driver_data;
uint32_t data = 0; uint32_t data;
/* Nothing to send anymore, mask the interrupt */ /* Nothing to send anymore, mask the interrupt */
if (dw->tx_len == 0) { if (dw->xfr_len == 0) {
_i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK, _i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK,
~(IC_INTR_TX_EMPTY)); ~(IC_INTR_TX_EMPTY));
if (dw->rx_len > 0) {
/* Tell controller to grab a byte.
* RESTART if something has ben sent.
*/
_i2c_qse_ss_data_ask(dev,
(dw->state & I2C_QSE_SS_CMD_SEND));
/* QUIRK:
* If requesting more than one byte, the process has
* to be jump-started by requesting two bytes first.
*/
_i2c_qse_ss_data_ask(dev, 0);
}
dw->state &= ~I2C_QSE_SS_CMD_SEND; dw->state &= ~I2C_QSE_SS_CMD_SEND;
return DEV_OK; return DEV_OK;
} }
while (_i2c_qse_ss_is_tfnf(dev) && (dw->tx_len > 0)) { while (_i2c_qse_ss_is_tfnf(dev) && (dw->xfr_len > 0)) {
/* We have something to transmit to a specific host */ /* We have something to transmit to a specific host */
data = dw->tx_buffer[0] | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP; data = dw->xfr_buf[0] | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP;
/* If this is the last byte to write /* Send RESTART if needed */
* and nothing to receive, send STOP. if (dw->xfr_flags & I2C_MSG_RESTART) {
*/ data |= IC_DATA_CMD_RESTART;
if ((dw->tx_len == 1) && (dw->rx_len == 0)) { dw->xfr_flags &= ~(I2C_MSG_RESTART);
}
/* Send STOP if needed */
if ((dw->xfr_len == 1) && (dw->xfr_flags & I2C_MSG_STOP)) {
data |= IC_DATA_CMD_STOP; data |= IC_DATA_CMD_STOP;
} }
_i2c_qse_ss_reg_write(dev, REG_DATA_CMD, data); _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, data);
dw->tx_len -= 1; dw->xfr_len--;
dw->tx_buffer += 1; dw->xfr_buf++;
if (_i2c_qse_ss_check_irq(dev, IC_INTR_TX_ABRT)) { if (_i2c_qse_ss_check_irq(dev, IC_INTR_TX_ABRT)) {
return DEV_FAIL; return DEV_FAIL;
} }
} }
return DEV_OK; return DEV_OK;
@ -248,10 +243,9 @@ static inline void _i2c_qse_ss_transfer_complete(struct device *dev)
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_CLR_ALL); _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_CLR_ALL);
synchronous_call_complete(&dw->sync); synchronous_call_complete(&dw->sync);
dw->state &= ~I2C_QSE_SS_BUSY;
} }
void i2c_qse_ss_isr(void *arg) void i2c_qse_ss_isr(void *arg)
{ {
struct device *dev = (struct device *)arg; struct device *dev = (struct device *)arg;
@ -259,7 +253,7 @@ void i2c_qse_ss_isr(void *arg)
uint32_t ic_intr_stat; uint32_t ic_intr_stat;
/* /*
* Causes of an intterrupt on ATP: * Causes of an intterrupts:
* - STOP condition is detected * - STOP condition is detected
* - Transfer is aborted * - Transfer is aborted
* - Transmit FIFO is empy * - Transmit FIFO is empy
@ -267,26 +261,12 @@ void i2c_qse_ss_isr(void *arg)
* - Receive FIFO is full * - Receive FIFO is full
* - Receive FIFO overflow * - Receive FIFO overflow
* - Received FIFO underrun * - Received FIFO underrun
* - Transmit data required (tx_req)
* - Receive data available (rx_avail)
*/ */
DBG("I2C_SS: interrupt received\n"); DBG("I2C_SS: interrupt received\n");
ic_intr_stat = _i2c_qse_ss_reg_read(dev, REG_INTR_STAT); ic_intr_stat = _i2c_qse_ss_reg_read(dev, REG_INTR_STAT);
/* Check if the Master TX is ready for sending */
if (ic_intr_stat & IC_INTR_TX_EMPTY) {
_i2c_qse_ss_data_send(dev);
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_TX_EMPTY);
}
/* Check if the RX FIFO reached threshold */
if (ic_intr_stat & IC_INTR_RX_FULL) {
_i2c_qse_ss_data_read(dev);
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_RX_FULL);
}
/* Error conditions */ /* Error conditions */
if ((IC_INTR_TX_ABRT | IC_INTR_TX_OVER | if ((IC_INTR_TX_ABRT | IC_INTR_TX_OVER |
IC_INTR_RX_OVER | IC_INTR_RX_UNDER) & IC_INTR_RX_OVER | IC_INTR_RX_UNDER) &
@ -296,10 +276,35 @@ void i2c_qse_ss_isr(void *arg)
return; return;
} }
/* /* Check if the RX FIFO reached threshold */
* We got a STOP_DET, this means stop right after this byte has been if (ic_intr_stat & IC_INTR_RX_FULL) {
* handled. _i2c_qse_ss_data_read(dev);
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_RX_FULL);
}
/* Check if the TX FIFO is ready for commands.
* TX FIFO also serves as command queue where read requests
* are written to TX FIFO.
*/ */
if (ic_intr_stat & IC_INTR_TX_EMPTY) {
if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
_i2c_qse_ss_data_send(dev);
} else {
_i2c_qse_ss_data_ask(dev);
}
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_TX_EMPTY);
/* If STOP is not expected, finish processing this
* message if there is nothing left to do anymore.
*/
if ((dw->xfr_len == 0)
&& !(dw->xfr_flags & I2C_MSG_STOP)) {
_i2c_qse_ss_transfer_complete(dev);
return;
}
}
/* STOP detected */
if (ic_intr_stat & IC_INTR_STOP_DET) { if (ic_intr_stat & IC_INTR_STOP_DET) {
_i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_STOP_DET); _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_STOP_DET);
_i2c_qse_ss_transfer_complete(dev); _i2c_qse_ss_transfer_complete(dev);
@ -382,70 +387,91 @@ done:
return rc; return rc;
} }
static int _i2c_qse_ss_transfer_init(struct device *dev, static int i2c_qse_ss_intr_transfer(struct device *dev,
uint8_t *write_buf, uint32_t write_len, struct i2c_msg *msgs, uint8_t num_msgs,
uint8_t *read_buf, uint32_t read_len, uint16_t slave_address)
uint16_t slave_address, uint32_t flags)
{ {
struct i2c_qse_ss_dev_config * const dw = dev->driver_data; struct i2c_qse_ss_dev_config * const dw = dev->driver_data;
int ret = 0; struct i2c_msg *cur_msg = msgs;
uint8_t msg_left = num_msgs;
uint8_t pflags;
int ret;
/* Why bother processing no messages */
if (!msgs || !num_msgs) {
return DEV_INVALID_OP;
}
/* First step, check if device is idle */
if (_i2c_qse_ss_is_busy(dev) || (dw->state & I2C_QSE_SS_BUSY)) {
return DEV_USED;
}
dw->state |= I2C_QSE_SS_BUSY; dw->state |= I2C_QSE_SS_BUSY;
if (write_len > 0) {
dw->state |= I2C_QSE_SS_CMD_SEND;
}
if (read_len > 0) {
dw->state |= I2C_QSE_SS_CMD_RECV;
}
dw->rx_len = read_len;
dw->rx_buffer = read_buf;
dw->tx_len = write_len;
dw->tx_buffer = write_buf;
dw->request_bytes = read_len;
ret = _i2c_qse_ss_setup(dev, slave_address); ret = _i2c_qse_ss_setup(dev, slave_address);
if (ret) { if (ret) {
dw->state = I2C_QSE_SS_STATE_READY;
return ret; return ret;
} }
return DEV_OK; /* To prevent RESTART for first message */
} dw->xfr_flags = msgs[0].flags;
static int i2c_qse_ss_intr_transfer(struct device *dev,
uint8_t *write_buf, uint32_t write_len,
uint8_t *read_buf, uint32_t read_len,
uint16_t slave_address, uint32_t flags)
{
struct i2c_qse_ss_dev_config * const dw = dev->driver_data;
int ret = DEV_OK;
/* First step, check if there is current activity */
if (_i2c_qse_ss_is_busy(dev)) {
return DEV_FAIL;
}
ret = _i2c_qse_ss_transfer_init(dev, write_buf, write_len,
read_buf, read_len, slave_address, 0);
if (ret) {
return ret;
}
/* Enable necessary interrupts */
_i2c_qse_ss_reg_write(dev, REG_INTR_MASK,
(IC_INTR_MASK_TX | IC_INTR_MASK_RX));
/* Enable controller */ /* Enable controller */
_i2c_qse_ss_reg_write_or(dev, REG_CON, IC_CON_ENABLE); _i2c_qse_ss_reg_write_or(dev, REG_CON, IC_CON_ENABLE);
synchronous_call_wait(&dw->sync); /* Process all the messages */
if (dw->state == I2C_QSE_SS_CMD_ERROR) { while (msg_left > 0) {
ret = DEV_FAIL; pflags = dw->xfr_flags;
dw->xfr_buf = cur_msg->buf;
dw->xfr_len = cur_msg->len;
dw->xfr_flags = cur_msg->flags;
/* Need to RESTART if changing transfer direction */
if ((pflags & I2C_MSG_RW_MASK)
!= (dw->xfr_flags & I2C_MSG_RW_MASK)) {
dw->xfr_flags |= I2C_MSG_RESTART;
}
/* Send STOP if this is the last message */
if (msg_left == 1) {
dw->xfr_flags |= I2C_MSG_STOP;
}
dw->state &= ~(I2C_QSE_SS_CMD_SEND | I2C_QSE_SS_CMD_RECV);
if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
dw->state |= I2C_QSE_SS_CMD_SEND;
dw->request_bytes = 0;
} else {
dw->state |= I2C_QSE_SS_CMD_RECV;
dw->request_bytes = dw->xfr_len;
}
/* Enable interrupts to trigger ISR */
_i2c_qse_ss_reg_write(dev, REG_INTR_MASK,
(IC_INTR_MASK_TX | IC_INTR_MASK_RX));
/* Wait for transfer to be done */
synchronous_call_wait(&dw->sync);
if (dw->state & I2C_QSE_SS_CMD_ERROR) {
ret = DEV_FAIL;
break;
}
/* Something wrong if there is something left to do */
if (dw->xfr_len > 0) {
ret = DEV_FAIL;
break;
}
cur_msg++;
msg_left--;
} }
dw->state = I2C_QSE_SS_STATE_READY; dw->state = I2C_QSE_SS_STATE_READY;
return ret; return ret;
} }

View file

@ -76,11 +76,11 @@ struct i2c_qse_ss_dev_config {
volatile uint8_t state; /* last direction of transfer */ volatile uint8_t state; /* last direction of transfer */
uint8_t request_bytes; uint8_t xfr_flags;
uint8_t rx_len;
uint8_t *rx_buffer; uint8_t *xfr_buf;
uint8_t tx_len; uint32_t xfr_len;
uint8_t *tx_buffer; uint32_t request_bytes;
uint16_t hcnt; uint16_t hcnt;
uint16_t lcnt; uint16_t lcnt;

View file

@ -45,6 +45,26 @@ extern "C" {
#define I2C_MODE_MASTER (1 << 4) #define I2C_MODE_MASTER (1 << 4)
#define I2C_MODE_SLAVE_READ (1 << 5) #define I2C_MODE_SLAVE_READ (1 << 5)
/* I2C Message Flags */
#define I2C_MSG_WRITE (0 << 0)
#define I2C_MSG_READ (1 << 0)
#define I2C_MSG_RW_MASK (1 << 0)
/* STOP I2C transaction (drive should take care of this) */
#define I2C_MSG_STOP (1 << 1)
/* RESTART I2C transaction */
#define I2C_MSG_RESTART (1 << 2)
struct i2c_msg {
/* Data buffer in bytes */
uint8_t *buf;
/* Length of buffer in bytes */
uint32_t len;
/* Flags for this message */
uint8_t flags;
};
union dev_config { union dev_config {
uint32_t raw; uint32_t raw;
@ -66,12 +86,9 @@ enum i2c_cb_type {
typedef int (*i2c_api_configure_t)(struct device *dev, typedef int (*i2c_api_configure_t)(struct device *dev,
uint32_t dev_config); uint32_t dev_config);
typedef int (*i2c_api_full_io_t)(struct device *dev, typedef int (*i2c_api_full_io_t)(struct device *dev,
uint8_t *tx_buf, struct i2c_msg *msgs,
uint32_t tx_len, uint8_t num_msgs,
uint8_t *rx_buf, uint16_t addr);
uint32_t rx_len,
uint16_t addr,
uint32_t ctrl_flags);
typedef int (*i2c_api_suspend_t)(struct device *dev); typedef int (*i2c_api_suspend_t)(struct device *dev);
typedef int (*i2c_api_resume_t)(struct device *dev); typedef int (*i2c_api_resume_t)(struct device *dev);
@ -115,9 +132,14 @@ static inline int i2c_write(struct device *dev, uint8_t *buf,
uint32_t len, uint16_t addr) uint32_t len, uint16_t addr)
{ {
struct i2c_driver_api *api; struct i2c_driver_api *api;
struct i2c_msg msg;
msg.buf = buf;
msg.len = len;
msg.flags = I2C_MSG_WRITE;
api = (struct i2c_driver_api *)dev->driver_api; api = (struct i2c_driver_api *)dev->driver_api;
return api->transfer(dev, buf, len, 0, 0, addr, 0); return api->transfer(dev, &msg, 1, addr);
} }
/** /**
@ -136,9 +158,14 @@ static inline int i2c_read(struct device *dev, uint8_t *buf,
uint32_t len, uint16_t addr) uint32_t len, uint16_t addr)
{ {
struct i2c_driver_api *api; struct i2c_driver_api *api;
struct i2c_msg msg;
msg.buf = buf;
msg.len = len;
msg.flags = I2C_MSG_READ;
api = (struct i2c_driver_api *)dev->driver_api; api = (struct i2c_driver_api *)dev->driver_api;
return api->transfer(dev, 0, 0, buf, len, addr, 0); return api->transfer(dev, &msg, 1, addr);
} }
/** /**
@ -149,25 +176,20 @@ static inline int i2c_read(struct device *dev, uint8_t *buf,
* use i2c_read()/i2c_write() instead. The function is synchronous. * use i2c_read()/i2c_write() instead. The function is synchronous.
* *
* @param dev Pointer to the device structure for the driver instance * @param dev Pointer to the device structure for the driver instance
* @param tx_buf Memory pool that data should be transferred from * @param msgs Array of messages
* @param tx_len Size of the memory pool available for reading from * @param num_msgs Number of messages
* @param rx_buf Memory pool that data should be transferred to
* @param rx_len Size of the memory pool available for writing to
* @param addr Address of the I2C target device * @param addr Address of the I2C target device
* @param ctrl_flags Control flags for this transfer
* *
* @return DEV_OK if successful, another DEV_* code otherwise. * @return DEV_OK if successful, another DEV_* code otherwise.
*/ */
static inline int i2c_transfer(struct device *dev, static inline int i2c_transfer(struct device *dev,
uint8_t *tx_buf, uint32_t tx_len, struct i2c_msg *msgs, uint8_t num_msgs,
uint8_t *rx_buf, uint32_t rx_len, uint16_t addr)
uint16_t addr, uint32_t ctrl_flags)
{ {
struct i2c_driver_api *api; struct i2c_driver_api *api;
api = (struct i2c_driver_api *)dev->driver_api; api = (struct i2c_driver_api *)dev->driver_api;
return api->transfer(dev, tx_buf, tx_len, return api->transfer(dev, msgs, num_msgs, addr);
rx_buf, rx_len, addr, ctrl_flags);
} }
/** /**

View file

@ -29,30 +29,52 @@
int write_byte(struct device *i2c_dev, uint16_t addr, uint8_t data) int write_byte(struct device *i2c_dev, uint16_t addr, uint8_t data)
{ {
uint8_t wr_data[3]; uint8_t wr_addr[2];
struct i2c_msg msgs[2];
/* FRAM address */ /* FRAM address */
wr_data[0] = (addr >> 8) & 0xFF; wr_addr[0] = (addr >> 8) & 0xFF;
wr_data[1] = addr & 0xFF; wr_addr[1] = addr & 0xFF;
/* The byte to write */ /* Setup I2C messages */
wr_data[2] = data;
return i2c_write(i2c_dev, wr_data, sizeof(wr_data), FRAM_I2C_ADDR); /* Send the address to write */
msgs[0].buf = wr_addr;
msgs[0].len = 2;
msgs[0].flags = I2C_MSG_WRITE;
/* Data to be written, and STOP after this. */
msgs[1].buf = &data;
msgs[1].len = 1;
msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
return i2c_transfer(i2c_dev, &msgs[0], 2, FRAM_I2C_ADDR);
} }
int read_byte(struct device *i2c_dev, uint16_t addr, uint8_t *data) int read_byte(struct device *i2c_dev, uint16_t addr, uint8_t *data)
{ {
uint8_t wr_data[2]; uint8_t wr_addr[2];
struct i2c_msg msgs[2];
/* Now try to read back from FRAM */ /* Now try to read back from FRAM */
/* FRAM address */ /* FRAM address */
wr_data[0] = (addr >> 8) & 0xFF; wr_addr[0] = (addr >> 8) & 0xFF;
wr_data[1] = addr & 0xFF; wr_addr[1] = addr & 0xFF;
return i2c_transfer(i2c_dev, wr_data, sizeof(wr_data), /* Setup I2C messages */
data, 1, FRAM_I2C_ADDR, 0);
/* Send the address to read */
msgs[0].buf = wr_addr;
msgs[0].len = 2;
msgs[0].flags = I2C_MSG_WRITE;
/* Read from device. RESTART as neededm and STOP after this. */
msgs[1].buf = data;
msgs[1].len = 1;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_RESTART;
return i2c_transfer(i2c_dev, &msgs[0], 2, FRAM_I2C_ADDR);
} }
void main(void) void main(void)