diff options
-rw-r--r-- | board/firefly/usb_pd_policy.c | 4 | ||||
-rw-r--r-- | board/fruitpie/usb_pd_policy.c | 3 | ||||
-rw-r--r-- | board/zinger/usb_pd_policy.c | 109 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 13 | ||||
-rw-r--r-- | include/usb_pd.h | 3 |
5 files changed, 120 insertions, 12 deletions
diff --git a/board/firefly/usb_pd_policy.c b/board/firefly/usb_pd_policy.c index 4a6d9da103..d00ac47916 100644 --- a/board/firefly/usb_pd_policy.c +++ b/board/firefly/usb_pd_policy.c @@ -85,7 +85,7 @@ void pd_power_supply_reset(void) { } -void pd_board_checks(void) +int pd_board_checks(void) { static int blinking; int vbus; @@ -122,4 +122,6 @@ void pd_board_checks(void) gpio_set_level(GPIO_LED_PP5000, led5); gpio_set_level(GPIO_LED_PP12000, led12); gpio_set_level(GPIO_LED_PP20000, led20); + + return EC_SUCCESS; } diff --git a/board/fruitpie/usb_pd_policy.c b/board/fruitpie/usb_pd_policy.c index c756dcc7bd..001dafd28f 100644 --- a/board/fruitpie/usb_pd_policy.c +++ b/board/fruitpie/usb_pd_policy.c @@ -113,6 +113,7 @@ void pd_power_supply_reset(void) { } -void pd_board_checks(void) +int pd_board_checks(void) { + return EC_SUCCESS; } 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; + } diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 442cccce2d..eee328f046 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -641,7 +641,13 @@ void pd_task(void) /* monitor for incoming packet */ pd_rx_enable_monitoring(); /* Verify board specific health status : current, voltages... */ - pd_board_checks(); + res = pd_board_checks(); + if (res != EC_SUCCESS) { + /* cut the power */ + execute_hard_reset(); + /* notify the other side of the issue */ + /* send_hard_reset(ctxt); */ + } /* wait for next event/packet or timeout expiration */ task_wait_event(timeout); /* incoming packet ? */ @@ -667,6 +673,8 @@ void pd_task(void) (cc2_volt < PD_SRC_VNC)) { pd_polarity = !(cc1_volt < PD_SRC_VNC); pd_select_polarity(pd_polarity); + /* Enable VBUS */ + pd_set_power_supply_ready(); pd_task_state = PD_STATE_SRC_DISCOVERY; } timeout = 10*MSEC; @@ -705,7 +713,8 @@ void pd_task(void) /* Verify that the sink is alive */ res = send_control(ctxt, PD_CTRL_PING); if (res < 0) { - /* The sink died ... TODO */ + /* The sink died ... */ + pd_power_supply_reset(); pd_task_state = PD_STATE_SRC_DISCOVERY; timeout = PD_T_SEND_SOURCE_CAP; } else { /* schedule next keep-alive */ diff --git a/include/usb_pd.h b/include/usb_pd.h index 84d98dff9b..659fc5cd54 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -153,8 +153,9 @@ void pd_request_source_voltage(int mv); /* * Verify board specific health status : current, voltages... * + * @return EC_SUCCESS if the board is good, <0 else. */ -void pd_board_checks(void); +int pd_board_checks(void); /* Power Data Objects for the source and the sink */ extern const uint32_t pd_src_pdo[]; |