summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/samus_pd/board.c162
-rw-r--r--board/samus_pd/board.h4
-rw-r--r--common/charge_manager.c20
-rw-r--r--common/charge_state_v2.c45
-rw-r--r--common/lightbar.c13
-rw-r--r--include/charge_manager.h1
6 files changed, 201 insertions, 44 deletions
diff --git a/board/samus_pd/board.c b/board/samus_pd/board.c
index 65e9f03bf7..8359d770f2 100644
--- a/board/samus_pd/board.c
+++ b/board/samus_pd/board.c
@@ -34,6 +34,11 @@ static enum power_state ps;
/* Battery state of charge */
static int batt_soc;
+static int fake_state_of_charge = -1; /* use real soc by default */
+
+/* Last charge port override when charging turned off due to full battery */
+static int chg_override_port = OVERRIDE_OFF;
+static int chg_is_cutoff;
/* PD MCU status and host event status for host command */
static struct ec_response_pd_status pd_status;
@@ -59,19 +64,20 @@ BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT);
static void pericom_port0_reenable_interrupts(void)
{
+ CPRINTS("VBUS p0 %d", gpio_get_level(GPIO_USB_C0_VBUS_WAKE));
pi3usb9281_enable_interrupts(0);
}
DECLARE_DEFERRED(pericom_port0_reenable_interrupts);
static void pericom_port1_reenable_interrupts(void)
{
+ CPRINTS("VBUS p1 %d", gpio_get_level(GPIO_USB_C1_VBUS_WAKE));
pi3usb9281_enable_interrupts(1);
}
DECLARE_DEFERRED(pericom_port1_reenable_interrupts);
void vbus0_evt(enum gpio_signal signal)
{
- ccprintf("VBUS %d, %d!\n", signal, gpio_get_level(signal));
/*
* Re-enable interrupts on pericom charger detector since the
* chip may periodically reset itself, and come back up with
@@ -84,7 +90,6 @@ void vbus0_evt(enum gpio_signal signal)
void vbus1_evt(enum gpio_signal signal)
{
- ccprintf("VBUS %d, %d!\n", signal, gpio_get_level(signal));
/*
* Re-enable interrupts on pericom charger detector since the
* chip may periodically reset itself, and come back up with
@@ -210,41 +215,96 @@ void usb1_evt(enum gpio_signal signal)
wake_usb_charger_task(1);
}
-void pch_evt(enum gpio_signal signal)
+/* When battery is full, cutoff charging by disabling AC input current */
+static void check_charging_cutoff(void)
+{
+ int port;
+
+ /* Only check if charging needs to be turned off when not in S0 */
+ if (ps == POWER_S0)
+ return;
+
+ port = charge_manager_get_active_charge_port();
+
+ /*
+ * If battery is full disable charging, if battery is not full, restore
+ * charge port.
+ */
+ if (!chg_is_cutoff && port != CHARGE_PORT_NONE && batt_soc == 100) {
+ charge_manager_set_override(OVERRIDE_DONT_CHARGE);
+ chg_is_cutoff = 1;
+ } else if (chg_is_cutoff && batt_soc < 100) {
+ charge_manager_set_override(chg_override_port);
+ chg_is_cutoff = 0;
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, check_charging_cutoff, HOOK_PRIO_DEFAULT);
+
+static void chipset_s5_to_s3(void)
{
- /* Determine new chipset state, trigger corresponding hook */
+ ps = POWER_S3;
+ hook_notify(HOOK_CHIPSET_STARTUP);
+}
+
+static void chipset_s3_to_s0(void)
+{
+ /* Disable deep sleep and restore charge override port */
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ charge_manager_set_override(chg_override_port);
+ chg_is_cutoff = 0;
+
+ ps = POWER_S0;
+ hook_notify(HOOK_CHIPSET_RESUME);
+}
+
+static void chipset_s3_to_s5(void)
+{
+ ps = POWER_S5;
+ hook_notify(HOOK_CHIPSET_SHUTDOWN);
+}
+
+static void chipset_s0_to_s3(void)
+{
+ /* Enable deep sleep and store charge override port */
+ enable_sleep(SLEEP_MASK_AP_RUN);
+ chg_override_port = charge_manager_get_override();
+
+ ps = POWER_S3;
+ hook_notify(HOOK_CHIPSET_SUSPEND);
+}
+
+static void pch_evt_deferred(void)
+{
+ /* Determine new chipset state, trigger corresponding transition */
switch (ps) {
case POWER_S5:
- if (gpio_get_level(GPIO_PCH_SLP_S5_L)) {
- /* S5 -> S3 */
- hook_notify(HOOK_CHIPSET_STARTUP);
- ps = POWER_S3;
- }
+ if (gpio_get_level(GPIO_PCH_SLP_S5_L))
+ chipset_s5_to_s3();
+ if (gpio_get_level(GPIO_PCH_SLP_S3_L))
+ chipset_s3_to_s0();
break;
case POWER_S3:
- if (gpio_get_level(GPIO_PCH_SLP_S3_L)) {
- /* S3 -> S0: disable deep sleep */
- disable_sleep(SLEEP_MASK_AP_RUN);
- hook_notify(HOOK_CHIPSET_RESUME);
- ps = POWER_S0;
- } else if (!gpio_get_level(GPIO_PCH_SLP_S5_L)) {
- /* S3 -> S5 */
- hook_notify(HOOK_CHIPSET_SHUTDOWN);
- ps = POWER_S5;
- }
+ if (gpio_get_level(GPIO_PCH_SLP_S3_L))
+ chipset_s3_to_s0();
+ else if (!gpio_get_level(GPIO_PCH_SLP_S5_L))
+ chipset_s3_to_s5();
break;
case POWER_S0:
- if (!gpio_get_level(GPIO_PCH_SLP_S3_L)) {
- /* S0 -> S3: enable deep sleep */
- enable_sleep(SLEEP_MASK_AP_RUN);
- hook_notify(HOOK_CHIPSET_SUSPEND);
- ps = POWER_S3;
- }
+ if (!gpio_get_level(GPIO_PCH_SLP_S3_L))
+ chipset_s0_to_s3();
+ if (!gpio_get_level(GPIO_PCH_SLP_S5_L))
+ chipset_s3_to_s5();
break;
default:
break;
}
}
+DECLARE_DEFERRED(pch_evt_deferred);
+
+void pch_evt(enum gpio_signal signal)
+{
+ hook_call_deferred(pch_evt_deferred, 0);
+}
void board_config_pre_init(void)
{
@@ -483,6 +543,7 @@ void board_flip_usb_mux(int port)
void board_update_battery_soc(int soc)
{
batt_soc = soc;
+ check_charging_cutoff();
}
int board_get_battery_soc(void)
@@ -521,8 +582,10 @@ static void pd_send_ec_int(void)
*/
int board_set_active_charge_port(int charge_port)
{
- if (charge_port >= 0 && charge_port < PD_PORT_COUNT &&
- pd_get_role(charge_port) != PD_ROLE_SINK) {
+ /* charge port is a realy physical port */
+ int is_real_port = (charge_port >= 0 && charge_port < PD_PORT_COUNT);
+
+ if (is_real_port && pd_get_role(charge_port) != PD_ROLE_SINK) {
CPRINTS("Skip enable p%d", charge_port);
return EC_ERROR_INVAL;
}
@@ -531,6 +594,17 @@ int board_set_active_charge_port(int charge_port)
gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, !(charge_port == 0));
gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, !(charge_port == 1));
+ /*
+ * If new charge port when charge is cutoff, then user must have
+ * plugged in a new dedicated charger. This resets the charge
+ * override port and clears the charge cutoff flag.
+ */
+ if (chg_is_cutoff && is_real_port) {
+ chg_override_port = OVERRIDE_OFF;
+ chg_is_cutoff = 0;
+ }
+ check_charging_cutoff();
+
CPRINTS("New chg p%d", charge_port);
return EC_SUCCESS;
}
@@ -602,6 +676,36 @@ DECLARE_CONSOLE_COMMAND(pdevent, command_pd_host_event,
"Send PD host event",
NULL);
+static int command_battfake(int argc, char **argv)
+{
+ char *e;
+ int v;
+
+ if (argc == 2) {
+ v = strtoi(argv[1], &e, 0);
+ if (*e || v < -1 || v > 100)
+ return EC_ERROR_PARAM1;
+
+ fake_state_of_charge = v;
+ }
+
+ if (fake_state_of_charge < 0) {
+ ccprintf("Using real batt level\n");
+ } else {
+ ccprintf("Using fake batt level %d%%\n",
+ fake_state_of_charge);
+ }
+
+ /* Send EC int to get batt info from EC */
+ pd_send_ec_int();
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(battfake, command_battfake,
+ "percent (-1 = use real level)",
+ "Set fake battery level",
+ NULL);
+
/****************************************************************************/
/* Host commands */
static int ec_status_host_cmd(struct host_cmd_handler_args *args)
@@ -609,7 +713,9 @@ static int ec_status_host_cmd(struct host_cmd_handler_args *args)
const struct ec_params_pd_status *p = args->params;
struct ec_response_pd_status *r = args->response;
- board_update_battery_soc(p->batt_soc);
+ /* if not using fake soc, then update battery soc */
+ board_update_battery_soc(fake_state_of_charge < 0 ?
+ p->batt_soc : fake_state_of_charge);
*r = pd_status;
diff --git a/board/samus_pd/board.h b/board/samus_pd/board.h
index ea660d1f84..adbb4f02df 100644
--- a/board/samus_pd/board.h
+++ b/board/samus_pd/board.h
@@ -66,9 +66,9 @@
*/
#define CONFIG_SYSTEM_UNLOCKED
-/* 10 deferrable tasks on this board */
+/* Maximum number of deferrable functions */
#undef DEFERRABLE_MAX_COUNT
-#define DEFERRABLE_MAX_COUNT 8
+#define DEFERRABLE_MAX_COUNT 9
#ifndef __ASSEMBLER__
diff --git a/common/charge_manager.c b/common/charge_manager.c
index 651cf3af5e..4b400f137c 100644
--- a/common/charge_manager.c
+++ b/common/charge_manager.c
@@ -347,6 +347,8 @@ int charge_manager_set_override(int port)
ASSERT(port >= OVERRIDE_DONT_CHARGE && port < PD_PORT_COUNT);
+ CPRINTS("Charge Override: %d", port);
+
/* Supersede any pending delayed overrides. */
if (delayed_override_port != OVERRIDE_OFF) {
if (delayed_override_port != port)
@@ -387,6 +389,17 @@ int charge_manager_set_override(int port)
return retval;
}
+/**
+ * Get the override port. OVERRIDE_OFF if no override port.
+ * OVERRIDE_DONT_CHARGE if override is set for no port.
+ *
+ * @return override port
+ */
+int charge_manager_get_override(void)
+{
+ return override_port;
+}
+
int charge_manager_get_active_charge_port(void)
{
return charge_port;
@@ -508,16 +521,19 @@ DECLARE_HOST_COMMAND(EC_CMD_PD_CHARGE_PORT_OVERRIDE,
static int command_charge_port_override(int argc, char **argv)
{
int port = OVERRIDE_OFF;
+ int ret = EC_SUCCESS;
char *e;
if (argc >= 2) {
port = strtoi(argv[1], &e, 0);
if (*e || port < OVERRIDE_DONT_CHARGE || port >= PD_PORT_COUNT)
return EC_ERROR_PARAM1;
+ ret = charge_manager_set_override(port);
}
- ccprintf("Set override: %d\n", port);
- return charge_manager_set_override(port);
+ ccprintf("Override: %d\n", (argc >= 2 && ret == EC_SUCCESS) ?
+ port : override_port);
+ return ret;
}
DECLARE_CONSOLE_COMMAND(chgoverride, command_charge_port_override,
"[port | -1 | -2]",
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
index de7cfea731..051113c0e2 100644
--- a/common/charge_state_v2.c
+++ b/common/charge_state_v2.c
@@ -39,7 +39,8 @@
*/
static const struct battery_info *batt_info;
static struct charge_state_data curr;
-static int prev_ac, prev_charge;
+static int prev_ac, prev_charge, prev_full;
+static int is_full; /* battery not accepting current */
static int state_machine_force_idle;
static int manual_mode; /* volt/curr are no longer maintained by charger */
static unsigned int user_current_limit = -1U;
@@ -275,14 +276,16 @@ static void show_charging_progress(void)
}
if (rv)
- CPRINTS("Battery %d%% / ??h:?? %s",
+ CPRINTS("Battery %d%% / ??h:?? %s%s",
curr.batt.state_of_charge,
- to_full ? "to full" : "to empty");
+ to_full ? "to full" : "to empty",
+ is_full ? ", not accepting current" : "");
else
- CPRINTS("Battery %d%% / %dh:%d %s",
+ CPRINTS("Battery %d%% / %dh:%d %s%s",
curr.batt.state_of_charge,
minutes / 60, minutes % 60,
- to_full ? "to full" : "to empty");
+ to_full ? "to full" : "to empty",
+ is_full ? ", not accepting current" : "");
if (debugging) {
ccprintf("battery:\n");
@@ -294,6 +297,26 @@ static void show_charging_progress(void)
}
}
+/* Calculate if battery is full based on whether it is accepting charge */
+static int calc_is_full(void)
+{
+ static int ret;
+
+ /* If bad state of charge reading, return last value */
+ if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE ||
+ curr.batt.state_of_charge > 100)
+ return ret;
+ /*
+ * Battery is full when SoC is above 90% and battery desired current
+ * is 0. This is necessary because some batteries stop charging when
+ * the SoC still reports <100%, so we need to check desired current
+ * to know if it is actually full.
+ */
+ ret = (curr.batt.state_of_charge >= 90 &&
+ curr.batt.desired_current == 0);
+ return ret;
+}
+
/*
* Ask the charger for some voltage and current. If either value is 0,
* charging is disabled; otherwise it's enabled. Negative values are ignored.
@@ -732,11 +755,17 @@ wait_for_it:
notify_host_of_low_battery();
/* And the EC console */
- if (!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
- curr.batt.state_of_charge != prev_charge) {
+ is_full = calc_is_full();
+ if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) &&
+ curr.batt.state_of_charge != prev_charge) ||
+ (is_full != prev_full)) {
show_charging_progress();
prev_charge = curr.batt.state_of_charge;
+#ifdef HAS_TASK_PDCMD
+ host_command_pd_send_status();
+#endif
}
+ prev_full = is_full;
/* Turn charger off if it's not needed */
if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) {
@@ -882,7 +911,7 @@ int charge_get_percent(void)
* to the battery, that'll be zero, which is probably as good as
* anything.
*/
- return curr.batt.state_of_charge;
+ return is_full ? 100 : curr.batt.state_of_charge;
}
int charge_temp_sensor_get_val(int idx, int *temp_ptr)
diff --git a/common/lightbar.c b/common/lightbar.c
index bc43a6080e..ab9bfa8985 100644
--- a/common/lightbar.c
+++ b/common/lightbar.c
@@ -80,7 +80,7 @@ static const struct lightbar_params_v1 default_params = {
.tap_display_time = 3 * SECOND, /* total sequence time */
.tap_pct_red = 10, /* below this is red */
- .tap_pct_green = 97, /* above this is green */
+ .tap_pct_green = 94, /* above this is green */
.tap_seg_min_on = 35, /* min intensity (%) for "on" */
.tap_seg_max_on = 100, /* max intensity (%) for "on" */
.tap_seg_osc = 50, /* amplitude for charging osc */
@@ -876,8 +876,9 @@ static uint32_t sequence_TAP_inner(int dir)
f_mult = f_mult * gate[i] / FP_SCALE;
- /* Pulse when charging */
- if (st.battery_is_charging) {
+ /* Pulse when charging and not yet full */
+ if (st.battery_is_charging &&
+ st.battery_percent <= st.p.tap_pct_green) {
int scale = (FP_SCALE -
f_osc * cycle_010(w++) / FP_SCALE);
f_mult = f_mult * scale / FP_SCALE;
@@ -906,6 +907,7 @@ static int force_dir = -1;
/* Return 0 (left or none) or 1 (right) */
static int get_tap_direction(void)
{
+ static int last_dir;
int dir = 0;
if (force_dir >= 0)
@@ -914,10 +916,13 @@ static int get_tap_direction(void)
else
pd_exchange_status(&dir);
#endif
- if (dir != 1)
+ if (dir < 0)
+ dir = last_dir;
+ else if (dir != 1)
dir = 0;
CPRINTS("LB tap direction %d", dir);
+ last_dir = dir;
return dir;
}
diff --git a/include/charge_manager.h b/include/charge_manager.h
index d1ee472a50..0b1de3186d 100644
--- a/include/charge_manager.h
+++ b/include/charge_manager.h
@@ -31,6 +31,7 @@ void charge_manager_set_ceil(int port, int ceil);
/* Select an 'override port', which is always the preferred charge port */
int charge_manager_set_override(int port);
+int charge_manager_get_override(void);
/* Returns the current active charge port, as determined by charge manager */
int charge_manager_get_active_charge_port(void);