diff --git a/board/spring/usb_charging.c b/board/spring/usb_charging.c index ba60e4dbaf..1818b4a489 100644 --- a/board/spring/usb_charging.c +++ b/board/spring/usb_charging.c @@ -31,6 +31,9 @@ #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 1335 @@ -63,11 +66,18 @@ static int current_dev_type = TSU6721_TYPE_NONE; static int nominal_pwm_duty; static int current_pwm_duty; +static int pending_tsu6721_reset; + static enum { LIMIT_NORMAL, LIMIT_AGGRESSIVE, } current_limit_mode = LIMIT_AGGRESSIVE; +static enum { + ADC_WATCH_NONE, + ADC_WATCH_TOAD, +} current_watchdog = ADC_WATCH_NONE; + /* * Last time we see a power source removed. Also records the power source * type and PWM duty cycle at that moment. @@ -299,6 +309,25 @@ void usb_charge_interrupt(enum gpio_signal signal) task_wake(TASK_ID_PMU_TPS65090_CHARGER); } +static void board_adc_watch_toad(void) +{ + /* Watch VBUS and interrupt if voltage goes under 3V. */ + adc_enable_watchdog(STM32_AIN(5), 4095, 1800); + task_clear_pending_irq(STM32_IRQ_ADC_1); + task_enable_irq(STM32_IRQ_ADC_1); + current_watchdog = ADC_WATCH_TOAD; +} + +static void board_adc_watchdog_interrupt(void) +{ + if (current_watchdog == ADC_WATCH_TOAD) { + pending_tsu6721_reset = 1; + task_disable_irq(STM32_IRQ_ADC_1); + task_wake(TASK_ID_PMU_TPS65090_CHARGER); + } +} +DECLARE_IRQ(STM32_IRQ_ADC_1, board_adc_watchdog_interrupt, 2); + static int usb_has_power_input(int dev_type) { if (dev_type & TSU6721_TYPE_JIG_UART_ON) @@ -390,6 +419,10 @@ static void usb_device_change(int dev_type) board_ilim_config(ILIM_CONFIG_MANUAL_ON); } + if ((dev_type & TOAD_DEVICE_TYPE) && + (dev_type & TSU6721_TYPE_VBUS_DEBOUNCED)) + board_adc_watch_toad(); + /* Log to console */ CPRINTF("[%T USB Attached: "); if (dev_type == TSU6721_TYPE_NONE) @@ -412,6 +445,10 @@ static void usb_device_change(int dev_type) CPRINTF("Apple charger]\n"); else if (dev_type & TSU6721_TYPE_JIG_UART_ON) CPRINTF("JIG UART ON]\n"); + else if (dev_type & TSU6721_TYPE_AUDIO3) + CPRINTF("Audio 3]\n"); + else if (dev_type & TSU6721_TYPE_UART) + CPRINTF("UART]\n"); else if (dev_type & TSU6721_TYPE_VBUS_DEBOUNCED) CPRINTF("Unknown with power]\n"); else @@ -443,7 +480,16 @@ DECLARE_HOOK(HOOK_SECOND, board_usb_monitor_detach, HOOK_PRIO_DEFAULT); void board_usb_charge_update(int force_update) { - int int_val = tsu6721_get_interrupts(); + int int_val = 0; + + if (pending_tsu6721_reset) { + current_watchdog = ADC_WATCH_NONE; + adc_disable_watchdog(); + tsu6721_reset(); + force_update = 1; + pending_tsu6721_reset = 0; + } else + int_val = tsu6721_get_interrupts(); if (int_val & TSU6721_INT_DETACH) usb_device_change(TSU6721_TYPE_NONE); diff --git a/chip/stm32/adc.c b/chip/stm32/adc.c index b1545dac20..1aad5d5744 100644 --- a/chip/stm32/adc.c +++ b/chip/stm32/adc.c @@ -97,6 +97,9 @@ static int adc_enable_watchdog_no_lock(void) /* Set channel */ STM32_ADC_CR1 = (STM32_ADC_CR1 & ~0x1f) | watchdog_ain_id; + /* Clear interrupt bit */ + STM32_ADC_SR &= ~0x1; + /* AWDSGL=1, SCAN=1, AWDIE=1, AWDEN=1 */ STM32_ADC_CR1 |= (1 << 9) | (1 << 8) | (1 << 6) | (1 << 23); @@ -138,8 +141,8 @@ static int adc_disable_watchdog_no_lock(void) if (!adc_watchdog_enabled()) return EC_ERROR_UNKNOWN; - /* AWDEN=0 */ - STM32_ADC_CR1 &= ~(1 << 23); + /* AWDEN=0, AWDIE=0 */ + STM32_ADC_CR1 &= ~(1 << 23) & ~(1 << 6); /* CONT=0 */ STM32_ADC_CR2 &= ~(1 << 1); diff --git a/common/tsu6721.c b/common/tsu6721.c index 7f3d1d0c7e..629e8e3e86 100644 --- a/common/tsu6721.c +++ b/common/tsu6721.c @@ -24,6 +24,9 @@ /* 8-bit I2C address */ #define TSU6721_I2C_ADDR (0x25 << 1) +/* Delay values */ +#define TSU6721_SW_RESET_DELAY 15 + static int saved_interrupts; uint8_t tsu6721_read(uint8_t reg) @@ -86,6 +89,14 @@ int tsu6721_get_device_type(void) (tsu6721_read(TSU6721_REG_DEV_TYPE1)); } +void tsu6721_reset(void) +{ + tsu6721_write(TSU6721_REG_RESET, 0x1); + /* TSU6721 reset takes ~10ms. Let's wait for 15ms to be safe. */ + msleep(TSU6721_SW_RESET_DELAY); + tsu6721_init(); +} + int tsu6721_mux(enum tsu6721_mux sel) { uint8_t id = tsu6721_read(TSU6721_REG_ADC); diff --git a/include/tsu6721.h b/include/tsu6721.h index 619465453b..da9fd0ea3e 100644 --- a/include/tsu6721.h +++ b/include/tsu6721.h @@ -105,4 +105,7 @@ int tsu6721_get_device_type(void); /* Control TSU6721 mux. */ int tsu6721_mux(enum tsu6721_mux sel); +/* Reset TSU6721. */ +void tsu6721_reset(void); + #endif /* TSU6721_H */