blob: 785cf8603fb7f6b28c91e07bf806d2ae6391bb36 [file]
/*
* 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.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* Smart battery driver
*/
#include <battery.h>
#include <common.h>
#include <i2c.h>
#include <smartbat.h>
/* Registers we can read/write */
enum {
REG_TEMPERATURE = 8,
REG_VOLTAGE,
REG_AVERAGE_CURRENT = 0xb,
REG_RELATIVE_CHARGE = 0xd,
REG_BATTERY_STATUS = 0x16,
REG_DESIGN_CAP = 0x18,
REG_DESIGN_VOLTAGE,
REG_SPEC_INFO,
REG_MANUF_DATE,
REG_SERIAL_NUM,
REG_MANUF_NAME = 0x20,
REG_DEVICE_NAME,
REG_DEVICE_CHEM,
};
static struct {
int bus;
int addr;
int old_bus;
} config;
#if 0 /* Currently not needed */
/**
* Write a value to a register
*
* @param reg_addr register address to write
* @param value value to be written
* @return 0 on success, non-0 on failure
*/
static int smartbat_i2c_write(unsigned int reg_addr, unsigned value)
{
uint8_t buff[2];
buff[0] = value & 0xff;
buff[1] = (value >> 8) & 0xff;
return i2c_write(config.addr, reg_addr, 1, buff, 2);
}
#endif
/**
* Read a halfword from the smartbat
*
* @param reg_addr register address to read
* @param value address to store the value to be read
* @return 0 on success, non-0 on failure
*/
static int smartbat_i2c_read(unsigned int reg_addr, unsigned *value)
{
uint8_t buff[2];
if (i2c_read(config.addr, reg_addr, 1, buff, 2)) {
debug("%s: Read failure, reg %#x\n", __func__, reg_addr);
return -1;
}
*value = buff[0] + (buff[1] << 8);
return 0;
}
/**
* Read a string from the smartbat
*
* The string starts with a length byte, so we read that first.
*
* @param reg_addr register address to read
* @param str Place to put string that is read.
* @param maxlen Maximum length of string to read, should be
* at least 2 bytes (1 for the length, another
* for the \0 terminator)
* @return 0 on success, non-0 on failure
*/
static int smartbat_i2c_read_string(unsigned int reg_addr, char *str,
int maxlen)
{
uint8_t len;
assert(maxlen >= 2);
if (i2c_read(config.addr, reg_addr, 1, &len, 1)) {
debug("%s: Read failure, addr %u, reg %#x\n", __func__,
config.addr, reg_addr);
return -1;
}
len = min(len, maxlen - 2);
if (i2c_read(config.addr, reg_addr, 1, (uint8_t *)str, len + 1)) {
debug("%s: Read failure, len %d\n", __func__, len);
return -1;
}
memmove(str, str + 1, len);
str[len] = '\0';
return 0;
}
static int smartbat_select(void)
{
int ret;
config.old_bus = i2c_get_bus_num();
if (config.old_bus != config.bus) {
debug("%s: Select bus %d\n", __func__, config.bus);
ret = i2c_set_bus_num(config.bus);
if (ret) {
debug("%s: Cannot select battery, err %d\n",
__func__, ret);
return -1;
}
}
return 0;
}
static int smartbat_deselect(void)
{
int ret;
if (config.old_bus != i2c_get_bus_num()) {
ret = i2c_set_bus_num(config.old_bus);
debug("%s: Select bus %d\n", __func__, config.old_bus);
if (ret) {
debug("%s: Cannot restore i2c bus, err %d\n",
__func__, ret);
return -1;
}
}
config.old_bus = -1;
return 0;
}
int smartbat_get_temperature(int *milli_degrees_c)
{
unsigned temp;
int ret;
if (smartbat_select())
return -1;
ret = smartbat_i2c_read(REG_TEMPERATURE, &temp);
smartbat_deselect();
if (ret)
return -1;
/* Convert to milli-degress celsius */
*milli_degrees_c = (temp * 100) - 273150;
return 0;
}
int smartbat_get_info(struct battery_info *info)
{
unsigned value;
int err;
if (smartbat_select())
return -1;
err = smartbat_i2c_read_string(REG_MANUF_NAME, info->manuf_name,
sizeof(info->manuf_name));
err |= smartbat_i2c_read_string(REG_DEVICE_NAME, info->device_name,
sizeof(info->manuf_name));
err |= smartbat_i2c_read_string(REG_DEVICE_CHEM, info->device_chem,
sizeof(info->manuf_name));
err |= smartbat_i2c_read(REG_SERIAL_NUM, &info->serial_num);
err |= smartbat_i2c_read(REG_DESIGN_CAP, &info->design_cap_mah);
err |= smartbat_i2c_read(REG_DESIGN_VOLTAGE, &info->design_voltage_mv);
err |= smartbat_i2c_read(REG_VOLTAGE, &info->voltage_mv);
err |= smartbat_i2c_read(REG_AVERAGE_CURRENT, &value);
info->average_current_ma = (short)value;
err |= smartbat_i2c_read(REG_RELATIVE_CHARGE, &info->relative_charge);
err |= smartbat_i2c_read(REG_BATTERY_STATUS, &info->battery_status);
smartbat_deselect();
return err;
}
int smartbat_get_status(void)
{
unsigned int status;
int err;
if (smartbat_select())
return -1;
err = smartbat_i2c_read(REG_BATTERY_STATUS, &status);
smartbat_deselect();
return err ? -1 : status;
}
int smartbat_init(void)
{
int ret;
/* TODO(sjg): Move to fdt */
config.old_bus = -1;
config.bus = 4;
config.addr = 0x0b;
if (smartbat_select())
return -1;
/* Probe the chip */
ret = i2c_probe(config.addr);
smartbat_deselect();
return ret;
}