/**
 * Turn off the AP
 */
static int system_off(void)
{
	if (chipset_in_state(CHIPSET_STATE_ON)) {
		CPRINTS("pmu turning system off");
		chipset_force_shutdown();
	}

	return ST_IDLE0;
}
Exemple #2
0
/*
 * Send host event to the AP if the battery is temperature or charge level
 * is critical. Force-shutdown if the problem isn't corrected after timeout.
 */
static void shutdown_on_critical_battery(void)
{
	int batt_temp_c;
	int battery_critical = 0;

	/*
	 * TODO(crosbug.com/p/27642): The thermal loop should watch the battery
	 * temp, so it can turn fans on.
	 */
	batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature);
	if (battery_too_hot(batt_temp_c)) {
		CPRINTS("Batt temp out of range: %dC", batt_temp_c);
		battery_critical = 1;
	}

	if (battery_too_low() && !curr.batt_is_charging) {
		CPRINTS("Low battery: %d%%, %dmV",
			curr.batt.state_of_charge, curr.batt.voltage);
		battery_critical = 1;
	}

	if (!battery_critical) {
		/* Reset shutdown warning time */
		shutdown_warning_time.val = 0;
		return;
	}

	if (!shutdown_warning_time.val) {
		CPRINTS("charge warn shutdown due to critical battery");
		shutdown_warning_time = get_time();
		if (!chipset_in_state(CHIPSET_STATE_ANY_OFF))
			host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	} else if (get_time().val > shutdown_warning_time.val +
		   CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US) {
		if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
#ifdef CONFIG_HIBERNATE
			/* Timeout waiting for charger to provide more power */
			CPRINTS(
			  "charge force EC hibernate due to critical battery");
			system_hibernate(0, 0);
#elif defined(CONFIG_BATTERY_CRITICAL_SHUTDOWN_CUT_OFF)
			CPRINTS(
			  "charge force battery cut-off due to critical level");
			board_cut_off_battery();
#endif
		} else {
			/* Timeout waiting for AP to shut down, so kill it */
			CPRINTS(
			  "charge force shutdown due to critical battery");
			chipset_force_shutdown();
		}
	}
}
Exemple #3
0
/**
 * Prevent battery from going into deep discharge state
 */
static void low_battery_shutdown(struct charge_state_context *ctx)
{
	if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
		/* AP is off, so shut down the EC now */
		CPRINTS("charge force EC hibernate due to low battery");
		system_hibernate(0, 0);
	} else if (!ctx->shutdown_warning_time.val) {
		/* Warn AP battery level is so low we'll shut down */
		CPRINTS("charge warn shutdown due to low battery");
		ctx->shutdown_warning_time = get_time();
		host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	} else if (get_time().val > ctx->shutdown_warning_time.val +
		   LOW_BATTERY_SHUTDOWN_TIMEOUT_US) {
		/* Timeout waiting for AP to shut down, so kill it */
		CPRINTS("charge force shutdown due to low battery");
		chipset_force_shutdown();
	}
}
Exemple #4
0
/**
 * Discharge state handler
 *
 *	- detect ac status
 *	- new state: INIT
 */
static enum charge_state state_discharge(struct charge_state_context *ctx)
{
	struct batt_params *batt = &ctx->curr.batt;
	int8_t bat_temp_c = DECI_KELVIN_TO_CELSIUS(batt->temperature);
	if (ctx->curr.ac)
		return PWR_STATE_REINIT;

	if (ctx->curr.error)
		return PWR_STATE_ERROR;

