/* 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");