diff --git a/drivers/gpio/gpio_pcal9535a.c b/drivers/gpio/gpio_pcal9535a.c index 22d039a6018..3c06a9673de 100644 --- a/drivers/gpio/gpio_pcal9535a.c +++ b/drivers/gpio/gpio_pcal9535a.c @@ -102,8 +102,17 @@ static int _read_port_regs(struct device *dev, uint8_t reg, uint16_t i2c_addr = config->i2c_slave_addr; int ret; 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) { DBG("PCAL9535A[0x%X]: error reading register 0x%X (%d)\n", i2c_addr, reg, ret); diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index 04fd52e00f0..04430c263fe 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -52,7 +52,7 @@ #endif /* CONFIG_STDOUT_CONSOLE */ #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_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 = (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) { + regs->ic_intr_mask.bits.tx_empty = 0; return; } /* Tell controller to get another byte */ data = IC_DATA_CMD_CMD; - /* Send restart if needed) */ - if (restart) { + /* 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 (dw->request_bytes == 1) { + /* 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; } @@ -92,21 +94,21 @@ static void _i2c_dw_data_read(struct device *dev) volatile struct i2c_dw_registers * const regs = (struct i2c_dw_registers *)rom->base_address; - while (regs->ic_status.bits.rfne && (dw->rx_len > 0)) { - dw->rx_buffer[0] = regs->ic_data_cmd.raw; + while (regs->ic_status.bits.rfne && (dw->xfr_len > 0)) { + dw->xfr_buf[0] = regs->ic_data_cmd.raw; - dw->rx_buffer += 1; - dw->rx_len -= 1; + dw->xfr_buf++; + dw->xfr_len--; - if (dw->rx_len == 0) { + if (dw->xfr_len == 0) { break; } - _i2c_dw_data_ask(dev, 0); + _i2c_dw_data_ask(dev); } /* Nothing to receive anymore */ - if (dw->rx_len == 0) { + if (dw->xfr_len == 0) { dw->state &= ~I2C_DW_CMD_RECV; return; } @@ -123,41 +125,33 @@ static void _i2c_dw_data_send(struct device *dev) (struct i2c_dw_registers *)rom->base_address; /* 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; - 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; 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 */ - data = dw->tx_buffer[0]; + data = dw->xfr_buf[0]; - /* If this is the last byte to write - * and nothing to receive, send STOP. - */ - if ((dw->tx_len == 1) && (dw->rx_len == 0)) { + /* Send RESTART if needed */ + if (dw->xfr_flags & I2C_MSG_RESTART) { + data |= IC_DATA_CMD_RESTART; + 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; } 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_dev_config * const dw = dev->driver_data; - bool done = false; uint32_t value; volatile struct i2c_dw_registers * const regs = (struct i2c_dw_registers *)rom->base_address; - if ((dw->state == I2C_DW_CMD_ERROR) || - (!dw->tx_len && !dw->rx_len) || - (dw->tx_buffer && !dw->tx_len && !dw->rx_buffer) || - (dw->rx_buffer && !dw->rx_len && !dw->tx_buffer)) { - done = true; - } + regs->ic_intr_mask.raw = DW_DISABLE_ALL_I2C_INT; + value = regs->ic_clr_intr; - if (done) { - 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; + synchronous_call_complete(&dw->sync); } void i2c_dw_isr(struct device *port) { struct i2c_dw_rom_config const * const rom = port->config->config_info; 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 = (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 using with shared IRQ, this function will be called * by the shared IRQ driver. So check here if the interrupt * is coming from the I2C controller (or somewhere else). */ - if (!regs->ic_intr_stat.raw) { + if (!intr_stat.raw) { return; } #endif @@ -222,41 +210,51 @@ void i2c_dw_isr(struct device *port) 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 */ if (regs->ic_con.bits.master_mode) { - /* Check if the Master TX is ready for sending */ - if (regs->ic_intr_stat.bits.tx_empty) { - _i2c_dw_data_send(port); + /* Bail early if there is any error. */ + if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER | + 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 */ - if (regs->ic_intr_stat.bits.rx_full) { + if (intr_stat.bits.rx_full) { _i2c_dw_data_read(port); } - if ((DW_INTR_STAT_TX_ABRT | DW_INTR_STAT_TX_OVER | - DW_INTR_STAT_RX_OVER | DW_INTR_STAT_RX_UNDER) & - regs->ic_intr_stat.raw) { - dw->state = I2C_DW_CMD_ERROR; - _i2c_dw_transfer_complete(port); + /* 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 (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 */ /* 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)) { /* 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 */ value = regs->ic_clr_tx_abrt; } @@ -266,11 +264,18 @@ void i2c_dw_isr(struct device *port) } /* 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) { _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, - uint8_t *write_buf, uint32_t write_len, - uint8_t *read_buf, uint32_t read_len, - uint16_t slave_address) +static int _i2c_dw_transfer_init(struct device *dev, uint16_t slave_address) { 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 = 0; - int ret = DEV_OK; + uint32_t value; + int ret; volatile struct i2c_dw_registers * const regs = (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 */ 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, - 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_msg *msgs, uint8_t num_msgs, + uint16_t slave_address) { struct i2c_dw_rom_config const * const rom = dev->config->config_info; 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; volatile struct i2c_dw_registers * const regs = (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 */ - if (regs->ic_status.bits.activity) { + if ((regs->ic_status.bits.activity) || (dw->state & I2C_DW_BUSY)) { return DEV_FAIL; } - ret = _i2c_dw_transfer_init(dev, write_buf, write_len, - read_buf, read_len, slave_address); + dw->state |= I2C_DW_BUSY; + + ret = _i2c_dw_transfer_init(dev, slave_address); if (ret) { + dw->state = I2C_DW_STATE_READY; 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 */ regs->ic_enable.bits.enable = 1; - synchronous_call_wait(&dw->sync); - if (dw->state == I2C_DW_CMD_ERROR) { - ret = DEV_FAIL; + /* Process all the messages */ + while (msg_left > 0) { + 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; @@ -739,7 +775,7 @@ IRQ_CONNECT_STATIC(i2c_dw_0, CONFIG_I2C_DW_0_IRQ, CONFIG_I2C_DW_0_INT_PRIORITY, i2c_dw_isr, - 0, + SYS_GET_DEVICE(i2c_0), I2C_DW_IRQ_FLAGS); #endif @@ -801,7 +837,7 @@ IRQ_CONNECT_STATIC(i2c_dw_1, CONFIG_I2C_DW_1_IRQ, CONFIG_I2C_DW_1_INT_PRIORITY, i2c_dw_isr, - 0, + SYS_GET_DEVICE(i2c_1), I2C_DW_IRQ_FLAGS); void i2c_config_1(struct device *port) diff --git a/drivers/i2c/i2c_dw.h b/drivers/i2c/i2c_dw.h index ba71f4f39fe..af3b2545883 100644 --- a/drivers/i2c/i2c_dw.h +++ b/drivers/i2c/i2c_dw.h @@ -112,10 +112,10 @@ struct i2c_dw_dev_config { volatile uint8_t state; /* last direction of transfer */ uint8_t request_bytes; - uint8_t rx_len; - uint8_t *rx_buffer; - uint8_t tx_len; - uint8_t *tx_buffer; + uint8_t xfr_flags; + + uint8_t *xfr_buf; + uint32_t xfr_len; bool support_hs_mode; uint16_t hcnt; diff --git a/drivers/i2c/i2c_quark_se_ss.c b/drivers/i2c/i2c_quark_se_ss.c index 4e038104d83..e418bfc43ee 100644 --- a/drivers/i2c/i2c_quark_se_ss.c +++ b/drivers/i2c/i2c_quark_se_ss.c @@ -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); } -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; 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) { + _i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK, + ~(IC_INTR_TX_EMPTY)); + return; } /* Tell controller to get another byte */ data = IC_DATA_CMD_CMD | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP; - /* Send restart if needed) */ - if (restart) { + /* 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 (dw->request_bytes == 1) { + /* 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; } @@ -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; - 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 * "pop" one byte from RX FIFO. */ _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, IC_DATA_CMD_STROBE); - dw->rx_buffer[0] = _i2c_qse_ss_reg_read(dev, REG_DATA_CMD) - & IC_DATA_CMD_DATA_MASK; + dw->xfr_buf[0] = _i2c_qse_ss_reg_read(dev, REG_DATA_CMD) + & IC_DATA_CMD_DATA_MASK; - dw->rx_buffer += 1; - dw->rx_len -= 1; + dw->xfr_buf++; + dw->xfr_len--; - if (dw->rx_len == 0) { + if (dw->xfr_len == 0) { break; } - _i2c_qse_ss_data_ask(dev, 0); + _i2c_qse_ss_data_ask(dev); } /* Nothing to receive anymore */ - if (dw->rx_len == 0) { + if (dw->xfr_len == 0) { dw->state &= ~I2C_QSE_SS_CMD_RECV; 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) { 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 */ - if (dw->tx_len == 0) { + if (dw->xfr_len == 0) { _i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK, ~(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; 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 */ - 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 - * and nothing to receive, send STOP. - */ - if ((dw->tx_len == 1) && (dw->rx_len == 0)) { + /* Send RESTART if needed */ + if (dw->xfr_flags & I2C_MSG_RESTART) { + data |= IC_DATA_CMD_RESTART; + 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; } _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, data); - dw->tx_len -= 1; - dw->tx_buffer += 1; + dw->xfr_len--; + dw->xfr_buf++; if (_i2c_qse_ss_check_irq(dev, IC_INTR_TX_ABRT)) { return DEV_FAIL; } + } 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); synchronous_call_complete(&dw->sync); - - dw->state &= ~I2C_QSE_SS_BUSY; } + void i2c_qse_ss_isr(void *arg) { struct device *dev = (struct device *)arg; @@ -259,7 +253,7 @@ void i2c_qse_ss_isr(void *arg) uint32_t ic_intr_stat; /* - * Causes of an intterrupt on ATP: + * Causes of an intterrupts: * - STOP condition is detected * - Transfer is aborted * - Transmit FIFO is empy @@ -267,26 +261,12 @@ void i2c_qse_ss_isr(void *arg) * - Receive FIFO is full * - Receive FIFO overflow * - Received FIFO underrun - * - Transmit data required (tx_req) - * - Receive data available (rx_avail) */ DBG("I2C_SS: interrupt received\n"); 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 */ if ((IC_INTR_TX_ABRT | IC_INTR_TX_OVER | IC_INTR_RX_OVER | IC_INTR_RX_UNDER) & @@ -296,10 +276,35 @@ void i2c_qse_ss_isr(void *arg) return; } - /* - * We got a STOP_DET, this means stop right after this byte has been - * handled. + /* 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); + } + + /* 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) { _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_STOP_DET); _i2c_qse_ss_transfer_complete(dev); @@ -382,70 +387,91 @@ done: return rc; } -static int _i2c_qse_ss_transfer_init(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) +static int i2c_qse_ss_intr_transfer(struct device *dev, + struct i2c_msg *msgs, uint8_t num_msgs, + uint16_t slave_address) { 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; - 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); if (ret) { + dw->state = I2C_QSE_SS_STATE_READY; return ret; } - return DEV_OK; -} - -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)); + /* To prevent RESTART for first message */ + dw->xfr_flags = msgs[0].flags; /* Enable controller */ _i2c_qse_ss_reg_write_or(dev, REG_CON, IC_CON_ENABLE); - synchronous_call_wait(&dw->sync); - if (dw->state == I2C_QSE_SS_CMD_ERROR) { - ret = DEV_FAIL; + /* Process all the messages */ + while (msg_left > 0) { + 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; - return ret; } diff --git a/drivers/i2c/i2c_quark_se_ss.h b/drivers/i2c/i2c_quark_se_ss.h index 33f1eab5c8e..f8e85c4b42f 100644 --- a/drivers/i2c/i2c_quark_se_ss.h +++ b/drivers/i2c/i2c_quark_se_ss.h @@ -76,11 +76,11 @@ struct i2c_qse_ss_dev_config { volatile uint8_t state; /* last direction of transfer */ - uint8_t request_bytes; - uint8_t rx_len; - uint8_t *rx_buffer; - uint8_t tx_len; - uint8_t *tx_buffer; + uint8_t xfr_flags; + + uint8_t *xfr_buf; + uint32_t xfr_len; + uint32_t request_bytes; uint16_t hcnt; uint16_t lcnt; diff --git a/include/i2c.h b/include/i2c.h index 3d7d48b62d0..f4137f7c13a 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -45,6 +45,26 @@ extern "C" { #define I2C_MODE_MASTER (1 << 4) #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 { uint32_t raw; @@ -66,12 +86,9 @@ enum i2c_cb_type { typedef int (*i2c_api_configure_t)(struct device *dev, uint32_t dev_config); typedef int (*i2c_api_full_io_t)(struct device *dev, - uint8_t *tx_buf, - uint32_t tx_len, - uint8_t *rx_buf, - uint32_t rx_len, - uint16_t addr, - uint32_t ctrl_flags); + struct i2c_msg *msgs, + uint8_t num_msgs, + uint16_t addr); typedef int (*i2c_api_suspend_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) { 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; - 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) { 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; - 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. * * @param dev Pointer to the device structure for the driver instance - * @param tx_buf Memory pool that data should be transferred from - * @param tx_len Size of the memory pool available for reading from - * @param rx_buf Memory pool that data should be transferred to - * @param rx_len Size of the memory pool available for writing to + * @param msgs Array of messages + * @param num_msgs Number of messages * @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. */ static inline int i2c_transfer(struct device *dev, - uint8_t *tx_buf, uint32_t tx_len, - uint8_t *rx_buf, uint32_t rx_len, - uint16_t addr, uint32_t ctrl_flags) + struct i2c_msg *msgs, uint8_t num_msgs, + uint16_t addr) { struct i2c_driver_api *api; api = (struct i2c_driver_api *)dev->driver_api; - return api->transfer(dev, tx_buf, tx_len, - rx_buf, rx_len, addr, ctrl_flags); + return api->transfer(dev, msgs, num_msgs, addr); } /** diff --git a/samples/nanokernel/apps/i2c_fujitsu_fram/src/main.c b/samples/nanokernel/apps/i2c_fujitsu_fram/src/main.c index 4dcadbb5ee4..07548ceca38 100644 --- a/samples/nanokernel/apps/i2c_fujitsu_fram/src/main.c +++ b/samples/nanokernel/apps/i2c_fujitsu_fram/src/main.c @@ -29,30 +29,52 @@ 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 */ - wr_data[0] = (addr >> 8) & 0xFF; - wr_data[1] = addr & 0xFF; + wr_addr[0] = (addr >> 8) & 0xFF; + wr_addr[1] = addr & 0xFF; - /* The byte to write */ - wr_data[2] = data; + /* Setup I2C messages */ - 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) { - uint8_t wr_data[2]; + uint8_t wr_addr[2]; + struct i2c_msg msgs[2]; /* Now try to read back from FRAM */ /* FRAM address */ - wr_data[0] = (addr >> 8) & 0xFF; - wr_data[1] = addr & 0xFF; + wr_addr[0] = (addr >> 8) & 0xFF; + wr_addr[1] = addr & 0xFF; - return i2c_transfer(i2c_dev, wr_data, sizeof(wr_data), - data, 1, FRAM_I2C_ADDR, 0); + /* Setup I2C messages */ + + /* 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)