	/* Handle overtemp in discharging state by powering off host */
	if ((bat_temp_c >= ctx->battery->discharging_max_c ||
	     bat_temp_c < ctx->battery->discharging_min_c) &&
	    chipset_in_state(CHIPSET_STATE_ON)) {
		CPRINTS("charge force shutdown due to battery temp");
		chipset_force_shutdown();
		host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN);
	}
	return PWR_STATE_UNCHANGE;
}
static void thermal_control(void)
{
	int i, j, t, rv, f;
	int count_over[EC_TEMP_THRESH_COUNT];
	int count_under[EC_TEMP_THRESH_COUNT];
	int num_valid_limits[EC_TEMP_THRESH_COUNT];
	int num_sensors_read;
	int fmax;
	int dptf_tripped;
	int temp_fan_configured;

	/* Get ready to count things */
	memset(count_over, 0, sizeof(count_over));
	memset(count_under, 0, sizeof(count_under));
	memset(num_valid_limits, 0, sizeof(num_valid_limits));
	num_sensors_read = 0;
	fmax = 0;
	dptf_tripped = 0;
	temp_fan_configured = 0;

	/* go through all the sensors */
	for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {

		/* read one */
		rv = temp_sensor_read(i, &t);
		if (rv != EC_SUCCESS)
			continue;
		else
			num_sensors_read++;

		/* check all the limits */
		for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) {
			int limit = thermal_params[i].temp_host[j];
			if (limit) {
				num_valid_limits[j]++;
				if (t > limit)
					count_over[j]++;
				else if (t < limit)
					count_under[j]++;
			}
		}

		/* figure out the max fan needed, too */
		if (thermal_params[i].temp_fan_off &&
		    thermal_params[i].temp_fan_max) {
			f = thermal_fan_percent(thermal_params[i].temp_fan_off,
						thermal_params[i].temp_fan_max,
						t);
			if (f > fmax)
				fmax = f;

			temp_fan_configured = 1;
		}

		/* and check the dptf thresholds */
		dptf_tripped |= dpft_check_temp_threshold(i, t);
	}

	if (!num_sensors_read) {
		/*
		 * Trigger a SMI event if we can't read any sensors.
		 *
		 * In theory we could do something more elaborate like forcing
		 * the system to shut down if no sensors are available after
		 * several retries.  This is a very unlikely scenario -
		 * particularly on LM4-based boards, since the LM4 has its own
		 * internal temp sensor.  It's most likely to occur during
		 * bringup of a new board, where we haven't debugged the I2C
		 * bus to the sensors; forcing a shutdown in that case would
		 * merely hamper board bringup.
		 */
		smi_sensor_failure_warning();
		return;
	}

	/* See what the aggregated limits are. Any temp over the limit
	 * means it's hot, but all temps have to be under the limit to
	 * be cool again.
	 */
	for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) {
		if (count_over[j])
			cond_set_true(&cond_hot[j]);
		else if (count_under[j] == num_valid_limits[j])
			cond_set_false(&cond_hot[j]);
	}


	/* What do we do about it? (note hard-coded logic). */

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) {
		CPRINTS("thermal SHUTDOWN");
		chipset_force_shutdown();
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HALT])) {
		/* We don't reboot automatically - the user has to push
		 * the power button. It's likely that we can't even
		 * detect this sensor transition until then, but we
		 * do have to check in order to clear the cond_t.
		 */
		CPRINTS("thermal no longer shutdown");
	}

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) {
		CPRINTS("thermal HIGH");
		throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_THERMAL);
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) {
		CPRINTS("thermal no longer high");
		throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_THERMAL);
	}

	if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) {
		CPRINTS("thermal WARN");
		throttle_ap(THROTTLE_ON, THROTTLE_SOFT, THROTTLE_SRC_THERMAL);
	} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) {
		CPRINTS("thermal no longer warn");
		throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, THROTTLE_SRC_THERMAL);
	}

	if (temp_fan_configured) {
#ifdef CONFIG_FANS
	/* TODO(crosbug.com/p/23797): For now, we just treat all fans the
	 * same. It would be better if we could assign different thermal
	 * profiles to each fan - in case one fan cools the CPU while another
	 * cools the radios or battery.
	 */
		for (i = 0; i < CONFIG_FANS; i++)
			fan_set_percent_needed(i, fmax);
#endif
	}

	/* Don't forget to signal any DPTF thresholds */
	if (dptf_tripped)
		host_set_single_event(EC_HOST_EVENT_THERMAL_THRESHOLD);
}
Exemple #6
0
enum power_state power_handle_state(enum power_state state)
{
	/*
	 * Pass through RSMRST asynchronously, as PCH may not react
	 * immediately to power changes.
	 */
	int rsmrst_in = gpio_get_level(GPIO_RSMRST_L_PGOOD);
	int rsmrst_out = gpio_get_level(GPIO_PCH_RSMRST_L);
#ifdef GLADOS_BOARD_V2
	int tries = 0;
#endif

