529 lines
15 KiB
C
529 lines
15 KiB
C
/* 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 "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 test_mockable implementations in charge_ramp module */
|
|
int chg_ramp_allowed(int supplier)
|
|
{
|
|
/* Ramp for TEST4-TEST8 */
|
|
return supplier > CHARGE_SUPPLIER_TEST3;
|
|
}
|
|
|
|
int chg_ramp_max(int supplier, int sup_curr)
|
|
{
|
|
if (supplier == CHARGE_SUPPLIER_TEST7)
|
|
return 1600;
|
|
else if (supplier == CHARGE_SUPPLIER_TEST8)
|
|
return 2400;
|
|
else
|
|
return 3000;
|
|
}
|
|
|
|
/* These usb_charger functions are unused, but necessary to link */
|
|
int usb_charger_ramp_allowed(int supplier)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int usb_charger_ramp_max(int supplier, int sup_curr)
|
|
{
|
|
return 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, 0);
|
|
}
|
|
|
|
/* 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(void)
|
|
{
|
|
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();
|
|
}
|