diff options
-rw-r--r-- | common/extpower_usb.c | 102 |
1 files changed, 81 insertions, 21 deletions
diff --git a/common/extpower_usb.c b/common/extpower_usb.c index 17ad401001..51068452e6 100644 --- a/common/extpower_usb.c +++ b/common/extpower_usb.c @@ -46,7 +46,7 @@ enum ilim_config { #define TOAD_DEVICE_TYPE (TSU6721_TYPE_UART | TSU6721_TYPE_AUDIO3) /* Voltage threshold of D+ for video */ -#define VIDEO_ID_THRESHOLD 1335 +#define VIDEO_ID_THRESHOLD 250 /* * Mapping from PWM duty to current: @@ -97,6 +97,7 @@ static int user_pwm_duty = -1; static int pwm_fast_mode; static int pending_tsu6721_reset; +static int restore_id_mux; static enum { LIMIT_NORMAL, @@ -106,6 +107,7 @@ static enum { static enum { ADC_WATCH_NONE, ADC_WATCH_TOAD, + ADC_WATCH_USB, } current_watchdog = ADC_WATCH_NONE; struct { @@ -284,12 +286,24 @@ static int probe_video(int device_type) gpio_set_level(GPIO_ID_MUX, 1); msleep(DELAY_ID_MUX_MS); - if (adc_read_channel(ADC_CH_USB_DP_SNS) > VIDEO_ID_THRESHOLD) { - /* Actually an USB host */ - gpio_set_level(GPIO_ID_MUX, 0); - msleep(DELAY_ID_MUX_MS); - tsu6721_enable_interrupts(); - return device_type; + if (adc_read_channel(ADC_CH_USB_DP_SNS) < VIDEO_ID_THRESHOLD) { + if (device_type & TSU6721_TYPE_VBUS_DEBOUNCED) { + /* + * 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; + } } else { /* Not USB host but video */ device_type = video_dev_type(device_type); @@ -369,15 +383,27 @@ static void pwm_nominal_duty_cycle(int percent) pwm_fast_mode = 1; } -static void adc_watch_toad(void) +static void adc_watch_vbus(int high, int low) { - /* Watch VBUS and interrupt if voltage goes under 3V. */ - adc_enable_watchdog(STM32_AIN(5), 4095, 1800); + 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) @@ -534,12 +560,15 @@ static void usb_device_change(int 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(); - usb_log_dev_type(dev_type); - - keyboard_send_battery_key(); + if (dev_type != current_dev_type) { + usb_log_dev_type(dev_type); + keyboard_send_battery_key(); + current_dev_type = dev_type; + } - current_dev_type = dev_type; if (dev_type) disable_sleep(SLEEP_MASK_USB_PWR); else @@ -584,6 +613,12 @@ 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_tsu6721_reset) { current_watchdog = ADC_WATCH_NONE; adc_disable_watchdog(); @@ -634,10 +669,17 @@ void extpower_interrupt(enum gpio_signal signal) static void adc_watchdog_interrupt(void) { - if (current_watchdog == ADC_WATCH_TOAD) { + switch (current_watchdog) { + case ADC_WATCH_USB: + restore_id_mux = 1; + /* Fall through */ + case ADC_WATCH_TOAD: pending_tsu6721_reset = 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); @@ -695,6 +737,18 @@ static void pwm_tweak(void) } 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; + set_video_power(0); + 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; @@ -702,12 +756,8 @@ static void usb_monitor_detach(void) if (!(current_dev_type & TSU6721_TYPE_JIG_UART_ON)) return; - if (adc_read_channel(ADC_CH_USB_DP_SNS) > VIDEO_ID_THRESHOLD) { - set_video_power(0); - gpio_set_level(GPIO_ID_MUX, 0); - msleep(DELAY_ID_MUX_MS); - tsu6721_enable_interrupts(); - usb_device_change(TSU6721_TYPE_NONE); + if (adc_read_channel(ADC_CH_USB_DP_SNS) < VIDEO_ID_THRESHOLD) { + usb_detach_video(); return; } @@ -726,6 +776,16 @@ static void usb_monitor_detach(void) } 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 (adc_read_channel(ADC_CH_USB_DP_SNS) < VIDEO_ID_THRESHOLD) + adc_watchdog_interrupt(); +} +DECLARE_HOOK(HOOK_SECOND, usb_monitor_cable_det, HOOK_PRIO_DEFAULT); + /*****************************************************************************/ /* * Console commands for debugging. |