charge_manager: Add module for managing battery charge limits

charge_manager is intended to manage charge limits from various tasks
(typically PD charge tasks and USB / BC 1.2 charge tasks). These tasks
can update the charge limit of a port by calling charge_manager_update
(thread-safe function). If the charge limit has changed,
charge_manager_refresh will be queued as a deferred task, which will
select the "best" charge port and set the proper charge limit.

In order to use charge_manager, a board needs to do the following:

1. Declare PD_PORT_COUNT in usb_pd_config.h
2. Implement board_set_charge_limit
3. Implement board_set_active_charge_port
4. Call charge_manager_update whenever the available charge on a port changes.

BUG=chrome-os-partner:31361
TEST=Manual on samus_pd, with subsequent commit. Insert and remove
various chargers, check console to verify PD charger always has priority
and correct current limit is set based upon 'best' charger.
BRANCH=samus

Change-Id: Iede120b69e0b46ed329bcf9b7e07c39ba5e9f77b
Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/222723
Reviewed-by: Alec Berg <alecaberg@chromium.org>
This commit is contained in:
Shawn Nematbakhsh
2014-10-09 18:45:34 -07:00
committed by chrome-internal-fetch
parent bcc5057ad3
commit b8f73a451d
3 changed files with 190 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ common-$(CONFIG_BATTERY_BQ27541)+=battery.o
common-$(CONFIG_BATTERY_SMART)+=battery.o
common-$(CONFIG_BUTTON_COUNT)+=button.o
common-$(CONFIG_CAPSENSE)+=capsense.o
common-$(CONFIG_CHARGE_MANAGER)+=charge_manager.o
common-$(CONFIG_CHARGER)+=charger.o
common-$(CONFIG_CHARGER_V1)+=charge_state_v1.o
common-$(CONFIG_CHARGER_V2)+=charge_state_v2.o

151
common/charge_manager.c Normal file
View File

@@ -0,0 +1,151 @@
/* Copyright (c) 2014 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.
*/
#include "charge_manager.h"
#include "console.h"
#include "hooks.h"
#include "usb_pd_config.h"
#include "util.h"
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
/* Keep track of available charge for each charge port. */
static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT]
[PD_PORT_COUNT];
/* Store current state of port enable / charge current. */
static int charge_port = CHARGE_PORT_NONE;
static int charge_current = CHARGE_CURRENT_UNINITIALIZED;
/**
* Initialize available charge. Run before board init, so board init can
* initialize data, if needed.
*/
static void charge_manager_init(void)
{
int i, j;
for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i)
for (j = 0; j < PD_PORT_COUNT; ++j) {
available_charge[i][j].current =
CHARGE_CURRENT_UNINITIALIZED;
available_charge[i][j].voltage =
CHARGE_VOLTAGE_UNINITIALIZED;
}
}
DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_DEFAULT-1);
/**
* Returns 1 if all ports + suppliers have reported in with some initial charge,
* 0 otherwise.
*/
static int charge_manager_is_seeded(void)
{
/* Once we're seeded, we don't need to check again. */
static int is_seeded;
int i, j;
if (is_seeded)
return 1;
for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i)
for (j = 0; j < PD_PORT_COUNT; ++j)
if (available_charge[i][j].current ==
CHARGE_CURRENT_UNINITIALIZED ||
available_charge[i][j].voltage ==
CHARGE_VOLTAGE_UNINITIALIZED)
return 0;
is_seeded = 1;
return 1;
}
/**
* Charge manager refresh -- responsible for selecting the active charge port
* and charge power. Called as a deferred task.
*/
static void charge_manager_refresh(void)
{
enum charge_supplier new_supplier = CHARGE_SUPPLIER_NONE;
int new_port = CHARGE_PORT_NONE;
int new_charge_current, new_charge_voltage, i, j;
/*
* Charge supplier selection logic:
* 1. Prefer higher priority (lower CHARGE_SUPPLIER index) supply.
* 2. Prefer higher power over lower.
* available_charge can be changed at any time by other tasks,
* so make no assumptions about its consistency.
*/
for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i)
for (j = 0; j < PD_PORT_COUNT; ++j)
if (available_charge[i][j].current > 0 &&
available_charge[i][j].voltage > 0) {
new_supplier = i;
new_port = j;
goto got_supplier;
}
got_supplier:
if (new_supplier != CHARGE_SUPPLIER_NONE)
for (i = new_port + 1; i < PD_PORT_COUNT; ++i)
if (POWER(available_charge[new_supplier][i]) >
POWER(available_charge[new_supplier][new_port]))
new_port = i;
if (new_supplier == CHARGE_SUPPLIER_NONE)
new_charge_current = new_charge_voltage = 0;
else {
new_charge_current =
available_charge[new_supplier][new_port].current;
new_charge_voltage =
available_charge[new_supplier][new_port].voltage;
}
/* Change the charge limit + charge port if changed. */
if (new_port != charge_port || new_charge_current != charge_current) {
CPRINTS("New charge limit: supplier %d port %d current %d "
"voltage %d", new_supplier, new_port,
new_charge_current, new_charge_voltage);
board_set_charge_limit(new_charge_current);
board_set_active_charge_port(new_port);
charge_current = new_charge_current;
charge_port = new_port;
}
}
DECLARE_DEFERRED(charge_manager_refresh);
/**
* Update available charge for a given port / supplier.
*
* @param supplier Charge supplier to update.
* @param charge_port Charge port to update.
* @param charge Charge port current / voltage.
*/
void charge_manager_update(enum charge_supplier supplier,
int charge_port,
struct charge_port_info *charge)
{
/* Update charge table if needed. */
if (available_charge[supplier][charge_port].current !=
charge->current ||
available_charge[supplier][charge_port].voltage !=
charge->voltage) {
available_charge[supplier][charge_port].current =
charge->current;
available_charge[supplier][charge_port].voltage =
charge->voltage;
/*
* Don't call charge_manager_refresh unless all ports +
* suppliers have reported in. We don't want to make changes
* to our charge port until we are certain we know what is
* attached.
*/
if (charge_manager_is_seeded())
hook_call_deferred(charge_manager_refresh, 0);
}
}

38
include/charge_manager.h Normal file
View File

@@ -0,0 +1,38 @@
/* Copyright (c) 2014 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.
*/
#ifndef __CHARGE_MANAGER_H
#define __CHARGE_MANAGER_H
/* Charge port that indicates no active port */
#define CHARGE_PORT_NONE -1
/* Initial charge state */
#define CHARGE_CURRENT_UNINITIALIZED -1
#define CHARGE_VOLTAGE_UNINITIALIZED -1
#define POWER(charge_port) ((charge_port.current) * (charge_port.voltage))
/* Charge suppliers, sorted by priority */
enum charge_supplier {
CHARGE_SUPPLIER_NONE = -1,
/* Highest priority supplier first */
CHARGE_SUPPLIER_PD = 0,
CHARGE_SUPPLIER_TYPEC = 1,
CHARGE_SUPPLIER_BC12 = 2,
CHARGE_SUPPLIER_COUNT
};
/* Charge tasks report available current and voltage */
struct charge_port_info {
int current;
int voltage;
};
/* Called by charging tasks to update their available charge */
void charge_manager_update(enum charge_supplier supplier,
int charge_port,
struct charge_port_info *charge);
#endif /* __CHARGE_MANAGER_H */