diff options
Diffstat (limited to 'chip/stm32/i2c-stm32l.c')
-rw-r--r-- | chip/stm32/i2c-stm32l.c | 424 |
1 files changed, 0 insertions, 424 deletions
diff --git a/chip/stm32/i2c-stm32l.c b/chip/stm32/i2c-stm32l.c deleted file mode 100644 index 2edcb1c0b6..0000000000 --- a/chip/stm32/i2c-stm32l.c +++ /dev/null @@ -1,424 +0,0 @@ -/* Copyright 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 "dma.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "i2c.h" -#include "registers.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_I2C, outstr) -#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) - -#define I2C_ERROR_FAILED_START EC_ERROR_INTERNAL_FIRST - -/* - * Transmit timeout in microseconds - * - * In theory we shouldn't have a timeout here (at least when we're in slave - * mode). The slave is supposed to wait forever for the master to read bytes. - * ...but we're going to keep the timeout to make sure we're robust. It may in - * fact be needed if the host resets itself mid-read. - * - * NOTE: One case where this timeout is useful is when the battery - * flips out. The battery may flip out and hold lines low for up to - * 25ms. If we just wait it will eventually let them go. - */ -#define I2C_TX_TIMEOUT_MASTER (30 * MSEC) - -/* - * Delay 5us in bitbang mode. That gives us roughly 5us low and 5us high or - * a frequency of 100kHz. - */ -#define I2C_BITBANG_HALF_CYCLE_US 5 - -#ifdef CONFIG_I2C_DEBUG -static void dump_i2c_reg(int port, const char *what) -{ - CPRINTS("i2c CR1=%04x CR2=%04x SR1=%04x SR2=%04x %s", - STM32_I2C_CR1(port), - STM32_I2C_CR2(port), - STM32_I2C_SR1(port), - STM32_I2C_SR2(port), - what); -} -#else -static inline void dump_i2c_reg(int port, const char *what) -{ -} -#endif - -/** - * Wait for SR1 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_sr1(int port, int mask) -{ - uint64_t timeout = get_time().val + I2C_TX_TIMEOUT_MASTER; - - while (get_time().val < timeout) { - int sr1 = STM32_I2C_SR1(port); - - /* Check for errors */ - if (sr1 & (STM32_I2C_SR1_ARLO | STM32_I2C_SR1_BERR | - STM32_I2C_SR1_AF)) { - dump_i2c_reg(port, "wait_sr1 failed"); - return EC_ERROR_UNKNOWN; - } - - /* Check for desired mask */ - if ((sr1 & mask) == mask) - return EC_SUCCESS; - - /* I2C is slow, so let other things run while we wait */ - usleep(100); - } - - return EC_ERROR_TIMEOUT; -} - -/** - * Send a start condition and slave address on the specified port. - * - * @param port I2C port - * @param slave_addr Slave address, with LSB set for receive-mode - * - * @return Non-zero if error. - */ -static int send_start(int port, uint16_t slave_addr_8bit) -{ - int rv; - - /* Send start bit */ - STM32_I2C_CR1(port) |= STM32_I2C_CR1_START; - dump_i2c_reg(port, "sent start"); - rv = wait_sr1(port, STM32_I2C_SR1_SB); - if (rv) - return I2C_ERROR_FAILED_START; - - /* Write slave address */ - STM32_I2C_DR(port) = slave_addr_8bit & 0xff; - rv = wait_sr1(port, STM32_I2C_SR1_ADDR); - if (rv) - return rv; - - /* Read SR2 to clear ADDR bit */ - rv = STM32_I2C_SR2(port); - - dump_i2c_reg(port, "wrote addr"); - - return EC_SUCCESS; -} - -static void i2c_set_freq_port(const struct i2c_port_t *p) -{ - int port = p->port; - int freq = clock_get_freq(); - - /* Force peripheral reset and disable port */ - STM32_I2C_CR1(port) = STM32_I2C_CR1_SWRST; - STM32_I2C_CR1(port) = 0; - - /* Set clock frequency */ - STM32_I2C_CCR(port) = freq / (2 * MSEC * p->kbps); - STM32_I2C_CR2(port) = freq / SECOND; - STM32_I2C_TRISE(port) = freq / SECOND + 1; - - /* Enable port */ - STM32_I2C_CR1(port) |= STM32_I2C_CR1_PE; -} - -/** - * 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; - - /* Enable clocks to I2C modules if necessary */ - if (!(STM32_RCC_APB1ENR & (1 << (21 + port)))) - STM32_RCC_APB1ENR |= 1 << (21 + port); - - /* Configure GPIOs */ - gpio_config_module(MODULE_I2C, 1); - - /* Set up initial bus frequencies */ - i2c_set_freq_port(p); -} - -/*****************************************************************************/ -/* Interface */ - -int chip_i2c_xfer(const int port, - const uint16_t slave_addr_flags, - const uint8_t *out, int out_bytes, - uint8_t *in, int in_bytes, int flags) -{ - int addr_8bit = I2C_STRIP_FLAGS(slave_addr_flags) << 1; - int started = (flags & I2C_XFER_START) ? 0 : 1; - int rv = EC_SUCCESS; - int i; - - ASSERT(out || !out_bytes); - ASSERT(in || !in_bytes); - - dump_i2c_reg(port, "xfer start"); - - /* - * Clear status - * - * TODO(crosbug.com/p/29314): should check for any leftover error - * status, and reset the port if present. - */ - STM32_I2C_SR1(port) = 0; - - /* Clear start, stop, POS, ACK bits to get us in a known state */ - STM32_I2C_CR1(port) &= ~(STM32_I2C_CR1_START | - STM32_I2C_CR1_STOP | - STM32_I2C_CR1_POS | - STM32_I2C_CR1_ACK); - - /* No out bytes and no in bytes means just check for active */ - if (out_bytes || !in_bytes) { - if (!started) { - rv = send_start(port, addr_8bit); - if (rv) - goto xfer_exit; - } - - /* Write data, if any */ - for (i = 0; i < out_bytes; i++) { - /* Write next data byte */ - STM32_I2C_DR(port) = out[i]; - dump_i2c_reg(port, "wrote data"); - - rv = wait_sr1(port, STM32_I2C_SR1_BTF); - if (rv) - goto xfer_exit; - } - - /* Need repeated start condition before reading */ - started = 0; - - /* If no input bytes, queue stop condition */ - if (!in_bytes && (flags & I2C_XFER_STOP)) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; - } - - if (in_bytes) { - /* Setup ACK/POS before sending start as per user manual */ - if (in_bytes == 2) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_POS; - else if (in_bytes != 1) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_ACK; - - if (!started) { - rv = send_start(port, addr_8bit | 0x01); - if (rv) - goto xfer_exit; - } - - if (in_bytes == 1) { - /* Set stop immediately after ADDR cleared */ - if (flags & I2C_XFER_STOP) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; - - rv = wait_sr1(port, STM32_I2C_SR1_RXNE); - if (rv) - goto xfer_exit; - - in[0] = STM32_I2C_DR(port); - } else if (in_bytes == 2) { - /* Wait till the shift register is full */ - rv = wait_sr1(port, STM32_I2C_SR1_BTF); - if (rv) - goto xfer_exit; - - if (flags & I2C_XFER_STOP) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; - - in[0] = STM32_I2C_DR(port); - in[1] = STM32_I2C_DR(port); - } else { - /* Read all but last three */ - for (i = 0; i < in_bytes - 3; i++) { - /* Wait for receive buffer not empty */ - rv = wait_sr1(port, STM32_I2C_SR1_RXNE); - if (rv) - goto xfer_exit; - - dump_i2c_reg(port, "read data"); - in[i] = STM32_I2C_DR(port); - dump_i2c_reg(port, "post read data"); - } - - /* Wait for BTF (data N-2 in DR, N-1 in shift) */ - rv = wait_sr1(port, STM32_I2C_SR1_BTF); - if (rv) - goto xfer_exit; - - /* No more acking */ - STM32_I2C_CR1(port) &= ~STM32_I2C_CR1_ACK; - in[i++] = STM32_I2C_DR(port); - - /* Wait for BTF (data N-1 in DR, N in shift) */ - rv = wait_sr1(port, STM32_I2C_SR1_BTF); - if (rv) - goto xfer_exit; - - /* If this is the last byte, queue stop condition */ - if (flags & I2C_XFER_STOP) - STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; - - /* Read the last two bytes */ - in[i++] = STM32_I2C_DR(port); - in[i++] = STM32_I2C_DR(port); - } - } - - xfer_exit: - /* On error, queue a stop condition */ - if (rv) { - flags |= I2C_XFER_STOP; - STM32_I2C_CR1(port) |= STM32_I2C_CR1_STOP; - dump_i2c_reg(port, "stop after error"); - - /* - * If failed at sending start, try resetting the port - * to unwedge the bus. - */ - if (rv == I2C_ERROR_FAILED_START) { - const struct i2c_port_t *p = i2c_ports; - CPRINTS("chip_i2c_xfer start error; " - "unwedging and resetting i2c %d", port); - - i2c_unwedge(port); - - for (i = 0; i < i2c_ports_used; i++, p++) { - if (p->port == port) { - i2c_init_port(p); - break; - } - } - } - } - - /* If a stop condition is queued, wait for it to take effect */ - if (flags & I2C_XFER_STOP) { - /* Wait up to 100 us for bus idle */ - for (i = 0; i < 10; i++) { - if (!(STM32_I2C_SR2(port) & STM32_I2C_SR2_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); - } - - 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); -} - -/*****************************************************************************/ -/* Hooks */ - -/* Handle CPU clock changing frequency */ -static void i2c_freq_change(void) -{ - const struct i2c_port_t *p = i2c_ports; - int i; - - for (i = 0; i < i2c_ports_used; i++, p++) - i2c_set_freq_port(p); -} - -static void i2c_pre_freq_change_hook(void) -{ - const struct i2c_port_t *p = i2c_ports; - int i; - - /* Lock I2C ports so freq change can't interrupt an I2C transaction */ - for (i = 0; i < i2c_ports_used; i++, p++) - i2c_lock(p->port, 1); -} -DECLARE_HOOK(HOOK_PRE_FREQ_CHANGE, i2c_pre_freq_change_hook, HOOK_PRIO_DEFAULT); -static void i2c_freq_change_hook(void) -{ - const struct i2c_port_t *p = i2c_ports; - int i; - - i2c_freq_change(); - - /* Unlock I2C ports we locked in pre-freq change hook */ - for (i = 0; i < i2c_ports_used; i++, p++) - i2c_lock(p->port, 0); -} -DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_change_hook, HOOK_PRIO_DEFAULT); - -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); -} - -/*****************************************************************************/ -/* Console commands */ - -static int command_i2cdump(int argc, char **argv) -{ - dump_i2c_reg(I2C_PORT_MASTER, "dump"); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(i2cdump, command_i2cdump, - NULL, - "Dump I2C regs"); |