summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2014-10-09 18:45:34 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-15 18:22:44 +0000
commitb8f73a451d9f0e62aba43dab1184bc1352ee968f (patch)
tree8054a2e14e9041468db21e19d51b709ebf47a9b6
parentbcc5057ad30a1252e7ae994dee58a689c210e302 (diff)
downloadchrome-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.mk1
-rw-r--r--common/charge_manager.c151
-rw-r--r--include/charge_manager.h38
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 */