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>
This commit is contained in:
Vincent Palatin
2014-03-21 15:49:14 -07:00
committed by chrome-internal-fetch
parent f47b6e84e5
commit 8f911f9285
3 changed files with 289 additions and 3 deletions

View File

@@ -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

259
chip/stm32/i2c-stm32f0.c Normal file
View File

@@ -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);

View File

@@ -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