Example #1
0
void chg_ramp_charge_supplier_change(int port, int supplier, int current,
				     timestamp_t registration_time)
{
	/*
	 * If the last active port was a valid port and the port
	 * has changed, then this may have been an over-current.
	 */
	if (active_port != CHARGE_PORT_NONE &&
	    port != active_port) {
		if (oc_info_idx[active_port] == RAMP_COUNT - 1)
			oc_info_idx[active_port] = 0;
		else
			oc_info_idx[active_port]++;
		ACTIVE_OC_INFO.ts = get_time();
		ACTIVE_OC_INFO.sup = active_sup;
		ACTIVE_OC_INFO.icl = active_icl;
	}

	/* Set new active port, set ramp state, and wake ramp task */
	active_port = port;
	active_sup = supplier;

	/* Set min and max input current limit based on if ramp is allowed */
	if (board_is_ramp_allowed(active_sup)) {
		min_icl = RAMP_CURR_START_MA;
		max_icl = board_get_ramp_current_limit(active_sup, current);
	} else {
		min_icl = max_icl = current;
	}

	reg_time = registration_time;
	if (ramp_st != CHG_RAMP_STABILIZE) {
		ramp_st = (active_port == CHARGE_PORT_NONE) ?
			  CHG_RAMP_DISCONNECTED : CHG_RAMP_CHARGE_DETECT_DELAY;
		CPRINTS("Ramp reset: st%d", ramp_st);
		task_wake(TASK_ID_CHG_RAMP);
	}
}
Example #2
0
/**
 * Charge manager refresh -- responsible for selecting the active charge port
 * and charge power. Called as a deferred task.
 */
static void charge_manager_refresh(void)
{
	int new_supplier, new_port;
	int new_charge_current, new_charge_current_uncapped;
	int new_charge_voltage, i;
	int updated_new_port = CHARGE_PORT_NONE;
	int updated_old_port = CHARGE_PORT_NONE;
	int ceil;

	/* Hunt for an acceptable charge port */
	while (1) {
		charge_manager_get_best_charge_port(&new_port, &new_supplier);

		/*
		 * If the port or supplier changed, make an attempt to switch to
		 * the port. We will re-set the active port on a supplier change
		 * to give the board-level function another chance to reject
		 * the port, for example, if the port has become a charge
		 * source.
		 */
		if ((new_port == charge_port &&
		    new_supplier == charge_supplier) ||
		    board_set_active_charge_port(new_port) == EC_SUCCESS)
			break;

		/* 'Dont charge' request must be accepted */
		ASSERT(new_port != CHARGE_PORT_NONE);

		/*
		 * Zero the available charge on the rejected port so that
		 * it is no longer chosen.
		 */
		for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i)
			available_charge[i][new_port].current = 0;
	}

	/*
	 * Clear override if it wasn't selected as the 'best' port -- it means
	 * that no charge is available on the port, or the port was rejected.
	 */
	if (override_port >= 0 && override_port != new_port)
		override_port = OVERRIDE_OFF;

	if (new_supplier == CHARGE_SUPPLIER_NONE) {
		new_charge_current = 0;
		new_charge_current_uncapped = 0;
		new_charge_voltage = 0;
	} else {
		new_charge_current_uncapped =
			available_charge[new_supplier][new_port].current;
#ifdef CONFIG_CHARGE_RAMP_HW
		/*
		 * Allow to set the maximum current value, so the hardware can
		 * know the range of acceptable current values for its ramping.
		 */
		if (board_is_ramp_allowed(new_supplier))
			new_charge_current_uncapped =
				board_get_ramp_current_limit(new_supplier,
						 new_charge_current_uncapped);
#endif /* CONFIG_CHARGE_RAMP_HW */
		/* Enforce port charge ceiling. */
		ceil = charge_manager_get_ceil(new_port);
		if (ceil != CHARGE_CEIL_NONE)
			new_charge_current = MIN(ceil,
						 new_charge_current_uncapped);
		else
			new_charge_current = new_charge_current_uncapped;

		new_charge_voltage =
			available_charge[new_supplier][new_port].voltage;
	}

	/* Change the charge limit + charge port/supplier if modified. */
	if (new_port != charge_port || new_charge_current != charge_current ||
	    new_supplier != charge_supplier) {
#ifdef HAS_TASK_CHG_RAMP
		chg_ramp_charge_supplier_change(
				new_port, new_supplier, new_charge_current,
				registration_time[new_port]);
#else
#ifdef CONFIG_CHARGE_RAMP_HW
		/* Enable or disable charge ramp */
		charger_set_hw_ramp(board_is_ramp_allowed(new_supplier));
#endif
		board_set_charge_limit(new_charge_current);
#endif
		CPRINTS("CL: p%d s%d i%d v%d", new_port, new_supplier,
			new_charge_current, new_charge_voltage);
	}

	/*
	 * Signal new power request only if the port changed, the voltage
	 * on the same port changed, or the actual uncapped current
	 * on the same port changed (don't consider ceil).
	 */
	if (new_port != CHARGE_PORT_NONE &&
	    (new_port != charge_port ||
	     new_charge_current_uncapped != charge_current_uncapped ||
	     new_charge_voltage != charge_voltage))
		updated_new_port = new_port;

	/* If charge port changed, cleanup old port */
	if (charge_port != new_port && charge_port != CHARGE_PORT_NONE) {
		/* Check if need power swap */
		charge_manager_switch_to_source(charge_port);
		/* Signal new power request on old port */
		updated_old_port = charge_port;
	}

	/* Update globals to reflect current state. */
	charge_current = new_charge_current;
	charge_current_uncapped = new_charge_current_uncapped;
	charge_voltage = new_charge_voltage;
	charge_supplier = new_supplier;
	charge_port = new_port;

#ifdef CONFIG_USB_PD_LOGGING
	/*
	 * Write a log under the following conditions:
	 *  1. A port becomes active or
	 *  2. A port becomes inactive or
	 *  3. The active charge port power limit changes or
	 *  4. Any supplier change on an inactive port
	 */
	if (updated_new_port != CHARGE_PORT_NONE)
		save_log[updated_new_port] = 1;
	/* Don't log non-meaningful changes on charge port */
	else if (charge_port != CHARGE_PORT_NONE)
		save_log[charge_port] = 0;

	if (updated_old_port != CHARGE_PORT_NONE)
		save_log[updated_old_port] = 1;

	for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i)
		if (save_log[i])
			charge_manager_save_log(i);
#endif

	/* New power requests must be set only after updating the globals. */
	if (updated_new_port != CHARGE_PORT_NONE)
		pd_set_new_power_request(updated_new_port);
	if (updated_old_port != CHARGE_PORT_NONE)
		pd_set_new_power_request(updated_old_port);
}
Example #3
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;
	}
}