summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@chromium.org>2012-05-25 21:07:12 +0800
committerRong Chang <rongchang@chromium.org>2012-05-31 18:00:17 +0800
commitee495ac6a699df75051b8aa2d7ab473a2ad79c87 (patch)
tree2b0f4c1b346dca6289ce2279b8e92fba6b2a46f9
parent1545262c9be106e4f107f623de17fdea568d8f22 (diff)
downloadchrome-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.h5
-rw-r--r--board/link/board.h1
-rw-r--r--chip/stm32/i2c.c467
-rw-r--r--common/build.mk4
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, &reg, 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, &reg, 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