	if (rsmrst_in != rsmrst_out) {
		/*
		 * Wait at least 10ms between power signals going high
		 * and deasserting RSMRST to PCH.
		 */
		if (rsmrst_in)
			msleep(10);
		gpio_set_level(GPIO_PCH_RSMRST_L, rsmrst_in);
		CPRINTS("RSMRST: %d", rsmrst_in);
	}

	switch (state) {
	case POWER_G3:
		if (forcing_shutdown) {
			power_button_pch_release();
			forcing_shutdown = 0;
		}
		break;

	case POWER_S5:
		if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1)
			return POWER_S5S3; /* Power up to next state */
		break;

	case POWER_S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			chipset_force_shutdown();
			return POWER_S0S3;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}
		break;

	case POWER_G3S5:
		/* Call hooks to initialize PMIC */
		hook_notify(HOOK_CHIPSET_PRE_INIT);

		if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) {
			chipset_force_shutdown();
			return POWER_G3;
		}

#ifdef GLADOS_BOARD_V2
		/*
		 * Allow up to 1s for charger to be initialized, in case
		 * we're trying to boot the AP with no battery.
		 */
		while (charge_prevent_power_on() &&
		       tries++ < CHARGER_INITIALIZED_TRIES) {
			msleep(CHARGER_INITIALIZED_DELAY_MS);
		}

		/* Return to G3 if battery level is too low */
		if (charge_want_shutdown() ||
		    tries == CHARGER_INITIALIZED_TRIES) {
			CPRINTS("power-up inhibited");
			chipset_force_shutdown();
			return POWER_G3;
		}

		/* Allow AP to power on */
		gpio_set_level(GPIO_PMIC_SLP_SUS_L, 1);
		gpio_set_level(GPIO_PCH_BATLOW_L, 1);
#endif

		return POWER_S5;

	case POWER_S5S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S5G3;
		}

		/* Enable TP + USB so that they can wake the system */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1);
		gpio_set_level(GPIO_USB1_ENABLE, 1);
		gpio_set_level(GPIO_USB2_ENABLE, 1);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		}

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1);

		/* Enable wireless */
		wireless_set_state(WIRELESS_ON);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S3;

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0);
		gpio_set_level(GPIO_USB1_ENABLE, 0);
		gpio_set_level(GPIO_USB2_ENABLE, 0);

		return POWER_S5G3;

	case POWER_S5G3:
#ifdef CONFIG_G3_SLEEP
		gpio_set_level(GPIO_G3_SLEEP_EN, 1);
#endif
		chipset_force_g3();
		return POWER_G3;

	default:
		break;
	}

	return state;
}
Exemple #7
0
static enum power_state _power_handle_state(enum power_state state)
{
	int tries = 0;

	switch (state) {
	case POWER_G3:
		break;

	case POWER_S5:
		if (forcing_shutdown) {
			power_button_pch_release();
			forcing_shutdown = 0;
		}

#ifdef CONFIG_BOARD_HAS_RTC_RESET
		/* Wait for S5 exit and attempt RTC reset it supported */
		if (power_s5_up)
			return power_wait_s5_rtc_reset();
#endif
		if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1)
			return POWER_S5S3; /* Power up to next state */
		break;

	case POWER_S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			chipset_force_shutdown();
			return POWER_S0S3;
#ifdef CONFIG_POWER_S0IX
		} else if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 0) &&
			   (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) {
			return POWER_S0S0ix;
#endif
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}

		break;

#ifdef CONFIG_POWER_S0IX
	case POWER_S0ix:
		/*
		 * TODO: add code for unexpected power loss
		 */
		if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 1) &&
		   (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) {
			return POWER_S0ixS0;
		}

		break;
