blob: 027062940e6fe09ed0bfb0e759999c80657542a8 [file] [log] [blame]
/* 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 pack vendor provided charging profile
*/
#include "battery.h"
#include "battery_smart.h"
#include "gpio.h"
#include "host_command.h"
#include "util.h"
#include "console.h"
/* Console output macros */
#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
/* These 2 defines are for cut_off command for 3S battery */
#define SB_SHIP_MODE_ADDR 0x3a
#define SB_SHIP_MODE_DATA 0xc574
static struct battery_info *battery_info;
static int support_cut_off;
struct battery_device {
char manuf[9];
char device[9];
int design_mv;
struct battery_info *battery_info;
int support_cut_off;
};
/*
* Used for the case that battery cannot be detected, such as the pre-charge
* case. In this case, we need to provide the battery with the enough voltage
* (usually the highest voltage among batteries, but the smallest precharge
* current). This should be as conservative as possible.
*/
static struct battery_info info_precharge = {
/*
* Design voltage
* max = 8.4V
* normal = 7.4V
* min = 6.0V
*/
.voltage_max = 12600, /* the max voltage among batteries */
.voltage_normal = 11100,
.voltage_min = 9000,
/* Pre-charge current: I <= 0.01C */
.precharge_current = 64, /* mA, the min current among batteries */
/*
* 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 = 0,
.discharging_max_c = 60,
};
static struct battery_info info_2s = {
/*
* 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 */
/*
* 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 struct battery_info info_3s = {
.voltage_max = 12600,
.voltage_normal = 11100, /* Average of max & min */
.voltage_min = 9000,
/* Pre-charge values. */
.precharge_current = 392, /* mA */
.start_charging_min_c = 0,
.start_charging_max_c = 60,
.charging_min_c = 0,
.charging_max_c = 60,
.discharging_min_c = 0,
.discharging_max_c = 50,
};
static struct battery_device support_batteries[] = {
{
.manuf = "NVT",
.device = "ARROW",
.design_mv = 7400,
.battery_info = &info_2s,
.support_cut_off = 0,
},
{
.manuf = "SANYO",
.device = "AP13J3K",
.design_mv = 11250,
.battery_info = &info_3s,
.support_cut_off = 1,
},
{
.manuf = "SONYCorp",
.device = "AP13J4K",
.design_mv = 11400,
.battery_info = &info_3s,
.support_cut_off = 1,
}
};
#ifdef CONFIG_BATTERY_OVERRIDE_PARAMS
/*
* The following parameters are for 2S battery.
* There is no corresponding params for 3S battery.
*/
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},
};
static inline void limit_value(int *val, int limit)
{
if (*val > limit)
*val = limit;
}
void battery_override_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);
if (battery_info == NULL)
return;
/* Return if the battery is not a 2S battery */
if (battery_info->voltage_max != info_2s.voltage_max)
return;
/* Limit charging voltage */
if (batt->desired_voltage > battery_info->voltage_max)
batt->desired_voltage = battery_info->voltage_max;
/* Don't charge if outside of allowable temperature range */
if (bat_temp_c >= battery_info->charging_max_c ||
bat_temp_c < battery_info->charging_min_c) {
batt->desired_voltage = 0;
batt->desired_current = 0;
batt->flags &= ~BATT_FLAG_WANT_CHARGE;
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 < battery_info->precharge_current)
*desired_current = battery_info->precharge_current;
}
#endif /* CONFIG_BATTERY_OVERRIDE_PARAMS */
const struct battery_info *battery_get_info(void)
{
int i;
char manuf[9];
char device[9];
int design_mv;
if (battery_manufacturer_name(manuf, sizeof(manuf))) {
CPRINTS("Failed to get MANUF name");
return &info_precharge;
}
if (battery_device_name(device, sizeof(device))) {
CPRINTS("Failed to get DEVICE name");
return &info_precharge;
}
if (battery_design_voltage((int *)&design_mv)) {
CPRINTS("Failed to get DESIGN_VOLTAGE");
return &info_precharge;
}
for (i = 0; i < ARRAY_SIZE(support_batteries); ++i) {
if ((strcasecmp(support_batteries[i].manuf, manuf) == 0) &&
(strcasecmp(support_batteries[i].device, device) == 0) &&
(support_batteries[i].design_mv == design_mv)) {
CPRINTS("battery Manuf:%s, Device=%s, design=%u",
manuf, device, design_mv);
support_cut_off = support_batteries[i].support_cut_off;
battery_info = support_batteries[i].battery_info;
return battery_info;
}
}
CPRINTS("un-recognized battery Manuf:%s, Device=%s",
manuf, device);
return &info_precharge;
}
int board_cut_off_battery(void)
{
if (support_cut_off)
return sb_write(SB_SHIP_MODE_ADDR, SB_SHIP_MODE_DATA);
else
return EC_RES_INVALID_COMMAND;
}