summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorRong Chang <rongchang@chromium.org>2015-04-24 06:56:33 +0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-20 09:18:59 +0000
commit6791ac717438ea7d53c640cb3c0f6da6f1114a59 (patch)
tree27d556b49776d6c521a395d19882dd06f4747d07 /chip
parent3402e760a95e4c632d11f25a392bde488e3fa69d (diff)
downloadchrome-ec-6791ac717438ea7d53c640cb3c0f6da6f1114a59.tar.gz
stm32f0: i2c: Add i2c_xfer repeated start support
stm32f051 I2C slave does not clear transmit interrupt status (TXIS) on receiving NACK. That fails to support I2C master repeated-start read. This change moves slave transmit from host command thread's TXIS loop to interrupt event loop. And enables NACK interrupt to handle master restart. On the I2C master side, this CL adds i2c_xfer flags. With this CL, stm32f0 EC can talk to stm32f051 PD through host commands. BRANCH=None BUG=None TEST=make BOARD=<board with stm32f0 EC and PD> Verify EC console command "pdcmd 1 0 0x10 0x20 0x30 0x40" Change-Id: I771b4fb3de3732f18da90ea5e27a79afb09689b0 Signed-off-by: Rong Chang <rongchang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/267041 Reviewed-by: Alec Berg <alecaberg@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Trybot-Ready: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/stm32/i2c-stm32f0.c69
-rw-r--r--chip/stm32/registers.h2
2 files changed, 51 insertions, 20 deletions
diff --git a/chip/stm32/i2c-stm32f0.c b/chip/stm32/i2c-stm32f0.c
index a72233d462..9d43f9cb27 100644
--- a/chip/stm32/i2c-stm32f0.c
+++ b/chip/stm32/i2c-stm32f0.c
@@ -139,13 +139,13 @@ static uint8_t host_buffer[I2C_MAX_HOST_PACKET_SIZE + 2];
static uint8_t params_copy[I2C_MAX_HOST_PACKET_SIZE] __aligned(4);
static int host_i2c_resp_port;
static int tx_pending;
+static int tx_index, tx_end;
static struct host_packet i2c_packet;
static void i2c_send_response_packet(struct host_packet *pkt)
{
int size = pkt->response_size;
uint8_t *out = host_buffer;
- int i = 0;
/* Ignore host command in-progress */
if (pkt->driver_result == EC_RES_IN_PROGRESS)
@@ -155,14 +155,9 @@ static void i2c_send_response_packet(struct host_packet *pkt)
*out++ = pkt->driver_result;
*out++ = size;
- /* Transmit data when I2C tx buffer is empty until finished. */
- while ((i < size + 2) && tx_pending) {
- if (STM32_I2C_ISR(host_i2c_resp_port) & STM32_I2C_ISR_TXIS)
- STM32_I2C_TXDR(host_i2c_resp_port) = host_buffer[i++];
-
- /* I2C is slow, so let other things run while we wait */
- usleep(50);
- }
+ /* host_buffer data range */
+ tx_index = 0;
+ tx_end = size + 2;
/*
* Set the transmitter to be in 'not full' state to keep sending
@@ -267,14 +262,40 @@ static void i2c_event_handler(int port)
if (i2c_isr & STM32_I2C_ISR_RXNE)
host_buffer[buf_idx++] = STM32_I2C_RXDR(port);
+ /* Master requested STOP or RESTART */
+ if (i2c_isr & STM32_I2C_ISR_NACK) {
+ /* Make sure TXIS interrupt is disabled */
+ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE;
+ /* Clear NACK */
+ STM32_I2C_ICR(port) |= STM32_I2C_ICR_NACKCF;
+ /* Resend last byte on RESTART */
+ if (port == I2C_PORT_EC && tx_index)
+ tx_index--;
+ }
+
/* Transmitter empty event */
if (i2c_isr & STM32_I2C_ISR_TXIS) {
if (port == I2C_PORT_EC) { /* host is waiting for PD response */
- if (rx_pending) {
+ if (tx_pending) {
+ if (tx_index < tx_end) {
+ STM32_I2C_TXDR(port) =
+ host_buffer[tx_index++];
+ } else {
+ STM32_I2C_TXDR(port) = 0xec;
+ /*
+ * Set tx_index = 0 to prevent NACK
+ * handler resending last buffer byte.
+ */
+ tx_index = 0;
+ tx_end = 0;
+ /* No pending data */
+ tx_pending = 0;
+ }
+ } else if (rx_pending) {
host_i2c_resp_port = port;
/*
* Disable TXIS interrupt, transmission will
- * be done by host command task.
+ * be prepared by host command task.
*/
STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE;
@@ -300,6 +321,8 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
{
int rv = EC_SUCCESS;
int i;
+ int xfer_start = flags & I2C_XFER_START;
+ int xfer_stop = flags & I2C_XFER_STOP;
#if defined(CONFIG_I2C_SCL_GATE_ADDR) && defined(CONFIG_I2C_SCL_GATE_PORT)
if (port == CONFIG_I2C_SCL_GATE_PORT &&
@@ -311,14 +334,17 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
ASSERT(in || !in_bytes);
/* Clear status */
- STM32_I2C_ICR(port) = 0x3F38;
- STM32_I2C_CR2(port) = 0;
+ if (xfer_start) {
+ STM32_I2C_ICR(port) = STM32_I2C_ICR_ALL;
+ STM32_I2C_CR2(port) = 0;
+ }
if (out_bytes || !in_bytes) {
/* Configure the write transfer */
STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16)
| slave_addr
- | (in_bytes == 0 ? STM32_I2C_CR2_AUTOEND : 0);
+ | ((in_bytes == 0 && xfer_stop) ?
+ STM32_I2C_CR2_AUTOEND : 0);
/* let's go ... */
STM32_I2C_CR2(port) |= STM32_I2C_CR2_START;
@@ -338,9 +364,9 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
}
/* Configure the read transfer and (re)start */
STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16)
- | STM32_I2C_CR2_RD_WRN | slave_addr
- | STM32_I2C_CR2_AUTOEND
- | STM32_I2C_CR2_START;
+ | STM32_I2C_CR2_RD_WRN | slave_addr
+ | (xfer_stop ? STM32_I2C_CR2_AUTOEND : 0)
+ | STM32_I2C_CR2_START;
for (i = 0; i < in_bytes; i++) {
/* Wait for receive buffer not empty */
@@ -351,13 +377,15 @@ int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
in[i] = STM32_I2C_RXDR(port);
}
}
- rv = wait_isr(port, STM32_I2C_ISR_STOP);
+ rv = wait_isr(port, xfer_stop ? STM32_I2C_ISR_STOP : STM32_I2C_ISR_TC);
if (rv)
goto xfer_exit;
xfer_exit:
/* clear status */
- STM32_I2C_ICR(port) = 0x3F38;
+ if (xfer_stop)
+ STM32_I2C_ICR(port) = STM32_I2C_ICR_ALL;
+
/* On error, queue a stop condition */
if (rv) {
/* queue a STOP condition */
@@ -430,7 +458,8 @@ static void i2c_init(void)
#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR
STM32_I2C_CR1(I2C_PORT_EC) |= STM32_I2C_CR1_RXIE | STM32_I2C_CR1_ERRIE
- | STM32_I2C_CR1_ADDRIE | STM32_I2C_CR1_STOPIE;
+ | STM32_I2C_CR1_ADDRIE | STM32_I2C_CR1_STOPIE
+ | STM32_I2C_CR1_NACKIE;
#if defined(CONFIG_LOW_POWER_IDLE) && (I2C_PORT_EC == STM32_I2C1_PORT)
/*
* If using low power idle and EC port is I2C1, then set I2C1 to wake
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 261e9fb26c..52c74ef64f 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -484,11 +484,13 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_I2C_ISR_DIR (1 << 16)
#define STM32_I2C_ICR(n) REG32(stm32_i2c_reg(n, 0x1C))
#define STM32_I2C_ICR_ADDRCF (1 << 3)
+#define STM32_I2C_ICR_NACKCF (1 << 4)
#define STM32_I2C_ICR_STOPCF (1 << 5)
#define STM32_I2C_ICR_BERRCF (1 << 8)
#define STM32_I2C_ICR_ARLOCF (1 << 9)
#define STM32_I2C_ICR_OVRCF (1 << 10)
#define STM32_I2C_ICR_TIMEOUTCF (1 << 12)
+#define STM32_I2C_ICR_ALL 0x3F38
#define STM32_I2C_PECR(n) REG32(stm32_i2c_reg(n, 0x20))
#define STM32_I2C_RXDR(n) REG32(stm32_i2c_reg(n, 0x24))
#define STM32_I2C_TXDR(n) REG32(stm32_i2c_reg(n, 0x28))