| /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "chipset.h" |
| #include "clock.h" |
| #include "common.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "host_command.h" |
| #include "hwtimer.h" |
| #include "i2c.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "usb_pd_tcpc.h" |
| #include "usb_pd_tcpm.h" |
| #include "util.h" |
| |
| /* Console output macros */ |
| #define CPUTS(outstr) cputs(CC_I2C, outstr) |
| #define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) |
| |
| /* Transmit timeout in microseconds */ |
| #define I2C_TX_TIMEOUT_MASTER (10 * MSEC) |
| |
| #ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR |
| #if (I2C_PORT_EC == STM32_I2C1_PORT) |
| #define IRQ_SLAVE STM32_IRQ_I2C1 |
| #else |
| #define IRQ_SLAVE STM32_IRQ_I2C2 |
| #endif |
| #endif |
| |
| |
| /* I2C port state data */ |
| struct i2c_port_data { |
| uint32_t timeout_us; /* Transaction timeout, or 0 to use default */ |
| enum i2c_freq freq; /* Port clock speed */ |
| }; |
| static struct i2c_port_data pdata[I2C_PORT_COUNT]; |
| |
| void i2c_set_timeout(int port, uint32_t timeout) |
| { |
| pdata[port].timeout_us = timeout ? timeout : I2C_TX_TIMEOUT_MASTER; |
| } |
| |
| /* timingr register values for supported input clks / i2c clk rates */ |
| static const uint32_t busyloop_us[I2C_FREQ_COUNT] = { |
| [I2C_FREQ_1000KHZ] = 16, /* Enough for 2 bytes */ |
| [I2C_FREQ_400KHZ] = 40, /* Enough for 2 bytes */ |
| [I2C_FREQ_100KHZ] = 0, /* No busy looping at 100kHz (bus is slow) */ |
| }; |
| |
| /** |
| * Wait for ISR register to contain the specified mask. |
| * |
| * Returns EC_SUCCESS, EC_ERROR_TIMEOUT if timed out waiting, or |
| * EC_ERROR_UNKNOWN if an error bit appeared in the status register. |
| */ |
| static int wait_isr(int port, int mask) |
| { |
| uint32_t start = __hw_clock_source_read(); |
| uint32_t delta = 0; |
| |
| do { |
| int isr = STM32_I2C_ISR(port); |
| |
| /* Check for errors */ |
| if (isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR | |
| STM32_I2C_ISR_NACK)) |
| return EC_ERROR_UNKNOWN; |
| |
| /* Check for desired mask */ |
| if ((isr & mask) == mask) |
| return EC_SUCCESS; |
| |
| delta = __hw_clock_source_read() - start; |
| |
| /** |
| * Depending on the bus speed, busy loop for a while before |
| * sleeping and letting other things run. |
| */ |
| if (delta >= busyloop_us[pdata[port].freq]) |
| usleep(100); |
| } while (delta < pdata[port].timeout_us); |
| |
| return EC_ERROR_TIMEOUT; |
| } |
| |
| /* Supported i2c input clocks */ |
| enum stm32_i2c_clk_src { |
| I2C_CLK_SRC_48MHZ = 0, |
| I2C_CLK_SRC_8MHZ = 1, |
| I2C_CLK_SRC_COUNT, |
| }; |
| |
| /* timingr register values for supported input clks / i2c clk rates */ |
| static const uint32_t timingr_regs[I2C_CLK_SRC_COUNT][I2C_FREQ_COUNT] = { |
| [I2C_CLK_SRC_48MHZ] = { |
| [I2C_FREQ_1000KHZ] = 0x50100103, |
| [I2C_FREQ_400KHZ] = 0x50330309, |
| [I2C_FREQ_100KHZ] = 0xB0421214, |
| }, |
| [I2C_CLK_SRC_8MHZ] = { |
| [I2C_FREQ_1000KHZ] = 0x00100306, |
| [I2C_FREQ_400KHZ] = 0x00310309, |
| [I2C_FREQ_100KHZ] = 0x10420f13, |
| }, |
| }; |
| |
| static void i2c_set_freq_port(const struct i2c_port_t *p, |
| enum stm32_i2c_clk_src src, |
| enum i2c_freq freq) |
| { |
| int port = p->port; |
| const uint32_t *regs = timingr_regs[src]; |
| |
| /* Disable port */ |
| STM32_I2C_CR1(port) = 0; |
| STM32_I2C_CR2(port) = 0; |
| /* Set clock frequency */ |
| STM32_I2C_TIMINGR(port) = regs[freq]; |
| /* Enable port */ |
| STM32_I2C_CR1(port) = STM32_I2C_CR1_PE; |
| |
| pdata[port].freq = freq; |
| } |
| |
| /** |
| * Initialize on the specified I2C port. |
| * |
| * @param p the I2c port |
| */ |
| static void i2c_init_port(const struct i2c_port_t *p) |
| { |
| int port = p->port; |
| enum stm32_i2c_clk_src src = I2C_CLK_SRC_48MHZ; |
| enum i2c_freq freq; |
| |
| /* Enable clocks to I2C modules if necessary */ |
| if (!(STM32_RCC_APB1ENR & (1 << (21 + port)))) |
| STM32_RCC_APB1ENR |= 1 << (21 + port); |
| |
| if (port == STM32_I2C1_PORT) { |
| #if defined(CONFIG_HOSTCMD_I2C_SLAVE_ADDR) && \ |
| defined(CONFIG_LOW_POWER_IDLE) && \ |
| (I2C_PORT_EC == STM32_I2C1_PORT) |
| /* |
| * Use HSI (8MHz) for i2c clock. This allows smooth wakeup |
| * from STOP mode since HSI is only clock running immediately |
| * upon exit from STOP mode. |
| */ |
| STM32_RCC_CFGR3 &= ~0x10; |
| src = I2C_CLK_SRC_8MHZ; |
| #else |
| /* Use SYSCLK for i2c clock. */ |
| STM32_RCC_CFGR3 |= 0x10; |
| #endif |
| } |
| |
| /* Configure GPIOs */ |
| gpio_config_module(MODULE_I2C, 1); |
| |
| /* Set clock frequency */ |
| switch (p->kbps) { |
| case 1000: |
| freq = I2C_FREQ_1000KHZ; |
| break; |
| case 400: |
| freq = I2C_FREQ_400KHZ; |
| break; |
| case 100: |
| freq = I2C_FREQ_100KHZ; |
| break; |
| default: /* unknown speed, defaults to 100kBps */ |
| CPRINTS("I2C bad speed %d kBps", p->kbps); |
| freq = I2C_FREQ_100KHZ; |
| } |
| |
| /* Set up initial bus frequencies */ |
| i2c_set_freq_port(p, src, freq); |
| |
| /* Set up default timeout */ |
| i2c_set_timeout(port, 0); |
| } |
| |
| /*****************************************************************************/ |
| #ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR |
| /* Host command slave */ |
| /* |
| * Buffer for received host command packets (including prefix byte on request, |
| * and result/size on response). After any protocol-specific headers, the |
| * buffers must be 32-bit aligned. |
| */ |
| static uint8_t host_buffer_padded[I2C_MAX_HOST_PACKET_SIZE + 4 + |
| CONFIG_I2C_EXTRA_PACKET_SIZE] __aligned(4); |
| static uint8_t * const host_buffer = host_buffer_padded + 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; |
| |
| /* 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; |
| |
| /* host_buffer data range */ |
| tx_index = 0; |
| tx_end = size + 2; |
| |
| /* |
| * Set the transmitter to be in 'not full' state to keep sending |
| * '0xec' in the event loop. Because of this, the master i2c |
| * doesn't need to snoop the response stream to abort transaction. |
| */ |
| STM32_I2C_CR1(host_i2c_resp_port) |= STM32_I2C_CR1_TXIE; |
| } |
| |
| /* 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. |
| */ |
| 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. Note |
| * that this 2-byte offset and the 2-byte offset from host_buffer |
| * add up to make the response buffer 32-bit aligned. |
| */ |
| i2c_packet.response = (void *)(&buff[2]); |
| i2c_packet.response_max = I2C_MAX_HOST_PACKET_SIZE; |
| i2c_packet.response_size = 0; |
| |
| if (*buff >= EC_COMMAND_PROTOCOL_3) { |
| 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); |
| } |
| |
| #ifdef TCPCI_I2C_SLAVE |
| static void i2c_send_tcpc_response(int len) |
| { |
| /* host_buffer data range, beyond this length, will return 0xec */ |
| tx_index = 0; |
| tx_end = len; |
| |
| /* enable transmit interrupt and use irq to send data back */ |
| STM32_I2C_CR1(host_i2c_resp_port) |= STM32_I2C_CR1_TXIE; |
| } |
| |
| static void i2c_process_tcpc_command(int read, int addr, int len) |
| { |
| tcpc_i2c_process(read, TCPC_ADDR_TO_PORT(addr), len, &host_buffer[0], |
| i2c_send_tcpc_response); |
| } |
| #endif |
| |
| static void i2c_event_handler(int port) |
| { |
| int i2c_isr; |
| static int rx_pending, buf_idx; |
| #ifdef TCPCI_I2C_SLAVE |
| int addr; |
| #endif |
| |
| 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; |
| /* Inhibit stop mode when addressed until STOPF flag is set */ |
| disable_sleep(SLEEP_MASK_I2C_SLAVE); |
| } |
| |
| /* Stop condition on bus */ |
| if (i2c_isr & STM32_I2C_ISR_STOP) { |
| #ifdef TCPCI_I2C_SLAVE |
| /* |
| * if tcpc is being addressed, and we received a stop |
| * while rx is pending, then this is a write only to |
| * the tcpc. |
| */ |
| addr = STM32_I2C_ISR_ADDCODE(STM32_I2C_ISR(port)); |
| if (rx_pending && ADDR_IS_TCPC(addr)) |
| i2c_process_tcpc_command(0, addr, buf_idx); |
| #endif |
| 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; |
| |
| /* No longer inhibit deep sleep after stop condition */ |
| enable_sleep(SLEEP_MASK_I2C_SLAVE); |
| } |
| |
| /* Receiver full event */ |
| 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 (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 prepared by host command task. |
| */ |
| STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_TXIE; |
| |
| #ifdef TCPCI_I2C_SLAVE |
| addr = STM32_I2C_ISR_ADDCODE( |
| STM32_I2C_ISR(port)); |
| if (ADDR_IS_TCPC(addr)) |
| i2c_process_tcpc_command(1, addr, |
| buf_idx); |
| else |
| #endif |
| 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(IRQ_SLAVE, i2c2_event_interrupt, 2); |
| #endif |
| |
| /*****************************************************************************/ |
| /* Interface */ |
| |
| int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_bytes, |
| uint8_t *in, int in_bytes, int flags) |
| { |
| 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 && |
| slave_addr == CONFIG_I2C_SCL_GATE_ADDR) |
| gpio_set_level(CONFIG_I2C_SCL_GATE_GPIO, 1); |
| #endif |
| |
| ASSERT(out || !out_bytes); |
| ASSERT(in || !in_bytes); |
| |
| /* Clear status */ |
| 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: if we are stopping then set |
| * AUTOEND bit to automatically set STOP bit after NBYTES. |
| * if we are not stopping, set RELOAD bit so that we can load |
| * NBYTES again. if we are starting, then set START bit. |
| */ |
| STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16) |
| | slave_addr |
| | ((in_bytes == 0 && xfer_stop) ? |
| STM32_I2C_CR2_AUTOEND : 0) |
| | ((in_bytes == 0 && !xfer_stop) ? |
| STM32_I2C_CR2_RELOAD : 0) |
| | (xfer_start ? STM32_I2C_CR2_START : 0); |
| |
| for (i = 0; i < out_bytes; i++) { |
| rv = wait_isr(port, STM32_I2C_ISR_TXIS); |
| if (rv) |
| goto xfer_exit; |
| /* Write next data byte */ |
| STM32_I2C_TXDR(port) = out[i]; |
| } |
| } |
| if (in_bytes) { |
| if (out_bytes) { /* wait for completion of the write */ |
| rv = wait_isr(port, STM32_I2C_ISR_TC); |
| if (rv) |
| goto xfer_exit; |
| } |
| /* |
| * Configure the read transfer: if we are stopping then set |
| * AUTOEND bit to automatically set STOP bit after NBYTES. |
| * if we are not stopping, set RELOAD bit so that we can load |
| * NBYTES again. if we were just transmitting, we need to |
| * set START bit to send (re)start and begin read transaction. |
| */ |
| STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16) |
| | STM32_I2C_CR2_RD_WRN | slave_addr |
| | (xfer_stop ? STM32_I2C_CR2_AUTOEND : 0) |
| | (!xfer_stop ? STM32_I2C_CR2_RELOAD : 0) |
| | (out_bytes || xfer_start ? STM32_I2C_CR2_START : 0); |
| |
| for (i = 0; i < in_bytes; i++) { |
| /* Wait for receive buffer not empty */ |
| rv = wait_isr(port, STM32_I2C_ISR_RXNE); |
| if (rv) |
| goto xfer_exit; |
| |
| in[i] = STM32_I2C_RXDR(port); |
| } |
| } |
| |
| /* |
| * If we are stopping, then we already set AUTOEND and we should |
| * wait for the stop bit to be transmitted. Otherwise, we set |
| * the RELOAD bit and we should wait for transfer complete |
| * reload (TCR). |
| */ |
| rv = wait_isr(port, xfer_stop ? STM32_I2C_ISR_STOP : STM32_I2C_ISR_TCR); |
| if (rv) |
| goto xfer_exit; |
| |
| xfer_exit: |
| /* clear status */ |
| if (xfer_stop) |
| STM32_I2C_ICR(port) = STM32_I2C_ICR_ALL; |
| |
| /* On error, queue a stop condition */ |
| if (rv) { |
| /* queue a STOP condition */ |
| STM32_I2C_CR2(port) |= STM32_I2C_CR2_STOP; |
| /* wait for it to take effect */ |
| /* Wait up to 100 us for bus idle */ |
| for (i = 0; i < 10; i++) { |
| if (!(STM32_I2C_ISR(port) & STM32_I2C_ISR_BUSY)) |
| break; |
| udelay(10); |
| } |
| |
| /* |
| * Allow bus to idle for at least one 100KHz clock = 10 us. |
| * This allows slaves on the bus to detect bus-idle before |
| * the next start condition. |
| */ |
| udelay(10); |
| /* re-initialize the controller */ |
| STM32_I2C_CR2(port) = 0; |
| STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_PE; |
| udelay(10); |
| STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE; |
| } |
| |
| #ifdef CONFIG_I2C_SCL_GATE_ADDR |
| if (port == CONFIG_I2C_SCL_GATE_PORT && |
| slave_addr == CONFIG_I2C_SCL_GATE_ADDR) |
| gpio_set_level(CONFIG_I2C_SCL_GATE_GPIO, 0); |
| #endif |
| |
| return rv; |
| } |
| |
| int i2c_raw_get_scl(int port) |
| { |
| enum gpio_signal g; |
| |
| if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS) |
| return gpio_get_level(g); |
| |
| /* If no SCL pin defined for this port, then return 1 to appear idle. */ |
| return 1; |
| } |
| |
| int i2c_raw_get_sda(int port) |
| { |
| enum gpio_signal g; |
| |
| if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS) |
| return gpio_get_level(g); |
| |
| /* If no SCL pin defined for this port, then return 1 to appear idle. */ |
| return 1; |
| } |
| |
| int i2c_get_line_levels(int port) |
| { |
| return (i2c_raw_get_sda(port) ? I2C_LINE_SDA_HIGH : 0) | |
| (i2c_raw_get_scl(port) ? I2C_LINE_SCL_HIGH : 0); |
| } |
| |
| static void i2c_init(void) |
| { |
| const struct i2c_port_t *p = i2c_ports; |
| int i; |
| |
| for (i = 0; i < i2c_ports_used; i++, p++) |
| i2c_init_port(p); |
| |
| #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_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 |
| * from STOP mode on address match. Note, this only works on I2C1 and |
| * only if the clock to I2C1 is HSI 8MHz. |
| */ |
| STM32_I2C_CR1(I2C_PORT_EC) |= STM32_I2C_CR1_WUPEN; |
| #endif |
| STM32_I2C_OAR1(I2C_PORT_EC) = 0x8000 | CONFIG_HOSTCMD_I2C_SLAVE_ADDR; |
| #ifdef TCPCI_I2C_SLAVE |
| /* |
| * Configure TCPC address with OA2[1] masked so that we respond |
| * to CONFIG_TCPC_I2C_BASE_ADDR and CONFIG_TCPC_I2C_BASE_ADDR + 2. |
| */ |
| STM32_I2C_OAR2(I2C_PORT_EC) = 0x8100 | CONFIG_TCPC_I2C_BASE_ADDR; |
| #endif |
| task_enable_irq(IRQ_SLAVE); |
| #endif |
| } |
| DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C); |
| |