diff options
Diffstat (limited to 'common/extpower_spring.c')
-rw-r--r-- | common/extpower_spring.c | 970 |
1 files changed, 0 insertions, 970 deletions
diff --git a/common/extpower_spring.c b/common/extpower_spring.c deleted file mode 100644 index 63735deab5..0000000000 --- a/common/extpower_spring.c +++ /dev/null @@ -1,970 +0,0 @@ -/* Copyright (c) 2013 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. - */ - -/* USB charging control for spring board */ - -#include "adc.h" -#include "adc_chip.h" -#include "battery.h" -#include "battery_smart.h" -#include "chipset.h" -#include "clock.h" -#include "console.h" -#include "driver/tsu6721.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_mkbp.h" -#include "pmu_tpschrome.h" -#include "pwm.h" -/* - * TODO(crosbug.com/p/23745): Refactor low-level STM32 ADC code out of this - * module. Files in common should not use chip registers directly. - */ -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define PWM_FREQUENCY 32000 /* Hz */ - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr) -#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -/* ILIM pin control */ -enum ilim_config { - ILIM_CONFIG_MANUAL_OFF, - ILIM_CONFIG_MANUAL_ON, - ILIM_CONFIG_PWM, -}; - -/* Devices that need VBUS power */ -#define POWERED_5000_DEVICE_TYPE (TSU6721_TYPE_OTG) -#define POWERED_3300_DEVICE_TYPE (TSU6721_TYPE_JIG_UART_ON) - -/* Toad cable */ -#define TOAD_DEVICE_TYPE (TSU6721_TYPE_UART | TSU6721_TYPE_AUDIO3) - -/* Voltage threshold of D+ for video */ -#define VIDEO_ID_THRESHOLD 1300 - -/* - * Mapping from PWM duty to current: - * Current = A + B * PWM_Duty - */ -#define PWM_MAPPING_A 2958 -#define PWM_MAPPING_B (-29) - -/* Map current in milli-amps to PWM duty cycle percentage */ -#define MA_TO_PWM(curr) (((curr) - PWM_MAPPING_A) / PWM_MAPPING_B) - -/* PWM controlled current limit */ -#define I_LIMIT_100MA MA_TO_PWM(100) -#define I_LIMIT_500MA MA_TO_PWM(500) -#define I_LIMIT_1000MA MA_TO_PWM(1000) -#define I_LIMIT_1500MA MA_TO_PWM(1500) -#define I_LIMIT_2000MA MA_TO_PWM(2000) -#define I_LIMIT_2400MA MA_TO_PWM(2400) -#define I_LIMIT_3000MA 0 - -/* PWM control loop parameters */ -#define PWM_CTRL_MAX_DUTY I_LIMIT_100MA /* Minimum current */ -#define PWM_CTRL_BEGIN_OFFSET 90 -#define PWM_CTRL_OC_MARGIN 15 -#define PWM_CTRL_OC_DETECT_TIME (1200 * MSEC) -#define PWM_CTRL_OC_BACK_OFF 3 -#define PWM_CTRL_OC_RETRY 2 -#define PWM_CTRL_STEP_DOWN 3 -#define PWM_CTRL_STEP_UP 5 -#define PWM_CTRL_VBUS_HARD_LOW 4400 -#define PWM_CTRL_VBUS_LOW 4500 -#define PWM_CTRL_VBUS_HIGH 4700 /* Must be higher than 4.5V */ -#define PWM_CTRL_VBUS_HIGH_500MA 4550 - -/* Delay before notifying kernel of device type change */ -#define BATTERY_KEY_DELAY (PWM_CTRL_OC_DETECT_TIME + 400 * MSEC) - -/* Delay for signals to settle */ -#define DELAY_POWER_MS 20 -#define DELAY_USB_DP_DN_MS 20 -#define DELAY_ID_MUX_MS 30 -#define CABLE_DET_POLL_MS 100 -#define CABLE_DET_POLL_COUNT 6 - -/* Current sense resistor values */ -#define R_INPUT_MOHM 20 /* mOhm */ -#define R_BATTERY_MOHM 33 /* mOhm */ - -static int current_dev_type = TSU6721_TYPE_NONE; -static int nominal_pwm_duty; -static int current_pwm_duty; -static int user_pwm_duty = -1; - -static int pending_tsu6721_reset; -static int pending_adc_watchdog_disable; -static int pending_dev_type_update; -static int pending_video_power_off; -static int restore_id_mux; - -static enum { - ADC_WATCH_NONE, - ADC_WATCH_TOAD, - ADC_WATCH_USB, -} current_watchdog = ADC_WATCH_NONE; - -struct { - int type; - const char *name; -} const known_dev_types[] = { - {TSU6721_TYPE_OTG, "OTG"}, - {TSU6721_TYPE_USB_HOST, "USB"}, - {TSU6721_TYPE_CHG12, "Type-1/2-Chg"}, - {TSU6721_TYPE_NON_STD_CHG, "Non-Std-Chg"}, - {TSU6721_TYPE_DCP, "DCP"}, - {TSU6721_TYPE_CDP, "CDP"}, - {TSU6721_TYPE_U200_CHG, "U200-Chg"}, - {TSU6721_TYPE_APPLE_CHG, "Apple-Chg"}, - {TSU6721_TYPE_JIG_UART_ON, "Video"}, - {TSU6721_TYPE_AUDIO3, "Audio-3"}, - {TSU6721_TYPE_UART, "UART"}, - {TSU6721_TYPE_VBUS_DEBOUNCED, "Power"} }; - -/* - * 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]; -static int oc_detect_retry[2] = {PWM_CTRL_OC_RETRY, PWM_CTRL_OC_RETRY}; - -/* 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, - I_LIMIT_1000MA, - I_LIMIT_2000MA, - I_LIMIT_2400MA}; - -static int video_power_enabled; - -#define NON_STD_CHARGER_REDETECT_DELAY (600 * MSEC) -static enum { - NO_REDETECT, - REDETECT_SCHEDULED, - REDETECTED, -} charger_need_redetect = NO_REDETECT; -static timestamp_t charger_redetection_time; - -/** - * Directly read discharge current in mA; negative = charging. - */ -static int battery_current(int *current) -{ - int rv, d; - - rv = sb_read(SB_CURRENT, &d); - if (rv) { - *current = 0; - return rv; - } - - *current = (int16_t)d; - return EC_SUCCESS; -} - -static int get_video_power(void) -{ - return video_power_enabled; -} - -static void set_video_power(int enabled) -{ - int power_good; - - pmu_enable_fet(FET_VIDEO, enabled, enabled ? &power_good : NULL); - if (enabled && !power_good) - pmu_enable_fet(FET_VIDEO, 0, NULL); - video_power_enabled = enabled; -} - -static void ilim_use_gpio(void) -{ - pwm_enable(PWM_CH_ILIM, 0); - gpio_set_flags(GPIO_ILIM, GPIO_OUTPUT); -} - -/** - * Set ILIM pin control type. - */ -static void ilim_config(enum ilim_config config) -{ - if (config == current_ilim_config) - return; - current_ilim_config = config; - - switch (config) { - case ILIM_CONFIG_MANUAL_OFF: - case ILIM_CONFIG_MANUAL_ON: - ilim_use_gpio(); - gpio_set_level(GPIO_ILIM, - config == ILIM_CONFIG_MANUAL_ON ? 1 : 0); - break; - case ILIM_CONFIG_PWM: - pwm_enable(PWM_CH_ILIM, 1); - break; - default: - break; - } -} - -/** - * Return Apple charger current limit. - */ -static int apple_charger_current(void) -{ - int vp, vn; - int type = 0; - int data[ADC_CH_COUNT]; - - tsu6721_disable_interrupts(); - tsu6721_mux(TSU6721_MUX_USB); - /* Wait for signal to stablize */ - msleep(DELAY_USB_DP_DN_MS); - adc_read_all_channels(data); - vp = data[ADC_CH_USB_DP_SNS]; - vn = data[ADC_CH_USB_DN_SNS]; - tsu6721_mux(TSU6721_MUX_AUTO); - tsu6721_enable_interrupts(); - if (vp > 1215) - type |= 0x2; - if (vn > 1215) - type |= 0x1; - - return apple_charger_type[type]; -} - -static int hard_current_limit(int limit) -{ - /* - * The PWM duty cycle goes lower than the nominal - * cycle for PWM_CTRL_OC_MARGIN. Therefore, increase duty cycle by - * PWM_CTRL_OC_MARGIN avoids going over the hard limit. - * (Note that lower PWM cycle translates to higher current) - */ - return MIN(limit + PWM_CTRL_OC_MARGIN, 100); -} - -static int video_dev_type(int device_type) -{ - return (device_type & ~TSU6721_TYPE_USB_HOST) | - TSU6721_TYPE_JIG_UART_ON; -} - -static int usb_video_id_present(void) -{ - return adc_read_channel(ADC_CH_USB_DP_SNS) > VIDEO_ID_THRESHOLD; -} - -static int usb_poll_video_id(void) -{ - int i; - for (i = 0; i < CABLE_DET_POLL_COUNT; ++i) { - msleep(CABLE_DET_POLL_MS); - if (usb_video_id_present()) - return 1; - } - return 0; -} - -static int probe_video(int device_type) -{ - tsu6721_disable_interrupts(); - gpio_set_level(GPIO_ID_MUX, 1); - msleep(DELAY_ID_MUX_MS); - - if (usb_poll_video_id()) { - /* Not USB host but video */ - device_type = video_dev_type(device_type); - return device_type; - } else { - if (adc_read_channel(ADC_CH_USB_VBUS_SNS) > 3500) { - /* - * Either USB host or video dongle. - * Leave ID_MUX high so we see the change on - * DP_SNS if any. - * - * ADC watchdog is responsible for sensing a - * detach event and switch back ID_MUX. - */ - return device_type; - } else { - /* Unhandled unpowered video dongle. Ignore it. */ - gpio_set_level(GPIO_ID_MUX, 0); - msleep(DELAY_ID_MUX_MS); - tsu6721_enable_interrupts(); - return TSU6721_TYPE_NONE; - } - } -} - -/** - * Set PWM duty cycle. - */ -static void set_pwm_duty_cycle(int percent) -{ - if (current_ilim_config != ILIM_CONFIG_PWM) - ilim_config(ILIM_CONFIG_PWM); - if (percent < 0) - percent = 0; - if (percent > 100) - percent = 100; - pwm_set_duty(PWM_CH_ILIM, percent); - current_pwm_duty = percent; -} - -/** - * Returns next lower PWM duty cycle, or -1 for unchanged duty cycle. - */ -static int pwm_get_next_lower(void) -{ - if (current_pwm_duty > nominal_pwm_duty - - PWM_CTRL_OC_MARGIN && - current_pwm_duty > over_current_pwm_duty && - current_pwm_duty > 0) - return MAX(current_pwm_duty - PWM_CTRL_STEP_DOWN, 0); - return -1; -} - -static int pwm_check_vbus_low(int vbus, int battery_current) -{ - if (battery_current >= 0) - return vbus < PWM_CTRL_VBUS_LOW && current_pwm_duty < 100; - else - return vbus < PWM_CTRL_VBUS_HARD_LOW && current_pwm_duty < 100; -} - -static int pwm_check_vbus_high(int vbus) -{ - if (vbus > PWM_CTRL_VBUS_HIGH) - return 1; - if (vbus > PWM_CTRL_VBUS_HIGH_500MA && current_pwm_duty > I_LIMIT_500MA) - return 1; - return 0; -} - -static void pwm_nominal_duty_cycle(int percent) -{ - int new_percent = percent; - - new_percent += PWM_CTRL_BEGIN_OFFSET; - new_percent = MIN(new_percent, PWM_CTRL_MAX_DUTY); - - set_pwm_duty_cycle(new_percent); - nominal_pwm_duty = percent; -} - -static void adc_watch_vbus(int high, int low) -{ - adc_enable_watchdog(STM32_AIN(5), high, low); - task_clear_pending_irq(STM32_IRQ_ADC_1); - task_enable_irq(STM32_IRQ_ADC_1); -} - -static void adc_watch_toad(void) -{ - /* Watch VBUS and interrupt if voltage goes under 3V. */ - adc_watch_vbus(4095, 1800); - current_watchdog = ADC_WATCH_TOAD; -} - -static void adc_watch_usb(void) -{ - /* Watch VBUS and interrupt if voltage goes under 3V. */ - adc_watch_vbus(4095, 1800); - current_watchdog = ADC_WATCH_USB; -} - -static int usb_has_power_input(int dev_type) -{ - if (dev_type & TSU6721_TYPE_JIG_UART_ON) - return 1; - return (dev_type & TSU6721_TYPE_VBUS_DEBOUNCED) && - !(dev_type & POWERED_5000_DEVICE_TYPE); -} - -static int usb_need_boost(int dev_type) -{ - if (dev_type & POWERED_5000_DEVICE_TYPE) - return 0; - if (chipset_in_state(CHIPSET_STATE_ON | CHIPSET_STATE_SUSPEND)) - return 1; - return (dev_type != TSU6721_TYPE_NONE); -} - -static void usb_boost_power_hook(int power_on) -{ - if (current_dev_type == TSU6721_TYPE_NONE) - gpio_set_level(GPIO_BOOST_EN, power_on); - else if (current_dev_type & TSU6721_TYPE_JIG_UART_ON) - set_video_power(power_on); -} - -static int usb_charger_removed(int dev_type) -{ - if (!(current_dev_type & TSU6721_TYPE_VBUS_DEBOUNCED)) - return 0; - - /* Charger is removed */ - if (dev_type == TSU6721_TYPE_NONE) - return 1; - - /* - * Device type changed from known type to unknown type. Assuming - * it went away and came back. - */ - if ((current_dev_type != TSU6721_TYPE_VBUS_DEBOUNCED) && - (dev_type == TSU6721_TYPE_VBUS_DEBOUNCED)) - return 1; - - return 0; -} - -/** - * Detect over-current events. - * - * 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. - */ -static void usb_detect_overcurrent(int dev_type) -{ - if (usb_charger_removed(dev_type)) { - int idx = !(current_dev_type == TSU6721_TYPE_VBUS_DEBOUNCED); - power_removed_time[idx] = get_time(); - power_removed_type[idx] = current_dev_type; - power_removed_pwm_duty[idx] = current_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 (now.val >= PWM_CTRL_OC_DETECT_TIME) { - oc_detect_retry[idx] = PWM_CTRL_OC_RETRY; - return; - } - if (power_removed_type[idx] == dev_type) { - if (oc_detect_retry[idx] > 0) { - CPRINTS("USB overcurrent: Retry (%d)", - oc_detect_retry[idx]); - oc_detect_retry[idx]--; - return; - } - over_current_pwm_duty = power_removed_pwm_duty[idx] + - PWM_CTRL_OC_BACK_OFF; - CPRINTS("USB overcurrent: Limited to %d%%", - over_current_pwm_duty); - } - } -} - -/** - * Supply 5V VBUS if needed. - * - * If we toggle power output, wait for a moment, and then update device - * type. To avoid race condition, check if power requirement changes during - * this time. - */ -static int usb_manage_boost(int dev_type) -{ - int need_boost; - int retry_limit = 3; - - do { - if (retry_limit-- <= 0) - break; - - need_boost = usb_need_boost(dev_type); - if (need_boost != gpio_get_level(GPIO_BOOST_EN)) { - gpio_set_level(GPIO_BOOST_EN, need_boost); - msleep(DELAY_POWER_MS); - dev_type = tsu6721_get_device_type(); - if (gpio_get_level(GPIO_ID_MUX)) - dev_type = video_dev_type(dev_type); - } - } while (need_boost == !usb_need_boost(dev_type)); - - return dev_type; -} - -/** - * Update ILIM current limit according to device type. - */ -static void usb_update_ilim(int dev_type) -{ - if (usb_has_power_input(dev_type)) { - /* Limit USB port current. 500mA for not listed types. */ - int current_limit = I_LIMIT_500MA; - if (dev_type & TSU6721_TYPE_CHG12) - current_limit = I_LIMIT_3000MA; - else if (dev_type & TSU6721_TYPE_APPLE_CHG) - current_limit = apple_charger_current(); - else if (dev_type & TSU6721_TYPE_CDP) - current_limit = I_LIMIT_1500MA; - else if (dev_type & TSU6721_TYPE_DCP) - current_limit = hard_current_limit(I_LIMIT_1500MA); - else if (dev_type & TSU6721_TYPE_JIG_UART_ON) - current_limit = hard_current_limit(I_LIMIT_2000MA); - else if (dev_type & TOAD_DEVICE_TYPE) - current_limit = hard_current_limit(I_LIMIT_500MA); - else if (dev_type == TSU6721_TYPE_VBUS_DEBOUNCED) - current_limit = hard_current_limit(I_LIMIT_100MA); - - pwm_nominal_duty_cycle(current_limit); - } else { - ilim_config(ILIM_CONFIG_MANUAL_ON); - } -} - -static void usb_log_dev_type(int dev_type) -{ - int i = sizeof(known_dev_types) / sizeof(known_dev_types[0]); - - CPRINTF("[%T USB: 0x%06x", dev_type); - for (--i; i >= 0; --i) - if (dev_type & known_dev_types[i].type) - CPRINTF(" %s", known_dev_types[i].name); - CPRINTF("]\n"); -} - -static void send_battery_key_deferred(void) -{ - keyboard_send_battery_key(); -} -DECLARE_DEFERRED(send_battery_key_deferred); - -static void notify_dev_type_change(int dev_type) -{ - usb_log_dev_type(dev_type); - current_dev_type = dev_type; - hook_call_deferred(send_battery_key_deferred, BATTERY_KEY_DELAY); -} - -static void usb_device_change(int dev_type) -{ - - if (current_dev_type == dev_type) - return; - - over_current_pwm_duty = 0; - - /* - * Video output is recognized incorrectly as USB host. When we see - * USB host, probe for video output. - */ - if (dev_type & TSU6721_TYPE_USB_HOST) - dev_type = probe_video(dev_type); - - usb_detect_overcurrent(dev_type); - - dev_type = usb_manage_boost(dev_type); - - /* Supply 3.3V VBUS if needed. */ - if (dev_type & POWERED_3300_DEVICE_TYPE) - set_video_power(1); - - usb_update_ilim(dev_type); - - if ((dev_type & TOAD_DEVICE_TYPE) && - (dev_type & TSU6721_TYPE_VBUS_DEBOUNCED)) - adc_watch_toad(); - else if (dev_type & TSU6721_TYPE_USB_HOST) - adc_watch_usb(); - - if (dev_type != current_dev_type) { - if ((dev_type & TSU6721_TYPE_NON_STD_CHG || - dev_type == TSU6721_TYPE_VBUS_DEBOUNCED) && - charger_need_redetect == NO_REDETECT) { - /* Schedule redetection */ - charger_need_redetect = REDETECT_SCHEDULED; - charger_redetection_time = get_time(); - charger_redetection_time.val += - NON_STD_CHARGER_REDETECT_DELAY; - } else if (dev_type != TSU6721_TYPE_VBUS_DEBOUNCED && - !(dev_type & TSU6721_TYPE_NON_STD_CHG)) { - /* Not non-std charger. Disarm redetection timer. */ - charger_need_redetect = NO_REDETECT; - } - notify_dev_type_change(dev_type); - } - - if (dev_type) - disable_sleep(SLEEP_MASK_USB_PWR); - else - enable_sleep(SLEEP_MASK_USB_PWR); -} - -/*****************************************************************************/ -/* External API */ - -/* - * TODO(crosbug.com/p/23741): Init here until we can do with HOOK_INIT. Just - * need to set prio so we init before the charger task does. - */ -void extpower_charge_init(void) -{ - set_pwm_duty_cycle(I_LIMIT_500MA); - - /* - * TODO(crosbug.com/p/23742): For some reason the TSU6721 comes up very - * slowly. Wait for a while before accessing it. This delay seems to - * be long enough. Once we understand the slow start, it may be - * possible to remove this delay. - */ - msleep(500); - - tsu6721_reset(); - gpio_enable_interrupt(GPIO_USB_CHG_INT); - - /* TODO(crosbug.com/p/23742): Need delay after reset as well */ - msleep(100); - - extpower_charge_update(1); -} - -void extpower_charge_update(int force_update) -{ - int int_val = 0; - - if (restore_id_mux) { - gpio_set_level(GPIO_ID_MUX, 0); - msleep(DELAY_ID_MUX_MS); - restore_id_mux = 0; - } - - if (pending_adc_watchdog_disable) { - current_watchdog = ADC_WATCH_NONE; - adc_disable_watchdog(); - pending_adc_watchdog_disable = 0; - } - - if (pending_video_power_off) { - set_video_power(0); - pending_video_power_off = 0; - } - - if (pending_tsu6721_reset) { - tsu6721_reset(); - force_update = 1; - pending_tsu6721_reset = 0; - } - - if (pending_dev_type_update) { - force_update = 1; - pending_dev_type_update = 0; - } - - /* - * Check device type except when: - * 1. Current device type is non-standard charger or undetermined - * charger type. This is handled by charger re-detection. - * 2. ID_MUX=1. This is handled by ADC watchdog. - */ - if (current_dev_type != TSU6721_TYPE_VBUS_DEBOUNCED && - !(current_dev_type & TSU6721_TYPE_NON_STD_CHG) && - gpio_get_level(GPIO_ID_MUX) == 0) - force_update |= (tsu6721_get_device_type() != current_dev_type); - - if (!force_update) - int_val = tsu6721_get_interrupts(); - - if (int_val & TSU6721_INT_DETACH) - usb_device_change(TSU6721_TYPE_NONE); - else if (int_val || force_update) - usb_device_change(tsu6721_get_device_type()); -} - -int extpower_charge_needs_update(void) -{ - return tsu6721_peek_interrupts(); -} - -int extpower_is_present(void) -{ - static int last_vbus; - int vbus, vbus_good; - - if (!gpio_get_level(GPIO_BOOST_EN)) - return 0; - - /* - * UVLO is 4.1V. We consider AC bad when its voltage drops below 4.2V - * for two consecutive samples. This is to give PWM a chance to bring - * voltage up. - */ - vbus = adc_read_channel(ADC_CH_USB_VBUS_SNS); - vbus_good = (vbus >= 4200 || last_vbus >= 4200); - last_vbus = vbus; - - return vbus_good; -} - -void extpower_interrupt(enum gpio_signal signal) -{ - task_wake(TASK_ID_CHARGER); -} - -/*****************************************************************************/ -/* Hooks */ - -void adc_watchdog_interrupt(void) -{ - switch (current_watchdog) { - case ADC_WATCH_USB: - restore_id_mux = 1; - /* Fall through */ - case ADC_WATCH_TOAD: - pending_tsu6721_reset = 1; - pending_adc_watchdog_disable = 1; - task_disable_irq(STM32_IRQ_ADC_1); - task_wake(TASK_ID_CHARGER); - break; - default: - break; - } -} -DECLARE_IRQ(STM32_IRQ_ADC_1, adc_watchdog_interrupt, 2); - -static void usb_boost_pwr_on_hook(void) -{ - usb_boost_power_hook(1); -} -DECLARE_HOOK(HOOK_CHIPSET_PRE_INIT, usb_boost_pwr_on_hook, HOOK_PRIO_DEFAULT); - -static void usb_boost_pwr_off_hook(void) -{ - usb_boost_power_hook(0); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_boost_pwr_off_hook, HOOK_PRIO_DEFAULT); - -static void pwm_tweak(void) -{ - int vbus, current; - int next; - - if (current_ilim_config != ILIM_CONFIG_PWM) - return; - - vbus = adc_read_channel(ADC_CH_USB_VBUS_SNS); - if (battery_current(¤t)) - current = 0; - - if (user_pwm_duty >= 0) { - if (current_pwm_duty != user_pwm_duty) - set_pwm_duty_cycle(user_pwm_duty); - return; - } - - /* - * If VBUS voltage is too low: - * - If battery is discharging, throttling more is going to draw - * more current from the battery, so do nothing unless VBUS is - * about to be lower than AC good threshold. - * - Otherwise, throttle input current to raise VBUS voltage. - * If VBUS voltage is high enough, allow more current until we hit - * current limit target. - */ - if (pwm_check_vbus_low(vbus, current)) { - set_pwm_duty_cycle(current_pwm_duty + PWM_CTRL_STEP_UP); - CPRINTS("PWM duty up %d%%", current_pwm_duty); - } else if (pwm_check_vbus_high(vbus)) { - next = pwm_get_next_lower(); - if (next >= 0) { - set_pwm_duty_cycle(next); - CPRINTS("PWM duty down %d%%", current_pwm_duty); - } - } -} -DECLARE_HOOK(HOOK_SECOND, pwm_tweak, HOOK_PRIO_DEFAULT); - -static void usb_detach_video(void) -{ - if (!(current_dev_type & TSU6721_TYPE_JIG_UART_ON)) - return; - pending_video_power_off = 1; - restore_id_mux = 1; - pending_tsu6721_reset = 1; - task_wake(TASK_ID_CHARGER); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, usb_detach_video, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_detach_video, HOOK_PRIO_DEFAULT); - -static void usb_monitor_detach(void) -{ - int vbus; - - if (!(current_dev_type & TSU6721_TYPE_JIG_UART_ON)) - return; - - if (!usb_video_id_present()) { - usb_detach_video(); - return; - } - - /* Check if there is external power */ - vbus = adc_read_channel(ADC_CH_USB_VBUS_SNS); - if (get_video_power() && vbus > 4000) { - set_video_power(0); - notify_dev_type_change(current_dev_type | - TSU6721_TYPE_VBUS_DEBOUNCED); - } else if (!get_video_power() && vbus <= 4000) { - set_pwm_duty_cycle(100); - set_video_power(1); - notify_dev_type_change(current_dev_type & - ~TSU6721_TYPE_VBUS_DEBOUNCED); - } -} -DECLARE_HOOK(HOOK_SECOND, usb_monitor_detach, HOOK_PRIO_DEFAULT); - -static void usb_monitor_cable_det(void) -{ - if (!(current_dev_type & TSU6721_TYPE_USB_HOST)) - return; - - if (usb_video_id_present()) - adc_watchdog_interrupt(); -} -DECLARE_HOOK(HOOK_SECOND, usb_monitor_cable_det, HOOK_PRIO_DEFAULT); - -static void usb_charger_redetect(void) -{ - if (charger_need_redetect != REDETECT_SCHEDULED) - return; - - if (timestamp_expired(charger_redetection_time, NULL)) { - CPRINTS("USB Redetecting"); - /* - * TSU6721 doesn't update device type if power or ID pin - * is present. Therefore, if the device type is the same, - * we need to reset TSU6721 to force a redetection. - */ - if (tsu6721_get_device_type() == current_dev_type) - pending_tsu6721_reset = 1; - else - pending_dev_type_update = 1; - if (gpio_get_level(GPIO_ID_MUX)) - restore_id_mux = 1; - charger_need_redetect = REDETECTED; - task_wake(TASK_ID_CHARGER); - } -} -DECLARE_HOOK(HOOK_SECOND, usb_charger_redetect, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Console commands for debugging */ - -#ifdef CONFIG_CMD_ILIM -static int command_ilim(int argc, char **argv) -{ - char *e; - int v; - - if (argc >= 2) { - if (parse_bool(argv[1], &v)) { - ilim_config(v ? ILIM_CONFIG_MANUAL_ON : - ILIM_CONFIG_MANUAL_OFF); - } else { - v = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - set_pwm_duty_cycle(v); - } - } - - if (current_ilim_config == ILIM_CONFIG_MANUAL_ON) - ccprintf("ILIM is GPIO high\n"); - else if (current_ilim_config == ILIM_CONFIG_MANUAL_OFF) - ccprintf("ILIM is GPIO low\n"); - else - ccprintf("ILIM is PWM duty cycle %d%%\n", - pwm_get_duty(PWM_CH_ILIM)); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ilim, command_ilim, - "[percent | on | off]", - "Set or show ILIM duty cycle/GPIO value", - NULL); -#endif /* CONFIG_CMD_ILIM */ - -#ifdef CONFIG_CMD_BATDEBUG -static int command_batdebug(int argc, char **argv) -{ - struct batt_params batt; - - battery_get_params(&batt); - - ccprintf("VBUS = %d mV\n", adc_read_channel(ADC_CH_USB_VBUS_SNS)); - ccprintf("VAC = %d mV\n", pmu_adc_read(ADC_VAC, ADC_FLAG_KEEP_ON) - * 17000 / 1024); - ccprintf("IAC = %d mA\n", pmu_adc_read(ADC_IAC, ADC_FLAG_KEEP_ON) - * (1000 / R_INPUT_MOHM) * 33 / 1024); - ccprintf("VBAT = %d mV\n", pmu_adc_read(ADC_VBAT, ADC_FLAG_KEEP_ON) - * 17000 / 1024); - ccprintf("IBAT = %d mA\n", pmu_adc_read(ADC_IBAT, 0) - * (1000 / R_BATTERY_MOHM) * 40 / 1024); - ccprintf("PWM = %d%%\n", pwm_get_duty(PWM_CH_ILIM)); - ccprintf("Battery Current = %d mA\n", batt.current); - ccprintf("Battery Voltage= %d mV\n", batt.voltage); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(batdebug, command_batdebug, - NULL, NULL, NULL); -#endif /* CONFIG_CMD_BATDEBUG */ - -/*****************************************************************************/ -/* Host commands */ - -static int ext_power_command_current_limit(struct host_cmd_handler_args *args) -{ - const struct ec_params_ext_power_current_limit *p = args->params; - - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - user_pwm_duty = ((int)(p->limit) - PWM_MAPPING_A) / PWM_MAPPING_B; - - return EC_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_EXT_POWER_CURRENT_LIMIT, - ext_power_command_current_limit, - EC_VER_MASK(0)); - -static int power_command_info(struct host_cmd_handler_args *args) -{ - struct ec_response_power_info *r = args->response; - - r->voltage_ac = adc_read_channel(ADC_CH_USB_VBUS_SNS); - r->voltage_system = pmu_adc_read(ADC_VAC, ADC_FLAG_KEEP_ON) - * 17000 / 1024; - r->current_system = pmu_adc_read(ADC_IAC, 0) - * (1000 / R_INPUT_MOHM) * 33 / 1024; - r->usb_dev_type = current_dev_type; - - /* Approximate value by PWM duty cycle */ - r->usb_current_limit = PWM_MAPPING_A + PWM_MAPPING_B * current_pwm_duty; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_POWER_INFO, power_command_info, EC_VER_MASK(0)); |