summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2014-05-21 07:32:59 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-06-05 04:22:56 +0000
commit31369a69dbf34cff8d9135f5d503efa717e3da35 (patch)
treea77f2778fb71da215b5d8748a551d49e21e51e89 /chip
parent565f1cb5ae1ebd287e1bf00ab8245274a5ecbaa6 (diff)
downloadchrome-ec-31369a69dbf34cff8d9135f5d503efa717e3da35.tar.gz
samus: Add EC <-> PD i2c interface using host commands
Initial support for EC to PD communication using host command interface over i2c. BUG=chrome-os-partner:28351, chrome-os-partner:28352 BRANCH=none TEST=on EC console send hello host command: > pdcmd 0x01 0 0xa0 0xb0 0xc0 0xd0 Host command 0x01, returned 4 a4 b3 c2 d1 Change-Id: I0969808f455574ee456d6db8a60ce9b1204a0739 Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/200786 Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/stm32/i2c-stm32f0.c194
-rw-r--r--chip/stm32/registers.h19
2 files changed, 213 insertions, 0 deletions
diff --git a/chip/stm32/i2c-stm32f0.c b/chip/stm32/i2c-stm32f0.c
index cd93965503..ab41b65b71 100644
--- a/chip/stm32/i2c-stm32f0.c
+++ b/chip/stm32/i2c-stm32f0.c
@@ -9,6 +9,7 @@
#include "console.h"
#include "gpio.h"
#include "hooks.h"
+#include "host_command.h"
#include "i2c.h"
#include "registers.h"
#include "task.h"
@@ -25,6 +26,12 @@
/* Transmit timeout in microseconds */
#define I2C_TX_TIMEOUT_MASTER (10 * MSEC)
+/*
+ * Max data size for a version 3 request/response packet. This is
+ * big enough for EC_CMD_GET_VERSION plus header info.
+ */
+#define I2C_MAX_HOST_PACKET_SIZE 128
+
/**
* Wait for ISR register to contain the specified mask.
*
@@ -92,6 +99,10 @@ static void i2c_init_port(const struct i2c_port_t *p)
if (!(STM32_RCC_APB1ENR & (1 << (21 + port))))
STM32_RCC_APB1ENR |= 1 << (21 + port);
+ /* Default clock to i2c port 0 is HSI (8MHz). Change to SYSCLK. */
+ if (port == 0)
+ STM32_RCC_CFGR3 |= 0x10;
+
/* Configure GPIOs */
gpio_config_module(MODULE_I2C, 1);
@@ -100,6 +111,160 @@ static void i2c_init_port(const struct i2c_port_t *p)
}
/*****************************************************************************/
+#ifdef HAS_TASK_HOSTCMD
+/* Host command slave */
+/* Buffer for host commands (including version, error code and checksum) */
+static uint8_t host_buffer[I2C_MAX_HOST_PACKET_SIZE];
+static uint8_t params_copy[I2C_MAX_HOST_PACKET_SIZE] __aligned(4);
+static int host_i2c_resp_port;
+static int tx_pending;
+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)
+ return;
+
+ /* Write result and size to first two bytes. */
+ *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_CR1_TXIE)
+ STM32_I2C_TXDR(host_i2c_resp_port) = host_buffer[i++];
+
+ /* I2C is slow, so let other things run while we wait */
+ usleep(50);
+ }
+}
+
+/* Process the command in the i2c host buffer */
+static void i2c_process_command(void)
+{
+ char *buff = host_buffer;
+
+ /*
+ * TODO(crosbug.com/p/29241): Combine this functionality with the
+ * i2c_process_command function in chip/stm32/i2c-stm32f.c to make one
+ * host command i2c process function which handles all protocol
+ * versions.
+ */
+ if (*buff >= EC_COMMAND_PROTOCOL_3) {
+ i2c_packet.send_response = i2c_send_response_packet;
+
+ i2c_packet.request = (const void *)(&buff[1]);
+ i2c_packet.request_temp = params_copy;
+ i2c_packet.request_max = sizeof(params_copy);
+ /* Don't know the request size so pass in the entire buffer */
+ i2c_packet.request_size = I2C_MAX_HOST_PACKET_SIZE;
+
+ /*
+ * Stuff response at buff[2] to leave the first two bytes of
+ * buffer available for the result and size to send over i2c.
+ */
+ i2c_packet.response = (void *)(&buff[2]);
+ i2c_packet.response_max = I2C_MAX_HOST_PACKET_SIZE;
+ i2c_packet.response_size = 0;
+
+ i2c_packet.driver_result = EC_RES_SUCCESS;
+ } else {
+ /* Only host command protocol 3 is supported. */
+ i2c_packet.driver_result = EC_RES_INVALID_HEADER;
+ }
+ host_packet_receive(&i2c_packet);
+}
+
+static void i2c_event_handler(int port)
+{
+ int i2c_isr;
+ static int rx_pending, buf_idx;
+
+ i2c_isr = STM32_I2C_ISR(port);
+
+ /*
+ * Check for error conditions. Note, arbitration loss and bus error
+ * are the only two errors we can get as a slave allowing clock
+ * stretching and in non-SMBus mode.
+ */
+ if (i2c_isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR)) {
+ rx_pending = 0;
+ tx_pending = 0;
+
+ /* Make sure TXIS interrupt is disabled */
+ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE;
+
+ /* Clear error status bits */
+ STM32_I2C_ICR(port) |= STM32_I2C_ICR_BERRCF |
+ STM32_I2C_ICR_ARLOCF;
+ }
+
+ /* Transfer matched our slave address */
+ if (i2c_isr & STM32_I2C_ISR_ADDR) {
+ if (i2c_isr & STM32_I2C_ISR_DIR) {
+ /* Transmitter slave */
+ /* Clear transmit buffer */
+ STM32_I2C_ISR(port) |= STM32_I2C_ISR_TXE;
+
+ /* Enable txis interrupt to start response */
+ STM32_I2C_CR1(port) |= STM32_I2C_CR1_TXIE;
+ } else {
+ /* Receiver slave */
+ buf_idx = 0;
+ rx_pending = 1;
+ }
+
+ /* Clear ADDR bit by writing to ADDRCF bit */
+ STM32_I2C_ICR(port) |= STM32_I2C_ICR_ADDRCF;
+ }
+
+ /* Stop condition on bus */
+ if (i2c_isr & STM32_I2C_ISR_STOP) {
+ rx_pending = 0;
+ tx_pending = 0;
+
+ /* Make sure TXIS interrupt is disabled */
+ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE;
+
+ /* Clear STOPF bit by writing to STOPCF bit */
+ STM32_I2C_ICR(port) |= STM32_I2C_ICR_STOPCF;
+ }
+
+ /* Receiver full event */
+ if (i2c_isr & STM32_I2C_ISR_RXNE)
+ host_buffer[buf_idx++] = STM32_I2C_RXDR(port);
+
+ /* Transmitter empty event */
+ if (i2c_isr & STM32_I2C_ISR_TXIS) {
+ if (port == I2C_PORT_EC) { /* host is waiting for PD response */
+ if (rx_pending) {
+ host_i2c_resp_port = port;
+ /*
+ * Disable TXIS interrupt, transmission will
+ * be done by host command task.
+ */
+ STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE;
+
+ i2c_process_command();
+ /* Reset host buffer after end of transfer */
+ rx_pending = 0;
+ tx_pending = 1;
+ } else {
+ STM32_I2C_TXDR(port) = 0xec;
+ }
+ }
+ }
+}
+void i2c2_event_interrupt(void) { i2c_event_handler(I2C_PORT_EC); }
+DECLARE_IRQ(STM32_IRQ_I2C1, i2c2_event_interrupt, 2);
+#endif
+
+/*****************************************************************************/
/* Interface */
int i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes,
@@ -255,5 +420,34 @@ static void i2c_init(void)
for (i = 0; i < i2c_ports_used; i++, p++)
i2c_init_port(p);
+
+#ifdef HAS_TASK_HOSTCMD
+ STM32_I2C_CR1(I2C_PORT_EC) |= STM32_I2C_CR1_RXIE | STM32_I2C_CR1_ERRIE
+ | STM32_I2C_CR1_ADDRIE | STM32_I2C_CR1_STOPIE;
+ STM32_I2C_OAR1(I2C_PORT_EC) = 0x8000 | CONFIG_USB_PD_I2C_SLAVE_ADDR;
+ task_enable_irq(STM32_IRQ_I2C1);
+#endif
}
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT);
+
+/**
+ * Get protocol information
+ */
+static int i2c_get_protocol_info(struct host_cmd_handler_args *args)
+{
+ struct ec_response_get_protocol_info *r = args->response;
+
+ memset(r, 0, sizeof(*r));
+ r->protocol_versions = (1 << 3);
+ r->max_request_packet_size = I2C_MAX_HOST_PACKET_SIZE;
+ r->max_response_packet_size = I2C_MAX_HOST_PACKET_SIZE;
+ r->flags = 0;
+
+ args->response_size = sizeof(*r);
+
+ return EC_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO,
+ i2c_get_protocol_info,
+ EC_VER_MASK(0));
+
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index c3e0f19e49..4e9dbd1419 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -407,6 +407,12 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#ifdef CHIP_FAMILY_STM32F0
#define STM32_I2C_CR1(n) REG32(stm32_i2c_reg(n, 0x00))
#define STM32_I2C_CR1_PE (1 << 0)
+#define STM32_I2C_CR1_TXIE (1 << 1)
+#define STM32_I2C_CR1_RXIE (1 << 2)
+#define STM32_I2C_CR1_ADDRIE (1 << 3)
+#define STM32_I2C_CR1_NACKIE (1 << 4)
+#define STM32_I2C_CR1_STOPIE (1 << 5)
+#define STM32_I2C_CR1_ERRIE (1 << 7)
#define STM32_I2C_CR2(n) REG32(stm32_i2c_reg(n, 0x04))
#define STM32_I2C_CR2_RD_WRN (1 << 10)
#define STM32_I2C_CR2_START (1 << 13)
@@ -419,15 +425,28 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_I2C_TIMINGR(n) REG32(stm32_i2c_reg(n, 0x10))
#define STM32_I2C_TIMEOUTR(n) REG32(stm32_i2c_reg(n, 0x14))
#define STM32_I2C_ISR(n) REG32(stm32_i2c_reg(n, 0x18))
+#define STM32_I2C_ISR_TXE (1 << 0)
#define STM32_I2C_ISR_TXIS (1 << 1)
#define STM32_I2C_ISR_RXNE (1 << 2)
+#define STM32_I2C_ISR_ADDR (1 << 3)
#define STM32_I2C_ISR_NACK (1 << 4)
#define STM32_I2C_ISR_STOP (1 << 5)
#define STM32_I2C_ISR_TC (1 << 6)
#define STM32_I2C_ISR_BERR (1 << 8)
#define STM32_I2C_ISR_ARLO (1 << 9)
+#define STM32_I2C_ISR_OVR (1 << 10)
+#define STM32_I2C_ISR_PECERR (1 << 11)
+#define STM32_I2C_ISR_TIMEOUT (1 << 12)
+#define STM32_I2C_ISR_ALERT (1 << 13)
#define STM32_I2C_ISR_BUSY (1 << 15)
+#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_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_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))