mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-08 16:41:55 +00:00
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>
152 lines
4.4 KiB
C
152 lines
4.4 KiB
C
/* 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);
|
|
}
|
|
}
|