summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-03-21 15:49:14 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-03-27 18:45:21 +0000
commit8f911f9285db6596e30d516a153aa0b24614eb5d (patch)
treec00dd7310321e77047ee598bfd3ff0d6441b0f28
parentf47b6e84e5ca3d5a7a5351e950b865e2ca6d7c32 (diff)
downloadchrome-ec-8f911f9285db6596e30d516a153aa0b24614eb5d.tar.gz
stm32: new I2C driver for STM32F0xx family
The STM32F0xx family has a new I2C peripheral, let's add a new driver for it. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=none TEST=On FruitPie, read TSU6721 registers and read the smart battery information. Change-Id: Ic49832205957559b4b6b647948b03f650a27ab04 Reviewed-on: https://chromium-review.googlesource.com/191210 Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--chip/stm32/build.mk4
-rw-r--r--chip/stm32/i2c-stm32f0.c259
-rw-r--r--chip/stm32/registers.h29
3 files changed, 289 insertions, 3 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index a4ce0f4bda..d878d8d0ad 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -20,8 +20,6 @@ endif
# STM32F0xx and STM32F1xx are using the same flash controller
FLASH_FAMILY=$(subst stm32f0,stm32f,$(CHIP_FAMILY))
-# STM32F0xx chips will re-use STM32L I2C code
-I2C_FAMILY=$(subst stm32f0,stm32l,$(CHIP_FAMILY))
# Select between 16-bit and 32-bit timer for clock source
TIMER_TYPE=$(if $(CONFIG_STM_HWTIMER32),32,)
@@ -30,7 +28,7 @@ chip-y+=jtag-$(CHIP_FAMILY).o clock-$(CHIP_FAMILY).o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_COMMON_GPIO)+=gpio-$(CHIP_FAMILY).o
chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o
-chip-$(CONFIG_I2C)+=i2c-$(I2C_FAMILY).o
+chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_CONSOLE)+=uart.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
diff --git a/chip/stm32/i2c-stm32f0.c b/chip/stm32/i2c-stm32f0.c
new file mode 100644
index 0000000000..1cc0c04412
--- /dev/null
+++ b/chip/stm32/i2c-stm32f0.c
@@ -0,0 +1,259 @@
+/* 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 "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 CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
+
+/* Maximum transfer of a SMBUS block transfer */
+#define SMBUS_MAX_BLOCK 32
+
+/* Transmit timeout in microseconds */
+#define I2C_TX_TIMEOUT_MASTER (10 * MSEC)
+
+/**
+ * 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)
+{
+ uint64_t timeout = get_time().val + I2C_TX_TIMEOUT_MASTER;
+
+ while (get_time().val < timeout) {
+ int isr = STM32_I2C_ISR(port);
+
+ /* Check for desired mask */
+ if ((isr & mask) == mask)
+ return EC_SUCCESS;
+
+ /* Check for errors */
+ if (isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR))
+ return EC_ERROR_UNKNOWN;
+
+ /* I2C is slow, so let other things run while we wait */
+ usleep(100);
+ }
+
+ return EC_ERROR_TIMEOUT;
+}
+
+static void i2c_set_freq_port(const struct i2c_port_t *p)
+{
+ int port = p->port;
+
+ /* Disable port */
+ STM32_I2C_CR1(port) = 0;
+ STM32_I2C_CR2(port) = 0;
+ /* Set clock frequency */
+ switch (p->kbps) {
+ case 1000:
+ STM32_I2C_TIMINGR(port) = 0x50110103;
+ break;
+ case 400:
+ STM32_I2C_TIMINGR(port) = 0x50330309;
+ break;
+ case 100:
+ STM32_I2C_TIMINGR(port) = 0xB0420F13;
+ break;
+ default: /* unknown speed, defaults to 100kBps */
+ CPRINTF("Unsupported speed %d kBps\n", p->kbps);
+ STM32_I2C_TIMINGR(port) = 0xB0420F13;
+ }
+ /* 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 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;
+
+ ASSERT(out || !out_bytes);
+ ASSERT(in || !in_bytes);
+
+ /* Clear status */
+ STM32_I2C_ICR(port) = 0x3F38;
+ STM32_I2C_CR2(port) = 0;
+
+ if (out_bytes || !in_bytes) {
+ /* Configure the write transfer */
+ STM32_I2C_CR2(port) = ((out_bytes & 0xFF) << 16)
+ | slave_addr
+ | (in_bytes == 0 ? STM32_I2C_CR2_AUTOEND : 0);
+ /* let's go ... */
+ STM32_I2C_CR2(port) |= STM32_I2C_CR2_START;
+
+ 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 */
+ STM32_I2C_CR2(port) = ((in_bytes & 0xFF) << 16)
+ | STM32_I2C_CR2_RD_WRN | slave_addr
+ | STM32_I2C_CR2_AUTOEND;
+ /* START or repeated start */
+ STM32_I2C_CR2(port) |= STM32_I2C_CR2_START;
+
+ 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);
+ }
+ }
+ rv = wait_isr(port, STM32_I2C_ISR_STOP);
+ if (rv)
+ goto xfer_exit;
+
+xfer_exit:
+ /* clear status */
+ STM32_I2C_ICR(port) = 0x3F38;
+ /* 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;
+ }
+
+ 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);
+}
+
+int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
+ int len)
+{
+ int rv;
+ uint8_t reg, block_length;
+
+ /*
+ * TODO(crosbug.com/p/23569): when i2c_xfer() supports start/stop bits,
+ * merge this with the LM4 implementation and move to i2c_common.c.
+ */
+
+ if ((len <= 0) || (len > SMBUS_MAX_BLOCK))
+ return EC_ERROR_INVAL;
+
+ i2c_lock(port, 1);
+
+ /* Read the counted string into the output buffer */
+ reg = offset;
+ rv = i2c_xfer(port, slave_addr, &reg, 1, data, len, I2C_XFER_SINGLE);
+ if (rv == EC_SUCCESS) {
+ /* Block length is the first byte of the returned buffer */
+ block_length = MIN(data[0], len - 1);
+
+ /* Move data down, then null-terminate it */
+ memmove(data, data + 1, block_length);
+ data[block_length] = 0;
+ }
+
+ i2c_lock(port, 0);
+ return rv;
+}
+
+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);
+}
+DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT);
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 9a26a3020e..4507d68d71 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -390,6 +390,34 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define stm32_i2c_reg(port, offset) \
((uint16_t *)((STM32_I2C1_BASE + ((port) * 0x400)) + (offset)))
+#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_CR2(n) REG32(stm32_i2c_reg(n, 0x04))
+#define STM32_I2C_CR2_RD_WRN (1 << 10)
+#define STM32_I2C_CR2_START (1 << 13)
+#define STM32_I2C_CR2_STOP (1 << 14)
+#define STM32_I2C_CR2_NACK (1 << 15)
+#define STM32_I2C_CR2_RELOAD (1 << 24)
+#define STM32_I2C_CR2_AUTOEND (1 << 25)
+#define STM32_I2C_OAR1(n) REG32(stm32_i2c_reg(n, 0x08))
+#define STM32_I2C_OAR2(n) REG32(stm32_i2c_reg(n, 0x0C))
+#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_TXIS (1 << 1)
+#define STM32_I2C_ISR_RXNE (1 << 2)
+#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_BUSY (1 << 15)
+#define STM32_I2C_ICR(n) REG32(stm32_i2c_reg(n, 0x1C))
+#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))
+#else /* !CHIP_FAMILY_STM32F0 */
#define STM32_I2C_CR1(n) REG16(stm32_i2c_reg(n, 0x00))
#define STM32_I2C_CR1_PE (1 << 0)
#define STM32_I2C_CR1_START (1 << 8)
@@ -416,6 +444,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_I2C_CCR(n) REG16(stm32_i2c_reg(n, 0x1C))
#define STM32_I2C_TRISE(n) REG16(stm32_i2c_reg(n, 0x20))
+#endif /* !CHIP_FAMILY_STM32F0 */
/* --- Power / Reset / Clocks --- */
#define STM32_PWR_BASE 0x40007000