예제 #1
0
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;
}
예제 #2
0
/**
 * 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;
	}
}
예제 #3
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;

}