summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-10-16 13:23:10 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-10-23 20:07:25 +0000
commit8cf03ac0563294fbdeca2dc133d06f0b51c9a546 (patch)
tree6b07c493e7567a3221d8592b4337d2787d6bc531 /driver
parent2464d08e4d310a3f63208f22df4502c5250c4b58 (diff)
downloadchrome-ec-8cf03ac0563294fbdeca2dc133d06f0b51c9a546.tar.gz
Move source files to driver/ and power/ subdirs
The common/ subdir was getting cluttered. Move drivers for external components to a new driver/ tree, and move what used to be called chipset_*.c to a new power/ directory. This does not move/rename header files or CONFIG options. That will be done in subsequent steps, since moving and modifying .c files in the same CL is harder to review. BUG=chrome-os-partner:18343 BRANCH=none TEST=build all boards; pass unit tests Change-Id: I67a3003dc8564783a320335cf0e9620a21982d5e Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/173601 Reviewed-by: Bill Richardson <wfrichar@chromium.org> Tested-by: Bill Richardson <wfrichar@chromium.org> Reviewed-by: Vic Yang <victoryang@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r--driver/battery/bq20z453.c37
-rw-r--r--driver/battery/bq27541.c219
-rw-r--r--driver/battery/link.c131
-rw-r--r--driver/battery/smart.c356
-rw-r--r--driver/build.mk34
-rw-r--r--driver/charger/bq24192.c273
-rw-r--r--driver/charger/bq24707a.c167
-rw-r--r--driver/charger/bq24715.c192
-rw-r--r--driver/charger/bq24725.c166
-rw-r--r--driver/charger/bq24738.c188
-rw-r--r--driver/led/ds2413.c166
-rw-r--r--driver/led/lp5562.c161
-rw-r--r--driver/regulator_ir357x.c247
-rw-r--r--driver/temp_sensor/g781.c210
-rw-r--r--driver/temp_sensor/tmp006.c444
-rw-r--r--driver/usb_switch_tsu6721.c252
16 files changed, 3243 insertions, 0 deletions
diff --git a/driver/battery/bq20z453.c b/driver/battery/bq20z453.c
new file mode 100644
index 0000000000..cda3a7cbdb
--- /dev/null
+++ b/driver/battery/bq20z453.c
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012 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.
+ *
+ * Smart battery driver for BQ20Z453.
+ */
+
+#include "battery_smart.h"
+#include "host_command.h"
+
+#define PARAM_CUT_OFF 0x0010
+
+int battery_command_cut_off(struct host_cmd_handler_args *args)
+{
+ /*
+ * TODO: Since this is a host command, the i2c bus is claimed by host.
+ * Thus, we would send back the response in advanced so that
+ * the host can release the bus and then EC can send command to
+ * battery.
+ *
+ * Refactoring this via task is a way. However, it is wasteful.
+ * Need a light-weight solution.
+ */
+ args->result = EC_RES_SUCCESS;
+ host_send_response(args);
+
+ /* This function would try to claim i2c and then send to battery. */
+ sb_write(SB_MANUFACTURER_ACCESS, PARAM_CUT_OFF);
+
+ return EC_RES_SUCCESS;
+ /*
+ * Not sure if there is a side-effect since this could send result
+ * back to host TWICE.
+ */
+}
+DECLARE_HOST_COMMAND(EC_CMD_BATTERY_CUT_OFF, battery_command_cut_off,
+ EC_VER_MASK(0));
diff --git a/driver/battery/bq27541.c b/driver/battery/bq27541.c
new file mode 100644
index 0000000000..3dce19ea54
--- /dev/null
+++ b/driver/battery/bq27541.c
@@ -0,0 +1,219 @@
+/* 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.
+ *
+ * Battery driver for BQ27541.
+ */
+
+#include "battery.h"
+#include "console.h"
+#include "i2c.h"
+#include "util.h"
+
+#define BQ27541_ADDR 0xaa
+#define BQ27541_TYPE_ID 0x0541
+
+#define REG_CTRL 0x00
+#define REG_AT_RATE 0x02
+#define REG_AT_RATE_TIME_TO_EMPTY 0x04
+#define REG_TEMPERATURE 0x06
+#define REG_VOLTAGE 0x08
+#define REG_FLAGS 0x0a
+#define REG_NOMINAL_CAPACITY 0x0c
+#define REG_FULL_AVAILABLE_CAPACITY 0x0e
+#define REG_REMAINING_CAPACITY 0x10
+#define REG_FULL_CHARGE_CAPACITY 0x12
+#define REG_AVERAGE_CURRENT 0x14
+#define REG_TIME_TO_EMPTY 0x16
+#define REG_TIME_TO_FULL 0x18
+#define REG_STANDBY_CURRENT 0x1a
+#define REG_STANDBY_TIME_TO_EMPTY 0x1c
+#define REG_MAX_LOAD_CURRENT 0x1e
+#define REG_MAX_LOAD_TIME_TO_EMPTY 0x20
+#define REG_AVAILABLE_ENERGY 0x22
+#define REG_AVERAGE_POEWR 0x24
+#define REG_TT_EAT_CONSTANT_POWER 0x26
+#define REG_CYCLE_COUNT 0x2a
+#define REG_STATE_OF_CHARGE 0x2c
+#define REG_DESIGN_CAPACITY 0x3c
+#define REG_DEVICE_NAME_LENGTH 0x62
+#define MAX_DEVICE_NAME_LENGTH 7
+#define REG_DEVICE_NAME 0x63
+
+static int bq27541_read(int offset, int *data)
+{
+ return i2c_read16(I2C_PORT_HOST, BQ27541_ADDR, offset, data);
+}
+
+static int bq27541_read8(int offset, int *data)
+{
+ return i2c_read8(I2C_PORT_HOST, BQ27541_ADDR, offset, data);
+}
+
+static int bq27541_write(int offset, int data)
+{
+ return i2c_write16(I2C_PORT_HOST, BQ27541_ADDR, offset, data);
+}
+
+int bq27541_probe(void)
+{
+ int rv;
+ int dev_type;
+
+ rv = bq27541_write(REG_CTRL, 0x1);
+ rv |= bq27541_read(REG_CTRL, &dev_type);
+
+ if (rv)
+ return rv;
+ return (dev_type == BQ27541_TYPE_ID) ? EC_SUCCESS : EC_ERROR_UNKNOWN;
+}
+
+int battery_device_name(char *device_name, int buf_size)
+{
+ int rv, i, val;
+ int len = MIN(7, buf_size - 1);
+
+ rv = bq27541_read8(REG_DEVICE_NAME_LENGTH, &val);
+ if (rv)
+ return rv;
+ len = MIN(len, val);
+
+ for (i = 0; i < len; ++i) {
+ rv |= bq27541_read8(REG_DEVICE_NAME + i, &val);
+ device_name[i] = val;
+ }
+ device_name[i] = '\0';
+
+ return rv;
+}
+
+int battery_temperature(int *deci_kelvin)
+{
+ return bq27541_read(REG_TEMPERATURE, deci_kelvin);
+}
+
+int battery_voltage(int *voltage)
+{
+ return bq27541_read(REG_VOLTAGE, voltage);
+}
+
+int battery_state_of_charge(int *percent)
+{
+ return bq27541_read(REG_STATE_OF_CHARGE, percent);
+}
+
+int battery_state_of_charge_abs(int *percent)
+{
+ return battery_state_of_charge(percent);
+}
+
+int battery_remaining_capacity(int *capacity)
+{
+ return bq27541_read(REG_REMAINING_CAPACITY, capacity);
+}
+
+int battery_full_charge_capacity(int *capacity)
+{
+ return bq27541_read(REG_FULL_CHARGE_CAPACITY, capacity);
+}
+
+int battery_time_to_empty(int *minutes)
+{
+ return bq27541_read(REG_TIME_TO_EMPTY, minutes);
+}
+
+int battery_time_to_full(int *minutes)
+{
+ return bq27541_read(REG_TIME_TO_FULL, minutes);
+}
+
+int battery_cycle_count(int *count)
+{
+ return bq27541_read(REG_CYCLE_COUNT, count);
+}
+
+int battery_design_capacity(int *capacity)
+{
+ return bq27541_read(REG_DESIGN_CAPACITY, capacity);
+}
+
+int battery_average_current(int *current)
+{
+ int rv = bq27541_read(REG_AVERAGE_CURRENT, current);
+ *current = (int)((int16_t)*current);
+ return rv;
+}
+
+int battery_current(int *current)
+{
+ return battery_average_current(current);
+}
+
+int battery_time_at_rate(int rate, int *minutes)
+{
+ int rv;
+
+ rv = bq27541_write(REG_AT_RATE, rate);
+ if (rv)
+ return rv;
+ return bq27541_read(REG_AT_RATE_TIME_TO_EMPTY, minutes);
+}
+
+int battery_manufacturer_name(char *dest, int size)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_device_chemistry(char *dest, int size)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_serial_number(int *serial)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_desired_voltage(int *voltage)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_design_voltage(int *voltage)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_charging_allowed(int *allowed)
+{
+ int rv, val;
+
+ rv = bq27541_read(REG_FLAGS, &val);
+ if (rv)
+ return rv;
+ *allowed = (val & 0x100);
+ return EC_SUCCESS;
+}
+
+int battery_desired_current(int *current)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_get_mode(int *mode)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int battery_is_in_10mw_mode(int *val)
+{
+ /* Always using mAh unit */
+ *val = 0;
+ return EC_SUCCESS;
+}
+
+int battery_set_10mw_mode(int enabled)
+{
+ /* Not supported by this battery chip */
+ return EC_ERROR_INVAL;
+}
diff --git a/driver/battery/link.c b/driver/battery/link.c
new file mode 100644
index 0000000000..a7877529a8
--- /dev/null
+++ b/driver/battery/link.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 2012 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.
+ *
+ * Battery pack vendor provided charging profile
+ */
+
+#include "battery.h"
+
+/*
+ * Design capacity
+ * Battery capacity = 8200 mAh
+ * 1C = 8200 mA
+ */
+#define DESIGN_CAPACITY 8200
+
+enum {
+ TEMP_RANGE_10,
+ TEMP_RANGE_23,
+ TEMP_RANGE_35,
+ TEMP_RANGE_45,
+ TEMP_RANGE_50,
+ TEMP_RANGE_MAX
+};
+
+enum {
+ VOLT_RANGE_7200,
+ VOLT_RANGE_8000,
+ VOLT_RANGE_8400,
+ VOLT_RANGE_MAX
+};
+
+/*
+ * Vendor provided charging method
+ * temp : < 7.2V, 7.2V ~ 8.0V, 8.0V ~ 8.4V
+ * - 0 ~ 10 : 0.8A 1.6A 0.8A
+ * - 10 ~ 23 : 1.6A 4.0A 1.6A
+ * - 23 ~ 35 : 4.0A 4.0A 4.0A
+ * - 35 ~ 45 : 1.6A 4.0A 1.6A
+ * - 45 ~ 50 : 0.8A 1.6A 0.8A
+ */
+static const int const current_limit[TEMP_RANGE_MAX][VOLT_RANGE_MAX] = {
+ { 800, 1600, 800},
+ {1600, 4000, 1600},
+ {4000, 4000, 4000},
+ {1600, 4000, 1600},
+ { 800, 1600, 800},
+};
+
+const struct battery_temperature_ranges bat_temp_ranges = {
+ /*
+ * Operational temperature range
+ * 0 <= T_charge <= 50 deg C
+ * -20 <= T_discharge <= 60 deg C
+ */
+ .start_charging_min_c = 0,
+ .start_charging_max_c = 50,
+ .charging_min_c = 0,
+ .charging_max_c = 50,
+ .discharging_min_c = -20,
+ .discharging_max_c = 60,
+};
+
+static const struct battery_info info = {
+ /*
+ * Design voltage
+ * max = 8.4V
+ * normal = 7.4V
+ * min = 6.0V
+ */
+ .voltage_max = 8400,
+ .voltage_normal = 7400,
+ .voltage_min = 6000,
+
+ /* Pre-charge current: I <= 0.01C */
+ .precharge_current = 64, /* mA */
+};
+
+static inline void limit_value(int *val, int limit)
+{
+ if (*val > limit)
+ *val = limit;
+}
+
+const struct battery_info *battery_get_info(void)
+{
+ return &info;
+}
+
+void battery_vendor_params(struct batt_params *batt)
+{
+ int *desired_current = &batt->desired_current;
+ int temp_range, volt_range;
+ int bat_temp_c = DECI_KELVIN_TO_CELSIUS(batt->temperature);
+
+ /* Limit charging voltage */
+ if (batt->desired_voltage > info.voltage_max)
+ batt->desired_voltage = info.voltage_max;
+
+ /* Don't charge if outside of allowable temperature range */
+ if (bat_temp_c >= bat_temp_ranges.charging_max_c ||
+ bat_temp_c < bat_temp_ranges.charging_min_c) {
+ batt->desired_voltage = 0;
+ batt->desired_current = 0;
+ return;
+ }
+
+ if (bat_temp_c <= 10)
+ temp_range = TEMP_RANGE_10;
+ else if (bat_temp_c <= 23)
+ temp_range = TEMP_RANGE_23;
+ else if (bat_temp_c <= 35)
+ temp_range = TEMP_RANGE_35;
+ else if (bat_temp_c <= 45)
+ temp_range = TEMP_RANGE_45;
+ else
+ temp_range = TEMP_RANGE_50;
+
+ if (batt->voltage < 7200)
+ volt_range = VOLT_RANGE_7200;
+ else if (batt->voltage < 8000)
+ volt_range = VOLT_RANGE_8000;
+ else
+ volt_range = VOLT_RANGE_8400;
+
+ limit_value(desired_current, current_limit[temp_range][volt_range]);
+
+ /* If battery wants current, give it at least the precharge current */
+ if (*desired_current > 0 && *desired_current < info.precharge_current)
+ *desired_current = info.precharge_current;
+}
diff --git a/driver/battery/smart.c b/driver/battery/smart.c
new file mode 100644
index 0000000000..29af6286ad
--- /dev/null
+++ b/driver/battery/smart.c
@@ -0,0 +1,356 @@
+/* Copyright (c) 2012 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.
+ *
+ * Smart battery driver.
+ */
+
+#include "battery.h"
+#include "battery_smart.h"
+#include "host_command.h"
+#include "i2c.h"
+#include "timer.h"
+
+test_mockable int sbc_read(int cmd, int *param)
+{
+ return i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, cmd, param);
+}
+
+test_mockable int sbc_write(int cmd, int param)
+{
+ return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR, cmd, param);
+}
+
+int sb_read(int cmd, int *param)
+{
+ return i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param);
+}
+
+int sb_write(int cmd, int param)
+{
+ return i2c_write16(I2C_PORT_BATTERY, BATTERY_ADDR, cmd, param);
+}
+
+int battery_get_mode(int *mode)
+{
+ return sb_read(SB_BATTERY_MODE, mode);
+}
+
+int battery_set_mode(int mode)
+{
+ return sb_write(SB_BATTERY_MODE, mode);
+}
+
+int battery_is_in_10mw_mode(int *ret)
+{
+ int val;
+ int rv = battery_get_mode(&val);
+ if (rv)
+ return rv;
+ *ret = val & MODE_CAPACITY;
+ return EC_SUCCESS;
+}
+
+int battery_set_10mw_mode(int enabled)
+{
+ int val, rv;
+ rv = battery_get_mode(&val);
+ if (rv)
+ return rv;
+ if (enabled)
+ val |= MODE_CAPACITY;
+ else
+ val &= ~MODE_CAPACITY;
+ return battery_set_mode(val);
+}
+
+int battery_temperature(int *deci_kelvin)
+{
+ return sb_read(SB_TEMPERATURE, deci_kelvin);
+}
+
+int battery_voltage(int *voltage)
+{
+ return sb_read(SB_VOLTAGE, voltage);
+}
+
+int battery_state_of_charge(int *percent)
+{
+ return sb_read(SB_RELATIVE_STATE_OF_CHARGE, percent);
+}
+
+int battery_state_of_charge_abs(int *percent)
+{
+ return sb_read(SB_ABSOLUTE_STATE_OF_CHARGE, percent);
+}
+
+int battery_remaining_capacity(int *capacity)
+{
+ return sb_read(SB_REMAINING_CAPACITY, capacity);
+}
+
+int battery_full_charge_capacity(int *capacity)
+{
+ return sb_read(SB_FULL_CHARGE_CAPACITY, capacity);
+}
+
+int battery_time_to_empty(int *minutes)
+{
+ return sb_read(SB_AVERAGE_TIME_TO_EMPTY, minutes);
+}
+
+int battery_run_time_to_empty(int *minutes)
+{
+ return sb_read(SB_RUN_TIME_TO_EMPTY, minutes);
+}
+
+int battery_time_to_full(int *minutes)
+{
+ return sb_read(SB_AVERAGE_TIME_TO_FULL, minutes);
+}
+
+int battery_desired_current(int *current)
+{
+ return sb_read(SB_CHARGING_CURRENT, current);
+}
+
+int battery_desired_voltage(int *voltage)
+{
+ return sb_read(SB_CHARGING_VOLTAGE, voltage);
+}
+
+int battery_charging_allowed(int *allowed)
+{
+ int v, c, rv;
+
+ /*
+ * TODO(rspangler): This re-reads the battery current and voltage,
+ * which is silly because charge_state.c just read them.
+ */
+ rv = battery_desired_voltage(&v) | battery_desired_current(&c);
+ if (rv)
+ return rv;
+ *allowed = (v != 0) && (c != 0);
+ return EC_SUCCESS;
+}
+
+/* Read battery status */
+int battery_status(int *status)
+{
+ return sb_read(SB_BATTERY_STATUS, status);
+}
+
+/* Battery charge cycle count */
+int battery_cycle_count(int *count)
+{
+ return sb_read(SB_CYCLE_COUNT, count);
+}
+
+/* Designed battery capacity
+ * unit: mAh or 10mW depends on battery mode
+ */
+int battery_design_capacity(int *capacity)
+{
+ return sb_read(SB_DESIGN_CAPACITY, capacity);
+}
+
+/* Designed battery output voltage
+ * unit: mV
+ */
+int battery_design_voltage(int *voltage)
+{
+ return sb_read(SB_DESIGN_VOLTAGE, voltage);
+}
+
+/* Read serial number */
+int battery_serial_number(int *serial)
+{
+ return sb_read(SB_SERIAL_NUMBER, serial);
+}
+
+/* Read battery discharging current
+ * unit: mA
+ * negative value: charging
+ */
+int battery_current(int *current)
+{
+ int rv, d;
+
+ rv = sb_read(SB_CURRENT, &d);
+ if (rv)
+ return rv;
+
+ *current = (int16_t)d;
+ return EC_SUCCESS;
+}
+
+
+int battery_average_current(int *current)
+{
+ int rv, d;
+
+ rv = sb_read(SB_AVERAGE_CURRENT, &d);
+ if (rv)
+ return rv;
+
+ *current = (int16_t)d;
+ return EC_SUCCESS;
+}
+
+test_mockable int battery_time_at_rate(int rate, int *minutes)
+{
+ int rv;
+ int ok, time;
+ int loop, cmd, output_sign;
+
+ if (rate == 0) {
+ *minutes = 0;
+ return EC_ERROR_INVAL;
+ }
+
+ rv = sb_write(SB_AT_RATE, rate);
+ if (rv)
+ return rv;
+ loop = 5;
+ while (loop--) {
+ rv = sb_read(SB_AT_RATE_OK, &ok);
+ if (rv)
+ return rv;
+ if (ok) {
+ if (rate > 0) {
+ cmd = SB_AT_RATE_TIME_TO_FULL;
+ output_sign = -1;
+ } else {
+ cmd = SB_AT_RATE_TIME_TO_EMPTY;
+ output_sign = 1;
+ }
+ rv = sb_read(cmd, &time);
+ if (rv)
+ return rv;
+
+ *minutes = (time == 0xffff) ? 0 : output_sign * time;
+ return EC_SUCCESS;
+ } else {
+ /* wait 10ms for AT_RATE_OK */
+ msleep(10);
+ }
+ }
+ return EC_ERROR_TIMEOUT;
+}
+
+test_mockable int battery_manufacture_date(int *year, int *month, int *day)
+{
+ int rv;
+ int ymd;
+
+ rv = sb_read(SB_SPECIFICATION_INFO, &ymd);
+ if (rv)
+ return rv;
+
+ /* battery date format:
+ * ymd = day + month * 32 + (year - 1980) * 256
+ */
+ *year = (ymd >> 8) + 1980;
+ *month = (ymd & 0xff) / 32;
+ *day = (ymd & 0xff) % 32;
+
+ return EC_SUCCESS;
+}
+
+/* Read manufacturer name */
+test_mockable int battery_manufacturer_name(char *dest, int size)
+{
+ return i2c_read_string(I2C_PORT_BATTERY, BATTERY_ADDR,
+ SB_MANUFACTURER_NAME, dest, size);
+}
+
+/* Read device name */
+test_mockable int battery_device_name(char *dest, int size)
+{
+ return i2c_read_string(I2C_PORT_BATTERY, BATTERY_ADDR,
+ SB_DEVICE_NAME, dest, size);
+}
+
+/* Read battery type/chemistry */
+test_mockable int battery_device_chemistry(char *dest, int size)
+{
+ return i2c_read_string(I2C_PORT_BATTERY, BATTERY_ADDR,
+ SB_DEVICE_CHEMISTRY, dest, size);
+}
+
+/*****************************************************************************/
+/* Smart battery pass-through
+ */
+#ifdef CONFIG_I2C_PASSTHROUGH
+static int host_command_sb_read_word(struct host_cmd_handler_args *args)
+{
+ int rv;
+ int val;
+ const struct ec_params_sb_rd *p = args->params;
+ struct ec_response_sb_rd_word *r = args->response;
+
+ if (p->reg > 0x1c)
+ return EC_RES_INVALID_PARAM;
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, p->reg, &val);
+ if (rv)
+ return EC_RES_ERROR;
+
+ r->value = val;
+ args->response_size = sizeof(struct ec_response_sb_rd_word);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_SB_READ_WORD,
+ host_command_sb_read_word,
+ EC_VER_MASK(0));
+
+static int host_command_sb_write_word(struct host_cmd_handler_args *args)
+{
+ int rv;
+ const struct ec_params_sb_wr_word *p = args->params;
+
+ if (p->reg > 0x1c)
+ return EC_RES_INVALID_PARAM;
+ rv = i2c_write16(I2C_PORT_BATTERY, BATTERY_ADDR, p->reg, p->value);
+ if (rv)
+ return EC_RES_ERROR;
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_SB_WRITE_WORD,
+ host_command_sb_write_word,
+ EC_VER_MASK(0));
+
+static int host_command_sb_read_block(struct host_cmd_handler_args *args)
+{
+ int rv;
+ const struct ec_params_sb_rd *p = args->params;
+ struct ec_response_sb_rd_block *r = args->response;
+
+ if ((p->reg != SB_MANUFACTURER_NAME) &&
+ (p->reg != SB_DEVICE_NAME) &&
+ (p->reg != SB_DEVICE_CHEMISTRY) &&
+ (p->reg != SB_MANUFACTURER_DATA))
+ return EC_RES_INVALID_PARAM;
+ rv = i2c_read_string(I2C_PORT_BATTERY, BATTERY_ADDR, p->reg,
+ r->data, 32);
+ if (rv)
+ return EC_RES_ERROR;
+
+ args->response_size = sizeof(struct ec_response_sb_rd_block);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_SB_READ_BLOCK,
+ host_command_sb_read_block,
+ EC_VER_MASK(0));
+
+static int host_command_sb_write_block(struct host_cmd_handler_args *args)
+{
+ /* Not implemented */
+ return EC_RES_INVALID_COMMAND;
+}
+DECLARE_HOST_COMMAND(EC_CMD_SB_WRITE_BLOCK,
+ host_command_sb_write_block,
+ EC_VER_MASK(0));
+#endif
diff --git a/driver/build.mk b/driver/build.mk
new file mode 100644
index 0000000000..013cfb4159
--- /dev/null
+++ b/driver/build.mk
@@ -0,0 +1,34 @@
+# -*- makefile -*-
+# 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.
+#
+# Drivers for off-chip devices
+#
+
+# Batteries
+driver-$(CONFIG_BATTERY_BQ20Z453)+=battery/bq20z453.o
+driver-$(CONFIG_BATTERY_BQ27541)+=battery/bq27541.o
+driver-$(CONFIG_BATTERY_LINK)+=battery/link.o
+driver-$(CONFIG_BATTERY_SMART)+=battery/smart.o
+
+# Battery charger ICs
+driver-$(CONFIG_CHARGER_BQ24192)+=charger/bq24192.o
+driver-$(CONFIG_CHARGER_BQ24707A)+=charger/bq24707a.o
+driver-$(CONFIG_CHARGER_BQ24715)+=charger/bq24715.o
+driver-$(CONFIG_CHARGER_BQ24725)+=charger/bq24725.o
+driver-$(CONFIG_CHARGER_BQ24738)+=charger/bq24738.o
+
+# LED drivers
+driver-$(CONFIG_LED_DRIVER_DS2413)+=led/ds2413.o
+driver-$(CONFIG_LED_DRIVER_LP5562)+=led/lp5562.o
+
+# Voltage regulators
+driver-$(CONFIG_REGULATOR_IR357X)+=regulator_ir357x.o
+
+# Temperature sensors
+driver-$(CONFIG_TEMP_SENSOR_G781)+=temp_sensor/g781.o
+driver-$(CONFIG_TEMP_SENSOR_TMP006)+=temp_sensor/tmp006.o
+
+# USB switches
+driver-$(CONFIG_USB_SWITCH_TSU6721)+=usb_switch_tsu6721.o
diff --git a/driver/charger/bq24192.c b/driver/charger/bq24192.c
new file mode 100644
index 0000000000..72f778d367
--- /dev/null
+++ b/driver/charger/bq24192.c
@@ -0,0 +1,273 @@
+/* Copyright (c) 2012 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.
+ *
+ * TI bq24192 battery charger driver.
+ */
+
+#include "charger.h"
+#include "charger_bq24192.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "printf.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CHARGER, outstr)
+#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
+
+/* Charger information */
+static const struct charger_info bq24192_charger_info = {
+ .name = "bq24192",
+ .voltage_max = 4400,
+ .voltage_min = 3504,
+ .voltage_step = 16,
+ .current_max = 4544,
+ .current_min = 512,
+ .current_step = 64,
+ .input_current_max = 3000,
+ .input_current_min = 100,
+ .input_current_step = -1,
+};
+
+static const int input_current_steps[] = {
+ 100, 150, 500, 900, 1200, 1500, 2000, 3000};
+
+int bq24192_read(int reg, int *value)
+{
+ return i2c_read8(I2C_PORT_HOST, BQ24192_ADDR, reg, value);
+}
+
+int bq24192_write(int reg, int value)
+{
+ return i2c_write8(I2C_PORT_HOST, BQ24192_ADDR, reg, value);
+}
+
+static int bq24192_watchdog_reset(void)
+{
+ int rv, val;
+
+ rv = bq24192_read(BQ24192_REG_POWER_ON_CFG, &val);
+ if (rv)
+ return rv;
+ val |= (1 << 6);
+ return bq24192_write(BQ24192_REG_POWER_ON_CFG, val) ||
+ bq24192_write(BQ24192_REG_POWER_ON_CFG, val);
+}
+
+static int bq24192_set_terminate_current(int current)
+{
+ int reg_val, rv;
+ int val = (current - 128) / 128;
+
+ rv = bq24192_read(BQ24192_REG_PRE_CHG_CURRENT, &reg_val);
+ if (rv)
+ return rv;
+ reg_val = (reg_val & ~0xf) | (val & 0xf);
+ return bq24192_write(BQ24192_REG_PRE_CHG_CURRENT, reg_val);
+}
+
+int charger_enable_otg_power(int enabled)
+{
+ int val, rv;
+
+ gpio_set_level(GPIO_BCHGR_OTG, enabled);
+ rv = bq24192_read(BQ24192_REG_POWER_ON_CFG, &val);
+ if (rv)
+ return rv;
+ val = (val & ~0x30) | (enabled ? 0x20 : 0x10);
+ return bq24192_write(BQ24192_REG_POWER_ON_CFG, val);
+}
+
+int charger_set_input_current(int input_current)
+{
+ int i, value, rv;
+
+ for (i = 1; i < ARRAY_SIZE(input_current_steps); ++i)
+ if (input_current_steps[i] > input_current) {
+ --i;
+ break;
+ }
+ if (i == ARRAY_SIZE(input_current_steps))
+ --i;
+
+ rv = bq24192_read(BQ24192_REG_INPUT_CTRL, &value);
+ if (rv)
+ return rv;
+ value = value & ~(0x7);
+ value |= (i & 0x7);
+ return bq24192_write(BQ24192_REG_INPUT_CTRL, value);
+}
+
+int charger_get_input_current(int *input_current)
+{
+ int rv, value;
+
+ rv = bq24192_read(BQ24192_REG_INPUT_CTRL, &value);
+ if (rv)
+ return rv;
+ *input_current = input_current_steps[value & 0x7];
+ return EC_SUCCESS;
+}
+
+int charger_manufacturer_id(int *id)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int charger_device_id(int *id)
+{
+ return bq24192_read(BQ24192_REG_ID, id);
+}
+
+int charger_get_option(int *option)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int charger_set_option(int option)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+const struct charger_info *charger_get_info(void)
+{
+ return &bq24192_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int charger_set_mode(int mode)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+int charger_get_current(int *current)
+{
+ int rv, val;
+ const struct charger_info * const info = charger_get_info();
+
+ rv = bq24192_read(BQ24192_REG_CHG_CURRENT, &val);
+ if (rv)
+ return rv;
+ val = (val >> 2) & 0x3f;
+ *current = val * info->current_step + info->current_min;
+ return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+ int rv, val;
+ const struct charger_info * const info = charger_get_info();
+
+ current = charger_closest_current(current);
+ rv = bq24192_read(BQ24192_REG_CHG_CURRENT, &val);
+ if (rv)
+ return rv;
+ val = val & 0x3;
+ val |= ((current - info->current_min) / info->current_step) << 2;
+ return bq24192_write(BQ24192_REG_CHG_CURRENT, val);
+}
+
+int charger_get_voltage(int *voltage)
+{
+ int rv, val;
+ const struct charger_info * const info = charger_get_info();
+
+ rv = bq24192_read(BQ24192_REG_CHG_VOLTAGE, &val);
+ if (rv)
+ return rv;
+ val = (val >> 2) & 0x3f;
+ *voltage = val * info->voltage_step + info->voltage_min;
+ return EC_SUCCESS;
+}
+
+int charger_set_voltage(int voltage)
+{
+ int rv, val;
+ const struct charger_info * const info = charger_get_info();
+
+ rv = bq24192_read(BQ24192_REG_CHG_VOLTAGE, &val);
+ if (rv)
+ return rv;
+ val = val & 0x3;
+ val |= ((voltage - info->voltage_min) / info->voltage_step) << 2;
+ return bq24192_write(BQ24192_REG_CHG_VOLTAGE, val);
+}
+
+/* Charging power state initialization */
+int charger_post_init(void)
+{
+ /* Input current controlled by extpower module. Do nothing here. */
+ return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Hooks */
+
+static void bq24192_init(void)
+{
+ int val, rv;
+
+ if (charger_device_id(&val) || val != BQ24192_DEVICE_ID) {
+ CPRINTF("[%T BQ24192 incorrent ID: 0x%02x]\n", val);
+ return;
+ }
+
+ /*
+ * Disable I2C watchdog timer.
+ * TODO(victoryang): Re-enable watchdog timer and kick it periodically
+ * in charger task.
+ */
+ rv = bq24192_read(BQ24192_REG_CHG_TERM_TMR, &val);
+ if (rv)
+ return;
+ val &= ~0x30;
+ rv = bq24192_write(BQ24192_REG_CHG_TERM_TMR, val);
+ if (rv)
+ return;
+
+ if (bq24192_set_terminate_current(128))
+ return;
+
+ if (bq24192_watchdog_reset())
+ return;
+
+ CPRINTF("[%T BQ24192 initialized]\n");
+}
+DECLARE_HOOK(HOOK_INIT, bq24192_init, HOOK_PRIO_LAST);
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_bq24192(int argc, char **argv)
+{
+ int i;
+ int value;
+ int rv;
+
+ ccprintf("REG:");
+ for (i = 0; i <= 0xa; ++i)
+ ccprintf(" %02x", i);
+ ccprintf("\n");
+
+ ccprintf("VAL:");
+ for (i = 0; i <= 0xa; ++i) {
+ rv = bq24192_read(i, &value);
+ if (rv)
+ return rv;
+ ccprintf(" %02x", value);
+ }
+ ccprintf("\n");
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(bq24192, command_bq24192,
+ NULL, NULL, NULL);
diff --git a/driver/charger/bq24707a.c b/driver/charger/bq24707a.c
new file mode 100644
index 0000000000..43bd32eb91
--- /dev/null
+++ b/driver/charger/bq24707a.c
@@ -0,0 +1,167 @@
+/* Copyright (c) 2012 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.
+ *
+ * TI bq24707A battery charger driver.
+ */
+
+#include "battery_smart.h"
+#include "charger.h"
+#include "charger_bq24707a.h"
+#include "console.h"
+#include "common.h"
+#include "util.h"
+
+/* Sense resistor configurations and macros */
+#define DEFAULT_SENSE_RESISTOR 10
+#define R_SNS CONFIG_CHARGER_SENSE_RESISTOR
+#define R_AC CONFIG_CHARGER_SENSE_RESISTOR_AC
+#define REG_TO_CURRENT(REG, RS) ((REG) * DEFAULT_SENSE_RESISTOR / (RS))
+#define CURRENT_TO_REG(CUR, RS) ((CUR) * (RS) / DEFAULT_SENSE_RESISTOR)
+
+/*
+ * charge voltage bitmask: 0111 1111 1111 0000
+ * charge current bitmask: 0001 1111 1100 0000
+ * input current bitmask : 0001 1111 1000 0000
+ */
+static const struct charger_info bq24707a_charger_info = {
+ .name = "bq24707A",
+ .voltage_max = 19200,
+ .voltage_min = 1024,
+ .voltage_step = 16,
+ .current_max = REG_TO_CURRENT(0x1fc0, R_SNS),
+ .current_min = REG_TO_CURRENT(0x40, R_SNS),
+ .current_step = REG_TO_CURRENT(0x40, R_SNS),
+ .input_current_max = REG_TO_CURRENT(0x1F80, R_AC),
+ .input_current_min = REG_TO_CURRENT(0x80, R_AC),
+ .input_current_step = REG_TO_CURRENT(0x80, R_AC),
+};
+
+/* bq24707a specific interfaces */
+
+int charger_set_input_current(int input_current)
+{
+ return sbc_write(BQ24707_INPUT_CURRENT,
+ CURRENT_TO_REG(input_current, R_AC));
+}
+
+int charger_get_input_current(int *input_current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(BQ24707_INPUT_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *input_current = REG_TO_CURRENT(reg, R_AC);
+
+ return EC_SUCCESS;
+}
+
+int charger_manufacturer_id(int *id)
+{
+ return sbc_read(BQ24707_MANUFACTURE_ID, id);
+}
+
+int charger_device_id(int *id)
+{
+ return sbc_read(BQ24707_DEVICE_ID, id);
+}
+
+int charger_get_option(int *option)
+{
+ return sbc_read(BQ24707_CHARGE_OPTION, option);
+}
+
+int charger_set_option(int option)
+{
+ return sbc_write(BQ24707_CHARGE_OPTION, option);
+}
+
+/* Charger interfaces */
+
+const struct charger_info *charger_get_info(void)
+{
+ return &bq24707a_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ /* Default status */
+ *status = CHARGER_LEVEL_2;
+
+ if (option & OPTION_CHARGE_INHIBIT)
+ *status |= CHARGER_CHARGE_INHIBITED;
+
+ return EC_SUCCESS;
+}
+
+int charger_set_mode(int mode)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ if (mode & CHARGE_FLAG_INHIBIT_CHARGE)
+ option |= OPTION_CHARGE_INHIBIT;
+ else
+ option &= ~OPTION_CHARGE_INHIBIT;
+ return charger_set_option(option);
+}
+
+int charger_get_current(int *current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(SB_CHARGING_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *current = REG_TO_CURRENT(reg, R_SNS);
+ return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+ current = charger_closest_current(current);
+
+ return sbc_write(SB_CHARGING_CURRENT, CURRENT_TO_REG(current, R_SNS));
+}
+
+int charger_get_voltage(int *voltage)
+{
+ return sbc_read(SB_CHARGING_VOLTAGE, voltage);
+}
+
+int charger_set_voltage(int voltage)
+{
+ return sbc_write(SB_CHARGING_VOLTAGE, voltage);
+}
+
+/* Charging power state initialization */
+int charger_post_init(void)
+{
+ /*
+ * Note: bq24725 power on reset state is:
+ * watch dog timer = 175 sec
+ * input current limit = ~1/2 maximum setting
+ * charging voltage = 0 mV
+ * charging current = 0 mA
+ * IOUT = 20x adapter current sense
+ */
+
+ /* Set charger input current limit */
+ return charger_set_input_current(CONFIG_CHARGER_INPUT_CURRENT);
+}
diff --git a/driver/charger/bq24715.c b/driver/charger/bq24715.c
new file mode 100644
index 0000000000..d90b35b021
--- /dev/null
+++ b/driver/charger/bq24715.c
@@ -0,0 +1,192 @@
+/* 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.
+ *
+ * TI bq24715 battery charger driver.
+ */
+
+#include "battery_smart.h"
+#include "charger.h"
+#include "charger_bq24715.h"
+#include "console.h"
+#include "common.h"
+#include "util.h"
+
+/* Sense resistor configurations and macros */
+#define DEFAULT_SENSE_RESISTOR 10
+#define R_SNS CONFIG_CHARGER_SENSE_RESISTOR
+#define R_AC CONFIG_CHARGER_SENSE_RESISTOR_AC
+#define REG_TO_CURRENT(REG, RS) ((REG) * DEFAULT_SENSE_RESISTOR / (RS))
+#define CURRENT_TO_REG(CUR, RS) ((CUR) * (RS) / DEFAULT_SENSE_RESISTOR)
+
+/* Note: it is assumed that the sense resistors are 10mOhm. */
+
+static const struct charger_info bq24725_charger_info = {
+ .name = "bq24715",
+ .voltage_max = CHARGE_V_MAX,
+ .voltage_min = CHARGE_V_MIN,
+ .voltage_step = CHARGE_V_STEP,
+ .current_max = REG_TO_CURRENT(CHARGE_I_MAX, R_SNS),
+ .current_min = REG_TO_CURRENT(CHARGE_I_MIN, R_SNS),
+ .current_step = REG_TO_CURRENT(CHARGE_I_STEP, R_SNS),
+ .input_current_max = REG_TO_CURRENT(INPUT_I_MAX, R_AC),
+ .input_current_min = REG_TO_CURRENT(INPUT_I_MIN, R_AC),
+ .input_current_step = REG_TO_CURRENT(INPUT_I_STEP, R_AC),
+};
+
+int charger_set_input_current(int input_current)
+{
+ return sbc_write(BQ24715_INPUT_CURRENT,
+ CURRENT_TO_REG(input_current, R_AC));
+}
+
+int charger_get_input_current(int *input_current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(BQ24715_INPUT_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *input_current = REG_TO_CURRENT(reg, R_AC);
+
+ return EC_SUCCESS;
+}
+
+int charger_manufacturer_id(int *id)
+{
+ return sbc_read(BQ24715_MANUFACTURER_ID, id);
+}
+
+int charger_device_id(int *id)
+{
+ return sbc_read(BQ24715_DEVICE_ID, id);
+}
+
+int charger_get_option(int *option)
+{
+ return sbc_read(BQ24715_CHARGE_OPTION, option);
+}
+
+int charger_set_option(int option)
+{
+ return sbc_write(BQ24715_CHARGE_OPTION, option);
+}
+
+/* Charger interfaces */
+
+const struct charger_info *charger_get_info(void)
+{
+ return &bq24725_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ /* Default status */
+ *status = CHARGER_LEVEL_2;
+
+ if ((option & OPT_CHARGE_INHIBIT_MASK) == OPT_CHARGE_DISABLE)
+ *status |= CHARGER_CHARGE_INHIBITED;
+
+ return EC_SUCCESS;
+}
+
+int charger_set_mode(int mode)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ option &= ~OPT_CHARGE_INHIBIT_MASK;
+ if (mode & CHARGE_FLAG_INHIBIT_CHARGE)
+ option |= OPT_CHARGE_DISABLE;
+ else
+ option |= OPT_CHARGE_ENABLE;
+ return charger_set_option(option);
+}
+
+int charger_get_current(int *current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(SB_CHARGING_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *current = REG_TO_CURRENT(reg, R_SNS);
+ return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+ current = charger_closest_current(current);
+
+ return sbc_write(SB_CHARGING_CURRENT, CURRENT_TO_REG(current, R_SNS));
+}
+
+/* The voltage setting needs to be cached to work with the current
+ * charging infrastructure and state machine. The reason is that
+ * the state machine expects to be able to set a 0V charging voltage.
+ * The bq24715 does not allow this in the hardware register. Therefore
+ * 0V is handled specially to appease the state machine. */
+static int cached_voltage;
+
+int charger_get_voltage(int *voltage)
+{
+ int ret;
+
+ if (cached_voltage == 0) {
+ *voltage = cached_voltage;
+ return EC_SUCCESS;
+ }
+
+ ret = sbc_read(SB_CHARGING_VOLTAGE, &cached_voltage);
+
+ if (ret == EC_SUCCESS)
+ *voltage = cached_voltage;
+
+ return ret;
+}
+
+int charger_set_voltage(int voltage)
+{
+ cached_voltage = voltage;
+ return sbc_write(SB_CHARGING_VOLTAGE, voltage);
+}
+
+/* Charging power state initialization */
+int charger_post_init(void)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ /* Don't be noisy */
+ option |= OPT_AUDIO_FREQ_40KHZ_LIMIT;
+
+ /* Always monitor adapter current (40X multiplier). */
+ option |= OPT_FIX_IOUT_ALWAYS;
+ option &= ~OPT_IOUT_MASK;
+
+ rv = charger_set_option(option);
+ if (rv)
+ return rv;
+
+ rv = charger_set_input_current(CONFIG_CHARGER_INPUT_CURRENT);
+ return rv;
+}
diff --git a/driver/charger/bq24725.c b/driver/charger/bq24725.c
new file mode 100644
index 0000000000..3e418c7216
--- /dev/null
+++ b/driver/charger/bq24725.c
@@ -0,0 +1,166 @@
+/* Copyright (c) 2012 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.
+ *
+ * TI bq24725 battery charger driver.
+ */
+
+#include "battery_smart.h"
+#include "charger.h"
+#include "charger_bq24725.h"
+#include "console.h"
+#include "common.h"
+#include "util.h"
+
+/* Sense resistor configurations and macros */
+#define DEFAULT_SENSE_RESISTOR 10
+#define R_SNS CONFIG_CHARGER_SENSE_RESISTOR
+#define R_AC CONFIG_CHARGER_SENSE_RESISTOR_AC
+#define REG_TO_CURRENT(REG, RS) ((REG) * DEFAULT_SENSE_RESISTOR / (RS))
+#define CURRENT_TO_REG(CUR, RS) ((CUR) * (RS) / DEFAULT_SENSE_RESISTOR)
+
+/* Charger infomation
+ * charge voltage bitmask: 0111 1111 1111 0000
+ * charge current bitmask: 0001 1111 1000 0000
+ * input current bitmask : 0000 0000 1000 0000
+ */
+static const struct charger_info bq24725_charger_info = {
+ .name = "bq24725",
+ .voltage_max = 19200,
+ .voltage_min = 1024,
+ .voltage_step = 16,
+ .current_max = REG_TO_CURRENT(8128, R_SNS),
+ .current_min = REG_TO_CURRENT(128, R_SNS),
+ .current_step = REG_TO_CURRENT(128, R_SNS),
+ .input_current_max = REG_TO_CURRENT(8064, R_AC),
+ .input_current_min = REG_TO_CURRENT(128, R_AC),
+ .input_current_step = REG_TO_CURRENT(128, R_AC),
+};
+
+/* bq24725 specific interfaces */
+
+int charger_set_input_current(int input_current)
+{
+ return sbc_write(BQ24725_INPUT_CURRENT,
+ CURRENT_TO_REG(input_current, R_AC));
+}
+
+int charger_get_input_current(int *input_current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(BQ24725_INPUT_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *input_current = REG_TO_CURRENT(reg, R_AC);
+
+ return EC_SUCCESS;
+}
+
+int charger_manufacturer_id(int *id)
+{
+ return sbc_read(BQ24725_MANUFACTURE_ID, id);
+}
+
+int charger_device_id(int *id)
+{
+ return sbc_read(BQ24725_DEVICE_ID, id);
+}
+
+int charger_get_option(int *option)
+{
+ return sbc_read(BQ24725_CHARGE_OPTION, option);
+}
+
+int charger_set_option(int option)
+{
+ return sbc_write(BQ24725_CHARGE_OPTION, option);
+}
+
+/* Charger interfaces */
+
+const struct charger_info *charger_get_info(void)
+{
+ return &bq24725_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ /* Default status */
+ *status = CHARGER_LEVEL_2;
+
+ if (option & OPTION_CHARGE_INHIBIT)
+ *status |= CHARGER_CHARGE_INHIBITED;
+
+ return EC_SUCCESS;
+}
+
+int charger_set_mode(int mode)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ if (mode & CHARGE_FLAG_INHIBIT_CHARGE)
+ option |= OPTION_CHARGE_INHIBIT;
+ else
+ option &= ~OPTION_CHARGE_INHIBIT;
+ return charger_set_option(option);
+}
+
+int charger_get_current(int *current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(SB_CHARGING_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *current = REG_TO_CURRENT(reg, R_SNS);
+ return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+ current = charger_closest_current(current);
+
+ return sbc_write(SB_CHARGING_CURRENT, CURRENT_TO_REG(current, R_SNS));
+}
+
+int charger_get_voltage(int *voltage)
+{
+ return sbc_read(SB_CHARGING_VOLTAGE, voltage);
+}
+
+int charger_set_voltage(int voltage)
+{
+ return sbc_write(SB_CHARGING_VOLTAGE, voltage);
+}
+
+/* Charging power state initialization */
+int charger_post_init(void)
+{
+ /*
+ * Note: bq24725 power on reset state is:
+ * watch dog timer = 175 sec
+ * input current limit = ~1/2 maximum setting
+ * charging voltage = 0 mV
+ * charging current = 0 mA
+ */
+
+ /* Set charger input current limit */
+ return charger_set_input_current(CONFIG_CHARGER_INPUT_CURRENT);
+}
diff --git a/driver/charger/bq24738.c b/driver/charger/bq24738.c
new file mode 100644
index 0000000000..057e71c12f
--- /dev/null
+++ b/driver/charger/bq24738.c
@@ -0,0 +1,188 @@
+/* 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.
+ *
+ * TI bq24738 battery charger driver.
+ */
+
+#include "battery_smart.h"
+#include "charger.h"
+#include "charger_bq24738.h"
+#include "console.h"
+#include "common.h"
+#include "util.h"
+
+/* Sense resistor configurations and macros */
+#define DEFAULT_SENSE_RESISTOR 10
+#define R_SNS CONFIG_CHARGER_SENSE_RESISTOR
+#define R_AC CONFIG_CHARGER_SENSE_RESISTOR_AC
+#define REG_TO_CURRENT(REG, RS) ((REG) * DEFAULT_SENSE_RESISTOR / (RS))
+#define CURRENT_TO_REG(CUR, RS) ((CUR) * (RS) / DEFAULT_SENSE_RESISTOR)
+
+/* Charger infomation
+ * charge voltage bitmask: 0111 1111 1111 0000
+ * charge current bitmask: 0001 1111 1100 0000
+ * input current bitmask : 0000 0000 1000 0000
+ */
+static const struct charger_info bq24738_charger_info = {
+ .name = "bq24738",
+ .voltage_max = 19200,
+ .voltage_min = 1024,
+ .voltage_step = 16,
+ .current_max = REG_TO_CURRENT(8128, R_SNS),
+ .current_min = REG_TO_CURRENT(128, R_SNS),
+ .current_step = REG_TO_CURRENT(64, R_SNS),
+ .input_current_max = REG_TO_CURRENT(8064, R_AC),
+ .input_current_min = REG_TO_CURRENT(128, R_AC),
+ .input_current_step = REG_TO_CURRENT(128, R_AC),
+};
+
+/* bq24738 specific interfaces */
+
+int charger_set_input_current(int input_current)
+{
+ return sbc_write(BQ24738_INPUT_CURRENT,
+ CURRENT_TO_REG(input_current, R_AC));
+}
+
+int charger_get_input_current(int *input_current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(BQ24738_INPUT_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *input_current = REG_TO_CURRENT(reg, R_AC);
+
+ return EC_SUCCESS;
+}
+
+int charger_manufacturer_id(int *id)
+{
+ return sbc_read(BQ24738_MANUFACTURE_ID, id);
+}
+
+int charger_device_id(int *id)
+{
+ return sbc_read(BQ24738_DEVICE_ID, id);
+}
+
+int charger_get_option(int *option)
+{
+ return sbc_read(BQ24738_CHARGE_OPTION, option);
+}
+
+int charger_set_option(int option)
+{
+ return sbc_write(BQ24738_CHARGE_OPTION, option);
+}
+
+/* Charger interfaces */
+
+const struct charger_info *charger_get_info(void)
+{
+ return &bq24738_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ /* Default status */
+ *status = CHARGER_LEVEL_2;
+
+ if (option & OPTION_CHARGE_INHIBIT)
+ *status |= CHARGER_CHARGE_INHIBITED;
+
+ return EC_SUCCESS;
+}
+
+int charger_set_mode(int mode)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ if (mode & CHARGE_FLAG_INHIBIT_CHARGE)
+ option |= OPTION_CHARGE_INHIBIT;
+ else
+ option &= ~OPTION_CHARGE_INHIBIT;
+ return charger_set_option(option);
+}
+
+int charger_get_current(int *current)
+{
+ int rv;
+ int reg;
+
+ rv = sbc_read(SB_CHARGING_CURRENT, &reg);
+ if (rv)
+ return rv;
+
+ *current = REG_TO_CURRENT(reg, R_SNS);
+ return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+ current = charger_closest_current(current);
+
+ return sbc_write(SB_CHARGING_CURRENT, CURRENT_TO_REG(current, R_SNS));
+}
+
+int charger_get_voltage(int *voltage)
+{
+ return sbc_read(SB_CHARGING_VOLTAGE, voltage);
+}
+
+int charger_set_voltage(int voltage)
+{
+ return sbc_write(SB_CHARGING_VOLTAGE, voltage);
+}
+
+/* Charging power state initialization */
+int charger_post_init(void)
+{
+ int rv;
+ int val;
+
+ /* Disable IFAULT_HI. See crosbug.com/p/19868 */
+ rv = charger_get_option(&val);
+ if (rv)
+ return rv;
+ val &= ~OPTION_IFAULT_HI_ENABLE;
+ rv = charger_set_option(val);
+ if (rv)
+ return rv;
+
+ /* Set charger input current limit */
+ rv = charger_set_input_current(CONFIG_CHARGER_INPUT_CURRENT);
+ return rv;
+}
+
+int charger_discharge_on_ac(int enable)
+{
+ int rv;
+ int option;
+
+ rv = charger_get_option(&option);
+ if (rv)
+ return rv;
+
+ if (enable)
+ rv = charger_set_option(option | OPTION_LEARN_ENABLE);
+ else
+ rv = charger_set_option(option & ~OPTION_LEARN_ENABLE);
+
+ return rv;
+}
diff --git a/driver/led/ds2413.c b/driver/led/ds2413.c
new file mode 100644
index 0000000000..9babd75992
--- /dev/null
+++ b/driver/led/ds2413.c
@@ -0,0 +1,166 @@
+/* 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.
+ */
+
+/* Power LED control for Chrome EC */
+
+#include "charge_state.h"
+#include "console.h"
+#include "hooks.h"
+#include "onewire.h"
+#include "timer.h"
+#include "util.h"
+
+#define ONEWIRE_RETRIES 10
+
+enum led_color {
+ LED_OFF = 0,
+ LED_RED,
+ LED_YELLOW,
+ LED_GREEN,
+ LED_COLOR_COUNT /* Number of colors, not a color itself */
+};
+
+static const uint8_t led_masks[LED_COLOR_COUNT] = {0xff, 0xfe, 0xfc, 0xfd};
+static const char * const color_names[LED_COLOR_COUNT] = {
+ "off", "red", "yellow", "green"};
+
+/**
+ * Set the onewire LED GPIO controller outputs
+ *
+ * @param mask Mask of outputs to enable
+ *
+ * @return EC_SUCCESS, or non-zero if error.
+ */
+static int led_set_mask(int mask)
+{
+ int rv;
+
+ /* Reset the 1-wire bus */
+ rv = onewire_reset();
+ if (rv)
+ return rv;
+
+ /* Skip ROM, since only one device */
+ onewire_write(0xcc);
+
+ /* Write and turn on the LEDs */
+ onewire_write(0x5a);
+ onewire_write(mask);
+ onewire_write(~mask); /* Repeat inverted */
+
+ rv = onewire_read(); /* Confirmation byte */
+ if (rv != 0xaa)
+ return EC_ERROR_UNKNOWN;
+
+ /* The next byte is a read-back of the chip status. Since we're only
+ * using lines as outputs, we can ignore it. */
+ return EC_SUCCESS;
+}
+
+static int led_set(enum led_color color)
+{
+ int rv = EC_SUCCESS;
+ int i;
+
+ /*
+ * 1-wire communication can fail for timing reasons in the current
+ * system. We have a limited timing window to send/receive bits, but
+ * we can't disable interrupts for the rest of the system to guarantee
+ * we hit that window. Instead, simply retry the low-level command a
+ * few times.
+ */
+ for (i = 0; i < ONEWIRE_RETRIES; i++) {
+ rv = led_set_mask(led_masks[color]);
+ if (rv == EC_SUCCESS)
+ break;
+
+ /*
+ * Sleep for a bit between tries. This gives the 1-wire GPIO
+ * chip time to recover from the failed attempt, and allows
+ * lower-priority tasks a chance to run.
+ */
+ usleep(100);
+ }
+
+ return rv;
+}
+
+/*****************************************************************************/
+/* Hooks */
+
+static void onewire_led_tick(void)
+{
+ static enum led_color current_color = LED_COLOR_COUNT;
+ static int tick_count;
+
+ enum led_color new_color = LED_OFF;
+ uint32_t chflags = charge_get_flags();
+
+ tick_count++;
+
+ if (!(chflags & CHARGE_FLAG_EXTERNAL_POWER)) {
+ /* AC isn't present, so the power LED on the AC plug is off */
+ current_color = LED_OFF;
+ return;
+ }
+
+ /* Translate charge state to LED color */
+ switch (charge_get_state()) {
+ case PWR_STATE_IDLE:
+ if (chflags & CHARGE_FLAG_FORCE_IDLE)
+ new_color = (tick_count & 1) ? LED_GREEN : LED_OFF;
+ else
+ new_color = LED_GREEN;
+ break;
+ case PWR_STATE_CHARGE:
+ new_color = LED_YELLOW;
+ break;
+ case PWR_STATE_CHARGE_NEAR_FULL:
+ new_color = LED_GREEN;
+ break;
+ case PWR_STATE_ERROR:
+ new_color = LED_RED;
+ break;
+ default:
+ /* Other states don't change LED color */
+ break;
+ }
+
+ /*
+ * The power adapter on link can partially unplug and lose its LED
+ * state. There's no way to detect this, so just assume it forgets its
+ * state every 10 seconds.
+ */
+ if (!(tick_count % 10))
+ current_color = LED_COLOR_COUNT;
+
+ /* If current color is still correct, leave now */
+ if (new_color == current_color)
+ return;
+
+ /* Update LED */
+ if (!led_set(new_color))
+ current_color = new_color;
+}
+DECLARE_HOOK(HOOK_SECOND, onewire_led_tick, HOOK_PRIO_DEFAULT);
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_powerled(int argc, char **argv)
+{
+ int i;
+
+ /* Pick a color, any color... */
+ for (i = 0; i < LED_COLOR_COUNT; i++) {
+ if (!strcasecmp(argv[1], color_names[i]))
+ return led_set(i);
+ }
+ return EC_ERROR_PARAM1;
+}
+DECLARE_CONSOLE_COMMAND(powerled, command_powerled,
+ "<off | red | yellow | green>",
+ "Set power LED color",
+ NULL);
diff --git a/driver/led/lp5562.c b/driver/led/lp5562.c
new file mode 100644
index 0000000000..fd92fae26f
--- /dev/null
+++ b/driver/led/lp5562.c
@@ -0,0 +1,161 @@
+/* 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.
+ *
+ * TI LP5562 driver.
+ */
+
+#include "console.h"
+#include "i2c.h"
+#include "lp5562.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/* 8-bit I2C address */
+#define LP5562_I2C_ADDR (0x30 << 1)
+
+inline int lp5562_write(uint8_t reg, uint8_t val)
+{
+ return i2c_write8(I2C_PORT_HOST, LP5562_I2C_ADDR, reg, val);
+}
+
+inline int lp5562_read(uint8_t reg, int *val)
+{
+ return i2c_read8(I2C_PORT_HOST, LP5562_I2C_ADDR, reg, val);
+}
+
+int lp5562_set_color(uint32_t rgb)
+{
+ int ret = 0;
+
+ ret |= lp5562_write(LP5562_REG_B_PWM, rgb & 0xff);
+ ret |= lp5562_write(LP5562_REG_G_PWM, (rgb >> 8) & 0xff);
+ ret |= lp5562_write(LP5562_REG_R_PWM, (rgb >> 16) & 0xff);
+
+ return ret;
+}
+
+int lp5562_set_engine(uint8_t r, uint8_t g, uint8_t b)
+{
+ return lp5562_write(LP5562_REG_LED_MAP, (r << 4) | (g << 2) | b);
+}
+
+int lp5562_engine_load(int engine, const uint8_t *program, int size)
+{
+ int prog_addr = LP5562_REG_ENG_PROG(engine);
+ int i, ret, val;
+ int shift = 6 - engine * 2;
+
+ ret = lp5562_read(LP5562_REG_OP_MODE, &val);
+ if (ret)
+ return ret;
+ val &= ~(0x3 << shift);
+ val |= 0x1 << shift;
+ ret = lp5562_write(LP5562_REG_OP_MODE, val);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < size; ++i) {
+ ret = lp5562_write(prog_addr + i, program[i]);
+ if (ret)
+ return ret;
+ }
+
+ val &= ~(0x3 << shift);
+ val |= 0x2 << shift;
+ ret = lp5562_write(LP5562_REG_OP_MODE, val);
+
+ return ret;
+}
+
+int lp5562_engine_control(int eng1, int eng2, int eng3)
+{
+ int ret, val;
+
+ ret = lp5562_read(LP5562_REG_ENABLE, &val);
+ if (ret)
+ return ret;
+ val &= 0xc0;
+ val |= (eng1 << 4) | (eng2 << 2) | eng3;
+ return lp5562_write(LP5562_REG_ENABLE, val);
+}
+
+int lp5562_get_engine_state(int engine)
+{
+ int val;
+
+ if (lp5562_read(LP5562_REG_ENABLE, &val))
+ return 0xee;
+ return (val >> (6 - engine * 2)) & 0x3;
+}
+
+int lp5562_poweron(void)
+{
+ int ret = 0;
+
+ ret |= lp5562_write(LP5562_REG_ENABLE, 0x40);
+ udelay(500); /* start-up delay */
+
+ ret |= lp5562_write(LP5562_REG_CONFIG, 0x1);
+ ret |= lp5562_write(LP5562_REG_LED_MAP, 0x0);
+
+ return ret;
+}
+
+int lp5562_poweroff(void)
+{
+ return lp5562_write(LP5562_REG_ENABLE, 0x0);
+}
+
+int lp5562_get_pc(int engine)
+{
+ int ret;
+ if (lp5562_read(LP5562_REG_ENG1_PC + engine - 1, &ret))
+ return 0xee;
+ return ret;
+}
+
+int lp5562_set_pc(int engine, int val)
+{
+ return lp5562_write(LP5562_REG_ENG1_PC + engine - 1, val);
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_lp5562(int argc, char **argv)
+{
+ if (argc == 4) {
+ char *e;
+ uint8_t red, green, blue;
+
+ red = strtoi(argv[1], &e, 0);
+ if (e && *e)
+ return EC_ERROR_PARAM1;
+ green = strtoi(argv[2], &e, 0);
+ if (e && *e)
+ return EC_ERROR_PARAM2;
+ blue = strtoi(argv[3], &e, 0);
+ if (e && *e)
+ return EC_ERROR_PARAM3;
+
+ return lp5562_set_color((red << 16) | (green << 8) | blue);
+ } else if (argc == 2) {
+ int v;
+
+ if (!parse_bool(argv[1], &v))
+ return EC_ERROR_PARAM1;
+
+ if (v)
+ return lp5562_poweron();
+ else
+ return lp5562_poweroff();
+ }
+
+ return EC_ERROR_INVAL;
+}
+DECLARE_CONSOLE_COMMAND(lp5562, command_lp5562,
+ "on | off | <red> <green> <blue>",
+ "Set the color of the LED",
+ NULL);
diff --git a/driver/regulator_ir357x.c b/driver/regulator_ir357x.c
new file mode 100644
index 0000000000..c6966b7e31
--- /dev/null
+++ b/driver/regulator_ir357x.c
@@ -0,0 +1,247 @@
+/* Copyright (c) 2012 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.
+ *
+ * IR357x driver.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
+#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args)
+
+/* 8-bit I2C address */
+#define IR357x_I2C_ADDR (0x8 << 1)
+
+struct ir_setting {
+ uint8_t reg;
+ uint8_t value;
+};
+
+static struct ir_setting ir3570_settings[] = {
+ {0x10, 0x22}, {0x11, 0x22}, {0x12, 0x88}, {0x13, 0x10},
+ {0x14, 0x0d}, {0x15, 0x21}, {0x16, 0x21}, {0x17, 0x00},
+ {0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00},
+ {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+ {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x60}, {0x23, 0x60},
+ {0x24, 0x74}, {0x25, 0x4e}, {0x26, 0xff}, {0x27, 0x80},
+ {0x28, 0x00}, {0x29, 0x20}, {0x2a, 0x15}, {0x2b, 0x26},
+ {0x2c, 0xb6}, {0x2d, 0x21}, {0x2e, 0x11}, {0x2f, 0x20},
+ {0x30, 0xab}, {0x31, 0x14}, {0x32, 0x90}, {0x33, 0x4d},
+ {0x34, 0x75}, {0x35, 0x64}, {0x36, 0x64}, {0x37, 0x09},
+ {0x38, 0xc4}, {0x39, 0x20}, {0x3a, 0x80}, {0x3b, 0x00},
+ {0x3c, 0x00}, {0x3d, 0xaa}, {0x3e, 0x00}, {0x3f, 0x05},
+ {0x40, 0x50}, {0x41, 0x40}, {0x42, 0x00}, {0x43, 0x00},
+ {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+ {0x48, 0x1c}, {0x49, 0x0c}, {0x4a, 0x0f}, {0x4b, 0x40},
+ {0x4c, 0x80}, {0x4d, 0x40}, {0x4e, 0x80},
+ {0x51, 0x00}, {0x52, 0x45}, {0x53, 0x59},
+ {0x54, 0x23}, {0x55, 0xae}, {0x56, 0x68}, {0x57, 0x24},
+ {0x58, 0x62}, {0x59, 0x42}, {0x5a, 0x34}, {0x5b, 0x00},
+ {0x5c, 0x30}, {0x5d, 0x05}, {0x5e, 0x02}, {0x5f, 0x35},
+ {0x60, 0x30}, {0x61, 0x00}, {0x62, 0xd8}, {0x63, 0x00},
+ {0x64, 0x52}, {0x65, 0x28}, {0x66, 0x14}, {0x67, 0x87},
+ {0x68, 0x80}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00},
+ {0x6c, 0x00}, {0x6d, 0xff}, {0x6e, 0x06}, {0x6f, 0xff},
+ {0x70, 0xff}, {0x71, 0x20}, {0x72, 0x00}, {0x73, 0x01},
+ {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+ {0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00},
+ {0x7c, 0x15}, {0x7d, 0x15}, {0x7e, 0x00}, {0x7f, 0x00},
+ {0x80, 0x00}, {0x81, 0x00}, {0x82, 0x00}, {0x83, 0x00},
+ {0x84, 0x00}, {0x85, 0x00}, {0x86, 0x00}, {0x87, 0x00},
+ {0x88, 0x88}, {0x89, 0x88}, {0x8a, 0x01}, {0x8b, 0x42},
+ {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0x1f},
+ {0, 0}
+};
+
+static struct ir_setting ir3571_settings[] = {
+ {0x18, 0x22}, {0x19, 0x22}, {0x1a, 0x08}, {0x1b, 0x10},
+ {0x1c, 0x06}, {0x1d, 0x21}, {0x1e, 0x21}, {0x1f, 0x83},
+ {0x20, 0x83}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+ {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x34},
+ {0x28, 0x34}, {0x29, 0x74}, {0x2a, 0x4e}, {0x2b, 0xff},
+ {0x2c, 0x00}, {0x2d, 0x1d}, {0x2e, 0x14}, {0x2f, 0x1f},
+ {0x30, 0x88}, {0x31, 0x9a}, {0x32, 0x1e}, {0x33, 0x19},
+ {0x34, 0xe9}, {0x35, 0x40}, {0x36, 0x90}, {0x37, 0x6d},
+ {0x38, 0x75}, {0x39, 0xa0}, {0x3a, 0x84}, {0x3b, 0x08},
+ {0x3c, 0xc5}, {0x3d, 0xa0}, {0x3e, 0x80}, {0x3f, 0xaa},
+ {0x40, 0x50}, {0x41, 0x4b}, {0x42, 0x02}, {0x43, 0x04},
+ {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x78},
+ {0x48, 0x56}, {0x49, 0x18}, {0x4a, 0x88}, {0x4b, 0x00},
+ {0x4c, 0x80}, {0x4d, 0x60}, {0x4e, 0x60}, {0x4f, 0xff},
+ {0x50, 0xff}, {0x51, 0x00}, {0x52, 0x9b}, {0x53, 0xaa},
+ {0x54, 0xd8}, {0x55, 0x56}, {0x56, 0x31}, {0x57, 0x1a},
+ {0x58, 0x12}, {0x59, 0x63}, {0x5a, 0x00}, {0x5b, 0x09},
+ {0x5c, 0x02}, {0x5d, 0x00}, {0x5e, 0xea}, {0x5f, 0x00},
+ {0x60, 0xb0}, {0x61, 0x1e}, {0x62, 0x00}, {0x63, 0x56},
+ {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+ {0x68, 0x28}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00},
+ {0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00},
+ {0x70, 0x80}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+ {0x74, 0x00}, {0x75, 0xbf}, {0x76, 0x06}, {0x77, 0xff},
+ {0x78, 0xff}, {0x79, 0x04}, {0x7a, 0x00}, {0x7b, 0x1d},
+ {0x7c, 0xa0}, {0x7d, 0x10}, {0x7e, 0x00}, {0x7f, 0x8a},
+ {0x80, 0x1b}, {0x81, 0x11}, {0x82, 0x00}, {0x83, 0x00},
+ {0x84, 0x00}, {0x85, 0x00}, {0x86, 0x00}, {0x87, 0x00},
+ {0x88, 0x00}, {0x89, 0x00}, {0x8a, 0x00}, {0x8b, 0x00},
+ {0x8c, 0x00}, {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0x00},
+ {0, 0}
+};
+
+static uint8_t ir357x_read(uint8_t reg)
+{
+ int res;
+ int val;
+
+ res = i2c_read8(I2C_PORT_REGULATOR, IR357x_I2C_ADDR, reg, &val);
+ if (res)
+ return 0xee;
+
+ return val;
+}
+
+static void ir357x_write(uint8_t reg, uint8_t val)
+{
+ int res;
+
+ res = i2c_write8(I2C_PORT_REGULATOR, IR357x_I2C_ADDR, reg, val);
+ if (res)
+ CPRINTF("[%T IR I2C write failed]\n");
+}
+
+static int ir357x_get_version(void)
+{
+ /* IR3571 on Link EVT */
+ if ((ir357x_read(0xfc) == 'I') && (ir357x_read(0xfd) == 'R') &&
+ ((ir357x_read(0x0a) & 0xe) == 0))
+ return 3571;
+
+ /* IR3570A on Link Proto 0/1 and Link DVT */
+ if ((ir357x_read(0x92) == 'C') && (ir357x_read(0xcd) == 0x24))
+ return 3570;
+
+ /* Unknown and unsupported chip */
+ return -1;
+}
+
+struct ir_setting *ir357x_get_settings(void)
+{
+ int version = ir357x_get_version();
+
+ if (version == 3570)
+ return ir3570_settings;
+ else if (version == 3571)
+ return ir3571_settings;
+ else
+ return NULL;
+}
+
+static void ir357x_prog(void)
+{
+ struct ir_setting *settings = ir357x_get_settings();
+
+ if (settings) {
+ for (; settings->reg; settings++)
+ ir357x_write(settings->reg, settings->value);
+ } else {
+ CPRINTF("[%T IR%d chip unsupported. Skip writing settings!\n",
+ ir357x_get_version());
+ return;
+ }
+
+ CPRINTF("[%T IR%d registers UPDATED]\n", ir357x_get_version());
+}
+
+static void ir357x_dump(void)
+{
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ if (!(i & 0xf)) {
+ ccprintf("\n%02x: ", i);
+ cflush();
+ }
+ ccprintf("%02x ", ir357x_read(i));
+ }
+ ccprintf("\n");
+}
+
+static int ir357x_check(void)
+{
+ uint8_t val;
+ int diff = 0;
+ struct ir_setting *settings = ir357x_get_settings();
+
+ if (!settings) {
+ ccprintf("no setting for chip IR%d !\n", ir357x_get_version());
+ return 1;
+ }
+
+ for (; settings->reg; settings++) {
+ val = ir357x_read(settings->reg);
+ if (val != settings->value) {
+ ccprintf("DIFF reg 0x%02x %02x->%02x\n",
+ settings->reg, settings->value, val);
+ cflush();
+ diff++;
+ }
+ }
+ return !!diff;
+}
+
+static int command_ir357x(int argc, char **argv)
+{
+ int reg, val;
+ char *rem;
+
+ if (1 == argc) { /* dump all registers */
+ ir357x_dump();
+ return EC_SUCCESS;
+ } else if (2 == argc) {
+ if (!strcasecmp(argv[1], "check")) {
+ ir357x_check();
+ } else { /* read one register */
+ reg = strtoi(argv[1], &rem, 16);
+ if (*rem) {
+ ccprintf("Invalid register: %s\n", argv[1]);
+ return EC_ERROR_INVAL;
+ }
+ ccprintf("reg 0x%02x = 0x%02x\n", reg,
+ ir357x_read(reg));
+ }
+ return EC_SUCCESS;
+ } else if (3 == argc) { /* write one register */
+ reg = strtoi(argv[1], &rem, 16);
+ if (*rem) {
+ ccprintf("Invalid register: %s\n", argv[1]);
+ return EC_ERROR_INVAL;
+ }
+ val = strtoi(argv[2], &rem, 16);
+ if (*rem) {
+ ccprintf("Invalid value: %s\n", argv[2]);
+ return EC_ERROR_INVAL;
+ }
+ ir357x_write(reg, val);
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_INVAL;
+}
+DECLARE_CONSOLE_COMMAND(ir357x, command_ir357x,
+ "[check|write]",
+ "IR357x core regulator control",
+ NULL);
+
+static void ir357x_hot_settings(void)
+{
+ /* dynamically apply settings to workaround issue */
+ ir357x_prog();
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, ir357x_hot_settings, HOOK_PRIO_DEFAULT);
diff --git a/driver/temp_sensor/g781.c b/driver/temp_sensor/g781.c
new file mode 100644
index 0000000000..389b12629e
--- /dev/null
+++ b/driver/temp_sensor/g781.c
@@ -0,0 +1,210 @@
+/* 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.
+ */
+
+/* G781 temperature sensor module for Chrome EC */
+
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "hooks.h"
+#include "temp_sensor_g781.h"
+#include "util.h"
+
+static int g781_temp_val_local;
+static int g781_temp_val_remote;
+
+/**
+ * Determine whether the sensor is powered.
+ *
+ * @return non-zero the g781 sensor is powered.
+ */
+static int g781_has_power(void)
+{
+#ifdef CONFIG_TEMP_SENSOR_POWER_GPIO
+ return gpio_get_level(CONFIG_TEMP_SENSOR_POWER_GPIO);
+#else
+ return 1;
+#endif
+}
+
+static int g781_read8(const int offset, int *data_ptr)
+{
+ return i2c_read8(I2C_PORT_THERMAL, G781_I2C_ADDR, offset, data_ptr);
+}
+
+static int g781_write8(const int offset, int data)
+{
+ return i2c_write8(I2C_PORT_THERMAL, G781_I2C_ADDR, offset, data);
+}
+
+static int g781_get_temp(const int offset, int *temp_ptr)
+{
+ int rv;
+ int temp_raw = 0;
+
+ rv = g781_read8(offset, &temp_raw);
+ if (rv < 0)
+ return rv;
+
+ *temp_ptr = (int)(int8_t)temp_raw;
+ return EC_SUCCESS;
+}
+
+static int g781_set_temp(const int offset, int temp)
+{
+ if (temp < -127 || temp > 127)
+ return EC_ERROR_INVAL;
+
+ return g781_write8(offset, (uint8_t)temp);
+}
+
+int g781_get_val(int idx, int *temp_ptr)
+{
+ if (!g781_has_power())
+ return EC_ERROR_NOT_POWERED;
+
+ switch (idx) {
+ case G781_IDX_INTERNAL:
+ *temp_ptr = g781_temp_val_local;
+ break;
+ case G781_IDX_EXTERNAL:
+ *temp_ptr = g781_temp_val_remote;
+ break;
+ default:
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+static void g781_temp_sensor_poll(void)
+{
+ if (!g781_has_power())
+ return;
+
+ g781_get_temp(G781_TEMP_LOCAL, &g781_temp_val_local);
+ g781_temp_val_local = C_TO_K(g781_temp_val_local);
+
+ g781_get_temp(G781_TEMP_REMOTE, &g781_temp_val_remote);
+ g781_temp_val_remote = C_TO_K(g781_temp_val_remote);
+}
+DECLARE_HOOK(HOOK_SECOND, g781_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR);
+
+static int g781_show_status(void)
+{
+ int value;
+ int rv;
+
+
+ rv = g781_get_temp(G781_TEMP_LOCAL, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf("Local Temp: %3dC\n", value);
+
+ rv = g781_get_temp(G781_LOCAL_TEMP_THERM_LIMIT, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" Therm Trip: %3dC\n", value);
+
+ rv = g781_get_temp(G781_LOCAL_TEMP_HIGH_LIMIT_R, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" High Alarm: %3dC\n", value);
+
+ rv = g781_get_temp(G781_LOCAL_TEMP_LOW_LIMIT_R, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" Low Alarm: %3dC\n", value);
+
+ rv = g781_get_temp(G781_TEMP_REMOTE, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf("Remote Temp: %3dC\n", value);
+
+ rv = g781_get_temp(G781_REMOTE_TEMP_THERM_LIMIT, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" Therm Trip: %3dC\n", value);
+
+ rv = g781_get_temp(G781_REMOTE_TEMP_HIGH_LIMIT_R, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" High Alarm: %3dC\n", value);
+
+ rv = g781_get_temp(G781_REMOTE_TEMP_LOW_LIMIT_R, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf(" Low Alarm: %3dC\n", value);
+
+ rv = g781_read8(G781_STATUS, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf("\nSTATUS: %08b\n", value);
+
+ rv = g781_read8(G781_CONFIGURATION_R, &value);
+ if (rv < 0)
+ return rv;
+ ccprintf("CONFIG: %08b\n", value);
+
+ return EC_SUCCESS;
+}
+
+static int command_g781(int argc, char **argv)
+{
+ char *command;
+ char *e;
+ int data;
+ int offset;
+ int rv;
+
+ if (!g781_has_power()) {
+ ccprintf("ERROR: Temp sensor not powered.\n");
+ return EC_ERROR_NOT_POWERED;
+ }
+
+ /* If no args just print status */
+ if (argc == 1)
+ return g781_show_status();
+
+ if (argc < 3)
+ return EC_ERROR_PARAM_COUNT;
+
+ command = argv[1];
+ offset = strtoi(argv[2], &e, 0);
+ if (*e || offset < 0 || offset > 255)
+ return EC_ERROR_PARAM2;
+
+ if (!strcasecmp(command, "getbyte")) {
+ rv = g781_read8(offset, &data);
+ if (rv < 0)
+ return rv;
+ ccprintf("Byte at offset 0x%02x is %08b\n", offset, data);
+ return rv;
+ }
+
+ /* Remaining commands are of the form "g781 set-command offset data" */
+ if (argc != 4)
+ return EC_ERROR_PARAM_COUNT;
+
+ data = strtoi(argv[3], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3;
+
+ if (!strcasecmp(command, "settemp")) {
+ ccprintf("Setting 0x%02x to %dC\n", offset, data);
+ rv = g781_set_temp(offset, data);
+ } else if (!strcasecmp(command, "setbyte")) {
+ ccprintf("Setting 0x%02x to 0x%02x\n", offset, data);
+ rv = g781_write8(offset, data);
+ } else
+ return EC_ERROR_PARAM1;
+
+ return rv;
+}
+DECLARE_CONSOLE_COMMAND(g781, command_g781,
+ "[settemp|setbyte <offset> <value>] or [getbyte <offset>]. "
+ "Temps in Celsius.",
+ "Print g781 temp sensor status or set parameters.", NULL);
diff --git a/driver/temp_sensor/tmp006.c b/driver/temp_sensor/tmp006.c
new file mode 100644
index 0000000000..19d66d87aa
--- /dev/null
+++ b/driver/temp_sensor/tmp006.c
@@ -0,0 +1,444 @@
+/* Copyright (c) 2012 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.
+ */
+
+/* TMP006 temperature sensor module for Chrome EC */
+
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "i2c.h"
+#include "math.h"
+#include "task.h"
+#include "temp_sensor.h"
+#include "tmp006.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_THERMAL, outstr)
+#define CPRINTF(format, args...) cprintf(CC_THERMAL, format, ## args)
+
+/* Constants for calculating target object temperatures */
+static const float A1 = 1.75e-3f;
+static const float A2 = -1.678e-5f;
+static const float B0 = -2.94e-5f;
+static const float B1 = -5.7e-7f;
+static const float B2 = 4.63e-9f;
+static const float C2 = 13.4f;
+
+/* Defined in board_temp_sensor.c. */
+extern const struct tmp006_t tmp006_sensors[TMP006_COUNT];
+
+/* Flags for tdata->fail */
+#define FAIL_INIT (1 << 0) /* Just initialized */
+#define FAIL_POWER (1 << 1) /* Sensor not powered */
+#define FAIL_I2C (1 << 2) /* I2C communication error */
+#define FAIL_NOT_READY (1 << 3) /* Data not ready */
+
+struct tmp006_data_t {
+ int v; /* Object voltage */
+ int t[4]; /* Circular buffer of last four die temperatures */
+ int tidx; /* Index of the current value in t[] */
+ int fail; /* Fail flags; non-zero if last read failed */
+ float s0; /* Sensitivity factor */
+ float b0, b1, b2; /* Coefficients for self-heating correction */
+};
+
+static struct tmp006_data_t tmp006_data[TMP006_COUNT];
+
+/**
+ * Check if sensor has power
+ *
+ * @param idx Sensor index
+ *
+ * @return non-zero if sensor has power.
+ */
+static int tmp006_has_power(int idx)
+{
+#ifdef CONFIG_TEMP_SENSOR_POWER_GPIO
+ return gpio_get_level(CONFIG_TEMP_SENSOR_POWER_GPIO);
+#else
+ return 1;
+#endif
+}
+
+static int tmp006_read_die_temp(const struct tmp006_data_t *tdata,
+ int *temp_ptr)
+{
+ if (tdata->fail)
+ return EC_ERROR_UNKNOWN;
+
+ /* Return previous die temperature */
+ *temp_ptr = tdata->t[(tdata->tidx - 1) & 0x3] / 100;
+ return EC_SUCCESS;
+}
+
+/**
+ * Calculate the remote object temperature.
+ *
+ * @param Tdie_i Die temperature in 1/100 K.
+ * @param Vobj_i Voltage read from register 0. In nV.
+ * @param tdata TMP006 data for this sensor.
+ *
+ * @return Object temperature in 1/100 K.
+ */
+static int tmp006_calculate_object_temp(int Tdie_i, int Vobj_i,
+ const struct tmp006_data_t *tdata)
+{
+ float Tdie, Vobj;
+ float Tx, S, Vos, Vx, fv, Tobj, T4;
+ int Tobj_i;
+
+ Tdie = (float)Tdie_i * 1e-2f;
+ Vobj = (float)Vobj_i * 1e-9f;
+
+ /* Calculate according to TMP006 users guide. */
+ Tx = Tdie - 298.15f;
+ /* S is the sensitivity */
+ S = tdata->s0 * (1.0f + A1 * Tx + A2 * Tx * Tx);
+ /* Vos is the offset voltage */
+ Vos = tdata->b0 + tdata->b1 * Tx + tdata->b2 * Tx * Tx;
+ Vx = Vobj - Vos;
+ /* fv is Seebeck coefficient f(Vobj) */
+ fv = Vx + C2 * Vx * Vx;
+
+ T4 = Tdie * Tdie * Tdie * Tdie + fv / S;
+ Tobj = sqrtf(sqrtf(T4));
+ Tobj_i = (int32_t)(Tobj * 100.0f);
+
+ return Tobj_i;
+}
+
+/**
+ * Apply TMP006 temporal correction.
+ *
+ * @param T1-T4 Four die temperature readings separated by 1s in 1/100K.
+ * @param Vobj Voltage read from register 0, in nV.
+ *
+ * @return Corrected object voltage in 1/100K.
+ */
+static int tmp006_correct_object_voltage(int T1, int T2, int T3, int T4,
+ int Vobj)
+{
+ int Tslope = 3 * T1 + T2 - T3 - 3 * T4;
+ return Vobj + 296 * Tslope;
+}
+
+static int tmp006_read_object_temp(const struct tmp006_data_t *tdata,
+ int *temp_ptr)
+{
+ int pidx = (tdata->tidx - 1) & 0x3;
+ int t = tdata->t[pidx];
+ int v = tdata->v;
+
+ if (tdata->fail)
+ return EC_ERROR_UNKNOWN;
+
+ if (!tdata->s0)
+ return EC_ERROR_NOT_CALIBRATED;
+
+ v = tmp006_correct_object_voltage(
+ t,
+ tdata->t[(pidx + 3) & 3],
+ tdata->t[(pidx + 2) & 3],
+ tdata->t[(pidx + 1) & 3],
+ v);
+
+ *temp_ptr = tmp006_calculate_object_temp(t, v, tdata) / 100;
+
+ return EC_SUCCESS;
+}
+
+static int tmp006_poll_sensor(int sensor_id)
+{
+ struct tmp006_data_t *tdata = tmp006_data + sensor_id;
+ int traw, t;
+ int vraw, v;
+ int rv;
+ int addr = tmp006_sensors[sensor_id].addr;
+ int idx;
+
+ if (!tmp006_has_power(sensor_id)) {
+ tdata->fail |= FAIL_POWER;
+ return EC_ERROR_UNKNOWN;
+ }
+
+ /*
+ * If sensor has just initialized and/or has lost power, wait for
+ * data ready; otherwise, we read garbage data.
+ */
+ if (tdata->fail && (FAIL_POWER | FAIL_INIT)) {
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &v);
+ if (rv) {
+ tdata->fail |= FAIL_I2C;
+ return EC_ERROR_UNKNOWN;
+ } else if (!(v & 0x80)) {
+ tdata->fail |= FAIL_NOT_READY;
+ return EC_ERROR_UNKNOWN;
+ }
+ }
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
+ if (rv) {
+ tdata->fail |= FAIL_I2C;
+ return EC_ERROR_UNKNOWN;
+ }
+
+ /* Convert temperature from raw to 1/100 K */
+ t = ((int)(int16_t)traw * 100) / 128 + 27300;
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
+ if (rv) {
+ tdata->fail |= FAIL_I2C;
+ return EC_ERROR_UNKNOWN;
+ }
+
+ /* Convert voltage from raw to nV */
+ v = ((int)(int16_t)vraw * 15625) / 100;
+
+ /*
+ * If last read failed, set the entire temperature history to the
+ * current temperature. This keeps us from making inaccurate temporal
+ * corrections based on stale data.
+ */
+ if (tdata->fail) {
+ for (idx = 0; idx < 4; idx++)
+ tdata->t[idx] = t;
+ } else {
+ idx = tdata->tidx;
+ tdata->t[idx] = t;
+ tdata->tidx = (idx + 1) & 3;
+ }
+
+ tdata->v = v;
+ tdata->fail = 0;
+
+ return EC_SUCCESS;
+}
+
+int tmp006_get_val(int idx, int *temp_ptr)
+{
+ /*
+ * Note: idx is a thermal sensor index, where the top N-1 bits are the
+ * TMP006 index and the bottom bit is (0=die, 1=remote).
+ */
+ int tidx = idx >> 1;
+ const struct tmp006_data_t *tdata = tmp006_data + tidx;
+
+ if (tdata->fail & FAIL_POWER) {
+ /*
+ * Sensor isn't powered, or hasn't successfully provided data
+ * since being powered. Keep reporting not-powered until
+ * we get good data (which will clear FAIL_POWER) or there is
+ * an I2C error.
+ */
+ return (tdata->fail & FAIL_I2C) ? EC_ERROR_UNKNOWN :
+ EC_ERROR_NOT_POWERED;
+ }
+
+ /* Check the low bit to determine which temperature to read. */
+ if ((idx & 0x1) == 0)
+ return tmp006_read_die_temp(tdata, temp_ptr);
+ else
+ return tmp006_read_object_temp(tdata, temp_ptr);
+}
+
+/*****************************************************************************/
+/* Hooks */
+
+static void tmp006_poll(void)
+{
+ int i;
+
+ for (i = 0; i < TMP006_COUNT; ++i)
+ tmp006_poll_sensor(i);
+}
+DECLARE_HOOK(HOOK_SECOND, tmp006_poll, HOOK_PRIO_TEMP_SENSOR);
+
+static void tmp006_init(void)
+{
+ int i;
+
+ for (i = 0; i < TMP006_COUNT; ++i) {
+ struct tmp006_data_t *tdata = tmp006_data + i;
+
+ /* Report error until we actually read the sensor */
+ tdata->fail = FAIL_INIT;
+
+ /* Use defaults for Bn params */
+ tdata->b0 = B0;
+ tdata->b1 = B1;
+ tdata->b2 = B2;
+ }
+}
+DECLARE_HOOK(HOOK_INIT, tmp006_init, HOOK_PRIO_DEFAULT);
+
+/*****************************************************************************/
+/* Host commands */
+
+int tmp006_get_calibration(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_tmp006_get_calibration *p = args->params;
+ struct ec_response_tmp006_get_calibration *r = args->response;
+ const struct tmp006_data_t *tdata;
+
+ if (p->index >= TMP006_COUNT)
+ return EC_RES_INVALID_PARAM;
+
+ tdata = tmp006_data + p->index;
+
+ r->s0 = tdata->s0;
+ r->b0 = tdata->b0;
+ r->b1 = tdata->b1;
+ r->b2 = tdata->b2;
+
+ args->response_size = sizeof(*r);
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_TMP006_GET_CALIBRATION,
+ tmp006_get_calibration,
+ EC_VER_MASK(0));
+
+int tmp006_set_calibration(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_tmp006_set_calibration *p = args->params;
+ struct tmp006_data_t *tdata;
+
+ if (p->index >= TMP006_COUNT)
+ return EC_RES_INVALID_PARAM;
+
+ tdata = tmp006_data + p->index;
+
+ tdata->s0 = p->s0;
+ tdata->b0 = p->b0;
+ tdata->b1 = p->b1;
+ tdata->b2 = p->b2;
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_TMP006_SET_CALIBRATION,
+ tmp006_set_calibration,
+ EC_VER_MASK(0));
+
+/*****************************************************************************/
+/* Console commands */
+
+/**
+ * Print temperature info for a sensor; used by console command.
+ */
+static int tmp006_print(int idx)
+{
+ int vraw, v;
+ int traw, t;
+ int rv;
+ int d;
+ int addr = tmp006_sensors[idx].addr;
+
+
+ ccprintf("Debug data from %s:\n", tmp006_sensors[idx].name);
+
+ if (!tmp006_has_power(idx)) {
+ ccputs("Sensor powered off.\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xfe, &d);
+ if (rv)
+ return rv;
+ ccprintf(" Manufacturer ID: 0x%04x\n", d);
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xff, &d);
+ ccprintf(" Device ID: 0x%04x\n", d);
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &d);
+ ccprintf(" Config: 0x%04x\n", d);
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
+ v = ((int)(int16_t)vraw * 15625) / 100;
+ ccprintf(" Voltage: 0x%04x = %d nV\n", vraw, v);
+
+ rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
+ t = ((int)(int16_t)traw * 100) / 128;
+ ccprintf(" Temperature: 0x%04x = %d.%02d C\n",
+ traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100));
+
+ return EC_SUCCESS;
+}
+
+static int command_sensor_info(int argc, char **argv)
+{
+ int i;
+ int rv, rv1;
+
+ rv1 = EC_SUCCESS;
+ for (i = 0; i < TMP006_COUNT; i++) {
+ rv = tmp006_print(i);
+ if (rv != EC_SUCCESS)
+ rv1 = rv;
+ cflush();
+ }
+
+ return rv1;
+}
+DECLARE_CONSOLE_COMMAND(tmp006, command_sensor_info,
+ NULL,
+ "Print TMP006 sensors",
+ NULL);
+
+static int command_t6cal(int argc, char **argv)
+{
+ struct tmp006_data_t *tdata;
+ char *e;
+ int v;
+ int i;
+
+ if (argc < 2) {
+ ccprintf("# Name S0 b0"
+ " b1 b2\n");
+ for (i = 0; i < TMP006_COUNT; i++) {
+ tdata = tmp006_data + i;
+ ccprintf("%d %-11s"
+ "%7de-17 %7de-8 %7de-10 %7de-12\n",
+ i, tmp006_sensors[i].name,
+ (int)(tdata->s0 * 1e17f),
+ (int)(tdata->b0 * 1e8f),
+ (int)(tdata->b1 * 1e10f),
+ (int)(tdata->b2 * 1e12f));
+ }
+
+ return EC_SUCCESS;
+ }
+
+ if (argc != 4)
+ return EC_ERROR_PARAM_COUNT;
+
+ i = strtoi(argv[1], &e, 0);
+ if (*e || i < 0 || i >= TMP006_COUNT)
+ return EC_ERROR_PARAM1;
+ tdata = tmp006_data + i;
+
+ v = strtoi(argv[3], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3;
+
+ if (!strcasecmp(argv[2], "s0"))
+ tdata->s0 = (float)v * 1e-17f;
+ else if (!strcasecmp(argv[2], "b0"))
+ tdata->b0 = (float)v * 1e-8f;
+ else if (!strcasecmp(argv[2], "b1"))
+ tdata->b1 = (float)v * 1e-10f;
+ else if (!strcasecmp(argv[2], "b2"))
+ tdata->b2 = (float)v * 1e-12f;
+ else
+ return EC_ERROR_PARAM2;
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(t6cal, command_t6cal,
+ "[<index> <coeff_name> <radix>]",
+ "Set/print TMP006 calibration",
+ NULL);
diff --git a/driver/usb_switch_tsu6721.c b/driver/usb_switch_tsu6721.c
new file mode 100644
index 0000000000..1eaa74de55
--- /dev/null
+++ b/driver/usb_switch_tsu6721.c
@@ -0,0 +1,252 @@
+/* Copyright (c) 2012 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.
+ *
+ * TI TSU6721 USB port switch driver.
+ */
+
+#include "console.h"
+#include "ec_commands.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "i2c.h"
+#include "system.h"
+#include "timer.h"
+#include "tsu6721.h"
+#include "uart.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr)
+#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
+
+/* 8-bit I2C address */
+#define TSU6721_I2C_ADDR (0x25 << 1)
+
+/* Delay values */
+#define TSU6721_SW_RESET_DELAY 15
+
+/* Number of retries when reset fails */
+#define TSU6721_SW_RESET_RETRY 3
+
+static int saved_interrupts;
+
+uint8_t tsu6721_read(uint8_t reg)
+{
+ int res;
+ int val;
+
+ res = i2c_read8(I2C_PORT_HOST, TSU6721_I2C_ADDR, reg, &val);
+ if (res)
+ return 0xee;
+
+ return val;
+}
+
+int tsu6721_write(uint8_t reg, uint8_t val)
+{
+ int res;
+
+ res = i2c_write8(I2C_PORT_HOST, TSU6721_I2C_ADDR, reg, val);
+ if (res)
+ CPRINTF("[%T TSU6721 I2C write failed]\n");
+ return res;
+}
+
+int tsu6721_enable_interrupts(void)
+{
+ int ctrl = tsu6721_read(TSU6721_REG_CONTROL);
+ return tsu6721_write(TSU6721_REG_CONTROL, ctrl & 0x1e);
+}
+
+int tsu6721_disable_interrupts(void)
+{
+ int ctrl = tsu6721_read(TSU6721_REG_CONTROL);
+ int rv;
+
+ rv = tsu6721_write(TSU6721_REG_CONTROL, ctrl | 0x1);
+ tsu6721_get_interrupts();
+ return rv;
+}
+
+int tsu6721_set_interrupt_mask(uint16_t mask)
+{
+ return tsu6721_write(TSU6721_REG_INT_MASK1, (~mask) & 0xff) |
+ tsu6721_write(TSU6721_REG_INT_MASK2, ((~mask) >> 8) & 0xff);
+}
+
+int tsu6721_get_interrupts(void)
+{
+ int ret = tsu6721_peek_interrupts();
+ saved_interrupts = 0;
+ return ret;
+}
+
+int tsu6721_peek_interrupts(void)
+{
+ saved_interrupts |= (tsu6721_read(TSU6721_REG_INT2) << 8) |
+ (tsu6721_read(TSU6721_REG_INT1));
+ return saved_interrupts;
+}
+
+int tsu6721_get_device_type(void)
+{
+ return (tsu6721_read(TSU6721_REG_DEV_TYPE3) << 16) |
+ (tsu6721_read(TSU6721_REG_DEV_TYPE2) << 8) |
+ (tsu6721_read(TSU6721_REG_DEV_TYPE1));
+}
+
+void tsu6721_reset(void)
+{
+ int i;
+
+ for (i = 0; i < TSU6721_SW_RESET_RETRY; ++i) {
+ if (i != 0) {
+ CPRINTF("[%T TSU6721 init failed. Retrying.]\n");
+ msleep(500);
+ }
+ if (tsu6721_write(TSU6721_REG_RESET, 0x1))
+ continue;
+ /* TSU6721 reset takes ~10ms. Let's wait for 15ms to be safe. */
+ msleep(TSU6721_SW_RESET_DELAY);
+ if (tsu6721_init() == EC_SUCCESS)
+ break;
+ }
+}
+
+int tsu6721_mux(enum tsu6721_mux sel)
+{
+ uint8_t id = tsu6721_read(TSU6721_REG_ADC);
+ uint8_t vbus1 = tsu6721_read(TSU6721_REG_DEV_TYPE1) & 0x74;
+ uint8_t vbus3 = tsu6721_read(TSU6721_REG_DEV_TYPE3) & 0x74;
+ uint8_t ctrl = tsu6721_read(TSU6721_REG_CONTROL);
+
+ /*
+ * silicon limitation: the chip stays in low power mode and cannot
+ * activate manual mode if it is not detecting either a VBUS or
+ * something known on the ID pin
+ */
+ if (sel != TSU6721_MUX_AUTO && (id == 0x1f) && !vbus1 && !vbus3) {
+ CPRINTF("TSU6721 cannot use manual mode: no VBUS or ID\n");
+ return EC_ERROR_INVAL;
+ }
+
+ if (sel == TSU6721_MUX_AUTO) {
+ tsu6721_write(TSU6721_REG_CONTROL, ctrl | TSU6721_CTRL_AUTO);
+ } else {
+ tsu6721_write(TSU6721_REG_MANUAL1, sel);
+ tsu6721_write(TSU6721_REG_CONTROL, ctrl & ~TSU6721_CTRL_AUTO);
+ }
+
+ return EC_SUCCESS;
+}
+
+int tsu6721_init(void)
+{
+ uint8_t settings;
+ uint8_t dev_id = tsu6721_read(TSU6721_REG_DEV_ID);
+ int res = 0;
+
+ if ((dev_id != 0x0a) && (dev_id != 0x12)) {
+ CPRINTF("TSU6721 invalid device ID 0x%02x\n", dev_id);
+ return EC_ERROR_UNKNOWN;
+ }
+
+ /* set USB charger detection timeout to 600ms */
+ settings = tsu6721_read(TSU6721_REG_TIMER);
+ if (settings == 0xee)
+ return EC_ERROR_UNKNOWN;
+ settings = (settings & ~0x38);
+ res |= tsu6721_write(TSU6721_REG_TIMER, settings);
+
+ res |= tsu6721_set_interrupt_mask(TSU6721_INT_ATTACH |
+ TSU6721_INT_DETACH |
+ TSU6721_INT_ADC_CHANGE |
+ TSU6721_INT_VBUS);
+ res |= tsu6721_enable_interrupts();
+
+ return res ? EC_ERROR_UNKNOWN : EC_SUCCESS;
+}
+/*
+ * TODO(vpalatin): using the I2C early in the HOOK_INIT
+ * currently triggers all sort of badness, I need to debug
+ * this before re-activatin this initialization.
+ */
+#if 0
+DECLARE_HOOK(HOOK_INIT, tsu6721_init, HOOK_PRIO_DEFAULT);
+#endif
+
+static void tsu6721_dump(void)
+{
+ int i;
+ uint8_t id = tsu6721_read(TSU6721_REG_ADC);
+ uint8_t ctrl = tsu6721_read(TSU6721_REG_CONTROL);
+
+ if (ctrl & TSU6721_CTRL_AUTO)
+ ccprintf("Auto: %02x %02x %02x\n",
+ tsu6721_read(TSU6721_REG_DEV_TYPE1),
+ tsu6721_read(TSU6721_REG_DEV_TYPE2),
+ tsu6721_read(TSU6721_REG_DEV_TYPE3));
+ else
+ ccprintf("Manual: %02x %02x\n",
+ tsu6721_read(TSU6721_REG_MANUAL1),
+ tsu6721_read(TSU6721_REG_MANUAL2));
+ ccprintf("ID: 0x%02x\n", id);
+ for (i = 1; i < 0x24; i++)
+ ccprintf("%02x ", tsu6721_read(i));
+ ccprintf("\n");
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_usbmux(int argc, char **argv)
+{
+ if (1 == argc) { /* dump all registers */
+ tsu6721_dump();
+ return EC_SUCCESS;
+ } else if (2 == argc) {
+ if (!strcasecmp(argv[1], "usb")) {
+ tsu6721_mux(TSU6721_MUX_USB);
+ } else if (!strcasecmp(argv[1], "uart1")) {
+ tsu6721_mux(TSU6721_MUX_UART);
+ } else if (!strcasecmp(argv[1], "uart2")) {
+ tsu6721_mux(TSU6721_MUX_AUDIO);
+ } else if (!strcasecmp(argv[1], "auto")) {
+ tsu6721_mux(TSU6721_MUX_AUTO);
+ } else { /* read one register */
+ ccprintf("Invalid mux value: %s\n", argv[1]);
+ return EC_ERROR_INVAL;
+ }
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_INVAL;
+}
+DECLARE_CONSOLE_COMMAND(usbmux, command_usbmux,
+ "[usb|uart1|uart2|auto]",
+ "TSU6721 USB mux control",
+ NULL);
+
+/*****************************************************************************/
+/* Host commands */
+
+static int usb_command_mux(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_usb_mux *p = args->params;
+
+ if (system_is_locked())
+ return EC_RES_ACCESS_DENIED;
+
+ /* Safety check */
+ if (p->mux != TSU6721_MUX_AUTO &&
+ p->mux != TSU6721_MUX_USB &&
+ p->mux != TSU6721_MUX_UART &&
+ p->mux != TSU6721_MUX_AUDIO)
+ return EC_RES_ERROR;
+
+ if (tsu6721_mux(p->mux))
+ return EC_RES_ERROR;
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_USB_MUX, usb_command_mux, EC_VER_MASK(0));