summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/spring/usb_charging.c92
1 files changed, 89 insertions, 3 deletions
diff --git a/board/spring/usb_charging.c b/board/spring/usb_charging.c
index 7efc698062..a7f3e45728 100644
--- a/board/spring/usb_charging.c
+++ b/board/spring/usb_charging.c
@@ -40,7 +40,10 @@
/* PWM control loop parameters */
#define PWM_CTRL_BEGIN_OFFSET 30
-#define PWM_CTRL_STEP_DOWN 1
+#define PWM_CTRL_OC_MARGIN 15
+#define PWM_CTRL_OC_DETECT_TIME (800 * MSEC)
+#define PWM_CTRL_OC_BACK_OFF 3
+#define PWM_CTRL_STEP_DOWN 2
#define PWM_CTRL_STEP_UP 5
#define PWM_CTRL_VBUS_LOW 4500
#define PWM_CTRL_VBUS_HIGH 4700 /* Must be higher than 4.5V */
@@ -49,6 +52,24 @@ static int current_dev_type = TSU6721_TYPE_NONE;
static int nominal_pwm_duty;
static int current_pwm_duty;
+static enum {
+ LIMIT_NORMAL,
+ LIMIT_AGGRESSIVE,
+} current_limit_mode;
+
+/*
+ * Last time we see a power source removed. Also records the power source
+ * type and PWM duty cycle at that moment.
+ * Index: 0 = Unknown power source.
+ * 1 = Recognized power source.
+ */
+static timestamp_t power_removed_time[2];
+static uint32_t power_removed_type[2];
+static int power_removed_pwm_duty[2];
+
+/* PWM duty cycle limit based on over current event */
+static int over_current_pwm_duty;
+
static enum ilim_config current_ilim_config = ILIM_CONFIG_MANUAL_OFF;
static const int apple_charger_type[4] = {I_LIMIT_500MA,
@@ -180,6 +201,18 @@ void board_pwm_init_limit(void)
board_ilim_config(ILIM_CONFIG_MANUAL_ON);
}
+static int board_pwm_check_lower_bound(void)
+{
+ if (current_limit_mode == LIMIT_AGGRESSIVE)
+ return (current_pwm_duty > nominal_pwm_duty -
+ PWM_CTRL_OC_MARGIN &&
+ current_pwm_duty > over_current_pwm_duty &&
+ current_pwm_duty > 0);
+ else
+ return (current_pwm_duty > nominal_pwm_duty &&
+ current_pwm_duty > 0);
+}
+
static void board_pwm_tweak(void)
{
int vbus, current;
@@ -203,8 +236,7 @@ static void board_pwm_tweak(void)
current >= 0) {
board_pwm_duty_cycle(current_pwm_duty + PWM_CTRL_STEP_UP);
CPRINTF("[%T PWM duty up %d%%]\n", current_pwm_duty);
- } else if (vbus > PWM_CTRL_VBUS_HIGH &&
- current_pwm_duty > nominal_pwm_duty) {
+ } else if (vbus > PWM_CTRL_VBUS_HIGH && board_pwm_check_lower_bound()) {
board_pwm_duty_cycle(current_pwm_duty - PWM_CTRL_STEP_DOWN);
CPRINTF("[%T PWM duty down %d%%]\n", current_pwm_duty);
}
@@ -235,6 +267,36 @@ static void usb_device_change(int dev_type)
if (current_dev_type == dev_type)
return;
+ over_current_pwm_duty = 0;
+
+ /*
+ * When a power source is removed, record time, power source type,
+ * and PWM duty cycle. Then when we see a power source, compare type
+ * and calculate time difference to determine if we have just
+ * encountered an over current event.
+ */
+ if ((current_dev_type & TSU6721_TYPE_VBUS_DEBOUNCED) &&
+ (dev_type == TSU6721_TYPE_NONE)) {
+ int idx = !(current_dev_type == TSU6721_TYPE_VBUS_DEBOUNCED);
+ power_removed_time[idx] = get_time();
+ power_removed_type[idx] = current_dev_type;
+ /*
+ * Considering user may plug/unplug the charger too fast, we
+ * don't limit current to lower than nominal current limit.
+ */
+ power_removed_pwm_duty[idx] = MIN(current_pwm_duty,
+ nominal_pwm_duty);
+ } else if (dev_type & TSU6721_TYPE_VBUS_DEBOUNCED) {
+ int idx = !(dev_type == TSU6721_TYPE_VBUS_DEBOUNCED);
+ timestamp_t now = get_time();
+ now.val -= power_removed_time[idx].val;
+ if (power_removed_type[idx] == dev_type &&
+ now.val < PWM_CTRL_OC_DETECT_TIME) {
+ over_current_pwm_duty = power_removed_pwm_duty[idx] +
+ PWM_CTRL_OC_BACK_OFF;
+ }
+ }
+
/*
* Supply VBUS if needed. If we toggle power output, wait for a moment,
* and then update device type. To avoid race condition, check if power
@@ -377,3 +439,27 @@ static int command_batdebug(int argc, char **argv)
}
DECLARE_CONSOLE_COMMAND(batdebug, command_batdebug,
NULL, NULL, NULL);
+
+static int command_current_limit_mode(int argc, char **argv)
+{
+ if (1 == argc) {
+ if (current_limit_mode == LIMIT_NORMAL)
+ ccprintf("Normal mode\n");
+ else
+ ccprintf("Aggressive mode\n");
+ return EC_SUCCESS;
+ } else if (2 == argc) {
+ if (!strcasecmp(argv[1], "normal"))
+ current_limit_mode = LIMIT_NORMAL;
+ else if (!strcasecmp(argv[1], "aggressive"))
+ current_limit_mode = LIMIT_AGGRESSIVE;
+ else
+ return EC_ERROR_INVAL;
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_INVAL;
+}
+DECLARE_CONSOLE_COMMAND(limitmode, command_current_limit_mode,
+ "[normal | aggressive]",
+ "Set current limit mode",
+ NULL);