summaryrefslogtreecommitdiff
path: root/board/zinger/usb_pd_policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/zinger/usb_pd_policy.c')
-rw-r--r--board/zinger/usb_pd_policy.c109
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;
+
}