#endif

	case POWER_G3S5:
		/* Call hooks to initialize PMIC */
		hook_notify(HOOK_CHIPSET_PRE_INIT);

		/*
		 * Allow up to 1s for charger to be initialized, in case
		 * we're trying to boot the AP with no battery.
		 */
		while (charge_prevent_power_on(0) &&
		       tries++ < CHARGER_INITIALIZED_TRIES) {
			msleep(CHARGER_INITIALIZED_DELAY_MS);
		}

		/* Return to G3 if battery level is too low */
		if (charge_want_shutdown() ||
		    tries > CHARGER_INITIALIZED_TRIES) {
			CPRINTS("power-up inhibited");
			chipset_force_shutdown();
			return POWER_G3;
		}

		if (power_wait_signals(IN_PCH_SLP_SUS_DEASSERTED)) {
			chipset_force_shutdown();
			return POWER_G3;
		}

		power_s5_up = 1;
		return POWER_S5;

	case POWER_S5S3:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S5G3;
		}

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		}

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 1);

		/* Enable wireless */
		wireless_set_state(WIRELESS_ON);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		gpio_set_level(GPIO_ENABLE_BACKLIGHT, 0);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S3;

#ifdef CONFIG_POWER_S0IX
	case POWER_S0S0ix:
		/* call hooks before standby */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		lpc_enable_wake_mask_for_lid_open();

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S0ix.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S0ix;


	case POWER_S0ixS0:
		lpc_disable_wake_mask_for_lid_open();

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		return POWER_S0;
#endif

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		/* Always enter into S5 state. The S5 state is required to
		 * correctly handle global resets which have a bit of delay
		 * while the SLP_Sx_L signals are asserted then deasserted. */
		power_s5_up = 0;
		return POWER_S5;

	case POWER_S5G3:
		chipset_force_g3();
		return POWER_G3;

	default:
		break;
	}

	return state;
}
Exemple #8
0
enum power_state power_handle_state(enum power_state state)
{
	switch (state) {
	case POWER_G3:
		break;

	case POWER_S5:
		if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1)
			return POWER_S5S3; /* Power up to next state */

		break;

