diff options
Diffstat (limited to 'board/zinger/usb_pd_policy.c')
-rw-r--r-- | board/zinger/usb_pd_policy.c | 109 |
1 files changed, 102 insertions, 7 deletions
diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c index b506c15aec..de5794771d 100644 --- a/board/zinger/usb_pd_policy.c +++ b/board/zinger/usb_pd_policy.c @@ -3,12 +3,14 @@ * found in the LICENSE file. */ +#include "adc.h" #include "board.h" #include "common.h" #include "console.h" #include "debug.h" #include "hooks.h" #include "registers.h" +#include "timer.h" #include "util.h" #include "usb_pd.h" @@ -44,6 +46,50 @@ static inline void output_disable(void) STM32_GPIO_BSRR(GPIO_F) = GPIO_SET(0); } +static inline int output_is_enabled(void) +{ + /* GPF0 = FET driver shutdown */ + return !(STM32_GPIO_IDR(GPIO_F) & 1); +} + +/* ----- fault conditions ----- */ + +enum faults { + FAULT_OK = 0, + FAULT_OCP, /* Over-Current Protection */ + FAULT_OVP, /* Under or Over-Voltage Protection */ +}; + +/* current fault condition */ +static enum faults fault; +/* expiration date of the last fault condition */ +static timestamp_t fault_deadline; + +/* ADC in 12-bit mode */ +#define ADC_SCALE (1 << 12) +/* ADC power supply : VDDA = 3.3V */ +#define VDDA_MV 3300 +/* Current sense resistor : 5 milliOhm */ +#define R_SENSE 5 +/* VBUS voltage is measured through 10k / 100k voltage divider = /11 */ +#define VOLT_DIV ((10+110)/10) +/* The current sensing op-amp has a x101 gain */ +#define CURR_GAIN 101 +/* convert VBUS voltage in raw ADC value */ +#define VBUS_MV(mv) ((mv)*ADC_SCALE/VOLT_DIV/VDDA_MV) +/* convert VBUS current in raw ADC value */ +#define VBUS_MA(ma) ((ma)*ADC_SCALE*R_SENSE/1000*CURR_GAIN/VDDA_MV) + +/* Max current : 10% over 3A = 3.3A */ +#define MAX_CURRENT VBUS_MA(3300) +/* reset over-current after 1 second */ +#define OCP_TIMEOUT SECOND + +/* Under-voltage limit is 0.8x Vnom */ +#define UVP_MV(mv) VBUS_MV((mv) * 8 / 10) +/* Over-voltage limit is 1.2x Vnom */ +#define OVP_MV(mv) VBUS_MV((mv) * 12 / 10) + /* ----------------------- USB Power delivery policy ---------------------- */ /* Power Delivery Objects */ @@ -56,13 +102,20 @@ const uint32_t pd_src_pdo[] = { const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); /* PDO voltages (should match the table above) */ -static enum volt voltages[ARRAY_SIZE(pd_src_pdo)] = { - VO_5V, - VO_5V, - VO_12V, - VO_20V, +static const struct { + enum volt select; /* GPIO configuration to select the voltage */ + int uvp; /* under-voltage limit in mV */ + int ovp; /* over-voltage limit in mV */ +} voltages[ARRAY_SIZE(pd_src_pdo)] = { + {VO_5V, UVP_MV(5000), OVP_MV(5000)}, + {VO_5V, UVP_MV(5000), OVP_MV(5000)}, + {VO_12V, UVP_MV(12000), OVP_MV(12000)}, + {VO_20V, UVP_MV(20000), OVP_MV(20000)}, }; +/* currently selected PDO entry */ +static int volt_idx; + int pd_request_voltage(uint32_t rdo) { int op_ma = rdo & 0x3FF; @@ -71,6 +124,11 @@ int pd_request_voltage(uint32_t rdo) uint32_t pdo; uint32_t pdo_ma; + + /* fault condition not cleared : reject transitions */ + if (fault != FAULT_OK) + return EC_ERROR_INVAL; + if (!idx || idx > pd_src_pdo_cnt) return EC_ERROR_INVAL; /* Invalid index */ @@ -88,13 +146,18 @@ int pd_request_voltage(uint32_t rdo) output_disable(); /* TODO discharge ? */ - set_output_voltage(voltages[idx-1]); + volt_idx = idx - 1; + set_output_voltage(voltages[volt_idx].select); return EC_SUCCESS; } int pd_set_power_supply_ready(void) { + /* fault condition not cleared : do not turn on power */ + if (fault != FAULT_OK) + return EC_ERROR_INVAL; + output_enable(); return EC_SUCCESS; /* we are ready */ } @@ -103,10 +166,42 @@ void pd_power_supply_reset(void) { output_disable(); /* TODO discharge ? */ + volt_idx = 0; set_output_voltage(VO_5V); /* TODO transition delay */ } -void pd_board_checks(void) +int pd_board_checks(void) { + int vbus_volt, vbus_amp; + + vbus_volt = adc_read_channel(ADC_CH_V_SENSE); + vbus_amp = adc_read_channel(ADC_CH_A_SENSE); + + if (vbus_amp > MAX_CURRENT) { + debug_printf("OverCurrent : %d mA\n", + vbus_amp * VDDA_MV / CURR_GAIN * 1000 / R_SENSE / ADC_SCALE); + fault = FAULT_OCP; + /* reset over-current after 1 second */ + fault_deadline.val = get_time().val + OCP_TIMEOUT; + return EC_ERROR_INVAL; + } + if (output_is_enabled() && (vbus_volt > voltages[volt_idx].ovp)) { + debug_printf("OverVoltage : %d mV\n", + vbus_volt * VDDA_MV * VOLT_DIV / ADC_SCALE); + /* TODO(crosbug.com/p/28331) discharge */ + fault = FAULT_OVP; + /* no timeout */ + fault_deadline.val = get_time().val; + return EC_ERROR_INVAL; + } + + /* everything is good *and* the error condition has expired */ + if ((fault != FAULT_OK) && (get_time().val > fault_deadline.val)) { + fault = FAULT_OK; + debug_printf("Reset fault\n"); + } + + return EC_SUCCESS; + } |