int pd_board_checks(void) { static int was_connected = -1; if (was_connected != 1 && pd_is_connected(0)) board_maybe_reset_usb_hub(); was_connected = pd_is_connected(0); return EC_SUCCESS; }
/** * Fills passed power_info structure with current info about the passed port. */ static void charge_manager_fill_power_info(int port, struct ec_response_usb_pd_power_info *r) { int sup = CHARGE_SUPPLIER_NONE; int i; /* Determine supplier information to show. */ if (port == charge_port) sup = charge_supplier; else /* Find highest priority supplier */ for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) if (available_charge[i][port].current > 0 && available_charge[i][port].voltage > 0 && (sup == CHARGE_SUPPLIER_NONE || supplier_priority[i] < supplier_priority[sup] || (supplier_priority[i] == supplier_priority[sup] && POWER(available_charge[i][port]) > POWER(available_charge[sup] [port])))) sup = i; /* Fill in power role */ if (charge_port == port) r->role = USB_PD_PORT_POWER_SINK; else if (pd_is_connected(port) && pd_get_role(port) == PD_ROLE_SOURCE) r->role = USB_PD_PORT_POWER_SOURCE; else if (sup != CHARGE_SUPPLIER_NONE) r->role = USB_PD_PORT_POWER_SINK_NOT_CHARGING; else r->role = USB_PD_PORT_POWER_DISCONNECTED; /* Is port partner dual-role capable */ r->dualrole = (dualrole_capability[port] == CAP_DUALROLE); if (sup == CHARGE_SUPPLIER_NONE || r->role == USB_PD_PORT_POWER_SOURCE) { r->type = USB_CHG_TYPE_NONE; r->meas.voltage_max = 0; r->meas.voltage_now = r->role == USB_PD_PORT_POWER_SOURCE ? 5000 : 0; r->meas.current_max = 0; r->max_power = 0; } else { #if defined(HAS_TASK_CHG_RAMP) || defined(CONFIG_CHARGE_RAMP_HW) /* Read ramped current if active charging port */ int use_ramp_current = (charge_port == port); #else const int use_ramp_current = 0; #endif switch (sup) { case CHARGE_SUPPLIER_PD: r->type = USB_CHG_TYPE_PD; break; case CHARGE_SUPPLIER_TYPEC: r->type = USB_CHG_TYPE_C; break; case CHARGE_SUPPLIER_PROPRIETARY: r->type = USB_CHG_TYPE_PROPRIETARY; break; case CHARGE_SUPPLIER_BC12_DCP: r->type = USB_CHG_TYPE_BC12_DCP; break; case CHARGE_SUPPLIER_BC12_CDP: r->type = USB_CHG_TYPE_BC12_CDP; break; case CHARGE_SUPPLIER_BC12_SDP: r->type = USB_CHG_TYPE_BC12_SDP; break; case CHARGE_SUPPLIER_VBUS: r->type = USB_CHG_TYPE_VBUS; break; default: r->type = USB_CHG_TYPE_OTHER; } r->meas.voltage_max = available_charge[sup][port].voltage; if (use_ramp_current) { /* * If charge_ramp has not detected charger yet, * then charger type is unknown. */ if (!chg_ramp_is_detected()) r->type = USB_CHG_TYPE_UNKNOWN; /* Current limit is output of ramp module */ r->meas.current_lim = chg_ramp_get_current_limit(); /* * If ramp is allowed, then the max current depends * on if ramp is stable. If ramp is stable, then * max current is same as input current limit. If * ramp is not stable, then we report the maximum * current we could ramp up to for this supplier. * If ramp is not allowed, max current is just the * available charge current. */ if (board_is_ramp_allowed(sup)) { r->meas.current_max = chg_ramp_is_stable() ? r->meas.current_lim : board_get_ramp_current_limit( sup, available_charge[sup][port].current); } else { r->meas.current_max = available_charge[sup][port].current; } r->max_power = r->meas.current_max * r->meas.voltage_max; } else { r->meas.current_max = r->meas.current_lim = available_charge[sup][port].current; r->max_power = POWER(available_charge[sup][port]); } /* * If we are sourcing power, or sinking but not charging, then * VBUS must be 5V. If we are charging, then read VBUS ADC. */ if (r->role == USB_PD_PORT_POWER_SINK_NOT_CHARGING) r->meas.voltage_now = 5000; else if (ADC_VBUS >= 0) r->meas.voltage_now = adc_read_channel(ADC_VBUS); else /* No VBUS ADC channel - voltage is unknown */ r->meas.voltage_now = 0; } }
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; }