Esempio n. 1
0
void chg_ramp_task(void)
{
	int task_wait_time = -1;
	int i, lim;
	uint64_t detect_end_time_us = 0, time_us;
	int last_active_port = CHARGE_PORT_NONE;

	/*
	 * Static initializer so that we don't clobber early calls to this
	 * module.
	 */
	static enum chg_ramp_state ramp_st_prev = CHG_RAMP_DISCONNECTED,
				   ramp_st_new = CHG_RAMP_DISCONNECTED;
	int active_icl_new;

	/* Clear last OCP supplier to guarantee we ramp on first connect */
	for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++)
		oc_info[i][0].sup = CHARGE_SUPPLIER_NONE;

	while (1) {
		ramp_st_new = ramp_st;
		active_icl_new = active_icl;
		switch (ramp_st) {
		case CHG_RAMP_DISCONNECTED:
			/* Do nothing */
			task_wait_time = -1;
			break;
		case CHG_RAMP_CHARGE_DETECT_DELAY:
			/* Delay for charge_manager to determine supplier */
			/*
			 * On entry to state, or if port changes, store the
			 * OC recovery time, and calculate the detect end
			 * time to exit this state.
			 */
			if (ramp_st_prev != ramp_st ||
			    active_port != last_active_port) {
				last_active_port = active_port;
				ACTIVE_OC_INFO.recover =
					reg_time.val - ACTIVE_OC_INFO.ts.val;
				detect_end_time_us = get_time().val +
						     CHARGE_DETECT_DELAY;
				task_wait_time = CHARGE_DETECT_DELAY;
				break;
			}

			/* If detect delay has not passed, set wait time */
			time_us = get_time().val;
			if (time_us < detect_end_time_us) {
				task_wait_time = detect_end_time_us - time_us;
				break;
			}

			/* Detect delay is over, fall through to next state */
			ramp_st_new = CHG_RAMP_OVERCURRENT_DETECT;
			/* notify host of power info change */
			pd_send_host_event(PD_EVENT_POWER_CHANGE);
		case CHG_RAMP_OVERCURRENT_DETECT:
			/* Check if we should ramp or go straight to stable */
			task_wait_time = SECOND;

			/* Skip ramp for specific suppliers */
			if (!board_is_ramp_allowed(active_sup)) {
				active_icl_new = min_icl;
				ramp_st_new = CHG_RAMP_STABLE;
				break;
			}

			/*
			 * If we are not drawing full charge, then don't ramp,
			 * just wait in this state, until we are.
			 */
			if (!board_is_consuming_full_charge()) {
				task_wait_time = CURRENT_DRAW_DELAY;
				break;
			}

			/*
			 * Compare recent OCP events, if all info matches,
			 * then we don't need to ramp anymore.
			 */
			for (i = 0; i < RAMP_COUNT; i++) {
				if (oc_info[active_port][i].sup != active_sup ||
				    oc_info[active_port][i].recover >
				    OC_RECOVER_MAX_TIME)
					break;
			}

			if (i == RAMP_COUNT) {
				/* Found OC threshold! */
				active_icl_new = ACTIVE_OC_INFO.icl -
						 RAMP_ICL_BACKOFF;
				ramp_st_new = CHG_RAMP_STABLE;
			} else {
				/*
				 * Need to ramp to find OC threshold, start
				 * at the minimum input current limit.
				 */
				active_icl_new = min_icl;
				ramp_st_new = CHG_RAMP_RAMP;
			}
			break;
		case CHG_RAMP_RAMP:
			/* Keep ramping until we find the limit */
			task_wait_time = RAMP_CURR_DELAY;

			/* Pause ramping if we are not drawing full current */
			if (!board_is_consuming_full_charge()) {
				task_wait_time = CURRENT_DRAW_DELAY;
				break;
			}

			/* If VBUS is sagging a lot, then stop ramping */
			if (board_is_vbus_too_low(CHG_RAMP_VBUS_RAMPING)) {
				CPRINTS("VBUS low");
				active_icl_new = MAX(min_icl, active_icl -
							      RAMP_ICL_BACKOFF);
				ramp_st_new = CHG_RAMP_STABILIZE;
				task_wait_time = STABLIZE_DELAY;
				stablize_port = active_port;
				stablize_sup = active_sup;
				break;
			}

			/* Ramp the current limit if we haven't reached max */
			if (active_icl == max_icl)
				ramp_st_new = CHG_RAMP_STABLE;
			else if (active_icl + RAMP_CURR_INCR_MA > max_icl)
				active_icl_new = max_icl;
			else
				active_icl_new = active_icl + RAMP_CURR_INCR_MA;
			break;
		case CHG_RAMP_STABILIZE:
			/* Wait for current to stabilize after ramp is done */
			/* Use default delay for exiting this state */
			task_wait_time = SECOND;
			if (active_port == stablize_port &&
			    active_sup == stablize_sup) {
				ramp_st_new = CHG_RAMP_STABLE;
				break;
			}

			ramp_st_new = active_port == CHARGE_PORT_NONE ?
				      CHG_RAMP_DISCONNECTED :
				      CHG_RAMP_CHARGE_DETECT_DELAY;
			break;
		case CHG_RAMP_STABLE:
			/* Maintain input current limit */
			/* On entry log charging stats */
			if (ramp_st_prev != ramp_st) {
#ifdef CONFIG_USB_PD_LOGGING
				charge_manager_save_log(active_port);
#endif
				/* notify host of power info change */
				pd_send_host_event(PD_EVENT_POWER_CHANGE);
			}

			/* Keep an eye on VBUS and restart ramping if it dips */
			if (board_is_ramp_allowed(active_sup) &&
			    board_is_vbus_too_low(CHG_RAMP_VBUS_STABLE)) {
				CPRINTS("VBUS low; Re-ramp");
				active_icl_new = min_icl;
				ramp_st_new = CHG_RAMP_RAMP;
			}
			task_wait_time = STABLE_VBUS_MONITOR_INTERVAL;
			break;
		}
		if (ramp_st != ramp_st_new || active_icl != active_icl_new)
			CPRINTS("Ramp p%d st%d %dmA %dmA",
				active_port, ramp_st_new, min_icl,
				active_icl_new);

		ramp_st_prev = ramp_st;
		ramp_st = ramp_st_new;
		active_icl = active_icl_new;

		/* Set the input current limit */
		lim = chg_ramp_get_current_limit();
		board_set_charge_limit(active_port, active_sup, lim, lim);

		if (ramp_st == CHG_RAMP_STABILIZE)
			/*
			 * When in stabilize state, supplier/port may change
			 * and we don't want to wake up task until we have
			 * slept this amount of time.
			 */
			usleep(task_wait_time);
		else
			task_wait_event(task_wait_time);
	}
}
Esempio n. 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);
}