summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/max32660/i2c_chip.c798
1 files changed, 395 insertions, 403 deletions
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,13 +404,353 @@ 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);
/**
+ * 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;
+}
+
+/**
+ * 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.
*/
@@ -417,8 +758,11 @@ void init_i2cs(int port)
{
int error;
- if ((error = i2c_init_peripheral(i2c_bus_ports[port], I2C_STD_MODE)) !=
- EC_SUCCESS) {
+ 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)
;
}
@@ -431,8 +775,8 @@ void init_i2cs(int port)
req_slave.restart = 0;
req_slave.callback = i2c_chip_callback;
- if ((error = i2c_slave_async(i2c_bus_ports[port], &req_slave)) !=
- EC_SUCCESS) {
+ error = i2c_slave_async(i2c_bus_ports[port], &req_slave);
+ if (error != EC_SUCCESS) {
while (1)
;
}
@@ -440,6 +784,41 @@ void init_i2cs(int port)
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 */
/**
@@ -846,390 +1225,3 @@ 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;
-
- /* 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;
- }
- }
-}
-
-/**
- * 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);
-}
-
-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);
- }
-}