blob: defc97115e5972893edf2395317e888bfba9fb28 [file] [log] [blame]
/* 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 "bq24192.h"
#include "charger.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};
static int bq24192_read(int reg, int *value)
{
return i2c_read8(I2C_PORT_CHARGER, BQ24192_ADDR, reg, value);
}
static int bq24192_write(int reg, int value)
{
return i2c_write8(I2C_PORT_CHARGER, 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;
if (charger_device_id(&val) || val != BQ24192_DEVICE_ID) {
CPRINTF("BQ24192 incorrent ID: 0x%02x\n", val);
return;
}
/*
* Disable I2C watchdog timer.
*
* TODO(crosbug.com/p/22238): Re-enable watchdog timer and kick it
* periodically in charger task.
*/
if (bq24192_read(BQ24192_REG_CHG_TERM_TMR, &val))
return;
val &= ~0x30;
if (bq24192_write(BQ24192_REG_CHG_TERM_TMR, val))
return;
if (bq24192_set_terminate_current(128))
return;
if (bq24192_watchdog_reset())
return;
CPRINTF("BQ24192 initialized\n");
}
DECLARE_HOOK(HOOK_INIT, bq24192_init, HOOK_PRIO_LAST);
/*****************************************************************************/
/* Console commands */
#ifdef CONFIG_CMD_CHARGER
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);
#endif