diff options
-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 */ |