summaryrefslogtreecommitdiff
path: root/driver/battery/smart.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/battery/smart.c')
-rw-r--r--driver/battery/smart.c356
1 files changed, 356 insertions, 0 deletions
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