From 31369a69dbf34cff8d9135f5d503efa717e3da35 Mon Sep 17 00:00:00 2001 From: Alec Berg Date: Wed, 21 May 2014 07:32:59 -0700 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/200786 Reviewed-by: Randall Spangler --- chip/stm32/i2c-stm32f0.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++ chip/stm32/registers.h | 19 +++++ 2 files changed, 213 insertions(+) (limited to 'chip') 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); @@ -99,6 +110,160 @@ static void i2c_init_port(const struct i2c_port_t *p) i2c_set_freq_port(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 */ @@ -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)) -- cgit v1.2.1