From cb856036beaa8d2c552b4d71c0b9c0144ff53aec Mon Sep 17 00:00:00 2001 From: Harry Cutts Date: Mon, 30 Sep 2019 16:50:42 -0700 Subject: max32660: put slave-only code in `#ifdef`s This fixes a bunch of " defined but not used" build warnings when working with a board that only uses the I2C master and not the slave. Also fixed various checkpatch.pl errors and warnings that appeared in the lines that were moved. BRANCH=none BUG=chromium:1008568 TEST=Build a MAX32660 board that only uses the I2C master and check that it succeeds with no warnings. Modify it to use the slave as well and check that host commands still work. Change-Id: Icee04ad6d7b641f934e56b670be8770710a41e64 Signed-off-by: Harry Cutts Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1832892 Reviewed-by: Jes Klinke Reviewed-by: Randall Spangler --- chip/max32660/i2c_chip.c | 1388 +++++++++++++++++++++++----------------------- 1 file changed, 690 insertions(+), 698 deletions(-) (limited to 'chip/max32660') diff --git a/chip/max32660/i2c_chip.c b/chip/max32660/i2c_chip.c index 4f8bc6f7ae..2069cf7921 100644 --- a/chip/max32660/i2c_chip.c +++ b/chip/max32660/i2c_chip.c @@ -144,7 +144,6 @@ struct i2c_req { }; static i2c_req_state_t states[MXC_I2C_INSTANCES]; -static int slave_rx_remain = 0, slave_tx_remain = 0; /** * struct i2c_port_data @@ -172,25 +171,23 @@ struct i2c_port_data { static struct i2c_port_data pdata[I2C_PORT_COUNT]; /* **** Function Prototypes **** */ -static void i2c_free_callback(int i2c_num, int error); -static void init_i2cs(int port); static int i2c_init_peripheral(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed); static int i2c_master_write(mxc_i2c_regs_t *i2c, uint8_t addr, int start, int stop, const uint8_t *data, int len, int restart); static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, int stop, uint8_t *data, int len, int restart); + +#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS +static void i2c_free_callback(int i2c_num, int error); +static void init_i2cs(int port); static int i2c_slave_async(mxc_i2c_regs_t *i2c, i2c_req_t *req); -static void i2c_handler(mxc_i2c_regs_t *i2c); +static void i2c_slave_handler(mxc_i2c_regs_t *i2c); +#endif /* CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS */ /* Port address for each I2C */ static mxc_i2c_regs_t *i2c_bus_ports[] = {MXC_I2C0, MXC_I2C1}; -#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS -/* IRQ for each I2C */ -static uint32_t i2c_bus_irqs[] = {EC_I2C0_IRQn, EC_I2C1_IRQn}; -#endif - /** * chip_i2c_xfer() - Low Level function for I2C Master Reads and Writes. * @port: Port to access @@ -291,6 +288,9 @@ DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C); */ #ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS +/* IRQ for each I2C */ +static uint32_t i2c_bus_irqs[] = {EC_I2C0_IRQn, EC_I2C1_IRQn}; + /** * Buffer for received host command packets (including prefix byte on request, * and result/size on response). After any protocol-specific headers, the @@ -301,6 +301,7 @@ static uint8_t host_buffer_padded[I2C_MAX_HOST_PACKET_SIZE + 4 + static uint8_t *const host_buffer = host_buffer_padded + 2; static uint8_t params_copy[I2C_MAX_HOST_PACKET_SIZE] __aligned(4); static struct host_packet i2c_packet; +static int slave_rx_remain, slave_tx_remain; static i2c_req_t req_slave; volatile int ec_pending_response = 0; @@ -328,7 +329,7 @@ static void i2c_send_response_packet(struct host_packet *pkt) req_slave.tx_len = size + 2; /* Call the handler for transmition of response packet. */ - i2c_handler(i2c_bus_ports[I2C_PORT_EC]); + i2c_slave_handler(i2c_bus_ports[I2C_PORT_EC]); } /** @@ -395,7 +396,7 @@ void i2c_chip_callback(i2c_req_t *req, int error) */ void I2C0_IRQHandler(void) { - i2c_handler(i2c_bus_ports[0]); + i2c_slave_handler(i2c_bus_ports[0]); } /** @@ -403,190 +404,568 @@ void I2C0_IRQHandler(void) */ void I2C1_IRQHandler(void) { - i2c_handler(i2c_bus_ports[1]); + i2c_slave_handler(i2c_bus_ports[1]); } DECLARE_IRQ(EC_I2C0_IRQn, I2C0_IRQHandler, 1); DECLARE_IRQ(EC_I2C1_IRQn, I2C1_IRQHandler, 1); /** - * init_i2cs() - Async Handler for I2C Slave driver. - * @port: I2C port number to initialize. - */ -void init_i2cs(int port) -{ - int error; - - if ((error = i2c_init_peripheral(i2c_bus_ports[port], I2C_STD_MODE)) != - EC_SUCCESS) { - while (1) - ; - } - /* Prepare SlaveAsync */ - req_slave.addr = CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS; - req_slave.tx_data = host_buffer; /* transmitted to host */ - req_slave.tx_len = -1; /* Nothing to send. */ - req_slave.rx_data = host_buffer; /* received from host */ - req_slave.rx_len = I2C_MAX_HOST_PACKET_SIZE; - req_slave.restart = 0; - req_slave.callback = i2c_chip_callback; - - if ((error = i2c_slave_async(i2c_bus_ports[port], &req_slave)) != - EC_SUCCESS) { - while (1) - ; - } - - task_enable_irq(i2c_bus_irqs[port]); -} - -#endif /* CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS */ - -/** - * i2c_set_speed() - Set the transfer speed of the selected I2C. - * @i2c: Pointer to I2C peripheral. - * @i2cspeed: Speed to set. + * i2c_slave_read() - Handles async read request from the I2c master. + * @i2c: I2C peripheral pointer. + * @req: Pointer to the request info. + * @int_flags: Current state of the interrupt flags for this request. * - * Return EC_SUCCESS, or non-zero if error. + * Return EC_SUCCESS if successful, otherwise returns a common error code. */ -static int i2c_set_speed(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed) +static int i2c_slave_read(mxc_i2c_regs_t *i2c, i2c_req_t *req, + uint32_t int_flags) { - uint32_t ticks; - uint32_t ticks_lo; - uint32_t ticks_hi; - uint32_t time_pclk; - uint32_t target_bus_freq; - uint32_t time_scl_min; - uint32_t clock_low_min; - uint32_t clock_high_min; - uint32_t clock_min; - - if (i2cspeed == I2C_HS_MODE) { - /* Compute dividers for high speed mode. */ - time_pclk = 1000000 / (PeripheralClock / 1000); + int i2c_num; - target_bus_freq = i2cspeed; - if (target_bus_freq < 1000) { - return EC_ERROR_INVAL; + i2c_num = MXC_I2C_GET_IDX(i2c); + req->direction = I2C_TRANSFER_DIRECTION_MASTER_READ; + if (slave_tx_remain != 0) { + /* Fill the FIFO */ + while ((slave_tx_remain > 0) && + !(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) { + i2c->fifo = *(req->tx_data)++; + states[i2c_num].num_wr++; + slave_tx_remain--; } + /* Set the TX threshold interrupt level. */ + if (slave_tx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) { + i2c->tx_ctrl0 = + ((i2c->tx_ctrl0 & + ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | + (MXC_I2C_FIFO_DEPTH - 1) + << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); - time_scl_min = 1000000 / (target_bus_freq / 1000); - clock_low_min = - ((T_LOW_MIN + T_F_MAX_HS + (time_pclk - 1) - T_AF_MIN) / - time_pclk) - 1; - clock_high_min = ((T_HIGH_MIN + T_R_MAX_HS + (time_pclk - 1) - - T_AF_MIN) / - time_pclk) - 1; - clock_min = ((time_scl_min + (time_pclk - 1)) / time_pclk) - 2; - - ticks_lo = (clock_low_min > (clock_min - clock_high_min)) - ? (clock_low_min) - : (clock_min - clock_high_min); - ticks_hi = clock_high_min; - - if ((ticks_lo > (MXC_F_I2C_HS_CLK_HS_CLK_LO >> - MXC_F_I2C_HS_CLK_HS_CLK_LO_POS)) || - (ticks_hi > (MXC_F_I2C_HS_CLK_HS_CLK_HI >> - MXC_F_I2C_HS_CLK_HS_CLK_HI_POS))) { - return EC_ERROR_INVAL; + } else { + i2c->tx_ctrl0 = + ((i2c->tx_ctrl0 & + ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | + (slave_tx_remain) + << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); } - - /* Write results to destination registers. */ - i2c->hs_clk = (ticks_lo << MXC_F_I2C_HS_CLK_HS_CLK_LO_POS) | - (ticks_hi << MXC_F_I2C_HS_CLK_HS_CLK_HI_POS); - - /* Still need to load dividers for the preamble that each - * high-speed transaction starts with. Switch setting to fast - * mode and fall out of if statement. + /* Enable TXTH interrupt and Error interrupts. */ + i2c->int_en0 |= (MXC_F_I2C_INT_EN0_TX_THRESH | I2C_ERROR); + if (int_flags & I2C_ERROR) { + i2c->int_en0 = 0; + /* Calculate the number of bytes sent by the slave. */ + req->tx_num = + states[i2c_num].num_wr - + ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> + MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + if (!req->sw_autoflush_disable) { + /* Manually clear the TXFIFO. */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + } + states[i2c_num].num_wr = 0; + if (req->callback != NULL) { + /* Disable and clear interrupts. */ + i2c->int_en0 = 0; + i2c->int_en1 = 0; + i2c->int_fl0 = i2c->int_fl0; + i2c->int_fl1 = i2c->int_fl1; + /* Cycle the I2C peripheral enable on error. */ + i2c->ctrl = 0; + i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; + i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); + } + return EC_ERROR_UNKNOWN; + } + } else { + /** + * If there is nothing to transmit to the EC HOST, then default + * to clock stretching. */ - i2cspeed = I2C_FAST_MODE; - } - - /* Get the number of periph clocks needed to achieve selected speed. */ - ticks = PeripheralClock / i2cspeed; - - /* For a 50% duty cycle, half the ticks will be spent high and half will - * be low. - */ - ticks_hi = (ticks >> 1) - 1; - ticks_lo = (ticks >> 1) - 1; - - /* Account for rounding error in odd tick counts. */ - if (ticks & 1) { - ticks_hi++; - } - - /* Will results fit into 9 bit registers? (ticks_hi will always be >= - * ticks_lo. No need to check ticks_lo.) - */ - if (ticks_hi > 0x1FF) { - return EC_ERROR_INVAL; - } - - /* 0 is an invalid value for the destination registers. (ticks_hi will - * always be >= ticks_lo. No need to check ticks_hi.) - */ - if (ticks_lo == 0) { - return EC_ERROR_INVAL; + if (req->tx_len == -1) + return EC_SUCCESS; + /** + * The EC HOST is requesting more that we are able to transmit. + * Fulfill the EC HOST reading of extra bytes by sending the + * EC_PADDING_BYTE. + */ + if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) + i2c->fifo = EC_PADDING_BYTE; + /* set tx threshold to zero */ + i2c->tx_ctrl0 = + ((i2c->tx_ctrl0 & ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | + (0) << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); + /* Enable TXTH interrupt and Error interrupts */ + i2c->int_en0 |= (MXC_F_I2C_INT_EN0_TX_THRESH | I2C_ERROR); } - - /* Write results to destination registers. */ - i2c->clk_lo = ticks_lo; - i2c->clk_hi = ticks_hi; - return EC_SUCCESS; } /** - * i2c_init_peripheral() - Initialize and enable I2C. - * @i2c: Pointer to I2C peripheral registers. - * @i2cspeed: Desired speed (I2C mode). - * @sys_cfg: System configuration object. + * i2c_slave_write() - Handles async write request from the I2c master. + * @i2c: I2C peripheral pointer. + * @req: Pointer to the request info. + * @int_flags: Current state of the interrupt flags for this request. * - * Return EC_SUCCESS, or non-zero if error. + * Return EC_SUCCESS if successful, otherwise returns a common error code. */ -static int i2c_init_peripheral(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed) +static int i2c_slave_write(mxc_i2c_regs_t *i2c, i2c_req_t *req, + uint32_t int_flags) { + int i2c_num; + /** - * Always disable the HW autoflush on data NACK and let the SW handle - * the flushing. + * Master Write has been called and if there is a + * rx_data buffer */ - i2c->tx_ctrl0 |= 0x20; - - states[MXC_I2C_GET_IDX(i2c)].num_wr = 0; - - i2c->ctrl = 0; /* clear configuration bits */ - i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; /* Enable I2C */ - i2c->master_ctrl = 0; /* clear master configuration bits */ - i2c->status = 0; /* clear status bits */ - - i2c->ctrl = 0; /* clear configuration bits */ - i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; /* Enable I2C */ - i2c->master_ctrl = 0; /* clear master configuration bits */ - i2c->status = 0; /* clear status bits */ - - /* Check for HS mode */ - if (i2cspeed == I2C_HS_MODE) { - i2c->ctrl |= MXC_F_I2C_CTRL_HS_MODE; /* Enable HS mode */ - } - - /* Disable and clear interrupts */ - i2c->int_en0 = 0; - i2c->int_en1 = 0; - i2c->int_fl0 = i2c->int_fl0; - i2c->int_fl1 = i2c->int_fl1; - - i2c->timeout = 0x0; /* set timeout */ - i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; /* clear the RX FIFO */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; /* clear the TX FIFO */ - - return i2c_set_speed(i2c, i2cspeed); -} - -/** - * i2c_master_write() - * @i2c: Pointer to I2C regs. - * @addr: I2C 7-bit Address left aligned, bit 7 to bit 1. + i2c_num = MXC_I2C_GET_IDX(i2c); + req->direction = I2C_TRANSFER_DIRECTION_MASTER_WRITE; + if (slave_rx_remain != 0) { + /* Read out any data in the RX FIFO. */ + while ((slave_rx_remain > 0) && + !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { + *(req->rx_data)++ = i2c->fifo; + req->rx_num++; + slave_rx_remain--; + } + /* Set the RX threshold interrupt level. */ + if (slave_rx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) { + i2c->rx_ctrl0 = + ((i2c->rx_ctrl0 & + ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) | + (MXC_I2C_FIFO_DEPTH - 1) + << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS); + } else { + i2c->rx_ctrl0 = + ((i2c->rx_ctrl0 & + ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) | + (slave_rx_remain) + << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS); + } + /* Enable RXTH interrupt and Error interrupts. */ + i2c->int_en0 |= (MXC_F_I2C_INT_EN0_RX_THRESH | I2C_ERROR); + if (int_flags & I2C_ERROR) { + i2c->int_en0 = 0; + /** + * Calculate the number of bytes sent + * by the slave. + */ + req->tx_num = + states[i2c_num].num_wr - + ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> + MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + + if (!req->sw_autoflush_disable) { + /* Manually clear the TXFIFO. */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + } + states[i2c_num].num_wr = 0; + if (req->callback != NULL) { + /* Disable and clear interrupts. */ + i2c->int_en0 = 0; + i2c->int_en1 = 0; + i2c->int_fl0 = i2c->int_fl0; + i2c->int_fl1 = i2c->int_fl1; + /* Cycle the I2C peripheral enable on error. */ + i2c->ctrl = 0; + i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; + i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); + } + return EC_ERROR_UNKNOWN; + } + } else { + /* Disable RXTH interrupt. */ + i2c->int_en0 &= ~(MXC_F_I2C_INT_EN0_RX_THRESH); + /* Flush any extra bytes in the RXFIFO */ + i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; + /* Store the current state of the slave */ + states[i2c_num].slave_state = I2C_SLAVE_READ_COMPLETE; + } + return EC_SUCCESS; +} + +/** + * i2c_slave_handler() - I2C interrupt handler. + * @i2c: Base address of the I2C module. + * + * This function should be called by the application from the interrupt + * handler if I2C interrupts are enabled. Alternately, this function + * can be periodically called by the application if I2C interrupts are + * disabled. + */ +static void i2c_slave_handler(mxc_i2c_regs_t *i2c) +{ + uint32_t int_flags; + int i2c_num; + i2c_req_t *req; + int status; + + i2c_num = MXC_I2C_GET_IDX(i2c); + req = states[i2c_num].req; + + /* Check for an Address match */ + if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_ADDR_MATCH) { + /* Clear AMI and TXLOI */ + i2c->int_fl0 |= MXC_F_I2C_INT_FL0_DONE; + i2c->int_fl0 |= MXC_F_I2C_INT_FL0_ADDR_MATCH; + i2c->int_fl0 |= MXC_F_I2C_INT_FL0_TX_LOCK_OUT; + /* Store the current state of the Slave */ + states[i2c_num].slave_state = I2C_SLAVE_ADDR_MATCH; + /* Set the Done, Stop interrupt */ + i2c->int_en0 |= MXC_F_I2C_INT_EN0_DONE | MXC_F_I2C_INT_EN0_STOP; + /* Inhibit sleep mode when addressed until STOPF flag is set */ + disable_sleep(SLEEP_MASK_I2C_SLAVE); + } + + /* Check for errors */ + int_flags = i2c->int_fl0; + /* Clear the interrupts */ + i2c->int_fl0 = int_flags; + + if (int_flags & I2C_ERROR) { + i2c->int_en0 = 0; + /* Calculate the number of bytes sent by the slave */ + req->tx_num = states[i2c_num].num_wr - + ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> + MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + + if (!req->sw_autoflush_disable) { + /* Manually clear the TXFIFO */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + } + states[i2c_num].num_wr = 0; + if (req->callback != NULL) { + /* Disable and clear interrupts */ + i2c->int_en0 = 0; + i2c->int_en1 = 0; + i2c->int_fl0 = i2c->int_fl0; + i2c->int_fl1 = i2c->int_fl1; + /* Cycle the I2C peripheral enable on error. */ + i2c->ctrl = 0; + i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; + i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); + } + return; + } + + slave_rx_remain = req->rx_len - req->rx_num; + /* determine if there is any data ready to transmit to the EC HOST */ + if (req->tx_len != -1) + slave_tx_remain = req->tx_len - states[i2c_num].num_wr; + else + slave_tx_remain = 0; + + /* Check for Stop interrupt */ + if (int_flags & MXC_F_I2C_INT_FL0_STOP) { + /* Disable all interrupts except address match. */ + i2c->int_en1 = 0; + i2c->int_en0 = MXC_F_I2C_INT_EN0_ADDR_MATCH; + /* Clear all interrupts except a possible address match. */ + i2c->int_fl0 = i2c->int_fl0 & ~MXC_F_I2C_INT_FL0_ADDR_MATCH; + i2c->int_fl1 = i2c->int_fl1; + if (req->direction == I2C_TRANSFER_DIRECTION_MASTER_WRITE) { + /* Read out any data in the RX FIFO */ + while (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { + *(req->rx_data)++ = i2c->fifo; + req->rx_num++; + } + } + + /* Calculate the number of bytes sent by the slave */ + req->tx_num = states[i2c_num].num_wr - + ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> + MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + slave_rx_remain = 0; + slave_tx_remain = 0; + if (!req->sw_autoflush_disable) { + /* Manually clear the TXFIFO */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + } + /* Callback to the EC request processor */ + i2c_free_callback(i2c_num, EC_SUCCESS); + req->direction = I2C_TRANSFER_DIRECTION_NONE; + states[i2c_num].num_wr = 0; + + /* Be ready to receive more data */ + req->rx_len = 128; + /* Clear the byte counters */ + req->tx_num = 0; + req->rx_num = 0; + req->tx_len = -1; /* Nothing to send. */ + + /* No longer inhibit deep sleep after stop condition */ + enable_sleep(SLEEP_MASK_I2C_SLAVE); + return; + } + + /* Check for DONE interrupt */ + if (int_flags & MXC_F_I2C_INT_FL0_DONE) { + if (req->direction == I2C_TRANSFER_DIRECTION_MASTER_WRITE) { + /* Read out any data in the RX FIFO */ + while (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { + *(req->rx_data)++ = i2c->fifo; + req->rx_num++; + } + } + /* Disable Done interrupt */ + i2c->int_en0 &= ~(MXC_F_I2C_INT_EN0_DONE); + /* Calculate the number of bytes sent by the slave */ + req->tx_num = states[i2c_num].num_wr - + ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> + MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + slave_rx_remain = 0; + slave_tx_remain = 0; + if (!req->sw_autoflush_disable) { + /* Manually clear the TXFIFO */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + } + i2c_free_callback(i2c_num, EC_SUCCESS); + req->direction = I2C_TRANSFER_DIRECTION_NONE; + states[i2c_num].num_wr = 0; + return; + } + + if (states[i2c_num].slave_state != I2C_SLAVE_ADDR_MATCH) + return; + /** + * Check if Master Read has been called and if there is a + * tx_data buffer + */ + if (i2c->ctrl & MXC_F_I2C_CTRL_READ) { + status = i2c_slave_read(i2c, req, int_flags); + if (status != EC_SUCCESS) + return; + } else { + status = i2c_slave_write(i2c, req, int_flags); + if (status != EC_SUCCESS) + return; + } +} + +static void i2c_free_callback(int i2c_num, int error) +{ + /* Save the request */ + i2c_req_t *temp_req = states[i2c_num].req; + + /* Callback if not NULL */ + if (temp_req->callback != NULL) + temp_req->callback(temp_req, error); +} + +/** + * init_i2cs() - Async Handler for I2C Slave driver. + * @port: I2C port number to initialize. + */ +void init_i2cs(int port) +{ + int error; + + slave_rx_remain = 0; + slave_tx_remain = 0; + + error = i2c_init_peripheral(i2c_bus_ports[port], I2C_STD_MODE); + if (error != EC_SUCCESS) { + while (1) + ; + } + /* Prepare SlaveAsync */ + req_slave.addr = CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS; + req_slave.tx_data = host_buffer; /* transmitted to host */ + req_slave.tx_len = -1; /* Nothing to send. */ + req_slave.rx_data = host_buffer; /* received from host */ + req_slave.rx_len = I2C_MAX_HOST_PACKET_SIZE; + req_slave.restart = 0; + req_slave.callback = i2c_chip_callback; + + error = i2c_slave_async(i2c_bus_ports[port], &req_slave); + if (error != EC_SUCCESS) { + while (1) + ; + } + + task_enable_irq(i2c_bus_irqs[port]); +} + +/** + * i2c_slave_async() - Slave Read and Write Asynchronous. + * @i2c: Pointer to I2C regs. + * @req: Request for an I2C transaction. + * + * Return EC_SUCCESS if successful, otherwise returns a common error code. + */ +static int i2c_slave_async(mxc_i2c_regs_t *i2c, i2c_req_t *req) +{ + /* Make sure the I2C has been initialized. */ + if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) + return EC_ERROR_UNKNOWN; + + states[MXC_I2C_GET_IDX(i2c)].req = req; + + /* Disable master mode */ + i2c->ctrl &= ~(MXC_F_I2C_CTRL_MST); + /* Set the Slave Address in the I2C peripheral register. */ + i2c->slave_addr = req->addr; + + /* Clear the byte counters */ + req->tx_num = 0; + req->rx_num = 0; + + /* Disable and clear the interrupts. */ + i2c->int_en0 = 0; + i2c->int_en1 = 0; + i2c->int_fl0 = i2c->int_fl0; + i2c->int_fl1 = i2c->int_fl1; + /* Only enable the I2C Address match interrupt. */ + i2c->int_en0 = MXC_F_I2C_INT_EN0_ADDR_MATCH; + + return EC_SUCCESS; +} + +#endif /* CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS */ + +/** + * i2c_set_speed() - Set the transfer speed of the selected I2C. + * @i2c: Pointer to I2C peripheral. + * @i2cspeed: Speed to set. + * + * Return EC_SUCCESS, or non-zero if error. + */ +static int i2c_set_speed(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed) +{ + uint32_t ticks; + uint32_t ticks_lo; + uint32_t ticks_hi; + uint32_t time_pclk; + uint32_t target_bus_freq; + uint32_t time_scl_min; + uint32_t clock_low_min; + uint32_t clock_high_min; + uint32_t clock_min; + + if (i2cspeed == I2C_HS_MODE) { + /* Compute dividers for high speed mode. */ + time_pclk = 1000000 / (PeripheralClock / 1000); + + target_bus_freq = i2cspeed; + if (target_bus_freq < 1000) { + return EC_ERROR_INVAL; + } + + time_scl_min = 1000000 / (target_bus_freq / 1000); + clock_low_min = + ((T_LOW_MIN + T_F_MAX_HS + (time_pclk - 1) - T_AF_MIN) / + time_pclk) - 1; + clock_high_min = ((T_HIGH_MIN + T_R_MAX_HS + (time_pclk - 1) - + T_AF_MIN) / + time_pclk) - 1; + clock_min = ((time_scl_min + (time_pclk - 1)) / time_pclk) - 2; + + ticks_lo = (clock_low_min > (clock_min - clock_high_min)) + ? (clock_low_min) + : (clock_min - clock_high_min); + ticks_hi = clock_high_min; + + if ((ticks_lo > (MXC_F_I2C_HS_CLK_HS_CLK_LO >> + MXC_F_I2C_HS_CLK_HS_CLK_LO_POS)) || + (ticks_hi > (MXC_F_I2C_HS_CLK_HS_CLK_HI >> + MXC_F_I2C_HS_CLK_HS_CLK_HI_POS))) { + return EC_ERROR_INVAL; + } + + /* Write results to destination registers. */ + i2c->hs_clk = (ticks_lo << MXC_F_I2C_HS_CLK_HS_CLK_LO_POS) | + (ticks_hi << MXC_F_I2C_HS_CLK_HS_CLK_HI_POS); + + /* Still need to load dividers for the preamble that each + * high-speed transaction starts with. Switch setting to fast + * mode and fall out of if statement. + */ + i2cspeed = I2C_FAST_MODE; + } + + /* Get the number of periph clocks needed to achieve selected speed. */ + ticks = PeripheralClock / i2cspeed; + + /* For a 50% duty cycle, half the ticks will be spent high and half will + * be low. + */ + ticks_hi = (ticks >> 1) - 1; + ticks_lo = (ticks >> 1) - 1; + + /* Account for rounding error in odd tick counts. */ + if (ticks & 1) { + ticks_hi++; + } + + /* Will results fit into 9 bit registers? (ticks_hi will always be >= + * ticks_lo. No need to check ticks_lo.) + */ + if (ticks_hi > 0x1FF) { + return EC_ERROR_INVAL; + } + + /* 0 is an invalid value for the destination registers. (ticks_hi will + * always be >= ticks_lo. No need to check ticks_hi.) + */ + if (ticks_lo == 0) { + return EC_ERROR_INVAL; + } + + /* Write results to destination registers. */ + i2c->clk_lo = ticks_lo; + i2c->clk_hi = ticks_hi; + + return EC_SUCCESS; +} + +/** + * i2c_init_peripheral() - Initialize and enable I2C. + * @i2c: Pointer to I2C peripheral registers. + * @i2cspeed: Desired speed (I2C mode). + * @sys_cfg: System configuration object. + * + * Return EC_SUCCESS, or non-zero if error. + */ +static int i2c_init_peripheral(mxc_i2c_regs_t *i2c, i2c_speed_t i2cspeed) +{ + /** + * Always disable the HW autoflush on data NACK and let the SW handle + * the flushing. + */ + i2c->tx_ctrl0 |= 0x20; + + states[MXC_I2C_GET_IDX(i2c)].num_wr = 0; + + i2c->ctrl = 0; /* clear configuration bits */ + i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; /* Enable I2C */ + i2c->master_ctrl = 0; /* clear master configuration bits */ + i2c->status = 0; /* clear status bits */ + + i2c->ctrl = 0; /* clear configuration bits */ + i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; /* Enable I2C */ + i2c->master_ctrl = 0; /* clear master configuration bits */ + i2c->status = 0; /* clear status bits */ + + /* Check for HS mode */ + if (i2cspeed == I2C_HS_MODE) { + i2c->ctrl |= MXC_F_I2C_CTRL_HS_MODE; /* Enable HS mode */ + } + + /* Disable and clear interrupts */ + i2c->int_en0 = 0; + i2c->int_en1 = 0; + i2c->int_fl0 = i2c->int_fl0; + i2c->int_fl1 = i2c->int_fl1; + + i2c->timeout = 0x0; /* set timeout */ + i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; /* clear the RX FIFO */ + i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; /* clear the TX FIFO */ + + return i2c_set_speed(i2c, i2cspeed); +} + +/** + * i2c_master_write() + * @i2c: Pointer to I2C regs. + * @addr: I2C 7-bit Address left aligned, bit 7 to bit 1. * Only supports 7-bit addressing. LSb of the given address * will be used as the read/write bit, the \p addr will * not be shifted. Used for both master and @@ -644,158 +1023,17 @@ static int i2c_master_write(mxc_i2c_regs_t *i2c, uint8_t addr, int start, /* Check for errors */ if (i2c->int_fl0 & I2C_ERROR) { /* Set the stop bit */ - i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART); - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; - return EC_ERROR_UNKNOWN; - } - - if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) { - i2c->fifo = *data++; - len--; - } - } - /* Check if Repeated Start requested */ - if (restart) { - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART; - } else { - if (stop) { - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; - } - } - - if (stop) { - /* Wait for Done */ - while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE)) { - /* Check for errors */ - if (i2c->int_fl0 & I2C_ERROR) { - /* Set the stop bit */ - i2c->master_ctrl &= - ~(MXC_F_I2C_MASTER_CTRL_RESTART); - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; - return EC_ERROR_UNKNOWN; - } - } - /* Clear Done interrupt flag */ - i2c->int_fl0 = MXC_F_I2C_INT_FL0_DONE; - } - - /* Wait for Stop if requested and there is no restart. */ - if (stop && !restart) { - while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_STOP)) { - /* Check for errors */ - if (i2c->int_fl0 & I2C_ERROR) { - /* Set the stop bit */ - i2c->master_ctrl &= - ~(MXC_F_I2C_MASTER_CTRL_RESTART); - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; - return EC_ERROR_UNKNOWN; - } - } - /* Clear stop interrupt flag */ - i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP; - } - - /* Check for errors */ - if (i2c->int_fl0 & I2C_ERROR) { - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -/** - * i2c_master_read() - * @i2c: Pointer to I2C regs. - * @addr: I2C 7-bit Address right aligned, bit 6 to bit 0. - * @data: Data to be written. - * @len: Number of bytes to Write. - * @restart: 0 to send a stop bit at the end of the transaction, - * otherwise send a restart. - * - * Will block until transaction is complete. - * - * Return: EC_SUCCESS if successful, otherwise returns a common error code - */ -static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, - int stop, uint8_t *data, int len, int restart) -{ - volatile int length = len; - int interactive_receive_mode; - - if (len == 0) { - return EC_SUCCESS; - } - - if (len > 255) { - return EC_ERROR_INVAL; - } - - /* Clear the interrupt flag */ - i2c->int_fl0 = i2c->int_fl0; - - /* Make sure the I2C has been initialized */ - if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) { - return EC_ERROR_UNKNOWN; - } - - /* Enable master mode */ - i2c->ctrl |= MXC_F_I2C_CTRL_MST; - - if (stop) { - /* Set receive count */ - i2c->ctrl &= ~MXC_F_I2C_CTRL_RX_MODE; - i2c->rx_ctrl1 = len; - interactive_receive_mode = 0; - } else { - i2c->ctrl |= MXC_F_I2C_CTRL_RX_MODE; - i2c->rx_ctrl1 = 1; - interactive_receive_mode = 1; - } - - /* Load FIFO with slave address */ - if (start) { - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START; - while (i2c->status & MXC_F_I2C_STATUS_TX_FULL) { - } - /** - * The slave address is right-aligned, bits 6 to 0, shift - * to the left and make room for the read bit. - */ - i2c->fifo = ((addr << 1) | 1); - } - - /* Wait for all data to be received or error. */ - while (length > 0) { - /* Check for errors */ - if (i2c->int_fl0 & I2C_ERROR) { - /* Set the stop bit */ - i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART); - i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; - return EC_ERROR_UNKNOWN; - } - - /* if in interactive receive mode then ack each received byte */ - if (interactive_receive_mode) { - while (!(i2c->int_fl0 & MXC_F_I2C_INT_EN0_RX_MODE)) - ; - if (i2c->int_fl0 & MXC_F_I2C_INT_EN0_RX_MODE) { - /* read the data */ - *data++ = i2c->fifo; - length--; - /* clear the bit */ - if (length != 1) { - i2c->int_fl0 = - MXC_F_I2C_INT_EN0_RX_MODE; - } - } - } else { - if (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { - *data++ = i2c->fifo; - length--; - } + i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART); + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; + return EC_ERROR_UNKNOWN; } - } + if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) { + i2c->fifo = *data++; + len--; + } + } + /* Check if Repeated Start requested */ if (restart) { i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART; } else { @@ -804,8 +1042,8 @@ static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, } } - /* Wait for Done */ if (stop) { + /* Wait for Done */ while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE)) { /* Check for errors */ if (i2c->int_fl0 & I2C_ERROR) { @@ -820,23 +1058,20 @@ static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, i2c->int_fl0 = MXC_F_I2C_INT_FL0_DONE; } - /* Wait for Stop */ - if (!restart) { - if (stop) { - while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_STOP)) { - /* Check for errors */ - if (i2c->int_fl0 & I2C_ERROR) { - /* Set the stop bit */ - i2c->master_ctrl &= ~( - MXC_F_I2C_MASTER_CTRL_RESTART); - i2c->master_ctrl |= - MXC_F_I2C_MASTER_CTRL_STOP; - return EC_ERROR_UNKNOWN; - } + /* Wait for Stop if requested and there is no restart. */ + if (stop && !restart) { + while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_STOP)) { + /* Check for errors */ + if (i2c->int_fl0 & I2C_ERROR) { + /* Set the stop bit */ + i2c->master_ctrl &= + ~(MXC_F_I2C_MASTER_CTRL_RESTART); + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; + return EC_ERROR_UNKNOWN; } - /* Clear Stop interrupt flag */ - i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP; } + /* Clear stop interrupt flag */ + i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP; } /* Check for errors */ @@ -845,391 +1080,148 @@ static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, } return EC_SUCCESS; -} - -/** - * i2c_slave_async() - Slave Read and Write Asynchronous. - * @i2c: Pointer to I2C regs. - * @req: Request for an I2C transaction. - * - * Return EC_SUCCESS if successful, otherwise returns a common error code. - */ -static int i2c_slave_async(mxc_i2c_regs_t *i2c, i2c_req_t *req) -{ - /* Make sure the I2C has been initialized. */ - if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) { - return EC_ERROR_UNKNOWN; - } - - states[MXC_I2C_GET_IDX(i2c)].req = req; - - /* Disable master mode */ - i2c->ctrl &= ~(MXC_F_I2C_CTRL_MST); - /* Set the Slave Address in the I2C peripheral register. */ - i2c->slave_addr = req->addr; - - /* Clear the byte counters */ - req->tx_num = 0; - req->rx_num = 0; - - /* Disable and clear the interrupts. */ - i2c->int_en0 = 0; - i2c->int_en1 = 0; - i2c->int_fl0 = i2c->int_fl0; - i2c->int_fl1 = i2c->int_fl1; - /* Only enable the I2C Address match interrupt. */ - i2c->int_en0 = MXC_F_I2C_INT_EN0_ADDR_MATCH; - - return EC_SUCCESS; -} - -/** - * i2c_slave_read() - Handles async read request from the I2c master. - * @i2c: I2C peripheral pointer. - * @req: Pointer to the request info. - * @int_flags: Current state of the interrupt flags for this request. - * - * Return EC_SUCCESS if successful, otherwise returns a common error code. - */ -static int i2c_slave_read(mxc_i2c_regs_t *i2c, i2c_req_t *req, - uint32_t int_flags) -{ - int i2c_num; - - i2c_num = MXC_I2C_GET_IDX(i2c); - req->direction = I2C_TRANSFER_DIRECTION_MASTER_READ; - if (slave_tx_remain != 0) { - /* Fill the FIFO */ - while ((slave_tx_remain > 0) && - !(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) { - i2c->fifo = *(req->tx_data)++; - states[i2c_num].num_wr++; - slave_tx_remain--; - } - /* Set the TX threshold interrupt level. */ - if (slave_tx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) { - i2c->tx_ctrl0 = - ((i2c->tx_ctrl0 & - ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | - (MXC_I2C_FIFO_DEPTH - 1) - << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); - - } else { - i2c->tx_ctrl0 = - ((i2c->tx_ctrl0 & - ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | - (slave_tx_remain) - << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); - } - /* Enable TXTH interrupt and Error interrupts. */ - i2c->int_en0 |= (MXC_F_I2C_INT_EN0_TX_THRESH | I2C_ERROR); - if (int_flags & I2C_ERROR) { - i2c->int_en0 = 0; - /* Calculate the number of bytes sent by the slave. */ - req->tx_num = - states[i2c_num].num_wr - - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> - MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); - if (!req->sw_autoflush_disable) { - /* Manually clear the TXFIFO. */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; - } - states[i2c_num].num_wr = 0; - if (req->callback != NULL) { - /* Disable and clear interrupts. */ - i2c->int_en0 = 0; - i2c->int_en1 = 0; - i2c->int_fl0 = i2c->int_fl0; - i2c->int_fl1 = i2c->int_fl1; - /* Cycle the I2C peripheral enable on error. */ - i2c->ctrl = 0; - i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; - i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); - } - return EC_ERROR_UNKNOWN; - } - } else { - /** - * If there is nothing to transmit to the EC HOST, then default - * to clock stretching. - */ - if (req->tx_len == -1) - return EC_SUCCESS; - /** - * The EC HOST is requesting more that we are able to transmit. - * Fulfill the EC HOST reading of extra bytes by sending the - * EC_PADDING_BYTE. - */ - if (!(i2c->status & MXC_F_I2C_STATUS_TX_FULL)) { - i2c->fifo = EC_PADDING_BYTE; - } - /* set tx threshold to zero */ - i2c->tx_ctrl0 = - ((i2c->tx_ctrl0 & ~(MXC_F_I2C_TX_CTRL0_TX_THRESH)) | - (0) << MXC_F_I2C_TX_CTRL0_TX_THRESH_POS); - /* Enable TXTH interrupt and Error interrupts */ - i2c->int_en0 |= (MXC_F_I2C_INT_EN0_TX_THRESH | I2C_ERROR); - } - return EC_SUCCESS; -} - -/** - * i2c_slave_write() - Handles async write request from the I2c master. - * @i2c: I2C peripheral pointer. - * @req: Pointer to the request info. - * @int_flags: Current state of the interrupt flags for this request. - * - * Return EC_SUCCESS if successful, otherwise returns a common error code. - */ -static int i2c_slave_write(mxc_i2c_regs_t *i2c, i2c_req_t *req, - uint32_t int_flags) -{ - int i2c_num; - - /** - * Master Write has been called and if there is a - * rx_data buffer - */ - i2c_num = MXC_I2C_GET_IDX(i2c); - req->direction = I2C_TRANSFER_DIRECTION_MASTER_WRITE; - if (slave_rx_remain != 0) { - /* Read out any data in the RX FIFO. */ - while ((slave_rx_remain > 0) && - !(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { - *(req->rx_data)++ = i2c->fifo; - req->rx_num++; - slave_rx_remain--; - } - /* Set the RX threshold interrupt level. */ - if (slave_rx_remain >= (MXC_I2C_FIFO_DEPTH - 1)) { - i2c->rx_ctrl0 = - ((i2c->rx_ctrl0 & - ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) | - (MXC_I2C_FIFO_DEPTH - 1) - << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS); - } else { - i2c->rx_ctrl0 = - ((i2c->rx_ctrl0 & - ~(MXC_F_I2C_RX_CTRL0_RX_THRESH)) | - (slave_rx_remain) - << MXC_F_I2C_RX_CTRL0_RX_THRESH_POS); - } - /* Enable RXTH interrupt and Error interrupts. */ - i2c->int_en0 |= (MXC_F_I2C_INT_EN0_RX_THRESH | I2C_ERROR); - if (int_flags & I2C_ERROR) { - i2c->int_en0 = 0; - /** - * Calculate the number of bytes sent - * by the slave. - */ - req->tx_num = - states[i2c_num].num_wr - - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> - MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); - - if (!req->sw_autoflush_disable) { - /* Manually clear the TXFIFO. */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; - } - states[i2c_num].num_wr = 0; - if (req->callback != NULL) { - /* Disable and clear interrupts. */ - i2c->int_en0 = 0; - i2c->int_en1 = 0; - i2c->int_fl0 = i2c->int_fl0; - i2c->int_fl1 = i2c->int_fl1; - /* Cycle the I2C peripheral enable on error. */ - i2c->ctrl = 0; - i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; - i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); - } - return EC_ERROR_UNKNOWN; - } - } else { - /* Disable RXTH interrupt. */ - i2c->int_en0 &= ~(MXC_F_I2C_INT_EN0_RX_THRESH); - /* Flush any extra bytes in the RXFIFO */ - i2c->rx_ctrl0 |= MXC_F_I2C_RX_CTRL0_RX_FLUSH; - /* Store the current state of the slave */ - states[i2c_num].slave_state = I2C_SLAVE_READ_COMPLETE; - } - return EC_SUCCESS; -} - -static void i2c_slave_handler(mxc_i2c_regs_t *i2c) -{ - uint32_t int_flags; - int i2c_num; - i2c_req_t *req; - int status; +} - i2c_num = MXC_I2C_GET_IDX(i2c); - req = states[i2c_num].req; +/** + * i2c_master_read() + * @i2c: Pointer to I2C regs. + * @addr: I2C 7-bit Address right aligned, bit 6 to bit 0. + * @data: Data to be written. + * @len: Number of bytes to Write. + * @restart: 0 to send a stop bit at the end of the transaction, + * otherwise send a restart. + * + * Will block until transaction is complete. + * + * Return: EC_SUCCESS if successful, otherwise returns a common error code + */ +static int i2c_master_read(mxc_i2c_regs_t *i2c, uint8_t addr, int start, + int stop, uint8_t *data, int len, int restart) +{ + volatile int length = len; + int interactive_receive_mode; - /* Check for an Address match */ - if (i2c->int_fl0 & MXC_F_I2C_INT_FL0_ADDR_MATCH) { - /* Clear AMI and TXLOI */ - i2c->int_fl0 |= MXC_F_I2C_INT_FL0_DONE; - i2c->int_fl0 |= MXC_F_I2C_INT_FL0_ADDR_MATCH; - i2c->int_fl0 |= MXC_F_I2C_INT_FL0_TX_LOCK_OUT; - /* Store the current state of the Slave */ - states[i2c_num].slave_state = I2C_SLAVE_ADDR_MATCH; - /* Set the Done, Stop interrupt */ - i2c->int_en0 |= MXC_F_I2C_INT_EN0_DONE | MXC_F_I2C_INT_EN0_STOP; - /* Inhibit sleep mode when addressed until STOPF flag is set */ - disable_sleep(SLEEP_MASK_I2C_SLAVE); + if (len == 0) { + return EC_SUCCESS; } - /* Check for errors */ - int_flags = i2c->int_fl0; - /* Clear the interrupts */ - i2c->int_fl0 = int_flags; + if (len > 255) { + return EC_ERROR_INVAL; + } - if (int_flags & I2C_ERROR) { - i2c->int_en0 = 0; - /* Calculate the number of bytes sent by the slave */ - req->tx_num = states[i2c_num].num_wr - - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> - MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); + /* Clear the interrupt flag */ + i2c->int_fl0 = i2c->int_fl0; - if (!req->sw_autoflush_disable) { - /* Manually clear the TXFIFO */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; - } - states[i2c_num].num_wr = 0; - if (req->callback != NULL) { - /* Disable and clear interrupts */ - i2c->int_en0 = 0; - i2c->int_en1 = 0; - i2c->int_fl0 = i2c->int_fl0; - i2c->int_fl1 = i2c->int_fl1; - /* Cycle the I2C peripheral enable on error. */ - i2c->ctrl = 0; - i2c->ctrl = MXC_F_I2C_CTRL_I2C_EN; - i2c_free_callback(i2c_num, EC_ERROR_UNKNOWN); - } - return; + /* Make sure the I2C has been initialized */ + if (!(i2c->ctrl & MXC_F_I2C_CTRL_I2C_EN)) { + return EC_ERROR_UNKNOWN; } - slave_rx_remain = req->rx_len - req->rx_num; - /* determine if there is any data ready to transmit to the EC HOST */ - if (req->tx_len != -1) { - slave_tx_remain = req->tx_len - states[i2c_num].num_wr; + /* Enable master mode */ + i2c->ctrl |= MXC_F_I2C_CTRL_MST; + + if (stop) { + /* Set receive count */ + i2c->ctrl &= ~MXC_F_I2C_CTRL_RX_MODE; + i2c->rx_ctrl1 = len; + interactive_receive_mode = 0; } else { - slave_tx_remain = 0; + i2c->ctrl |= MXC_F_I2C_CTRL_RX_MODE; + i2c->rx_ctrl1 = 1; + interactive_receive_mode = 1; } - /* Check for Stop interrupt */ - if (int_flags & MXC_F_I2C_INT_FL0_STOP) { - /* Disable all interrupts except address match. */ - i2c->int_en1 = 0; - i2c->int_en0 = MXC_F_I2C_INT_EN0_ADDR_MATCH; - /* Clear all interrupts except a possible address match. */ - i2c->int_fl0 = i2c->int_fl0 & ~MXC_F_I2C_INT_FL0_ADDR_MATCH; - i2c->int_fl1 = i2c->int_fl1; - if (req->direction == I2C_TRANSFER_DIRECTION_MASTER_WRITE) { - /* Read out any data in the RX FIFO */ - while (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { - *(req->rx_data)++ = i2c->fifo; - req->rx_num++; - } + /* Load FIFO with slave address */ + if (start) { + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_START; + while (i2c->status & MXC_F_I2C_STATUS_TX_FULL) { } + /** + * The slave address is right-aligned, bits 6 to 0, shift + * to the left and make room for the read bit. + */ + i2c->fifo = ((addr << 1) | 1); + } - /* Calculate the number of bytes sent by the slave */ - req->tx_num = states[i2c_num].num_wr - - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> - MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); - slave_rx_remain = 0; - slave_tx_remain = 0; - if (!req->sw_autoflush_disable) { - /* Manually clear the TXFIFO */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; + /* Wait for all data to be received or error. */ + while (length > 0) { + /* Check for errors */ + if (i2c->int_fl0 & I2C_ERROR) { + /* Set the stop bit */ + i2c->master_ctrl &= ~(MXC_F_I2C_MASTER_CTRL_RESTART); + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; + return EC_ERROR_UNKNOWN; } - /* Callback to the EC request processor */ - i2c_free_callback(i2c_num, EC_SUCCESS); - req->direction = I2C_TRANSFER_DIRECTION_NONE; - states[i2c_num].num_wr = 0; - - /* Be ready to receive more data */ - req->rx_len = 128; - /* Clear the byte counters */ - req->tx_num = 0; - req->rx_num = 0; - req->tx_len = -1; /* Nothing to send. */ - - /* No longer inhibit deep sleep after stop condition */ - enable_sleep(SLEEP_MASK_I2C_SLAVE); - return; - } - /* Check for DONE interrupt */ - if (int_flags & MXC_F_I2C_INT_FL0_DONE) { - if (req->direction == I2C_TRANSFER_DIRECTION_MASTER_WRITE) { - /* Read out any data in the RX FIFO */ - while (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { - *(req->rx_data)++ = i2c->fifo; - req->rx_num++; + /* if in interactive receive mode then ack each received byte */ + if (interactive_receive_mode) { + while (!(i2c->int_fl0 & MXC_F_I2C_INT_EN0_RX_MODE)) + ; + if (i2c->int_fl0 & MXC_F_I2C_INT_EN0_RX_MODE) { + /* read the data */ + *data++ = i2c->fifo; + length--; + /* clear the bit */ + if (length != 1) { + i2c->int_fl0 = + MXC_F_I2C_INT_EN0_RX_MODE; + } + } + } else { + if (!(i2c->status & MXC_F_I2C_STATUS_RX_EMPTY)) { + *data++ = i2c->fifo; + length--; } } - /* Disable Done interrupt */ - i2c->int_en0 &= ~(MXC_F_I2C_INT_EN0_DONE); - /* Calculate the number of bytes sent by the slave */ - req->tx_num = states[i2c_num].num_wr - - ((i2c->tx_ctrl1 & MXC_F_I2C_TX_CTRL1_TX_FIFO) >> - MXC_F_I2C_TX_CTRL1_TX_FIFO_POS); - slave_rx_remain = 0; - slave_tx_remain = 0; - if (!req->sw_autoflush_disable) { - /* Manually clear the TXFIFO */ - i2c->tx_ctrl0 |= MXC_F_I2C_TX_CTRL0_TX_FLUSH; - } - i2c_free_callback(i2c_num, EC_SUCCESS); - req->direction = I2C_TRANSFER_DIRECTION_NONE; - states[i2c_num].num_wr = 0; - return; } - if (states[i2c_num].slave_state != I2C_SLAVE_ADDR_MATCH) { - return; - } - /** - * Check if Master Read has been called and if there is a - * tx_data buffer - */ - if (i2c->ctrl & MXC_F_I2C_CTRL_READ) { - status = i2c_slave_read(i2c, req, int_flags); - if (status != EC_SUCCESS) { - return; - } + if (restart) { + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_RESTART; } else { - status = i2c_slave_write(i2c, req, int_flags); - if (status != EC_SUCCESS) { - return; + if (stop) { + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; } } -} -/** - * i2c_handler() - I2C interrupt handler. - * @i2c: Base address of the I2C module. - * - * This function should be called by the application from the interrupt - * handler if I2C interrupts are enabled. Alternately, this function - * can be periodically called by the application if I2C interrupts are - * disabled. - */ -static void i2c_handler(mxc_i2c_regs_t *i2c) -{ - i2c_slave_handler(i2c); -} + /* Wait for Done */ + if (stop) { + while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_DONE)) { + /* Check for errors */ + if (i2c->int_fl0 & I2C_ERROR) { + /* Set the stop bit */ + i2c->master_ctrl &= + ~(MXC_F_I2C_MASTER_CTRL_RESTART); + i2c->master_ctrl |= MXC_F_I2C_MASTER_CTRL_STOP; + return EC_ERROR_UNKNOWN; + } + } + /* Clear Done interrupt flag */ + i2c->int_fl0 = MXC_F_I2C_INT_FL0_DONE; + } -static void i2c_free_callback(int i2c_num, int error) -{ - /* Save the request */ - i2c_req_t *temp_req = states[i2c_num].req; + /* Wait for Stop */ + if (!restart) { + if (stop) { + while (!(i2c->int_fl0 & MXC_F_I2C_INT_FL0_STOP)) { + /* Check for errors */ + if (i2c->int_fl0 & I2C_ERROR) { + /* Set the stop bit */ + i2c->master_ctrl &= ~( + MXC_F_I2C_MASTER_CTRL_RESTART); + i2c->master_ctrl |= + MXC_F_I2C_MASTER_CTRL_STOP; + return EC_ERROR_UNKNOWN; + } + } + /* Clear Stop interrupt flag */ + i2c->int_fl0 = MXC_F_I2C_INT_FL0_STOP; + } + } - /* Callback if not NULL */ - if (temp_req->callback != NULL) { - temp_req->callback(temp_req, error); + /* Check for errors */ + if (i2c->int_fl0 & I2C_ERROR) { + return EC_ERROR_UNKNOWN; } + + return EC_SUCCESS; } -- cgit v1.2.1