summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-05-14 17:39:30 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-08-23 02:42:17 +0000
commit2e0de3c50f0106264303d2b8b55ec6253d9eda55 (patch)
tree1a71cadbfdaa8b1cb7a6476c1cf9c75cf0634c54
parent6f22113a78ba2958128bf129fae80e48148d890e (diff)
downloadchrome-ec-2e0de3c50f0106264303d2b8b55ec6253d9eda55.tar.gz
zinger: add voltage discharge
When the power supply voltage is transitioning to a lower value, use the discharging FET to ensure that the voltage is acceptable before re-enabling the output. Note: when discharging, we must disable the fast OCP ADC interrupt, but that is ok because we still have the slow OCP check in board_checks(). BRANCH=none BUG=chrome-os-partner:28332 TEST=on Zinger, transition from 20V to 5V using Firefly buttons and observe that we no longer have an over-voltage event. Also, verified that fast OCP triggering still works after a discharge. Change-Id: Ie327645e74819aebd1260f5ce16b2ba46a674a7b Signed-off-by: Vincent Palatin <vpalatin@chromium.org> Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/201577 Reviewed-by: Todd Broch <tbroch@chromium.org>
-rw-r--r--board/zinger/hardware.c2
-rw-r--r--board/zinger/usb_pd_policy.c111
2 files changed, 89 insertions, 24 deletions
diff --git a/board/zinger/hardware.c b/board/zinger/hardware.c
index fd9b86ff15..fc0612eeb9 100644
--- a/board/zinger/hardware.c
+++ b/board/zinger/hardware.c
@@ -244,6 +244,8 @@ int adc_disable_watchdog(void)
STM32_ADC_CFGR1 = 1 << 12;
/* Disable interrupt */
STM32_ADC_IER = 0;
+ /* Clear flags */
+ STM32_ADC_ISR = 0x8e;
return EC_SUCCESS;
}
diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c
index e4aa2d1f98..a4fcc38fe1 100644
--- a/board/zinger/usb_pd_policy.c
+++ b/board/zinger/usb_pd_policy.c
@@ -61,6 +61,7 @@ enum faults {
FAULT_OCP, /* Over-Current Protection */
FAULT_FAST_OCP, /* Over-Current Protection for interrupt context */
FAULT_OVP, /* Under or Over-Voltage Protection */
+ FAULT_DISCHARGE, /* Discharge was ineffective */
};
/* current fault condition */
@@ -84,6 +85,8 @@ static timestamp_t fault_deadline;
#define VBUS_MA(ma) ((ma)*ADC_SCALE*R_SENSE/1000*CURR_GAIN/VDDA_MV)
/* convert raw ADC value to mA */
#define ADC_TO_CURR_MA(vbus) ((vbus)*1000/(ADC_SCALE*R_SENSE)*VDDA_MV/CURR_GAIN)
+/* convert raw ADC value to mV */
+#define ADC_TO_VOLT_MV(vbus) ((vbus)*VOLT_DIV*VDDA_MV/ADC_SCALE)
/* Max current : 20% over 3A = 3.6A */
#define MAX_CURRENT VBUS_MA(3600)
@@ -99,9 +102,41 @@ static timestamp_t fault_deadline;
/* Over-voltage recovery threshold is 1.1x Vnom */
#define OVP_REC_MV(mv) VBUS_MV((mv) * 11 / 10)
-/* Time to allow for voltage down stepping */
-/* TODO reduce this time if possible when voltage discharging is implemented */
-#define VOLTAGE_DOWN_STEP_TIME (500*MSEC)
+/* Maximum discharging delay */
+#define DISCHARGE_TIMEOUT (90*MSEC)
+/* Voltage overshoot below the OVP threshold for discharging to avoid OVP */
+#define DISCHARGE_OVERSHOOT_MV VBUS_MV(200)
+
+/* ----- output voltage discharging ----- */
+
+/* expiration date of the discharge */
+static timestamp_t discharge_deadline;
+
+static inline void discharge_enable(void)
+{
+ STM32_GPIO_BSRR(GPIO_F) = GPIO_SET(1);
+}
+
+static inline void discharge_disable(void)
+{
+ STM32_GPIO_BSRR(GPIO_F) = GPIO_RESET(1);
+ adc_disable_watchdog();
+}
+
+static inline int discharge_is_enabled(void)
+{
+ /* GPF1 = enable discharge FET */
+ return STM32_GPIO_IDR(GPIO_F) & 2;
+}
+
+static void discharge_voltage(int target_volt)
+{
+ discharge_enable();
+ discharge_deadline.val = get_time().val + DISCHARGE_TIMEOUT;
+ /* Monitor VBUS voltage */
+ target_volt -= DISCHARGE_OVERSHOOT_MV;
+ adc_enable_watchdog(ADC_CH_V_SENSE, 0xFFF, target_volt);
+}
/* ----------------------- USB Power delivery policy ---------------------- */
@@ -131,10 +166,6 @@ static const struct {
static int volt_idx;
static int last_volt_idx;
-/* flag and timestamp for down-stepping the voltage */
-static int down_step;
-static uint64_t down_step_done_time;
-
/* output current measurement */
int vbus_amp;
@@ -146,9 +177,8 @@ int pd_request_voltage(uint32_t rdo)
uint32_t pdo;
uint32_t pdo_ma;
-
- /* fault condition not cleared : reject transitions */
- if (fault != FAULT_OK)
+ /* fault condition or output disabled: reject transitions */
+ if (fault != FAULT_OK || !output_is_enabled())
return EC_ERROR_INVAL;
if (!idx || idx > pd_src_pdo_cnt)
@@ -167,9 +197,18 @@ int pd_request_voltage(uint32_t rdo)
((rdo >> 10) & 0x3ff) * 10, (rdo & 0x3ff) * 10);
if (idx - 1 < volt_idx) { /* down voltage transition */
- down_step = 1;
- down_step_done_time = get_time().val + VOLTAGE_DOWN_STEP_TIME;
- /* TODO discharge on down voltage transitions ? */
+ /* Stop OCP monitoring */
+ adc_disable_watchdog();
+
+ discharge_voltage(voltages[idx - 1].ovp);
+ } else if (idx - 1 > volt_idx) { /* up voltage transition */
+ if (discharge_is_enabled()) {
+ /* Make sure discharging is disabled */
+ discharge_disable();
+ /* Enable over-current monitoring */
+ adc_enable_watchdog(ADC_CH_A_SENSE,
+ MAX_CURRENT_FAST, 0);
+ }
}
last_volt_idx = volt_idx;
volt_idx = idx - 1;
@@ -181,7 +220,7 @@ int pd_request_voltage(uint32_t rdo)
int pd_set_power_supply_ready(int port)
{
/* fault condition not cleared : do not turn on power */
- if (fault != FAULT_OK)
+ if ((fault != FAULT_OK) || discharge_is_enabled())
return EC_ERROR_INVAL;
output_enable();
@@ -193,14 +232,19 @@ int pd_set_power_supply_ready(int port)
void pd_power_supply_reset(int port)
{
+ int need_discharge = (volt_idx > 1) || discharge_is_enabled();
+
output_disable();
- /* TODO discharge ? */
volt_idx = 0;
set_output_voltage(VO_5V);
/* TODO transition delay */
/* Stop OCP monitoring to save power */
adc_disable_watchdog();
+
+ /* discharge voltage to 5V ? */
+ if (need_discharge)
+ discharge_voltage(voltages[0].ovp);
}
int pd_board_checks(void)
@@ -244,22 +288,33 @@ int pd_board_checks(void)
* Set the voltage index to use for checking OVP. During a down step
* transition, use the previous voltage index to check for OVP.
*/
- if (down_step && get_time().val >= down_step_done_time)
- down_step = 0;
- ovp_idx = down_step ? last_volt_idx : volt_idx;
+ ovp_idx = discharge_is_enabled() ? last_volt_idx : volt_idx;
if ((output_is_enabled() && (vbus_volt > voltages[ovp_idx].ovp)) ||
(fault && (vbus_volt > voltages[ovp_idx].ovp_rec))) {
if (!fault)
debug_printf("OverVoltage : %d mV\n",
- vbus_volt * VDDA_MV * VOLT_DIV / ADC_SCALE);
- /* TODO(crosbug.com/p/28331) discharge */
+ ADC_TO_VOLT_MV(vbus_volt));
fault = FAULT_OVP;
/* no timeout */
fault_deadline.val = get_time().val;
return EC_ERROR_INVAL;
}
+ /* the discharge did not work properly */
+ if (discharge_is_enabled() &&
+ (get_time().val > discharge_deadline.val)) {
+ /* stop it */
+ discharge_disable();
+ /* enable over-current monitoring */
+ adc_enable_watchdog(ADC_CH_A_SENSE, MAX_CURRENT_FAST, 0);
+ debug_printf("Discharge failure : %d mV\n",
+ ADC_TO_VOLT_MV(vbus_volt));
+ fault = FAULT_DISCHARGE;
+ /* reset it after 1 second */
+ fault_deadline.val = get_time().val + OCP_TIMEOUT;
+ }
+
/* everything is good *and* the error condition has expired */
if ((fault != FAULT_OK) && (get_time().val > fault_deadline.val)) {
fault = FAULT_OK;
@@ -277,12 +332,20 @@ int pd_board_checks(void)
void pd_adc_interrupt(void)
{
- /* cut the power output */
- pd_power_supply_reset(0);
/* Clear flags */
STM32_ADC_ISR = 0x8e;
- /* record a special fault, the normal check will record the timeout */
- fault = FAULT_FAST_OCP;
+
+ if (discharge_is_enabled()) { /* discharge completed */
+ discharge_disable();
+ /* enable over-current monitoring */
+ adc_enable_watchdog(ADC_CH_A_SENSE, MAX_CURRENT_FAST, 0);
+ } else {/* Over-current detection */
+ /* cut the power output */
+ pd_power_supply_reset(0);
+ /* record a special fault */
+ fault = FAULT_FAST_OCP;
+ /* pd_board_checks() will record the timeout later */
+ }
}
DECLARE_IRQ(STM32_IRQ_ADC_COMP, pd_adc_interrupt, 1);