Kunimitsu: Add S0ix on SLP_S0 assertion

On assertion of SLP_S0, EC goes to S0ix while system is in Lucid sleep
and EC is eligable to enter heavy sleep idle task.
Wakeup from S0ix by lid open, any key press, power button or track pad
will be done by PCH block by asserting SLP_S0.
At S0ix, 1 msec pulse will be generated every 8sec and this signal
should be ignored since this is NOT S0ix entry/exit related and defered
interrupt for SLP_S0 were added.

BRANCH=master
BUG=none
TEST=in OS shell, run following commands.
	Following command is valid with coreboot with S0ix patches.
	"echo freeze > /sys/power/state"
	then,
	Measure EC power consumption and compare it with one in S0.
	And on EC console, there should be NO periodic message, "power
	state 4 = S0ix, in 0x001d" every 8 sec.

Change-Id: Ia9cf5256b1ad7234815d4b6dbe2b45788aaf49dd
Signed-off-by: Kyoung Kim <kyoung.il.kim@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/307947
Commit-Ready: Kyoung Il Kim <kyoung.il.kim@intel.com>
Tested-by: Kyoung Il Kim <kyoung.il.kim@intel.com>
Reviewed-by: Kyoung Il Kim <kyoung.il.kim@intel.com>
Reviewed-by: Shawn N <shawnn@chromium.org>
This commit is contained in:
Kyoung Kim
2015-10-01 19:22:01 -07:00
committed by chrome-bot
parent 8704de934e
commit ebf92ecc83
7 changed files with 162 additions and 5 deletions

View File

@@ -68,6 +68,7 @@
/* We're space constrained on Kunimitsu, so reduce the UART TX buffer size. */
#undef CONFIG_UART_TX_BUF_SIZE
#define CONFIG_UART_TX_BUF_SIZE 512
#define CONFIG_POWER_S0IX
#define CONFIG_USB_CHARGER
#define CONFIG_USB_MUX_PI3USB30532
#define CONFIG_USB_POWER_DELIVERY
@@ -124,7 +125,7 @@
#define I2C_PORT_USB_CHARGER_2 MEC1322_I2C0_0
#undef DEFERRABLE_MAX_COUNT
#define DEFERRABLE_MAX_COUNT 13
#define DEFERRABLE_MAX_COUNT 14
#define CONFIG_ALS
#define CONFIG_ALS_OPT3001

View File

@@ -14,7 +14,7 @@ GPIO_INT(POWER_BUTTON_L, PIN(35), GPIO_INT_BOTH, power_button_interrupt)
GPIO_INT(RSMRST_L_PGOOD, PIN(63), GPIO_INT_BOTH, power_signal_interrupt)
GPIO_INT(PCH_SLP_S4_L, PIN(200), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt)
GPIO_INT(PCH_SLP_S3_L, PIN(206), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt)
GPIO_INT(PCH_SLP_S0_L, PIN(141), GPIO_INT_BOTH, power_signal_interrupt)
GPIO_INT(PCH_SLP_S0_L, PIN(141), GPIO_INT_BOTH, power_signal_interrupt_S0)
GPIO_INT(PCH_SLP_SUS_L, PIN(12), GPIO_INT_BOTH | GPIO_PULL_UP, power_signal_interrupt)
GPIO_INT(VOLUME_UP_L, PIN(31), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt)
GPIO_INT(VOLUME_DOWN_L, PIN(47), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt)

View File

