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.c565
1 files changed, 0 insertions, 565 deletions
diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c
deleted file mode 100644
index f47789e063..0000000000
--- a/board/zinger/usb_pd_policy.c
+++ /dev/null
@@ -1,565 +0,0 @@
-/* Copyright 2014 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "adc.h"
-#include "common.h"
-#include "console.h"
-#include "debug_printf.h"
-#include "ec_commands.h"
-#include "hooks.h"
-#include "registers.h"
-#include "system.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-#include "usb_pd.h"
-
-/* ------------------------- Power supply control ------------------------ */
-
-/* GPIO level setting helpers through BSRR register */
-#define GPIO_SET(n) (1 << (n))
-#define GPIO_RESET(n) (1 << ((n) + 16))
-
-/* Output voltage selection */
-enum volt {
- VO_5V = GPIO_RESET(13) | GPIO_RESET(14),
- VO_12V = GPIO_SET(13) | GPIO_RESET(14),
- VO_13V = GPIO_RESET(13) | GPIO_SET(14),
- VO_20V = GPIO_SET(13) | GPIO_SET(14),
-};
-
-static inline void set_output_voltage(enum volt v)
-{
- /* set voltage_select on PA13/PA14 */
- STM32_GPIO_BSRR(GPIO_A) = v;
-}
-
-static inline void output_enable(void)
-{
- /* GPF0 (enable OR'ing FETs) = 1 */
- STM32_GPIO_BSRR(GPIO_F) = GPIO_SET(0);
-}
-
-static inline void output_disable(void)
-{
- /* GPF0 (disable OR'ing FETs) = 0 */
- STM32_GPIO_BSRR(GPIO_F) = GPIO_RESET(0);
-}
-
-static inline int output_is_enabled(void)
-{
- /* GPF0 = enable output FET */
- return STM32_GPIO_ODR(GPIO_F) & 1;
-}
-
-/* ----- fault conditions ----- */
-
-enum faults {
- FAULT_OK = 0,
- 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 */
-static enum faults fault;
-/* expiration date of the last fault condition */
-static timestamp_t fault_deadline;
-
-/* ADC in 12-bit mode */
-#define ADC_SCALE BIT(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+100)/10)
-/* The current sensing op-amp has a x100 gain */
-#define CURR_GAIN 100
-/* 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)
-/* 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 */
-#if defined(BOARD_ZINGER)
-#define RATED_CURRENT 3000
-#elif defined(BOARD_MINIMUFFIN)
-#define RATED_CURRENT 2250
-#endif
-
-/* Max current : 20% over rated current */
-#define MAX_CURRENT VBUS_MA(RATED_CURRENT * 6/5)
-/* Fast short circuit protection : 50% over rated current */
-#define MAX_CURRENT_FAST VBUS_MA(RATED_CURRENT * 3/2)
-/* reset over-current after 1 second */
-#define OCP_TIMEOUT SECOND
-
-/* Threshold below which we stop fast OCP to save power */
-#define SINK_IDLE_CURRENT VBUS_MA(500 /* mA */)
-
-/* 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)
-/* Over-voltage recovery threshold is 1.1x Vnom */
-#define OVP_REC_MV(mv) VBUS_MV((mv) * 11 / 10)
-
-/* Maximum discharging delay */
-#define DISCHARGE_TIMEOUT (275*MSEC)
-/* Voltage overshoot below the OVP threshold for discharging to avoid OVP */
-#define DISCHARGE_OVERSHOOT_MV VBUS_MV(200)
-
-/* Time to wait after last RX edge interrupt before allowing deep sleep */
-#define PD_RX_SLEEP_TIMEOUT (100*MSEC)
-
-/* ----- 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_ODR(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;
- disable_sleep(SLEEP_MASK_USB_PWR);
- adc_enable_watchdog(ADC_CH_V_SENSE, 0xFFF, target_volt);
-}
-
-/* ----------------------- USB Power delivery policy ---------------------- */
-
-#define PDO_FIXED_FLAGS (PDO_FIXED_UNCONSTRAINED | PDO_FIXED_DATA_SWAP)
-
-/* Voltage indexes for the PDOs */
-enum volt_idx {
- PDO_IDX_5V = 0,
- PDO_IDX_12V = 1,
- PDO_IDX_20V = 2,
-
- PDO_IDX_COUNT
-};
-
-/* Power Delivery Objects */
-const uint32_t pd_src_pdo[] = {
- [PDO_IDX_5V] = PDO_FIXED(5000, RATED_CURRENT, PDO_FIXED_FLAGS),
- [PDO_IDX_12V] = PDO_FIXED(12000, RATED_CURRENT, PDO_FIXED_FLAGS),
- [PDO_IDX_20V] = PDO_FIXED(20000, RATED_CURRENT, PDO_FIXED_FLAGS),
-};
-const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
-BUILD_ASSERT(ARRAY_SIZE(pd_src_pdo) == PDO_IDX_COUNT);
-
-/* PDO voltages (should match the table above) */
-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 */
- int ovp_rec;/* over-voltage recovery threshold in mV */
-} voltages[ARRAY_SIZE(pd_src_pdo)] = {
- [PDO_IDX_5V] = {VO_5V, UVP_MV(5000), OVP_MV(5000),
- OVP_REC_MV(5000)},
- [PDO_IDX_12V] = {VO_12V, UVP_MV(12000), OVP_MV(12000),
- OVP_REC_MV(12000)},
- [PDO_IDX_20V] = {VO_20V, UVP_MV(20000), OVP_MV(20000),
- OVP_REC_MV(20000)},
-};
-
-/* current and previous selected PDO entry */
-static int volt_idx;
-static int last_volt_idx;
-/* target voltage at the end of discharge */
-static int discharge_volt_idx;
-
-/* output current measurement */
-int vbus_amp;
-
-__override int pd_board_check_request(uint32_t rdo, int pdo_cnt)
-{
- /* fault condition or output disabled: reject transitions */
- if (fault != FAULT_OK || !output_is_enabled())
- return EC_ERROR_INVAL;
-
- return EC_SUCCESS;
-}
-
-void pd_transition_voltage(int idx)
-{
- last_volt_idx = volt_idx;
- volt_idx = idx - 1;
- if (volt_idx < last_volt_idx) { /* down voltage transition */
- /* Stop OCP monitoring */
- adc_disable_watchdog();
-
- discharge_volt_idx = volt_idx;
- /* from 20V : do an intermediate step at 12V */
- if (volt_idx == PDO_IDX_5V && last_volt_idx == PDO_IDX_20V)
- volt_idx = PDO_IDX_12V;
- discharge_voltage(voltages[volt_idx].ovp);
- } else if (volt_idx > last_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);
- }
- }
- set_output_voltage(voltages[volt_idx].select);
-}
-
-int pd_set_power_supply_ready(int port)
-{
- /* fault condition not cleared : do not turn on power */
- if ((fault != FAULT_OK) || discharge_is_enabled())
- return EC_ERROR_INVAL;
-
- output_enable();
- /* Over-current monitoring */
- adc_enable_watchdog(ADC_CH_A_SENSE, MAX_CURRENT_FAST, 0);
-
- return EC_SUCCESS; /* we are ready */
-}
-
-void pd_power_supply_reset(int port)
-{
- int need_discharge = (volt_idx > 0) || discharge_is_enabled();
-
- output_disable();
- last_volt_idx = volt_idx;
- /* from 20V : do an intermediate step at 12V */
- volt_idx = volt_idx == PDO_IDX_20V ? PDO_IDX_12V : PDO_IDX_5V;
- set_output_voltage(voltages[volt_idx].select);
- /* TODO transition delay */
-
- /* Stop OCP monitoring to save power */
- adc_disable_watchdog();
-
- /* discharge voltage to 5V ? */
- if (need_discharge) {
- /* final target : 5V */
- discharge_volt_idx = PDO_IDX_5V;
- discharge_voltage(voltages[volt_idx].ovp);
- }
-}
-
-int pd_check_data_swap(int port,
- enum pd_data_role data_role)
-{
- /* Allow data swap if we are a DFP, otherwise don't allow */
- return (data_role == PD_ROLE_DFP) ? 1 : 0;
-}
-
-void pd_execute_data_swap(int port,
- enum pd_data_role data_role)
-{
- /* Do nothing */
-}
-
-void pd_check_pr_role(int port,
- enum pd_power_role pr_role,
- int flags)
-{
-}
-
-void pd_check_dr_role(int port,
- enum pd_data_role dr_role,
- int flags)
-{
- /* If DFP, try to switch to UFP */
- if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_DFP)
- pd_request_data_swap(port);
-}
-
-int pd_board_checks(void)
-{
-#ifdef CONFIG_HIBERNATE
- static timestamp_t hib_to;
- static int hib_to_ready;
-#endif
- int vbus_volt;
- int ovp_idx;
-
- /* Reload the watchdog */
- STM32_IWDG_KR = STM32_IWDG_KR_RELOAD;
-
-#ifdef CONFIG_HIBERNATE
- /* If output is disabled for long enough, then hibernate */
- if (!pd_is_connected(0) && hib_to_ready) {
- if (get_time().val >= hib_to.val) {
- debug_printf("hib\n");
- __enter_hibernate(0, 0);
- }
- } else {
- hib_to.val = get_time().val + 60*SECOND;
- hib_to_ready = 1;
- }
-#endif
-
- /* if it's been a while since last RX edge, then allow deep sleep */
- if (get_time_since_last_edge(0) > PD_RX_SLEEP_TIMEOUT)
- enable_sleep(SLEEP_MASK_USB_PD);
-
- vbus_volt = adc_read_channel(ADC_CH_V_SENSE);
- vbus_amp = adc_read_channel(ADC_CH_A_SENSE);
-
- if (fault == FAULT_FAST_OCP) {
- debug_printf("Fast OCP\n");
- pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_FAST_OCP, NULL);
- fault = FAULT_OCP;
- /* reset over-current after 1 second */
- fault_deadline.val = get_time().val + OCP_TIMEOUT;
- return EC_ERROR_INVAL;
- }
-
- if (vbus_amp > MAX_CURRENT) {
- /* 3 more samples to check whether this is just a transient */
- int count;
- for (count = 0; count < 3; count++)
- if (adc_read_channel(ADC_CH_A_SENSE) < MAX_CURRENT)
- break;
- /* trigger the slow OCP iff all 4 samples are above the max */
- if (count == 3) {
- debug_printf("OCP %d mA\n",
- vbus_amp * VDDA_MV / CURR_GAIN * 1000
- / R_SENSE / ADC_SCALE);
- pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_OCP, NULL);
- fault = FAULT_OCP;
- /* reset over-current after 1 second */
- fault_deadline.val = get_time().val + OCP_TIMEOUT;
- return EC_ERROR_INVAL;
- }
- }
- /*
- * Optimize power consumption when the sink is idle :
- * Enable STOP mode while we are connected,
- * this kills fast OCP as the actual ADC conversion for the analog
- * watchdog will happen on the next wake-up (x0 ms latency).
- */
- if (vbus_amp < SINK_IDLE_CURRENT && !discharge_is_enabled())
- /* override the PD state machine sleep mask */
- enable_sleep(SLEEP_MASK_USB_PWR);
- else if (vbus_amp > SINK_IDLE_CURRENT)
- disable_sleep(SLEEP_MASK_USB_PWR);
-
- /*
- * Set the voltage index to use for checking OVP. During a down step
- * transition, use the previous voltage index to check for OVP.
- */
- 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("OVP %d mV\n",
- ADC_TO_VOLT_MV(vbus_volt));
- pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_OVP, NULL);
- }
- 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)) {
- /* ensure we always finish a 2-step discharge */
- volt_idx = discharge_volt_idx;
- set_output_voltage(voltages[volt_idx].select);
- /* stop it */
- discharge_disable();
- /* enable over-current monitoring */
- adc_enable_watchdog(ADC_CH_A_SENSE, MAX_CURRENT_FAST, 0);
- debug_printf("Disch FAIL %d mV\n",
- ADC_TO_VOLT_MV(vbus_volt));
- pd_log_event(PD_EVENT_PS_FAULT, 0, PS_FAULT_DISCH, NULL);
- fault = FAULT_DISCHARGE;
- /* reset it after 1 second */
- fault_deadline.val = get_time().val + OCP_TIMEOUT;
- 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");
- /*
- * Reset the PD state and communication on both side,
- * so we can now re-negociate a voltage.
- */
- return EC_ERROR_INVAL;
- }
-
- return EC_SUCCESS;
-
-}
-
-void pd_adc_interrupt(void)
-{
- /* Clear flags */
- STM32_ADC_ISR = 0x8e;
-
- if (discharge_is_enabled()) {
- if (discharge_volt_idx != volt_idx) {
- /* first step of the discharge completed: now 12V->5V */
- volt_idx = PDO_IDX_5V;
- set_output_voltage(VO_5V);
- discharge_voltage(voltages[PDO_IDX_5V].ovp);
- } else { /* discharge complete */
- 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 */
- }
-
- /* clear ADC irq so we don't get a second interrupt */
- task_clear_pending_irq(STM32_IRQ_ADC_COMP);
-}
-DECLARE_IRQ(STM32_IRQ_ADC_COMP, pd_adc_interrupt, 1);
-
-/* ----------------- Vendor Defined Messages ------------------ */
-const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */
- 0, /* data caps as USB device */
- IDH_PTYPE_UNDEF, /* Undefined */
- 1, /* supports alt modes */
- USB_VID_GOOGLE);
-
-const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV);
-
-/* When set true, we are in GFU mode */
-static int gfu_mode;
-
-static int svdm_response_identity(int port, uint32_t *payload)
-{
- payload[VDO_I(IDH)] = vdo_idh;
- payload[VDO_I(CSTAT)] = VDO_CSTAT(0);
- payload[VDO_I(PRODUCT)] = vdo_product;
- return VDO_I(PRODUCT) + 1;
-}
-
-static int svdm_response_svids(int port, uint32_t *payload)
-{
- payload[1] = VDO_SVID(USB_VID_GOOGLE, 0);
- return 2;
-}
-
-/* Will only ever be a single mode for this device */
-#define MODE_CNT 1
-#define OPOS 1
-
-const uint32_t vdo_dp_mode[MODE_CNT] = {
- VDO_MODE_GOOGLE(MODE_GOOGLE_FU)
-};
-
-static int svdm_response_modes(int port, uint32_t *payload)
-{
- if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE)
- return 0; /* nak */
-
- memcpy(payload + 1, vdo_dp_mode, sizeof(vdo_dp_mode));
- return MODE_CNT + 1;
-}
-
-static int svdm_enter_mode(int port, uint32_t *payload)
-{
- /* SID & mode request is valid */
- if ((PD_VDO_VID(payload[0]) != USB_VID_GOOGLE) ||
- (PD_VDO_OPOS(payload[0]) != OPOS))
- return 0; /* will generate NAK */
-
- gfu_mode = 1;
- debug_printf("GFU\n");
- return 1;
-}
-
-static int svdm_exit_mode(int port, uint32_t *payload)
-{
- gfu_mode = 0;
- return 1; /* Must return ACK */
-}
-
-static struct amode_fx dp_fx = {
- .status = NULL,
- .config = NULL,
-};
-
-const struct svdm_response svdm_rsp = {
- .identity = &svdm_response_identity,
- .svids = &svdm_response_svids,
- .modes = &svdm_response_modes,
- .enter_mode = &svdm_enter_mode,
- .amode = &dp_fx,
- .exit_mode = &svdm_exit_mode,
-};
-
-__override int pd_custom_vdm(int port, int cnt, uint32_t *payload,
- uint32_t **rpayload)
-{
- int cmd = PD_VDO_CMD(payload[0]);
- int rsize;
-
- if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE || !gfu_mode)
- return 0;
-
- debug_printf("%pT] VDM/%d [%d] %08x\n",
- PRINTF_TIMESTAMP_NOW, cnt, cmd, payload[0]);
- *rpayload = payload;
-
- rsize = pd_custom_flash_vdm(port, cnt, payload);
- if (!rsize) {
- switch (cmd) {
- case VDO_CMD_PING_ENABLE:
- pd_ping_enable(0, payload[1]);
- rsize = 1;
- break;
- case VDO_CMD_CURRENT:
- /* return last measured current */
- payload[1] = ADC_TO_CURR_MA(vbus_amp);
- rsize = 2;
- break;
- case VDO_CMD_GET_LOG:
- rsize = pd_vdm_get_log_entry(payload);
- break;
- default:
- /* Unknown : do not answer */
- return 0;
- }
- }
-
- /* respond (positively) to the request */
- payload[0] |= VDO_SRC_RESPONDER;
-
- return rsize;
-}