	case POWER_S3:
		/* Check for state transitions */
		if (!power_has_signals(IN_PGOOD_S3)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S3S5;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
			/* Power up to next state */
			return POWER_S3S0;
		} else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) {
			/* Power down to next state */
			return POWER_S3S5;
		}
		break;

	case POWER_S0:
		if (!power_has_signals(IN_PGOOD_S0)) {
			/* Required rail went away */
			chipset_force_shutdown();
			return POWER_S0S3;
		} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
			/* Power down to next state */
			return POWER_S0S3;
		}
		break;

	case POWER_G3S5:
		/* Enable 3.3V DSW */
		gpio_set_level(GPIO_PP3300_DSW_EN, 1);

		/*
		 * Wait 10ms after +3VALW good, since that powers VccDSW and
		 * VccSUS.
		 */
		msleep(10);

		/* Enable PP5000 (5V) rail as 1.05V and 1.2V rails need 5V
		 * rail to regulate properly. */
		gpio_set_level(GPIO_PP5000_EN, 1);

		/* Wait for PP1050/PP1200 PGOOD to go LOW to
		 * indicate that PP5000 is stable */
		while ((power_get_signals() & IN_PGOOD_PP5000) != 0) {
			if (task_wait_event(SECOND) == TASK_EVENT_TIMER) {
				CPRINTS("timeout waiting for PP5000");
				gpio_set_level(GPIO_PP5000_EN, 0);
				chipset_force_shutdown();
				return POWER_G3;
			}
		}

		/* Turn on 3.3V DSW gated rail for core regulator */
		gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 1);

		/* Assert DPWROK */
		gpio_set_level(GPIO_PCH_DPWROK, 1);

		/* Enable PP1050 rail. */
		gpio_set_level(GPIO_PP1050_EN, 1);

		/* Wait for 1.05V to come up and CPU to notice */
		if (power_wait_signals(IN_PGOOD_PP1050 |
				     IN_PCH_SLP_SUS_DEASSERTED)) {
			gpio_set_level(GPIO_PP1050_EN, 0);
			gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0);
			gpio_set_level(GPIO_PP5000_EN, 0);
			chipset_force_shutdown();
			return POWER_G3;
		}

		/* Wait 5ms for SUSCLK to stabilize */
		msleep(5);

		/* Call hook to indicate out of G3 state */
		hook_notify(HOOK_CHIPSET_PRE_INIT);
		return POWER_S5;

	case POWER_S5S3:
		/* Turn on power to RAM */
		gpio_set_level(GPIO_PP1800_EN, 1);
		gpio_set_level(GPIO_PP1200_EN, 1);
		if (power_wait_signals(IN_PGOOD_S3)) {
			gpio_set_level(GPIO_PP1800_EN, 0);
			gpio_set_level(GPIO_PP1200_EN, 0);
			chipset_force_shutdown();
			return POWER_S5;
		}

		/*
		 * Take lightbar out of reset, now that +5VALW is
		 * available and we won't leak +3VALW through the reset
		 * line.
		 */
		gpio_set_level(GPIO_LIGHTBAR_RESET_L, 1);

		/*
		 * Enable touchpad power so it can wake the system from
		 * suspend.
		 */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1);

		/* Turn on USB power rail. */
		gpio_set_level(GPIO_PP5000_USB_EN, 1);

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_STARTUP);
		return POWER_S3;

	case POWER_S3S0:
		/* Wait 20ms before allowing VCCST_PGOOD to rise. */
		msleep(20);

		/* Enable wireless. */
		wireless_set_state(WIRELESS_ON);

		/* Make sure the touchscreen is on, too. */
		gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 1);

		/* Wait for non-core power rails good */
		if (power_wait_signals(IN_PGOOD_S0)) {
			chipset_force_shutdown();
			wireless_set_state(WIRELESS_OFF);
			return POWER_S3;
		}

		/* Call hooks now that rails are up */
		hook_notify(HOOK_CHIPSET_RESUME);

		/*
		 * Disable idle task deep sleep. This means that the low
		 * power idle task will not go into deep sleep while in S0.
		 */
		disable_sleep(SLEEP_MASK_AP_RUN);

		/* Wait 99ms after all voltages good */
		msleep(99);

		/*
		 * Throttle CPU if necessary.  This should only be asserted
		 * when +VCCP is powered (it is by now).
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu);

		/* Set PCH_PWROK */
		gpio_set_level(GPIO_PCH_PWROK, 1);
		gpio_set_level(GPIO_SYS_PWROK, 1);
		return POWER_S0;

	case POWER_S0S3:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SUSPEND);

		/* Clear PCH_PWROK */
		gpio_set_level(GPIO_SYS_PWROK, 0);
		gpio_set_level(GPIO_PCH_PWROK, 0);

		/* Wait 40ns */
		udelay(1);

		/* Suspend wireless */
		wireless_set_state(WIRELESS_SUSPEND);

		/*
		 * Enable idle task deep sleep. Allow the low power idle task
		 * to go into deep sleep in S3 or lower.
		 */
		enable_sleep(SLEEP_MASK_AP_RUN);

		/*
		 * Deassert prochot since CPU is off and we're about to drop
		 * +VCCP.
		 */
		gpio_set_level(GPIO_CPU_PROCHOT, 0);

		return POWER_S3;

	case POWER_S3S5:
		/* Call hooks before we remove power rails */
		hook_notify(HOOK_CHIPSET_SHUTDOWN);

		/* Disable wireless */
		wireless_set_state(WIRELESS_OFF);

		/* Disable peripheral power */
		gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0);
		gpio_set_level(GPIO_PP5000_USB_EN, 0);

		/* Turn off power to RAM */
		gpio_set_level(GPIO_PP1800_EN, 0);
		gpio_set_level(GPIO_PP1200_EN, 0);

		/*
		 * Put touchscreen and lightbar in reset, so we won't
		 * leak +3VALW through the reset line to chips powered
		 * by +5VALW.
		 *
		 * (Note that we're no longer powering down +5VALW due
		 * to crosbug.com/p/16600, but to minimize side effects
		 * of that change we'll still reset these components in
		 * S5.)
		 */
		gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0);
		gpio_set_level(GPIO_LIGHTBAR_RESET_L, 0);

		return pause_in_s5 ? POWER_S5 : POWER_S5G3;

	case POWER_S5G3:
		/* Deassert DPWROK */
		gpio_set_level(GPIO_PCH_DPWROK, 0);

		/* Turn off power rails enabled in S5 */
		gpio_set_level(GPIO_PP1050_EN, 0);
		gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 0);
		gpio_set_level(GPIO_PP5000_EN, 0);
		/* Disable 3.3V DSW */
		gpio_set_level(GPIO_PP3300_DSW_EN, 0);
		return POWER_G3;
	}

	return state;
}
static int command_apshutdown(int argc, char **argv)
{
	chipset_force_shutdown();
	return EC_SUCCESS;
}