diff options
author | Rong Chang <rongchang@chromium.org> | 2012-05-25 21:07:12 +0800 |
---|---|---|
committer | Rong Chang <rongchang@chromium.org> | 2012-05-31 18:00:17 +0800 |
commit | ee495ac6a699df75051b8aa2d7ab473a2ad79c87 (patch) | |
tree | 2b0f4c1b346dca6289ce2279b8e92fba6b2a46f9 | |
parent | 1545262c9be106e4f107f623de17fdea568d8f22 (diff) | |
download | chrome-ec-ee495ac6a699df75051b8aa2d7ab473a2ad79c87.tar.gz |
Add stm32 I2C master driver
A polling mode I2C master driver. Interfaces for read/write byte
and word are implemented. i2c_read_string() is currently an empty
function.
CONFIG_SMART_BATTERY added to daisy board for testing.
Move smart_battery.o back to CONFIG_SMART_BATTERY since it is not
depended on charging state machine.
Signed-off-by: Rong Chang <rongchang@chromium.org>
BUG=chrome-os-partner:9724
TEST=manual/host commands
> battery
Temp: 0x0bad = 298.9 K (25.8 C)
Manuf:
Device:
Chem:
Serial: 0x0001
V: 0x1cb7 = 7351 mV
V-desired: 0x20d0 = 8400 mV
V-design: 0x1c20 = 7200 mV
I: 0x0000 = 0 mA
I-desired: 0x0bb8 = 3000 mA
Mode: 0x6001
Charge: 49 %
Abs: 47 %
Remaining: 2705 mAh
Cap-full: 5575 mAh
Design: 5800 mAh
Time-full: 0h:0
Empty: 0h:0
Change-Id: I9f4e9e8819955ad1b107fb3b70ac2559d9b02b55
-rw-r--r-- | board/daisy/board.h | 5 | ||||
-rw-r--r-- | board/link/board.h | 1 | ||||
-rw-r--r-- | chip/stm32/i2c.c | 467 | ||||
-rw-r--r-- | common/build.mk | 4 |
4 files changed, 475 insertions, 2 deletions
diff --git a/board/daisy/board.h b/board/daisy/board.h index f93355ce21..21a3f206de 100644 --- a/board/daisy/board.h +++ b/board/daisy/board.h @@ -32,6 +32,11 @@ /* EC drives 13 outputs to keyboard matrix */ #define KB_OUTPUTS 13 +/* Charging */ +#define CONFIG_SMART_BATTERY +#define I2C_PORT_BATTERY 1 +#define I2C_PORT_CHARGER 1 + /* GPIO signal list */ enum gpio_signal { /* Inputs with interrupt handlers are first for efficiency */ diff --git a/board/link/board.h b/board/link/board.h index ac8ead8ed6..2e985f95d9 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -9,6 +9,7 @@ #define __BOARD_H /* Optional features */ +#define CONFIG_SMART_BATTERY #define CONFIG_BATTERY_ATL706486 #define CONFIG_CHARGER #define CONFIG_CHARGER_BQ24725 diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c index d4df0648ee..6bd69bd1bb 100644 --- a/chip/stm32/i2c.c +++ b/chip/stm32/i2c.c @@ -38,6 +38,7 @@ static uint16_t i2c_sr1[NUM_PORTS]; +static struct mutex i2c_mutex; /* buffer for host commands (including error code and checksum) */ static uint8_t host_buffer[EC_PARAM_SIZE + 2]; @@ -219,6 +220,35 @@ static int i2c_init2(void) return EC_SUCCESS; } +static int i2c_init1(void) +{ + /* enable clock */ + STM32_RCC_APB1ENR |= 1 << 21; + + /* force reset if the bus is stuck in BUSY state */ + if (STM32_I2C_SR2(I2C1) & 0x2) { + STM32_I2C_CR1(I2C1) = 0x8000; + STM32_I2C_CR1(I2C1) = 0x0000; + } + + /* set clock configuration : standard mode (100kHz) */ + STM32_I2C_CCR(I2C1) = I2C_CCR; + + /* configuration : I2C mode / Periphal enabled */ + STM32_I2C_CR1(I2C1) = (1 << 0); + /* error and event interrupts enabled / input clock is 16Mhz */ + STM32_I2C_CR2(I2C1) = (1 << 9) | (1 << 8) | 0x10; + + /* clear status */ + STM32_I2C_SR1(I2C1) = 0; + + /* enable event and error interrupts */ + task_enable_irq(STM32_IRQ_I2C1_EV); + task_enable_irq(STM32_IRQ_I2C1_ER); + + return EC_SUCCESS; + +} static int i2c_init(void) { @@ -226,6 +256,443 @@ static int i2c_init(void) /* FIXME: Add #defines to determine which channels to init */ rc |= i2c_init2(); + rc |= i2c_init1(); + return rc; } DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT); + + +/*****************************************************************************/ +/* STM32 Host I2C */ + +#define SR1_SB (1 << 0) /* Start bit sent */ +#define SR1_ADDR (1 << 1) /* Address sent */ +#define SR1_BTF (1 << 2) /* Byte transfered */ +#define SR1_ADD10 (1 << 3) /* 10bit address sent */ +#define SR1_STOPF (1 << 4) /* Stop detected */ +#define SR1_RxNE (1 << 6) /* Data reg not empty */ +#define SR1_TxE (1 << 7) /* Data reg empty */ +#define SR1_BERR (1 << 8) /* Buss error */ +#define SR1_ARLO (1 << 9) /* Arbitration lost */ +#define SR1_AF (1 << 10) /* Ack failure */ +#define SR1_OVR (1 << 11) /* Overrun/underrun */ +#define SR1_PECERR (1 << 12) /* PEC err in reception */ +#define SR1_TIMEOUT (1 << 14) /* Timeout : 25ms */ + +static inline void disable_i2c_interrupt(int port) +{ + STM32_I2C_CR2(port) &= ~(3 << 8); +} + +static inline void enable_i2c_interrupt(int port) +{ + STM32_I2C_CR2(port) |= 3 << 8; +} + +static inline void enable_ack(int port) +{ + STM32_I2C_CR1(port) |= (1 << 10); +} + +static inline void disable_ack(int port) +{ + STM32_I2C_CR1(port) &= ~(1 << 10); +} + +static inline void dump_i2c_reg(int port) +{ +#ifdef CONFIG_DEBUG + CPRINTF("CR1 : %016b\n", STM32_I2C_CR1(port)); + CPRINTF("CR2 : %016b\n", STM32_I2C_CR2(port)); + CPRINTF("SR2 : %016b\n", STM32_I2C_SR2(port)); + CPRINTF("SR1 : %016b\n", STM32_I2C_SR1(port)); + CPRINTF("OAR1 : %016b\n", STM32_I2C_OAR1(port)); + CPRINTF("OAR2 : %016b\n", STM32_I2C_OAR2(port)); + CPRINTF("DR : %016b\n", STM32_I2C_DR(port)); + CPRINTF("CCR : %016b\n", STM32_I2C_CCR(port)); + CPRINTF("TRISE: %016b\n", STM32_I2C_TRISE(port)); +#endif /* CONFIG_DEBUG */ +} + +static int wait_status(int port, uint32_t mask) +{ + uint32_t r; + timestamp_t t1, t2; + + + t1 = get_time(); + r = STM32_I2C_SR1(port); + while (mask ? ((r & mask) != mask) : r) { + t2 = get_time(); + if (t2.val - t1.val > 100000) { + CPRINTF(" m %016b\n", mask); + CPRINTF(" - %016b\n", r); + return EC_ERROR_TIMEOUT; + } + usleep(2000); + r = STM32_I2C_SR1(port); + } + + return EC_SUCCESS; +} + +static inline uint32_t read_clear_status(int port) +{ + uint32_t sr1, sr2; + + sr1 = STM32_I2C_SR1(port); + sr2 = STM32_I2C_SR2(port); + return (sr2 << 16) | (sr1 & 0xffff); +} + +static int master_start(int port, int slave_addr) +{ + int rv; + + /* Change to master send mode, send start bit */ + STM32_I2C_CR1(port) |= (1 << 8); + /* Wait for start bit sent event */ + rv = wait_status(port, SR1_SB); + if (rv) + return rv; + /* Send address */ + STM32_I2C_DR(port) = slave_addr; + /* Wait for addr ready */ + rv = wait_status(port, SR1_ADDR); + if (rv) + return rv; + read_clear_status(port); + + return EC_SUCCESS; +} + +static void master_stop(int port) +{ + STM32_I2C_CR1(port) |= (1 << 9); +} + +static void handle_i2c_error(int port, int rv) +{ + timestamp_t t1, t2; + uint32_t r; + + if (rv) + dump_i2c_reg(port); + + /* Clear rc_w0 bits */ + STM32_I2C_SR1(port) = 0; + /* Clear seq read status bits */ + r = STM32_I2C_SR1(port); + r = STM32_I2C_SR2(port); + /* Clear busy state */ + t1 = get_time(); + while (r & 2) { + t2 = get_time(); + if (t2.val - t1.val > 1000000) { + dump_i2c_reg(port); + return; + } + /* Send stop */ + master_stop(port); + usleep(10000); + r = STM32_I2C_SR2(port); + } +} + +static int i2c_master_transmit(int port, int slave_addr, uint8_t *data, + int size, int stop) +{ + int rv, i; + + disable_ack(port); + rv = master_start(port, slave_addr); + if (rv) + return rv; + + /* TODO: use common i2c_write_raw instead */ + for (i = 0; i < size; i++) { + rv = wait_status(port, SR1_TxE); + if (rv) + return rv; + STM32_I2C_DR(port) = data[i]; + } + rv = wait_status(port, SR1_BTF | SR1_TxE); + if (rv) + return rv; + + if (stop) { + master_stop(port); + return wait_status(port, 0); + } + + return EC_SUCCESS; +} + +static int i2c_master_receive(int port, int slave_addr, uint8_t *data, + int size) +{ + /* Master receiver sequence + * + * 1 byte + * S ADDR ACK D0 NACK P + * -o- -oooo- -iii- -ii- -oooo- -o- + * + * multi bytes + * S ADDR ACK D0 ACK Dn-2 ACK Dn-1 NACK P + * -o- -oooo- -iii- -ii- -ooo- -iiii- -ooo- -iiii- -oooo- -o- + * + */ + int rv, i; + + if (data == NULL || size < 1) + return EC_ERROR_INVAL; + + /* Set ACK to high only on receiving more than 1 byte */ + if (size > 1) + enable_ack(port); + else + disable_ack(port); + + /* Send START pulse, slave address, receive mode */ + rv = master_start(port, slave_addr | 1); + if (rv) + return rv; + + if (size >= 2) { + for (i = 0; i < (size - 2); i++) { + rv = wait_status(port, SR1_RxNE); + if (rv) + return rv; + + data[i] = STM32_I2C_DR(port); + } + + /* Process last two bytes: data[n-2], data[n-1] + * => wait rx ready + * => [n-2] in data-reg, [n-1] in shift-reg + * => [n-2] ACK done + * => disable ACK to let [n-1] byte send NACK + * => set STOP high + * => read [n-2] + * => wait rx ready + * => read [n-1] + */ + rv = wait_status(port, SR1_RxNE); + if (rv) + return rv; + + disable_ack(port); + master_stop(port); + + data[i] = STM32_I2C_DR(port); + + rv = wait_status(port, SR1_RxNE); + if (rv) + return rv; + + i++; + data[i] = STM32_I2C_DR(port); + } else { + master_stop(port); + rv = wait_status(port, SR1_RxNE); + if (rv) + return rv; + data[0] = STM32_I2C_DR(port); + } + + return EC_SUCCESS; +} + +int i2c_read16(int port, int slave_addr, int offset, int *data) +{ + int rv; + uint8_t reg, buf[2] = {0, 0}; + + reg = offset & 0xff; + + mutex_lock(&i2c_mutex); + disable_i2c_interrupt(port); + + rv = i2c_master_transmit(port, slave_addr, ®, 1, 0); + if (!rv) + rv = i2c_master_receive(port, slave_addr, buf, 2); + handle_i2c_error(port, rv); + + enable_i2c_interrupt(port); + mutex_unlock(&i2c_mutex); + + if (rv) + return rv; + + *data = ((int)buf[1] << 8) | buf[0]; + + return EC_SUCCESS; +} + +int i2c_write16(int port, int slave_addr, int offset, int data) +{ + int rv; + uint8_t buf[3]; + + buf[0] = offset & 0xff; + buf[1] = data & 0xff; + buf[2] = (data >> 8) & 0xff; + + mutex_lock(&i2c_mutex); + disable_i2c_interrupt(port); + + rv = i2c_master_transmit(port, slave_addr, buf, 3, 1); + handle_i2c_error(port, rv); + + enable_i2c_interrupt(port); + mutex_unlock(&i2c_mutex); + + return rv; +} + +int i2c_read8(int port, int slave_addr, int offset, int *data) +{ + int rv; + uint8_t reg, buf; + + reg = offset & 0xff; + mutex_lock(&i2c_mutex); + disable_i2c_interrupt(port); + + rv = i2c_master_transmit(port, slave_addr, ®, 1, 0); + if (!rv) + rv = i2c_master_receive(port, slave_addr, &buf, 1); + handle_i2c_error(port, rv); + + enable_i2c_interrupt(port); + mutex_unlock(&i2c_mutex); + + if (rv) + return rv; + + *data = buf; + + return EC_SUCCESS; +} + +int i2c_write8(int port, int slave_addr, int offset, int data) +{ + int rv; + uint8_t buf[2]; + + buf[0] = offset & 0xff; + buf[1] = data & 0xff; + + mutex_lock(&i2c_mutex); + disable_i2c_interrupt(port); + + rv = i2c_master_transmit(port, slave_addr, buf, 2, 1); + handle_i2c_error(port, rv); + + enable_i2c_interrupt(port); + mutex_unlock(&i2c_mutex); + + return rv; +} + +int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, + int len) +{ + /* TODO: implement i2c_read_block and i2c_read_string */ + + if (len && data) + *data = 0; + + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_i2c(int argc, char **argv) +{ + int rw = 0; + int slave_addr, offset; + int value = 0; + char *e; + int rv = 0; + + if (argc < 4) { + ccputs("Usage: i2c r/r16/w/w16 slave_addr offset [value]\n"); + return EC_ERROR_UNKNOWN; + } + + if (strcasecmp(argv[1], "r") == 0) { + rw = 0; + } else if (strcasecmp(argv[1], "r16") == 0) { + rw = 1; + } else if (strcasecmp(argv[1], "w") == 0) { + rw = 2; + } else if (strcasecmp(argv[1], "w16") == 0) { + rw = 3; + } else { + ccputs("Invalid rw mode : r / w / r16 / w16\n"); + return EC_ERROR_INVAL; + } + + slave_addr = strtoi(argv[2], &e, 0); + if (*e) { + ccputs("Invalid slave_addr\n"); + return EC_ERROR_INVAL; + } + + offset = strtoi(argv[3], &e, 0); + if (*e) { + ccputs("Invalid addr\n"); + return EC_ERROR_INVAL; + } + + if (rw > 1) { + if (argc < 5) { + ccputs("No write value\n"); + return EC_ERROR_INVAL; + } + value = strtoi(argv[4], &e, 0); + if (*e) { + ccputs("Invalid write value\n"); + return EC_ERROR_INVAL; + } + } + + + switch (rw) { + case 0: + rv = i2c_read8(I2C2, slave_addr, offset, &value); + break; + case 1: + rv = i2c_read16(I2C2, slave_addr, offset, &value); + break; + case 2: + rv = i2c_write8(I2C2, slave_addr, offset, value); + break; + case 3: + rv = i2c_write16(I2C2, slave_addr, offset, value); + break; + } + + + if (rv) { + ccprintf("i2c command failed\n", rv); + return rv; + } + + if (rw == 0) + ccprintf("0x%02x [%d]\n", value); + else if (rw == 1) + ccprintf("0x%04x [%d]\n", value); + + ccputs("ok\n"); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(i2c, command_i2c, + "r/r16/w/w16 slave_addr offset [value]", + "Read write i2c", + NULL); + diff --git a/common/build.mk b/common/build.mk index 35616eb73c..5afb9a86e8 100644 --- a/common/build.mk +++ b/common/build.mk @@ -16,13 +16,13 @@ common-$(CONFIG_FLASH)+=flash_common.o flash_commands.o fmap.o common-$(CONFIG_LPC)+=port80.o host_event_commands.o common-$(CONFIG_POWER_LED)+=power_led.o common-$(CONFIG_PSTORE)+=pstore_commands.o +common-$(CONFIG_SMART_BATTERY)+=smart_battery.o common-$(CONFIG_TASK_PWM)+=pwm_commands.o common-$(CONFIG_TASK_GAIAPOWER)+=gaia_power.o common-$(CONFIG_TASK_HOSTCMD)+=host_command.o common-$(CONFIG_TASK_I8042CMD)+=i8042.o keyboard.o common-$(CONFIG_TASK_LIGHTBAR)+=lightbar.o -common-$(CONFIG_TASK_POWERSTATE)+=smart_battery.o charge_state.o \ - battery_precharge.o +common-$(CONFIG_TASK_POWERSTATE)+=charge_state.o battery_precharge.o common-$(CONFIG_TASK_TEMPSENSOR)+=temp_sensor.o temp_sensor_commands.o common-$(CONFIG_TASK_THERMAL)+=thermal.o thermal_commands.o common-$(CONFIG_TASK_X86POWER)+=x86_power.o |