@@ -30,6 +30,7 @@ enum chipset_state_mask {
CHIPSET_STATE_SOFT_OFF = 0x02, /* Soft off (S5) */
CHIPSET_STATE_SUSPEND = 0x04, /* Suspend (S3) */
CHIPSET_STATE_ON = 0x08, /* On (S0) */
CHIPSET_STATE_STANDBY = 0x10, /* Standby (S0ix) */
/* Common combinations */
CHIPSET_STATE_ANY_OFF = (CHIPSET_STATE_HARD_OFF |
CHIPSET_STATE_SOFT_OFF), /* Any off state */

View File

@@ -1441,6 +1441,8 @@
/* Use part of the EC's data EEPROM to hold persistent storage for the AP. */
#undef CONFIG_PSTORE
/* Support S0ix */
#undef CONFIG_POWER_S0IX
/*****************************************************************************/
/* Support PWM control */
#undef CONFIG_PWM

View File

@@ -20,7 +20,9 @@ enum power_state {
POWER_S5, /* System is soft-off */
POWER_S3, /* Suspend; RAM on, processor is asleep */
POWER_S0, /* System is on */
#ifdef CONFIG_POWER_S0IX
POWER_S0ix,
#endif
/* Transitions */
POWER_G3S5, /* G3 -> S5 (at system init time) */
POWER_S5S3, /* S5 -> S3 */
@@ -28,6 +30,10 @@ enum power_state {
POWER_S0S3, /* S0 -> S3 */
POWER_S3S5, /* S3 -> S5 */
POWER_S5G3, /* S5 -> G3 */
#ifdef CONFIG_POWER_S0IX
POWER_S0ixS0, /* S0ix -> S0 */
POWER_S0S0ix, /* S0 -> S0ix */
#endif
};
/* Information on an power signal */
@@ -99,10 +105,20 @@ enum power_state power_handle_state(enum power_state state);
*/
#ifdef HAS_TASK_CHIPSET
void power_signal_interrupt(enum gpio_signal signal);
#ifdef CONFIG_POWER_S0IX
void power_signal_interrupt_S0(enum gpio_signal signal);
#endif
#else
static inline void power_signal_interrupt(enum gpio_signal signal) { }
#ifdef CONFIG_POWER_S0IX
static inline void power_signal_interrupt_S0(enum gpio_signal signal) { }
#endif
#endif /* !HAS_TASK_CHIPSET */
#ifdef CONFIG_POWER_S0IX
int chipset_get_ps_debounced_level(enum gpio_signal signal);
#endif
/**
* pause_in_s5 getter method.
*

View File

@@ -38,12 +38,19 @@ static const char * const state_names[] = {
"S5",
"S3",
"S0",
#ifdef CONFIG_POWER_S0IX
"S0ix",
#endif
"G3->S5",
"S5->S3",
"S3->S0",
"S0->S3",
"S3->S5",
"S5->G3",
#ifdef CONFIG_POWER_S0IX
"S0ix->S0",
"S0->S0ix",
#endif
};
static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */
@@ -64,6 +71,15 @@ static uint32_t hibernate_delay = CONFIG_HIBERNATE_DELAY_SEC;
static int pause_in_s5;
#endif
static int power_signal_get_level(enum gpio_signal signal)
{
#ifdef CONFIG_POWER_S0IX
return chipset_get_ps_debounced_level(signal);
#else
return gpio_get_level(signal);
#endif
}
/**
* Update input signals mask
*/
@@ -74,7 +90,7 @@ static void power_update_signals(void)
int i;
for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) {
if (gpio_get_level(s->gpio) == s->level)
if (power_signal_get_level(s->gpio) == s->level)
inew |= 1 << i;
}
@@ -229,7 +245,13 @@ static enum power_state power_common_state(enum power_state state)
power_wait_signals(0);
task_wait_event(-1);
break;
#ifdef CONFIG_POWER_S0IX
case POWER_S0ix:
/* Wait for a message */
power_wait_signals(0);
task_wait_event(-1);
break;
#endif
default:
/* No common functionality for transition states */
break;
@@ -279,6 +301,15 @@ int chipset_in_state(int state_mask)
case POWER_S0:
need_mask = CHIPSET_STATE_ON;
break;
#ifdef CONFIG_POWER_S0IX
case POWER_S0ixS0:
case POWER_S0S0ix:
need_mask = CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY;
break;
case POWER_S0ix:
need_mask = CHIPSET_STATE_STANDBY;
break;
#endif
}
/* Return non-zero if all needed bits are present */

View File

@@ -14,6 +14,7 @@
#include "power.h"
#include "power_button.h"
#include "system.h"
#include "task.h"
#include "util.h"
#include "wireless.h"
@@ -27,9 +28,16 @@
#define IN_PCH_SLP_S4_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S4_DEASSERTED)
#define IN_PCH_SLP_SUS_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_SUS_DEASSERTED)
#ifdef CONFIG_POWER_S0IX
#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S0_DEASSERTED | \
IN_PCH_SLP_S3_DEASSERTED | \
IN_PCH_SLP_S4_DEASSERTED | \
IN_PCH_SLP_SUS_DEASSERTED)
#else
#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3_DEASSERTED | \
IN_PCH_SLP_S4_DEASSERTED | \
IN_PCH_SLP_SUS_DEASSERTED)
#endif
/*
* DPWROK is NC / stuffing option on initial boards.
@@ -164,12 +172,31 @@ static enum power_state _power_handle_state(enum power_state state)
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);
@@ -257,6 +284,33 @@ static enum power_state _power_handle_state(enum power_state state)
return POWER_S3;
#ifdef CONFIG_POWER_S0IX
case POWER_S0S0ix:
/* call hooks before standby */
hook_notify(HOOK_CHIPSET_SUSPEND);
/*
* 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:
/* 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);
@@ -347,3 +401,55 @@ void enter_pseudo_g3(void)
;
}
#endif
#ifdef CONFIG_POWER_S0IX
static struct {
int required; /* indicates de-bounce required. */
int done; /* debounced */
} slp_s0_debounce = {
.required = 0,
.done = 1,
};
int chipset_get_ps_debounced_level(enum gpio_signal signal)
{
/*
* If power state is updated in power_update_signal() by any interrupts
* other than SLP_S0 during the 1 msec pulse(invalid SLP_S0 signal),
* reading SLP_S0 should be corrected with slp_s0_debounce.done flag.
*/
int level = gpio_get_level(signal);
return (signal == GPIO_PCH_SLP_S0_L) ?
(level & slp_s0_debounce.done) : level;
}
static void slp_s0_assertion_deferred(void)
{
int s0_level = gpio_get_level(GPIO_PCH_SLP_S0_L);
/*
(s0_level != 0) ||
((s0_level == 0) && (slp_s0_debounce.required == 0))
*/
if (s0_level == slp_s0_debounce.required) {
if (s0_level)
slp_s0_debounce.done = 1; /* debounced! */
power_signal_interrupt(GPIO_PCH_SLP_S0_L);
}
slp_s0_debounce.required = 0;
}
DECLARE_DEFERRED(slp_s0_assertion_deferred);
void power_signal_interrupt_S0(enum gpio_signal signal)
{
if (gpio_get_level(GPIO_PCH_SLP_S0_L)) {
slp_s0_debounce.required = 1;
hook_call_deferred(slp_s0_assertion_deferred, 3 * MSEC);
}
else if (slp_s0_debounce.required == 0) {
slp_s0_debounce.done = 0;
slp_s0_assertion_deferred();
}
}
#endif