diff options
author | Shawn Nematbakhsh <shawnn@chromium.org> | 2014-10-09 18:45:34 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-10-15 18:22:44 +0000 |
commit | b8f73a451d9f0e62aba43dab1184bc1352ee968f (patch) | |
tree | 8054a2e14e9041468db21e19d51b709ebf47a9b6 | |
parent | bcc5057ad30a1252e7ae994dee58a689c210e302 (diff) | |
download | chrome-ec-b8f73a451d9f0e62aba43dab1184bc1352ee968f.tar.gz |
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>
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/charge_manager.c | 151 | ||||
-rw-r--r-- | include/charge_manager.h | 38 |
3 files changed, 190 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index 7627310345..ed2fb92456 100644 --- a/common/build.mk +++ b/common/build.mk @@ -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 diff --git a/common/charge_manager.c b/common/charge_manager.c new file mode 100644 index 0000000000..a7a787247d --- /dev/null +++ b/common/charge_manager.c @@ -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); + } +} diff --git a/include/charge_manager.h b/include/charge_manager.h new file mode 100644 index 0000000000..a5b4b6d275 --- /dev/null +++ b/include/charge_manager.h @@ -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 */ |