i2c/i2c_dw: extends to do multi-byte read requests
This changes to request bytes that will fill the RX FIFO instead of requesting byte one-by-one. Change-Id: I30696c624c9f828818f6df9f1ee744a5bc515f27 Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
parent
9b8acb7b26
commit
61ea58b6cf
2 changed files with 48 additions and 18 deletions
|
@ -30,6 +30,8 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys_io.h>
|
#include <sys_io.h>
|
||||||
|
|
||||||
|
#include <misc/util.h>
|
||||||
|
|
||||||
#ifdef CONFIG_SHARED_IRQ
|
#ifdef CONFIG_SHARED_IRQ
|
||||||
#include <shared_irq.h>
|
#include <shared_irq.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,6 +59,9 @@ 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;
|
||||||
uint32_t data;
|
uint32_t data;
|
||||||
|
uint8_t tx_empty;
|
||||||
|
int8_t rx_empty;
|
||||||
|
uint8_t cnt;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -67,23 +72,45 @@ static inline void _i2c_dw_data_ask(struct device *dev)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tell controller to get another byte */
|
/* How many bytes we can actually ask */
|
||||||
data = IC_DATA_CMD_CMD;
|
rx_empty = (I2C_DW_FIFO_DEPTH - regs->ic_rxflr) - dw->rx_pending;
|
||||||
|
|
||||||
/* Send RESTART if needed */
|
if (rx_empty < 0) {
|
||||||
if (dw->xfr_flags & I2C_MSG_RESTART) {
|
/* RX FIFO expected to be full.
|
||||||
data |= IC_DATA_CMD_RESTART;
|
* So don't request any bytes, yet.
|
||||||
dw->xfr_flags &= ~(I2C_MSG_RESTART);
|
*/
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After receiving the last byte, send STOP if needed */
|
/* How many empty slots in TX FIFO (as command queue) */
|
||||||
if ((dw->xfr_flags & I2C_MSG_STOP) && (dw->request_bytes == 1)) {
|
tx_empty = I2C_DW_FIFO_DEPTH - regs->ic_txflr;
|
||||||
data |= IC_DATA_CMD_STOP;
|
|
||||||
|
/* Figure out how many bytes we can request */
|
||||||
|
cnt = min(I2C_DW_FIFO_DEPTH, dw->request_bytes);
|
||||||
|
cnt = min(min(tx_empty, rx_empty), cnt);
|
||||||
|
|
||||||
|
while (cnt > 0) {
|
||||||
|
/* Tell controller to get another byte */
|
||||||
|
data = IC_DATA_CMD_CMD;
|
||||||
|
|
||||||
|
/* Send RESTART if needed */
|
||||||
|
if (dw->xfr_flags & I2C_MSG_RESTART) {
|
||||||
|
data |= IC_DATA_CMD_RESTART;
|
||||||
|
dw->xfr_flags &= ~(I2C_MSG_RESTART);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* After receiving the last byte, send STOP if needed */
|
||||||
|
if ((dw->xfr_flags & I2C_MSG_STOP)
|
||||||
|
&& (dw->request_bytes == 1)) {
|
||||||
|
data |= IC_DATA_CMD_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
regs->ic_data_cmd.raw = data;
|
||||||
|
|
||||||
|
dw->rx_pending++;
|
||||||
|
dw->request_bytes--;
|
||||||
|
cnt--;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs->ic_data_cmd.raw = data;
|
|
||||||
|
|
||||||
dw->request_bytes--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _i2c_dw_data_read(struct device *dev)
|
static void _i2c_dw_data_read(struct device *dev)
|
||||||
|
@ -99,12 +126,11 @@ static void _i2c_dw_data_read(struct device *dev)
|
||||||
|
|
||||||
dw->xfr_buf++;
|
dw->xfr_buf++;
|
||||||
dw->xfr_len--;
|
dw->xfr_len--;
|
||||||
|
dw->rx_pending--;
|
||||||
|
|
||||||
if (dw->xfr_len == 0) {
|
if (dw->xfr_len == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_i2c_dw_data_ask(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nothing to receive anymore */
|
/* Nothing to receive anymore */
|
||||||
|
@ -369,9 +395,11 @@ static int _i2c_dw_setup(struct device *dev, uint16_t slave_address)
|
||||||
|
|
||||||
/* Set TX fifo threshold level.
|
/* Set TX fifo threshold level.
|
||||||
* TX_EMPTY interrupt is triggered only when the
|
* TX_EMPTY interrupt is triggered only when the
|
||||||
* TX FIFO is truly empty.
|
* TX FIFO is truly empty. So that we can let
|
||||||
*
|
* the controller do the transfers for longer period
|
||||||
* TODO: threshold set to just enough for TX
|
* before we need to fill the FIFO again. This may
|
||||||
|
* cause some pauses during transfers, but this keeps
|
||||||
|
* the device from interrupting often.
|
||||||
*/
|
*/
|
||||||
regs->ic_tx_tl = 0;
|
regs->ic_tx_tl = 0;
|
||||||
|
|
||||||
|
@ -428,6 +456,7 @@ static int i2c_dw_transfer(struct device *dev,
|
||||||
dw->xfr_buf = cur_msg->buf;
|
dw->xfr_buf = cur_msg->buf;
|
||||||
dw->xfr_len = cur_msg->len;
|
dw->xfr_len = cur_msg->len;
|
||||||
dw->xfr_flags = cur_msg->flags;
|
dw->xfr_flags = cur_msg->flags;
|
||||||
|
dw->rx_pending = 0;
|
||||||
|
|
||||||
/* Need to RESTART if changing transfer direction */
|
/* Need to RESTART if changing transfer direction */
|
||||||
if ((pflags & I2C_MSG_RW_MASK)
|
if ((pflags & I2C_MSG_RW_MASK)
|
||||||
|
|
|
@ -116,6 +116,7 @@ struct i2c_dw_dev_config {
|
||||||
|
|
||||||
uint8_t *xfr_buf;
|
uint8_t *xfr_buf;
|
||||||
uint32_t xfr_len;
|
uint32_t xfr_len;
|
||||||
|
uint32_t rx_pending;
|
||||||
|
|
||||||
bool support_hs_mode;
|
bool support_hs_mode;
|
||||||
uint16_t hcnt;
|
uint16_t hcnt;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue