blob: 84cac57b8e97e8f4fadf6f22517a11452b5106a8 [file] [log] [blame]
/* Copyright 2015 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.
*
* Test AC input current ramp.
*/
#include "charge_manager.h"
#include "charge_ramp.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "task.h"
#include "test_util.h"
#include "timer.h"
#include "usb_charge.h"
#include "util.h"
#define TASK_EVENT_OVERCURRENT (1 << 0)
#define RAMP_STABLE_DELAY (120*SECOND)
/*
* Time to delay for detecting the charger type. This value follows
* the value in common/charge_ramp.c, but must be less than the real
* CHARGE_DETECT_DELAY so we guarantee we wake up before the ramp
* has started.
*/
#define CHARGE_DETECT_DELAY_TEST (CHARGE_DETECT_DELAY - 100*MSEC)
static int system_load_current_ma;
static int vbus_low_current_ma = 500;
static int overcurrent_current_ma = 3000;
static int charge_limit_ma;
/* Mock functions */
__override uint8_t board_get_usb_pd_port_count(void)
{
return CONFIG_USB_PD_PORT_MAX_COUNT;
}
/* Override test_mockable implementations in charge_ramp module */
int chg_ramp_allowed(int port, int supplier)
{
/* Ramp for TEST4-TEST8 */
return supplier > CHARGE_SUPPLIER_TEST3;
}
int chg_ramp_max(int port, int supplier, int sup_curr)
{
if (supplier == CHARGE_SUPPLIER_TEST7)
return 1600;
else if (supplier == CHARGE_SUPPLIER_TEST8)
return 2400;
else
return 3000;
}
/* Mock bc12_ports[] array to make linker happy */
struct bc12_config bc12_ports[0];
int charge_is_consuming_full_input_current(void)
{
return charge_limit_ma <= system_load_current_ma;
}
int board_is_vbus_too_low(int port, enum chg_ramp_vbus_state ramp_state)
{
return MIN(system_load_current_ma, charge_limit_ma) >
vbus_low_current_ma;
}
void board_set_charge_limit(int port, int supplier, int limit_ma,
int max_ma, int max_mv)
{
charge_limit_ma = limit_ma;
if (charge_limit_ma > overcurrent_current_ma)
task_set_event(TASK_ID_TEST_RUNNER, TASK_EVENT_OVERCURRENT);
}
/* Test utilities */
static void plug_charger_with_ts(int supplier_type, int port, int min_current,
int vbus_low_current, int overcurrent_current,
timestamp_t reg_time)
{
vbus_low_current_ma = vbus_low_current;
overcurrent_current_ma = overcurrent_current;
chg_ramp_charge_supplier_change(port, supplier_type, min_current,
reg_time, 0);
}
static void plug_charger(int supplier_type, int port, int min_current,
int vbus_low_current, int overcurrent_current)
{
plug_charger_with_ts(supplier_type, port, min_current,
vbus_low_current, overcurrent_current,
get_time());
}
static void unplug_charger(void)
{
chg_ramp_charge_supplier_change(CHARGE_PORT_NONE, CHARGE_SUPPLIER_NONE,
0, get_time(), 0);
}
static int unplug_charger_and_check(void)
{
unplug_charger();
usleep(CHARGE_DETECT_DELAY_TEST);
return charge_limit_ma == 0;
}
static int wait_stable_no_overcurrent(void)
{
return task_wait_event(RAMP_STABLE_DELAY) != TASK_EVENT_OVERCURRENT;
}
static int is_in_range(int x, int min, int max)
{
return x >= min && x <= max;
}
/* Tests */
static int test_no_ramp(void)
{
system_load_current_ma = 3000;
/* A powerful charger, but hey, you're not allowed to ramp! */
plug_charger(CHARGE_SUPPLIER_TEST1, 0, 500, 3000, 3000);
/*
* NOTE: Since this is currently the first test being run, give the
* charge ramp task enough time to actually transition states and set
* the charge limit. This just needs at least transition to the
* CHG_RAMP_OVERCURRENT_DETECT state.
*/
usleep(CHARGE_DETECT_DELAY_TEST + 200*MSEC);
/* That's right. Start at 500 mA */
TEST_ASSERT(charge_limit_ma == 500);
TEST_ASSERT(wait_stable_no_overcurrent());
/* ... and stays at 500 mA */
TEST_ASSERT(charge_limit_ma == 500);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_full_ramp(void)
{
system_load_current_ma = 3000;
/* Now you get to ramp with this 3A charger */
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 3000);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Start with something around 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 800));
TEST_ASSERT(wait_stable_no_overcurrent());
/* And ramp up to 3A */
TEST_ASSERT(charge_limit_ma == 3000);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_vbus_dip(void)
{
system_load_current_ma = 3000;
/* VBUS dips too low right before the charger shuts down */
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 1000, 1500, 1600);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1300, 1500));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_overcurrent(void)
{
system_load_current_ma = 3000;
/* Huh...VBUS doesn't dip before the charger shuts down */
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 3000, 1500);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp starts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
while (task_wait_event(RAMP_STABLE_DELAY) == TASK_EVENT_OVERCURRENT) {
/* Charger goes away but comes back after 0.6 seconds */
unplug_charger();
usleep(MSEC * 600);
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 3000, 1500);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp restarts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
}
TEST_ASSERT(is_in_range(charge_limit_ma, 1300, 1500));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_switch_outlet(void)
{
int i;
system_load_current_ma = 3000;
/* Here's a nice powerful charger */
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 3000, 3000);
/*
* Now the user decides to move it to a nearby outlet...actually
* they decide to move it 5 times!
*/
for (i = 0; i < 5; ++i) {
usleep(SECOND * 20);
unplug_charger();
usleep(SECOND * 1.5);
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 3000, 3000);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp restarts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
}
/* Should still ramp up to 3000 mA */
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 3000);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_fast_switch(void)
{
int i;
system_load_current_ma = 3000;
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 3000);
/*
* Here comes that naughty user again, and this time they are switching
* outlet really quickly. Fortunately this time they only do it twice.
*/
for (i = 0; i < 2; ++i) {
usleep(SECOND * 20);
unplug_charger();
usleep(600 * MSEC);
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 3000);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp restarts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
}
/* Should still ramp up to 3000 mA */
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 3000);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_overcurrent_after_switch_outlet(void)
{
system_load_current_ma = 3000;
/* Here's a less powerful charger */
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 500, 3000, 1500);
usleep(SECOND * 5);
/* Now the user decides to move it to a nearby outlet */
unplug_charger();
usleep(SECOND * 1.5);
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 500, 3000, 1500);
/* Okay the user is satisified */
while (task_wait_event(RAMP_STABLE_DELAY) == TASK_EVENT_OVERCURRENT) {
/* Charger goes away but comes back after 0.6 seconds */
unplug_charger();
usleep(MSEC * 600);
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 500, 3000, 1500);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp restarts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
}
TEST_ASSERT(is_in_range(charge_limit_ma, 1300, 1500));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_partial_load(void)
{
/* We have a 3A charger, but we just want 1.5A */
system_load_current_ma = 1500;
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 2500);
/* We should end up with a little bit more than 1.5A */
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1500, 1600));
/* Ok someone just started watching YouTube */
system_load_current_ma = 2000;
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 2000, 2100));
/* Somehow the system load increases again */
system_load_current_ma = 2600;
while (task_wait_event(RAMP_STABLE_DELAY) == TASK_EVENT_OVERCURRENT) {
/* Charger goes away but comes back after 0.6 seconds */
unplug_charger();
usleep(MSEC * 600);
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 2500);
usleep(CHARGE_DETECT_DELAY_TEST);
/* Ramp restarts at 500 mA */
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
}
/* Alright the charger isn't powerful enough, so we'll stop at 2.5A */
TEST_ASSERT(is_in_range(charge_limit_ma, 2300, 2500));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_charge_supplier_stable(void)
{
system_load_current_ma = 3000;
/* The charger says it's of type TEST4 initially */
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 1500, 1600);
/*
* And then it decides it's actually TEST2 after 0.5 seconds,
* why? Well, this charger is just evil.
*/
usleep(500 * MSEC);
plug_charger(CHARGE_SUPPLIER_TEST2, 0, 3000, 3000, 3000);
/* We should get 3A right away. */
usleep(SECOND);
TEST_ASSERT(charge_limit_ma == 3000);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_charge_supplier_stable_ramp(void)
{
system_load_current_ma = 3000;
/* This time we start with a non-ramp charge supplier */
plug_charger(CHARGE_SUPPLIER_TEST3, 0, 500, 3000, 3000);
/*
* After 0.5 seconds, it's decided that the supplier is actually
* a 1.5A ramp supplier.
*/
usleep(500 * MSEC);
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 500, 1400, 1500);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1200, 1400));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_charge_supplier_change(void)
{
system_load_current_ma = 3000;
/* Start with a 3A ramp charge supplier */
plug_charger(CHARGE_SUPPLIER_TEST4, 0, 500, 3000, 3000);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 3000);
/* The charger decides to change type to a 1.5A non-ramp supplier */
plug_charger(CHARGE_SUPPLIER_TEST1, 0, 1500, 3000, 3000);
usleep(500 * MSEC);
TEST_ASSERT(charge_limit_ma == 1500);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 1500);
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_charge_port_change(void)
{
system_load_current_ma = 3000;
/* Start with a 1.5A ramp charge supplier on port 0 */
plug_charger(CHARGE_SUPPLIER_TEST5, 0, 500, 1400, 1500);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1200, 1400));
/* Here comes a 2.1A ramp charge supplier on port 1 */
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 2000, 2100);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1800, 2000));
/* Now we have a 2.5A non-ramp charge supplier on port 0 */
plug_charger(CHARGE_SUPPLIER_TEST1, 0, 2500, 3000, 3000);
usleep(SECOND);
TEST_ASSERT(charge_limit_ma == 2500);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 2500);
/* Unplug on port 0 */
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 2000, 2100);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1800, 2000));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_vbus_shift(void)
{
system_load_current_ma = 3000;
/*
* At first, the charger is able to supply up to 1900 mA before
* the VBUS voltage starts to drop.
*/
plug_charger(CHARGE_SUPPLIER_TEST6, 0, 500, 1900, 2000);
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1700, 1900));
/* The charger heats up and VBUS voltage drops by 100mV */
vbus_low_current_ma = 1800;
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1600, 1800));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_equal_priority_overcurrent(void)
{
int overcurrent_count = 0;
timestamp_t oc_time = get_time();
system_load_current_ma = 3000;
/*
* Now we have two charge suppliers of equal priorties plugged into
* port 0 and port 1. If the active one browns out, charge manager
* switches to the other one.
*/
while (1) {
plug_charger_with_ts(CHARGE_SUPPLIER_TEST4, 0, 500, 3000,
2000, oc_time);
oc_time = get_time();
oc_time.val += 600 * MSEC;
if (wait_stable_no_overcurrent())
break;
plug_charger_with_ts(CHARGE_SUPPLIER_TEST4, 1, 500, 3000,
2000, oc_time);
oc_time = get_time();
oc_time.val += 600 * MSEC;
if (wait_stable_no_overcurrent())
break;
if (overcurrent_count++ >= 10) {
/*
* Apparently we are in a loop and can never reach
* stable state.
*/
unplug_charger();
return EC_ERROR_UNKNOWN;
}
}
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
static int test_ramp_limit(void)
{
system_load_current_ma = 3000;
/* Plug in supplier that is limited to 1.6A */
plug_charger(CHARGE_SUPPLIER_TEST7, 0, 500, 3000, 3000);
usleep(SECOND);
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 1600);
/* Switch to supplier that is limited to 2.4A */
plug_charger(CHARGE_SUPPLIER_TEST8, 1, 500, 3000, 3000);
usleep(SECOND);
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(charge_limit_ma == 2400);
/* Go back to 1.6A limited, but VBUS goes low before that point */
plug_charger(CHARGE_SUPPLIER_TEST7, 0, 500, 1200, 1300);
usleep(SECOND);
TEST_ASSERT(is_in_range(charge_limit_ma, 500, 700));
TEST_ASSERT(wait_stable_no_overcurrent());
TEST_ASSERT(is_in_range(charge_limit_ma, 1000, 1200));
TEST_ASSERT(unplug_charger_and_check());
return EC_SUCCESS;
}
void run_test(int argc, char **argv)
{
test_reset();
/*
* If the following test order changes, make sure to add enough time for
* the charge ramp task to make its first transition after plugging in a
* charger. See the comment in test_no_ramp().
*/
RUN_TEST(test_no_ramp);
RUN_TEST(test_full_ramp);
RUN_TEST(test_vbus_dip);
RUN_TEST(test_overcurrent);
RUN_TEST(test_switch_outlet);
RUN_TEST(test_fast_switch);
RUN_TEST(test_overcurrent_after_switch_outlet);
RUN_TEST(test_partial_load);
RUN_TEST(test_charge_supplier_stable);
RUN_TEST(test_charge_supplier_stable_ramp);
RUN_TEST(test_charge_supplier_change);
RUN_TEST(test_charge_port_change);
RUN_TEST(test_vbus_shift);
RUN_TEST(test_equal_priority_overcurrent);
RUN_TEST(test_ramp_limit);
test_print_result();
}