diff --git a/board/falco/board.c b/board/falco/board.c index b088bcf3c5..f394244d63 100644 --- a/board/falco/board.c +++ b/board/falco/board.c @@ -7,6 +7,8 @@ #include "adc.h" #include "board.h" #include "chip_temp_sensor.h" +#include "chipset_haswell.h" +#include "chipset_x86_common.h" #include "common.h" #include "ec_commands.h" #include "extpower.h" @@ -24,7 +26,6 @@ #include "timer.h" #include "tmp006.h" #include "util.h" -#include "x86_power.h" /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { @@ -38,25 +39,23 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH, switch_interrupt}, {"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, - {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1050_PGOOD", LM4_GPIO_H, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1350_PGOOD", LM4_GPIO_H, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP5000_PGOOD", LM4_GPIO_N, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"VCORE_PGOOD", LM4_GPIO_C, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_EDP_VDD_EN", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + haswell_interrupt}, {"RECOVERY_L", LM4_GPIO_A, (1<<5), GPIO_PULL_UP|GPIO_INT_BOTH, switch_interrupt}, {"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH, @@ -64,6 +63,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Other inputs */ {"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL}, + {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, NULL}, {"USB1_OC_L", LM4_GPIO_E, (1<<7), GPIO_INPUT, NULL}, {"USB2_OC_L", LM4_GPIO_E, (1<<0), GPIO_INPUT, NULL}, {"BOARD_VERSION1", LM4_GPIO_Q, (1<<5), GPIO_INPUT, NULL}, @@ -122,6 +122,18 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"BAT_LED1", LM4_GPIO_D, (1<<1), GPIO_OUT_LOW, NULL}, }; +/* x86 signal list. Must match order of enum x86_signal. */ +const struct x86_signal_info x86_signal_list[X86_SIGNAL_COUNT] = { + {GPIO_PP5000_PGOOD, 1, "PGOOD_PP5000"}, + {GPIO_PP1350_PGOOD, 1, "PGOOD_PP1350"}, + {GPIO_PP1050_PGOOD, 1, "PGOOD_PP1050"}, + {GPIO_VCORE_PGOOD, 1, "PGOOD_VCORE"}, + {GPIO_PCH_SLP_S0_L, 1, "SLP_S0#_DEASSERTED"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S5_L, 1, "SLP_S5#_DEASSERTED"}, + {GPIO_PCH_SLP_SUS_L, 1, "SLP_SUS#_DEASSERTED"}, +}; + /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ const struct adc_t adc_channels[ADC_CH_COUNT] = { /* EC internal temperature is calculated by diff --git a/board/falco/board.h b/board/falco/board.h index 42f64f30da..beedb6e5f6 100644 --- a/board/falco/board.h +++ b/board/falco/board.h @@ -77,7 +77,6 @@ enum gpio_signal { GPIO_PCH_SLP_S3_L, /* SLP_S3# signal from PCH */ GPIO_PCH_SLP_S5_L, /* SLP_S5# signal from PCH */ GPIO_PCH_SLP_SUS_L, /* SLP_SUS# signal from PCH */ - GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_PP1050_PGOOD, /* Power good on 1.05V */ GPIO_PP1350_PGOOD, /* Power good on 1.35V (DRAM) */ GPIO_PP5000_PGOOD, /* Power good on 5V */ @@ -88,6 +87,7 @@ enum gpio_signal { /* Other inputs */ GPIO_FAN_ALERT_L, /* From thermal sensor */ + GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_USB1_OC_L, /* USB port overcurrent warning */ GPIO_USB2_OC_L, /* USB port overcurrent warning */ GPIO_BOARD_VERSION1, /* Board version stuffing resistor 1 */ @@ -143,6 +143,21 @@ enum gpio_signal { GPIO_COUNT }; +/* x86 signal definitions */ +enum x86_signal { + X86_PGOOD_PP5000 = 0, + X86_PGOOD_PP1350, + X86_PGOOD_PP1050, + X86_PGOOD_VCORE, + X86_PCH_SLP_S0n_DEASSERTED, + X86_PCH_SLP_S3n_DEASSERTED, + X86_PCH_SLP_S5n_DEASSERTED, + X86_PCH_SLP_SUSn_DEASSERTED, + + /* Number of X86 signals */ + X86_SIGNAL_COUNT +}; + /* Charger module */ /* Set charger input current limit * Note - this value should depend on external power adapter, diff --git a/board/link/board.c b/board/link/board.c index 29cefeea4f..301140d9b9 100644 --- a/board/link/board.c +++ b/board/link/board.c @@ -7,6 +7,8 @@ #include "adc.h" #include "board.h" #include "chip_temp_sensor.h" +#include "chipset_ivybridge.h" +#include "chipset_x86_common.h" #include "common.h" #include "ec_commands.h" #include "extpower.h" @@ -23,7 +25,6 @@ #include "timer.h" #include "tmp006.h" #include "util.h" -#include "x86_power.h" /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { @@ -42,35 +43,35 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PCH_BKLTEN", LM4_GPIO_J, (1<<3), GPIO_INT_BOTH, switch_interrupt}, {"PCH_SLP_A_L", LM4_GPIO_G, (1<<5), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_ME_CSW_DEV_L", LM4_GPIO_G, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S3_L", LM4_GPIO_J, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S4_L", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S5_L", LM4_GPIO_J, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + ivybridge_interrupt}, {"PGOOD_1_5V_DDR", LM4_GPIO_K, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_1_5V_PCH", LM4_GPIO_K, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_1_8VS", LM4_GPIO_K, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_5VALW", LM4_GPIO_H, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_CPU_CORE", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_VCCP", LM4_GPIO_K, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_VCCSA", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PGOOD_VGFX_CORE", LM4_GPIO_D, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"RECOVERY_L", LM4_GPIO_H, (1<<7), GPIO_INT_BOTH, switch_interrupt}, {"USB1_STATUS_L", LM4_GPIO_E, (1<<7), 0, NULL}, @@ -121,6 +122,24 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"USB2_ILIM_SEL", LM4_GPIO_E, (1<<0), GPIO_OUT_LOW, NULL}, }; +/* x86 signal list. Must match order of enum x86_signal. */ +const struct x86_signal_info x86_signal_list[X86_SIGNAL_COUNT] = { + {GPIO_PGOOD_5VALW, 1, "PGOOD_5VALW"}, + {GPIO_PGOOD_1_5V_DDR, 1, "PGOOD_1_5V_DDR"}, + {GPIO_PGOOD_1_5V_PCH, 1, "PGOOD_1_5V_PCH"}, + {GPIO_PGOOD_1_8VS, 1, "PGOOD_1_8VS"}, + {GPIO_PGOOD_VCCP, 1, "PGOOD_VCCP"}, + {GPIO_PGOOD_VCCSA, 1, "PGOOD_VCCSA"}, + {GPIO_PGOOD_CPU_CORE, 1, "PGOOD_CPU_CORE"}, + {GPIO_PGOOD_VGFX_CORE, 1, "PGOOD_VGFX_CORE"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S4_L, 1, "SLP_S4#_DEASSERTED"}, + {GPIO_PCH_SLP_S5_L, 1, "SLP_S5#_DEASSERTED"}, + {GPIO_PCH_SLP_A_L, 1, "SLP_A#_DEASSERTED"}, + {GPIO_PCH_SLP_SUS_L, 1, "SLP_SUS#_DEASSERTED"}, + {GPIO_PCH_SLP_ME_CSW_DEV_L, 1, "SLP_ME#_DEASSERTED"}, +}; + /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ const struct adc_t adc_channels[ADC_CH_COUNT] = { /* EC internal temperature is calculated by diff --git a/board/link/board.h b/board/link/board.h index c451d1269b..711c36c2c3 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -177,6 +177,27 @@ enum gpio_signal { GPIO_COUNT }; +/* x86 signal definitions */ +enum x86_signal { + X86_PGOOD_5VALW = 0, + X86_PGOOD_1_5V_DDR, + X86_PGOOD_1_5V_PCH, + X86_PGOOD_1_8VS, + X86_PGOOD_VCCP, + X86_PGOOD_VCCSA, + X86_PGOOD_CPU_CORE, + X86_PGOOD_VGFX_CORE, + X86_PCH_SLP_S3n_DEASSERTED, + X86_PCH_SLP_S4n_DEASSERTED, + X86_PCH_SLP_S5n_DEASSERTED, + X86_PCH_SLP_An_DEASSERTED, + X86_PCH_SLP_SUSn_DEASSERTED, + X86_PCH_SLP_MEn_DEASSERTED, + + /* Number of X86 signals */ + X86_SIGNAL_COUNT +}; + enum temp_sensor_id { /* TMP006 U20, die/object temperature near Mini-DP / USB connectors */ TEMP_SENSOR_I2C_U20_DIE = 0, diff --git a/board/peppy/board.c b/board/peppy/board.c index 1f3afd49b5..5f1422e98c 100644 --- a/board/peppy/board.c +++ b/board/peppy/board.c @@ -7,6 +7,8 @@ #include "adc.h" #include "board.h" #include "chip_temp_sensor.h" +#include "chipset_haswell.h" +#include "chipset_x86_common.h" #include "common.h" #include "ec_commands.h" #include "extpower.h" @@ -24,7 +26,6 @@ #include "timer.h" #include "tmp006.h" #include "util.h" -#include "x86_power.h" /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { @@ -38,25 +39,23 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH, switch_interrupt}, {"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, - {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1050_PGOOD", LM4_GPIO_H, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1350_PGOOD", LM4_GPIO_H, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP5000_PGOOD", LM4_GPIO_N, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"VCORE_PGOOD", LM4_GPIO_C, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_EDP_VDD_EN", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + haswell_interrupt}, {"RECOVERY_L", LM4_GPIO_A, (1<<5), GPIO_PULL_UP|GPIO_INT_BOTH, switch_interrupt}, {"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH, @@ -64,6 +63,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Other inputs */ {"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL}, + {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, NULL}, {"USB1_OC_L", LM4_GPIO_E, (1<<7), GPIO_INPUT, NULL}, {"USB2_OC_L", LM4_GPIO_E, (1<<0), GPIO_INPUT, NULL}, {"BOARD_VERSION1", LM4_GPIO_Q, (1<<5), GPIO_INPUT, NULL}, @@ -122,6 +122,18 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PWR_LED1_L", LM4_GPIO_N, (1<<6), GPIO_ODR_HIGH, NULL}, }; +/* x86 signal list. Must match order of enum x86_signal. */ +const struct x86_signal_info x86_signal_list[X86_SIGNAL_COUNT] = { + {GPIO_PP5000_PGOOD, 1, "PGOOD_PP5000"}, + {GPIO_PP1350_PGOOD, 1, "PGOOD_PP1350"}, + {GPIO_PP1050_PGOOD, 1, "PGOOD_PP1050"}, + {GPIO_VCORE_PGOOD, 1, "PGOOD_VCORE"}, + {GPIO_PCH_SLP_S0_L, 1, "SLP_S0#_DEASSERTED"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S5_L, 1, "SLP_S5#_DEASSERTED"}, + {GPIO_PCH_SLP_SUS_L, 1, "SLP_SUS#_DEASSERTED"}, +}; + /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ const struct adc_t adc_channels[ADC_CH_COUNT] = { /* EC internal temperature is calculated by diff --git a/board/peppy/board.h b/board/peppy/board.h index efcc467198..102c2fffa9 100644 --- a/board/peppy/board.h +++ b/board/peppy/board.h @@ -76,7 +76,6 @@ enum gpio_signal { GPIO_PCH_SLP_S3_L, /* SLP_S3# signal from PCH */ GPIO_PCH_SLP_S5_L, /* SLP_S5# signal from PCH */ GPIO_PCH_SLP_SUS_L, /* SLP_SUS# signal from PCH */ - GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_PP1050_PGOOD, /* Power good on 1.05V */ GPIO_PP1350_PGOOD, /* Power good on 1.35V (DRAM) */ GPIO_PP5000_PGOOD, /* Power good on 5V */ @@ -87,6 +86,7 @@ enum gpio_signal { /* Other inputs */ GPIO_FAN_ALERT_L, /* From thermal sensor */ + GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_USB1_OC_L, /* USB port overcurrent warning */ GPIO_USB2_OC_L, /* USB port overcurrent warning */ GPIO_BOARD_VERSION1, /* Board version stuffing resistor 1 */ @@ -142,6 +142,21 @@ enum gpio_signal { GPIO_COUNT }; +/* x86 signal definitions */ +enum x86_signal { + X86_PGOOD_PP5000 = 0, + X86_PGOOD_PP1350, + X86_PGOOD_PP1050, + X86_PGOOD_VCORE, + X86_PCH_SLP_S0n_DEASSERTED, + X86_PCH_SLP_S3n_DEASSERTED, + X86_PCH_SLP_S5n_DEASSERTED, + X86_PCH_SLP_SUSn_DEASSERTED, + + /* Number of X86 signals */ + X86_SIGNAL_COUNT +}; + /* Charger module */ /* Set charger input current limit * Note - this value should depend on external power adapter, diff --git a/board/slippy/board.c b/board/slippy/board.c index 2fc24797ce..bf06a80b54 100644 --- a/board/slippy/board.c +++ b/board/slippy/board.c @@ -7,6 +7,8 @@ #include "adc.h" #include "board.h" #include "chip_temp_sensor.h" +#include "chipset_haswell.h" +#include "chipset_x86_common.h" #include "common.h" #include "ec_commands.h" #include "extpower.h" @@ -24,7 +26,6 @@ #include "timer.h" #include "tmp006.h" #include "util.h" -#include "x86_power.h" /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { @@ -38,25 +39,23 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH, switch_interrupt}, {"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, - {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1050_PGOOD", LM4_GPIO_H, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1350_PGOOD", LM4_GPIO_H, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP5000_PGOOD", LM4_GPIO_N, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"VCORE_PGOOD", LM4_GPIO_C, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_EDP_VDD_EN", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + haswell_interrupt}, {"RECOVERY_L", LM4_GPIO_A, (1<<5), GPIO_PULL_UP|GPIO_INT_BOTH, switch_interrupt}, {"WP", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH, @@ -64,6 +63,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Other inputs */ {"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL}, + {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, NULL}, {"USB1_OC_L", LM4_GPIO_E, (1<<7), GPIO_INPUT, NULL}, {"USB2_OC_L", LM4_GPIO_E, (1<<0), GPIO_INPUT, NULL}, {"BOARD_VERSION1", LM4_GPIO_Q, (1<<5), GPIO_INPUT, NULL}, @@ -120,6 +120,18 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"BAT_LED1_L", LM4_GPIO_N, (1<<4), GPIO_ODR_HIGH, NULL}, }; +/* x86 signal list. Must match order of enum x86_signal. */ +const struct x86_signal_info x86_signal_list[X86_SIGNAL_COUNT] = { + {GPIO_PP5000_PGOOD, 1, "PGOOD_PP5000"}, + {GPIO_PP1350_PGOOD, 1, "PGOOD_PP1350"}, + {GPIO_PP1050_PGOOD, 1, "PGOOD_PP1050"}, + {GPIO_VCORE_PGOOD, 1, "PGOOD_VCORE"}, + {GPIO_PCH_SLP_S0_L, 1, "SLP_S0#_DEASSERTED"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S5_L, 1, "SLP_S5#_DEASSERTED"}, + {GPIO_PCH_SLP_SUS_L, 1, "SLP_SUS#_DEASSERTED"}, +}; + /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ const struct adc_t adc_channels[ADC_CH_COUNT] = { /* EC internal temperature is calculated by diff --git a/board/slippy/board.h b/board/slippy/board.h index 7ea26b4139..e322d1abd5 100644 --- a/board/slippy/board.h +++ b/board/slippy/board.h @@ -76,7 +76,6 @@ enum gpio_signal { GPIO_PCH_SLP_S3_L, /* SLP_S3# signal from PCH */ GPIO_PCH_SLP_S5_L, /* SLP_S5# signal from PCH */ GPIO_PCH_SLP_SUS_L, /* SLP_SUS# signal from PCH */ - GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_PP1050_PGOOD, /* Power good on 1.05V */ GPIO_PP1350_PGOOD, /* Power good on 1.35V (DRAM) */ GPIO_PP5000_PGOOD, /* Power good on 5V */ @@ -87,6 +86,7 @@ enum gpio_signal { /* Other inputs */ GPIO_FAN_ALERT_L, /* From thermal sensor */ + GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_USB1_OC_L, /* USB port overcurrent warning */ GPIO_USB2_OC_L, /* USB port overcurrent warning */ GPIO_BOARD_VERSION1, /* Board version stuffing resistor 1 */ @@ -140,6 +140,21 @@ enum gpio_signal { GPIO_COUNT }; +/* x86 signal definitions */ +enum x86_signal { + X86_PGOOD_PP5000 = 0, + X86_PGOOD_PP1350, + X86_PGOOD_PP1050, + X86_PGOOD_VCORE, + X86_PCH_SLP_S0n_DEASSERTED, + X86_PCH_SLP_S3n_DEASSERTED, + X86_PCH_SLP_S5n_DEASSERTED, + X86_PCH_SLP_SUSn_DEASSERTED, + + /* Number of X86 signals */ + X86_SIGNAL_COUNT +}; + /* Charger module */ /* Set charger input current limit * Note - this value should depend on external power adapter, diff --git a/board/wolf/board.c b/board/wolf/board.c index 913a9b8a89..8c05c72ae8 100644 --- a/board/wolf/board.c +++ b/board/wolf/board.c @@ -7,6 +7,8 @@ #include "adc.h" #include "board.h" #include "chip_temp_sensor.h" +#include "chipset_haswell.h" +#include "chipset_x86_common.h" #include "common.h" #include "ec_commands.h" #include "extpower.h" @@ -23,7 +25,6 @@ #include "temp_sensor.h" #include "timer.h" #include "util.h" -#include "x86_power.h" /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { @@ -37,25 +38,23 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH, switch_interrupt}, {"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH, - x86_power_interrupt}, - {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1050_PGOOD", LM4_GPIO_H, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP1350_PGOOD", LM4_GPIO_H, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PP5000_PGOOD", LM4_GPIO_N, (1<<0), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"VCORE_PGOOD", LM4_GPIO_C, (1<<6), GPIO_INT_BOTH, - x86_power_interrupt}, + x86_interrupt}, {"PCH_EDP_VDD_EN", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, - x86_power_interrupt}, + haswell_interrupt}, {"RECOVERY_L", LM4_GPIO_A, (1<<5), GPIO_PULL_UP|GPIO_INT_BOTH, switch_interrupt}, {"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH, @@ -63,6 +62,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Other inputs */ {"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL}, + {"PCH_SUSWARN_L", LM4_GPIO_G, (1<<2), GPIO_INT_BOTH, NULL}, {"USB1_OC_L", LM4_GPIO_E, (1<<7), GPIO_INPUT, NULL}, {"USB2_OC_L", LM4_GPIO_E, (1<<0), GPIO_INPUT, NULL}, {"BOARD_VERSION1", LM4_GPIO_Q, (1<<5), GPIO_INPUT, NULL}, @@ -119,6 +119,18 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"BAT_LED1_L", LM4_GPIO_N, (1<<4), GPIO_ODR_HIGH, NULL}, }; +/* x86 signal list. Must match order of enum x86_signal. */ +const struct x86_signal_info x86_signal_list[X86_SIGNAL_COUNT] = { + {GPIO_PP5000_PGOOD, 1, "PGOOD_PP5000"}, + {GPIO_PP1350_PGOOD, 1, "PGOOD_PP1350"}, + {GPIO_PP1050_PGOOD, 1, "PGOOD_PP1050"}, + {GPIO_VCORE_PGOOD, 1, "PGOOD_VCORE"}, + {GPIO_PCH_SLP_S0_L, 1, "SLP_S0#_DEASSERTED"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S5_L, 1, "SLP_S5#_DEASSERTED"}, + {GPIO_PCH_SLP_SUS_L, 1, "SLP_SUS#_DEASSERTED"}, +}; + /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ const struct adc_t adc_channels[ADC_CH_COUNT] = { /* EC internal temperature is calculated by diff --git a/board/wolf/board.h b/board/wolf/board.h index 1e8dd9467f..06598cfe66 100644 --- a/board/wolf/board.h +++ b/board/wolf/board.h @@ -70,7 +70,6 @@ enum gpio_signal { GPIO_PCH_SLP_S3_L, /* SLP_S3# signal from PCH */ GPIO_PCH_SLP_S5_L, /* SLP_S5# signal from PCH */ GPIO_PCH_SLP_SUS_L, /* SLP_SUS# signal from PCH */ - GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_PP1050_PGOOD, /* Power good on 1.05V */ GPIO_PP1350_PGOOD, /* Power good on 1.35V (DRAM) */ GPIO_PP5000_PGOOD, /* Power good on 5V */ @@ -81,6 +80,7 @@ enum gpio_signal { /* Other inputs */ GPIO_FAN_ALERT_L, /* From thermal sensor */ + GPIO_PCH_SUSWARN_L, /* SUSWARN# signal from PCH */ GPIO_USB1_OC_L, /* USB port overcurrent warning */ GPIO_USB2_OC_L, /* USB port overcurrent warning */ GPIO_BOARD_VERSION1, /* Board version stuffing resistor 1 */ @@ -134,6 +134,21 @@ enum gpio_signal { GPIO_COUNT }; +/* x86 signal definitions */ +enum x86_signal { + X86_PGOOD_PP5000 = 0, + X86_PGOOD_PP1350, + X86_PGOOD_PP1050, + X86_PGOOD_VCORE, + X86_PCH_SLP_S0n_DEASSERTED, + X86_PCH_SLP_S3n_DEASSERTED, + X86_PCH_SLP_S5n_DEASSERTED, + X86_PCH_SLP_SUSn_DEASSERTED, + + /* Number of X86 signals */ + X86_SIGNAL_COUNT +}; + /* Charger module */ /* Set charger input current limit * Note - this value should depend on external power adapter, diff --git a/chip/lm4/switch.c b/chip/lm4/switch.c index 168b57eb7c..a70a163dbb 100644 --- a/chip/lm4/switch.c +++ b/chip/lm4/switch.c @@ -409,6 +409,7 @@ static void switch_init(void) *host_get_memmap(EC_MEMMAP_SWITCHES_VERSION) = 1; /* Enable interrupts, now that we've initialized */ + gpio_enable_interrupt(GPIO_PCH_BKLTEN); gpio_enable_interrupt(GPIO_POWER_BUTTON_L); gpio_enable_interrupt(GPIO_RECOVERY_L); #ifdef CONFIG_WP_ACTIVE_HIGH diff --git a/common/build.mk b/common/build.mk index 4d0d473554..148b2ac894 100644 --- a/common/build.mk +++ b/common/build.mk @@ -22,8 +22,8 @@ common-$(CONFIG_CHARGER_BQ24707A)+=charger_bq24707a.o common-$(CONFIG_CHARGER_BQ24738)+=charger_bq24738.o common-$(CONFIG_CHARGER_TPS65090)+=pmu_tps65090_charger.o common-$(CONFIG_CHIPSET_GAIA)+=chipset_gaia.o -common-$(CONFIG_CHIPSET_HASWELL)+=chipset_haswell.o -common-$(CONFIG_CHIPSET_IVYBRIDGE)+=chipset_ivybridge.o +common-$(CONFIG_CHIPSET_HASWELL)+=chipset_haswell.o chipset_x86_common.o +common-$(CONFIG_CHIPSET_IVYBRIDGE)+=chipset_ivybridge.o chipset_x86_common.o common-$(CONFIG_PMU_TPS65090)+=pmu_tps65090.o common-$(CONFIG_EOPTION)+=eoption.o common-$(CONFIG_EXTPOWER_GPIO)+=extpower_gpio.o diff --git a/common/chipset_haswell.c b/common/chipset_haswell.c index d4d4e868e5..18ea5494f4 100644 --- a/common/chipset_haswell.c +++ b/common/chipset_haswell.c @@ -6,77 +6,29 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" +#include "chipset_x86_common.h" #include "common.h" #include "console.h" -#include "extpower.h" #include "gpio.h" #include "hooks.h" -#include "host_command.h" -#include "lid_switch.h" -#include "power_button.h" #include "registers.h" -#include "switch.h" #include "system.h" -#include "task.h" #include "timer.h" #include "util.h" -#include "x86_power.h" /* Console output macros */ #define CPUTS(outstr) cputs(CC_CHIPSET, outstr) #define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) -/* - * Default timeout in us; if we've been waiting this long for an input - * transition, just jump to the next state. - */ -#define DEFAULT_TIMEOUT SECOND - -/* Timeout for dropping back from S5 to G3 */ -#define S5_INACTIVITY_TIMEOUT (10 * SECOND) - -enum x86_state { - X86_G3 = 0, /* - * System is off (not technically all the - * way into G3, which means totally - * unpowered...) - */ - X86_S5, /* System is soft-off */ - X86_S3, /* Suspend; RAM on, processor is asleep */ - X86_S0, /* System is on */ - - /* Transitions */ - X86_G3S5, /* G3 -> S5 (at system init time) */ - X86_S5S3, /* S5 -> S3 */ - X86_S3S0, /* S3 -> S0 */ - X86_S0S3, /* S0 -> S3 */ - X86_S3S5, /* S3 -> S5 */ - X86_S5G3, /* S5 -> G3 */ -}; - -static const char * const state_names[] = { - "G3", - "S5", - "S3", - "S0", - "G3->S5", - "S5->S3", - "S3->S0", - "S0->S3", - "S3->S5", - "S5->G3", -}; - /* Input state flags */ -#define IN_PGOOD_PP5000 0x0001 -#define IN_PGOOD_PP1350 0x0002 -#define IN_PGOOD_PP1050 0x0004 -#define IN_PGOOD_VCORE 0x0008 -#define IN_PCH_SLP_S0n_DEASSERTED 0x0010 -#define IN_PCH_SLP_S3n_DEASSERTED 0x0020 -#define IN_PCH_SLP_S5n_DEASSERTED 0x0040 -#define IN_PCH_SLP_SUSn_DEASSERTED 0x0080 -#define IN_PCH_SUSWARNn_DEASSERTED 0x0100 +#define IN_PGOOD_PP5000 X86_SIGNAL_MASK(X86_PGOOD_PP5000) +#define IN_PGOOD_PP1350 X86_SIGNAL_MASK(X86_PGOOD_PP1350) +#define IN_PGOOD_PP1050 X86_SIGNAL_MASK(X86_PGOOD_PP1050) +#define IN_PGOOD_VCORE X86_SIGNAL_MASK(X86_PGOOD_VCORE) +#define IN_PCH_SLP_S0n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S0n_DEASSERTED) +#define IN_PCH_SLP_S3n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S3n_DEASSERTED) +#define IN_PCH_SLP_S5n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S5n_DEASSERTED) +#define IN_PCH_SLP_SUSn_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_SUSn_DEASSERTED) /* All always-on supplies */ #define IN_PGOOD_ALWAYS_ON (IN_PGOOD_PP5000) @@ -96,107 +48,8 @@ static const char * const state_names[] = { #define IN_ALL_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE | \ IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) -static enum x86_state state = X86_G3; /* Current state */ -static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ -static uint32_t in_want; /* Input signal state we're waiting for */ -static uint32_t in_debug; /* Signal values which print debug output */ -static int want_g3_exit; /* Should we exit the G3 state? */ static int throttle_cpu; /* Throttle CPU? */ -/* When did we enter G3? */ -static uint64_t last_shutdown_time; -/* Delay before go into hibernation in seconds*/ -static uint32_t hibernate_delay = 3600; /* 1 Hour */ - -/** - * Update input signal state. - */ -static void update_in_signals(void) -{ - uint32_t inew = 0; - int v; - - if (gpio_get_level(GPIO_PP5000_PGOOD)) - inew |= IN_PGOOD_PP5000; - if (gpio_get_level(GPIO_PP1350_PGOOD)) - inew |= IN_PGOOD_PP1350; - if (gpio_get_level(GPIO_PP1050_PGOOD)) - inew |= IN_PGOOD_PP1050; - if (gpio_get_level(GPIO_VCORE_PGOOD)) - inew |= IN_PGOOD_VCORE; - - if (gpio_get_level(GPIO_PCH_SLP_S0_L)) - inew |= IN_PCH_SLP_S0n_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_S3_L)) - inew |= IN_PCH_SLP_S3n_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_S5_L)) - inew |= IN_PCH_SLP_S5n_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_SUS_L)) - inew |= IN_PCH_SLP_SUSn_DEASSERTED; - - v = gpio_get_level(GPIO_PCH_SUSWARN_L); - if (v) - inew |= IN_PCH_SUSWARNn_DEASSERTED; - /* Copy SUSWARN# signal from PCH to SUSACK# */ - gpio_set_level(GPIO_PCH_SUSACK_L, v); - - if ((in_signals & in_debug) != (inew & in_debug)) - CPRINTF("[%T x86 in 0x%04x]\n", inew); - - in_signals = inew; -} - -/** - * Check for required inputs - * - * @param want Input flags which must be present (IN_*) - * - * @return Non-zero if all present; zero if a required signal is missing. - */ -static int have_all_in_signals(uint32_t want) -{ - if ((in_signals & want) == want) - return 1; - - CPRINTF("[%T x86 power lost input; wanted 0x%04x, got 0x%04x]\n", - want, in_signals & want); - - return 0; -} - -/** - * Wait for inputs to be present - * - * @param want Input flags which must be present (IN_*) - * - * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout - * before reaching the desired state. - */ -static int wait_in_signals(uint32_t want) -{ - in_want = want; - - while ((in_signals & in_want) != in_want) { - if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { - update_in_signals(); - CPRINTF("[%T x86 power timeout on input; " - "wanted 0x%04x, got 0x%04x]\n", - in_want, in_signals & in_want); - return EC_ERROR_TIMEOUT; - } - /* - * TODO: should really shrink the remaining timeout if we woke - * up but didn't have all the signals we wanted. Also need to - * handle aborts if we're no longer in the same state we were - * when we started waiting. - */ - } - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Chipset interface */ - void chipset_force_shutdown(void) { CPRINTF("[%T %s()]\n", __func__); @@ -250,101 +103,15 @@ void chipset_reset(int cold_reset) } } -int chipset_in_state(int state_mask) -{ - int need_mask = 0; - - /* - * TODO: what to do about state transitions? If the caller wants - * HARD_OFF|SOFT_OFF and we're in G3S5, we could still return - * non-zero. - */ - switch (state) { - case X86_G3: - need_mask = CHIPSET_STATE_HARD_OFF; - break; - case X86_G3S5: - case X86_S5G3: - /* - * In between hard and soft off states. Match only if caller - * will accept both. - */ - need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF; - break; - case X86_S5: - need_mask = CHIPSET_STATE_SOFT_OFF; - break; - case X86_S5S3: - case X86_S3S5: - need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; - break; - case X86_S3: - need_mask = CHIPSET_STATE_SUSPEND; - break; - case X86_S3S0: - case X86_S0S3: - need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; - break; - case X86_S0: - need_mask = CHIPSET_STATE_ON; - break; - } - - /* Return non-zero if all needed bits are present */ - return (state_mask & need_mask) == need_mask; -} - -void chipset_exit_hard_off(void) -{ - /* If not in the hard-off state nor headed there, nothing to do */ - if (state != X86_G3 && state != X86_S5G3) - return; - - /* Set a flag to leave G3, then wake the task */ - want_g3_exit = 1; - - if (task_start_called()) - task_wake(TASK_ID_CHIPSET); -} - void chipset_throttle_cpu(int throttle) { /* FIXME CPRINTF("[%T %s(%d)]\n", __func__, throttle);*/ } -/*****************************************************************************/ -/* Hooks */ - -static void x86_lid_change(void) +enum x86_state x86_chipset_init(void) { - /* Wake up the task to update power state */ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); - -static void x86_power_ac_change(void) -{ - if (extpower_is_present()) { - CPRINTF("[%T x86 AC on]\n"); - } else { - CPRINTF("[%T x86 AC off]\n"); - - if (state == X86_G3) { - last_shutdown_time = get_time().val; - task_wake(TASK_ID_CHIPSET); - } - } -} -DECLARE_HOOK(HOOK_AC_CHANGE, x86_power_ac_change, HOOK_PRIO_DEFAULT); - -static void x86_power_init(void) -{ - /* Update input state */ - update_in_signals(); - in_want = 0; - - /* The initial state is G3. Set shut down timestamp to now. */ - last_shutdown_time = get_time().val; + /* Enable interrupts for our GPIOs */ + gpio_enable_interrupt(GPIO_PCH_EDP_VDD_EN); /* * If we're switching between images without rebooting, see if the x86 @@ -352,9 +119,9 @@ static void x86_power_init(void) * through G3. */ if (system_jumped_to_this_image()) { - if ((in_signals & IN_ALL_S0) == IN_ALL_S0) { + if ((x86_get_signals() & IN_ALL_S0) == IN_ALL_S0) { CPRINTF("[%T x86 already in S0]\n"); - state = X86_S0; + return X86_S0; } else { /* Force all signals to their G3 states */ CPRINTF("[%T x86 forcing G3]\n"); @@ -372,385 +139,209 @@ static void x86_power_init(void) } } - /* Enable interrupts for our GPIOs */ - gpio_enable_interrupt(GPIO_LID_OPEN); - gpio_enable_interrupt(GPIO_AC_PRESENT); - gpio_enable_interrupt(GPIO_PCH_BKLTEN); - gpio_enable_interrupt(GPIO_PCH_SLP_S0_L); - gpio_enable_interrupt(GPIO_PCH_SLP_S3_L); - gpio_enable_interrupt(GPIO_PCH_SLP_S5_L); - gpio_enable_interrupt(GPIO_PCH_SLP_SUS_L); - gpio_enable_interrupt(GPIO_PCH_SUSWARN_L); - gpio_enable_interrupt(GPIO_PP1050_PGOOD); - gpio_enable_interrupt(GPIO_PP1350_PGOOD); - gpio_enable_interrupt(GPIO_PP5000_PGOOD); - gpio_enable_interrupt(GPIO_VCORE_PGOOD); - gpio_enable_interrupt(GPIO_PCH_EDP_VDD_EN); -} -DECLARE_HOOK(HOOK_INIT, x86_power_init, HOOK_PRIO_INIT_CHIPSET); - -/*****************************************************************************/ -/* Interrupts */ - -void x86_power_interrupt(enum gpio_signal signal) -{ - /* Shadow signals and compare with our desired signal state. */ - update_in_signals(); - - /* Pass through eDP VDD enable from PCH. Put this on own interrupt? */ - if (gpio_get_level(GPIO_PCH_EDP_VDD_EN)) - gpio_set_level(GPIO_EC_EDP_VDD_EN, 1); - else - gpio_set_level(GPIO_EC_EDP_VDD_EN, 0); - - /* Wake up the task */ - task_wake(TASK_ID_CHIPSET); + return X86_G3; } -/*****************************************************************************/ -/* Task function */ - -void chipset_task(void) +enum x86_state x86_handle_state(enum x86_state state) { - uint64_t time_now; + switch (state) { + case X86_G3: + break; - while (1) { - CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", - state, state_names[state], in_signals); + case X86_S5: + if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) + return X86_S5S3; /* Power up to next state */ - switch (state) { - case X86_G3: - if (want_g3_exit) { - want_g3_exit = 0; - state = X86_G3S5; - break; - } + break; - in_want = 0; - if (extpower_is_present()) - task_wait_event(-1); - else { - uint64_t target_time = last_shutdown_time + - hibernate_delay * 1000000ull; - time_now = get_time().val; - if (time_now > target_time) { - /* - * Time's up. Hibernate until wake pin - * asserted. - */ - CPRINTF("[%T x86 hibernating]\n"); - system_hibernate(0, 0); - } else { - uint64_t wait = target_time - time_now; - if (wait > TASK_MAX_WAIT_US) - wait = TASK_MAX_WAIT_US; + case X86_S3: + /* Check for state transitions */ + if (!x86_has_signals(IN_PGOOD_S3)) { + /* Required rail went away */ + chipset_force_shutdown(); + return X86_S3S5; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { + /* Power up to next state */ + return X86_S3S0; + } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { + /* Power down to next state */ + return X86_S3S5; + } + break; - /* Wait for a message */ - task_wait_event(wait); - } - } - break; + case X86_S0: + if (!x86_has_signals(IN_PGOOD_S0)) { + /* Required rail went away */ + chipset_force_shutdown(); + return X86_S0S3; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { + /* Power down to next state */ + return X86_S0S3; + } + break; - case X86_S5: - if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) { - /* Power up to next state */ - state = X86_S5S3; - break; - } + case X86_G3S5: + /* + * Wait 10ms after +3VALW good, since that powers VccDSW and + * VccSUS. + */ + msleep(10); - /* Wait for inactivity timeout */ - in_want = 0; - if (task_wait_event(S5_INACTIVITY_TIMEOUT) == - TASK_EVENT_TIMER) { - /* Drop to G3; wake not requested yet */ - want_g3_exit = 0; - state = X86_S5G3; - } - break; + /* Assert DPWROK */ + gpio_set_level(GPIO_PCH_DPWROK, 1); + if (x86_wait_signals(IN_PCH_SLP_SUSn_DEASSERTED)) { + chipset_force_shutdown(); + return X86_G3; + } - case X86_S3: - /* Check for state transitions */ - if (!have_all_in_signals(IN_PGOOD_S3)) { - /* Required rail went away */ - chipset_force_shutdown(); - state = X86_S3S5; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { - /* Power up to next state */ - state = X86_S3S0; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { - /* Power down to next state */ - state = X86_S3S5; - break; - } + gpio_set_level(GPIO_SUSP_VR_EN, 1); + if (x86_wait_signals(IN_PGOOD_PP1050)) { + chipset_force_shutdown(); + return X86_G3; + } - /* Otherwise, steady state; wait for a message */ - in_want = 0; - task_wait_event(-1); - break; + /* Deassert RSMRST# */ + gpio_set_level(GPIO_PCH_RSMRST_L, 1); - case X86_S0: - if (!have_all_in_signals(IN_PGOOD_S0)) { - /* Required rail went away */ - chipset_force_shutdown(); - state = X86_S0S3; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { - /* Power down to next state */ - state = X86_S0S3; - break; - } + /* Wait 5ms for SUSCLK to stabilize */ + msleep(5); + return X86_S5; - /* Otherwise, steady state; wait for a message */ - in_want = 0; - task_wait_event(-1); - break; + case X86_S5S3: + /* Enable PP5000 (5V) rail. */ + gpio_set_level(GPIO_PP5000_EN, 1); + if (x86_wait_signals(IN_PGOOD_PP5000)) { + chipset_force_shutdown(); + return X86_G3; + } - case X86_G3S5: - /* - * Wait 10ms after +3VALW good, since that powers - * VccDSW and VccSUS. - */ - msleep(10); + /* Wait for the always-on rails to be good */ + if (x86_wait_signals(IN_PGOOD_ALWAYS_ON)) { + chipset_force_shutdown(); + return X86_S5; + } - /* Assert DPWROK */ - gpio_set_level(GPIO_PCH_DPWROK, 1); - if (wait_in_signals(IN_PCH_SLP_SUSn_DEASSERTED)) { - chipset_force_shutdown(); - state = X86_G3; - break; - } + /* Turn on power to RAM */ + gpio_set_level(GPIO_PP1350_EN, 1); + if (x86_wait_signals(IN_PGOOD_S3)) { + chipset_force_shutdown(); + return X86_S5; + } - gpio_set_level(GPIO_SUSP_VR_EN, 1); - if (wait_in_signals(IN_PGOOD_PP1050)) { - chipset_force_shutdown(); - state = X86_G3; - break; - } + /* + * Enable touchpad power so it can wake the system from + * suspend. + */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); - /* Deassert RSMRST# */ - gpio_set_level(GPIO_PCH_RSMRST_L, 1); + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_STARTUP); + return X86_S3; - /* Wait 5ms for SUSCLK to stabilize */ - msleep(5); + case X86_S3S0: + /* Turn on power rails */ + gpio_set_level(GPIO_PP3300_DX_EN, 1); - state = X86_S5; - break; + /* Enable WLAN */ + gpio_set_level(GPIO_PP3300_WLAN_EN, 1); + gpio_set_level(GPIO_WLAN_OFF_L, 1); - case X86_S5S3: - /* Enable PP5000 (5V) rail. */ - gpio_set_level(GPIO_PP5000_EN, 1); - if (wait_in_signals(IN_PGOOD_PP5000)) { - chipset_force_shutdown(); - state = X86_G3; - break; - } + /* Enable LTE/WWAN */ + gpio_set_level(GPIO_PP3300_LTE_EN, 1); - /* Wait for the always-on rails to be good */ - if (wait_in_signals(IN_PGOOD_ALWAYS_ON)) { - chipset_force_shutdown(); - state = X86_S5; - } - - /* Turn on power to RAM */ - gpio_set_level(GPIO_PP1350_EN, 1); - if (wait_in_signals(IN_PGOOD_S3)) { - chipset_force_shutdown(); - state = X86_S5; - } - - /* - * Enable touchpad power so it can wake the system from - * suspend. - */ - gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - - state = X86_S3; - break; - - case X86_S3S0: - /* Turn on power rails */ - gpio_set_level(GPIO_PP3300_DX_EN, 1); - - /* Enable WLAN */ - gpio_set_level(GPIO_PP3300_WLAN_EN, 1); - gpio_set_level(GPIO_WLAN_OFF_L, 1); - - /* Enable LTE/WWAN */ - gpio_set_level(GPIO_PP3300_LTE_EN, 1); - - /* Wait for non-core power rails good */ - if (wait_in_signals(IN_PGOOD_S0)) { - chipset_force_shutdown(); - gpio_set_level(GPIO_WLAN_OFF_L, 0); - gpio_set_level(GPIO_PP3300_LTE_EN, 0); - gpio_set_level(GPIO_PP3300_WLAN_EN, 0); - gpio_set_level(GPIO_EC_EDP_VDD_EN, 0); - gpio_set_level(GPIO_PP3300_DX_EN, 0); - state = X86_S3; - break; - } - - /* - * Enable +CPU_CORE. The CPU itself will request - * the supplies when it's ready. - */ - gpio_set_level(GPIO_VCORE_EN, 1); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* 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); - - state = X86_S0; - break; - - case X86_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); - - /* Disable +CPU_CORE */ - gpio_set_level(GPIO_VCORE_EN, 0); - - /* Disable WLAN */ + /* Wait for non-core power rails good */ + if (x86_wait_signals(IN_PGOOD_S0)) { + chipset_force_shutdown(); gpio_set_level(GPIO_WLAN_OFF_L, 0); - gpio_set_level(GPIO_PP3300_WLAN_EN, 0); - - /* Disable LTE/WWAN */ gpio_set_level(GPIO_PP3300_LTE_EN, 0); - - /* - * Deassert prochot since CPU is off and we're about - * to drop +VCCP. - */ - gpio_set_level(GPIO_CPU_PROCHOT, 0); - - /* Turn off power rails */ + gpio_set_level(GPIO_PP3300_WLAN_EN, 0); gpio_set_level(GPIO_EC_EDP_VDD_EN, 0); gpio_set_level(GPIO_PP3300_DX_EN, 0); - - state = X86_S3; - break; - - case X86_S3S5: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /* Disable touchpad power */ - gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); - - /* Turn off power to RAM */ - gpio_set_level(GPIO_PP1350_EN, 0); - - /* Disable PP5000 (5V) rail. */ - gpio_set_level(GPIO_PP5000_EN, 0); - - state = X86_S5; - break; - - case X86_S5G3: - /* Deassert DPWROK, assert RSMRST# */ - gpio_set_level(GPIO_PCH_DPWROK, 0); - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - gpio_set_level(GPIO_SUSP_VR_EN, 0); - - /* Record the time we go into G3 */ - last_shutdown_time = get_time().val; - - state = X86_G3; - break; + return X86_S3; } + + /* + * Enable +CPU_CORE. The CPU itself will request the supplies + * when it's ready. + */ + gpio_set_level(GPIO_VCORE_EN, 1); + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_RESUME); + + /* 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 X86_S0; + + case X86_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); + + /* Disable +CPU_CORE */ + gpio_set_level(GPIO_VCORE_EN, 0); + + /* Disable WLAN */ + gpio_set_level(GPIO_WLAN_OFF_L, 0); + gpio_set_level(GPIO_PP3300_WLAN_EN, 0); + + /* Disable LTE/WWAN */ + gpio_set_level(GPIO_PP3300_LTE_EN, 0); + + /* + * Deassert prochot since CPU is off and we're about to drop + * +VCCP. + */ + gpio_set_level(GPIO_CPU_PROCHOT, 0); + + /* Turn off power rails */ + gpio_set_level(GPIO_EC_EDP_VDD_EN, 0); + gpio_set_level(GPIO_PP3300_DX_EN, 0); + return X86_S3; + + case X86_S3S5: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + + /* Disable touchpad power */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); + + /* Turn off power to RAM */ + gpio_set_level(GPIO_PP1350_EN, 0); + + /* Disable PP5000 (5V) rail. */ + gpio_set_level(GPIO_PP5000_EN, 0); + return X86_S5; + + case X86_S5G3: + /* Deassert DPWROK, assert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); + gpio_set_level(GPIO_SUSP_VR_EN, 0); + return X86_G3; } + + return state; } -/*****************************************************************************/ -/* Console commands */ - -static int command_powerinfo(int argc, char **argv) +void haswell_interrupt(enum gpio_signal signal) { - /* - * Print x86 power state in same format as state machine. This is - * used by FAFT tests, so must match exactly. - */ - ccprintf("[%T x86 power state %d = %s, in 0x%04x]\n", - state, state_names[state], in_signals); - - return EC_SUCCESS; + /* Pass through eDP VDD enable from PCH */ + gpio_set_level(GPIO_EC_EDP_VDD_EN, gpio_get_level(GPIO_PCH_EDP_VDD_EN)); } -DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, - NULL, - "Show current x86 power state", - NULL); - -static int command_x86indebug(int argc, char **argv) -{ - char *e; - - /* If one arg, set the mask */ - if (argc == 2) { - int m = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - in_debug = m; - } - - /* Print the mask */ - ccprintf("x86 in: 0x%04x\n", in_signals); - ccprintf("debug mask: 0x%04x\n", in_debug); - return EC_SUCCESS; -}; -DECLARE_CONSOLE_COMMAND(x86indebug, command_x86indebug, - "[mask]", - "Get/set x86 input debug mask", - NULL); - -static int command_hibernation_delay(int argc, char **argv) -{ - char *e; - uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) - / SECOND; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - hibernate_delay = s; - } - - /* Print the current setting */ - ccprintf("Hibernation delay: %d s\n", hibernate_delay); - if (state == X86_G3 && !extpower_is_present()) { - ccprintf("Time G3: %d s\n", time_g3); - ccprintf("Time left: %d s\n", hibernate_delay - time_g3); - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, - "[sec]", - "Set the delay before going into hibernation", - NULL); diff --git a/common/chipset_ivybridge.c b/common/chipset_ivybridge.c index 469702b224..32e35031b5 100644 --- a/common/chipset_ivybridge.c +++ b/common/chipset_ivybridge.c @@ -6,81 +6,36 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" +#include "chipset_x86_common.h" #include "common.h" #include "console.h" -#include "extpower.h" #include "gpio.h" #include "hooks.h" -#include "host_command.h" #include "lid_switch.h" -#include "switch.h" #include "system.h" -#include "task.h" #include "timer.h" #include "util.h" -#include "x86_power.h" /* Console output macros */ #define CPUTS(outstr) cputs(CC_CHIPSET, outstr) #define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) -/* - * Default timeout in us; if we've been waiting this long for an input - * transition, just jump to the next state. - */ -#define DEFAULT_TIMEOUT SECOND - -/* Timeout for dropping back from S5 to G3 */ -#define S5_INACTIVITY_TIMEOUT (10 * SECOND) - -enum x86_state { - X86_G3 = 0, /* - * System is off (not technically all the - * way into G3, which means totally - * unpowered...) - */ - X86_S5, /* System is soft-off */ - X86_S3, /* Suspend; RAM on, processor is asleep */ - X86_S0, /* System is on */ - - /* Transitions */ - X86_G3S5, /* G3 -> S5 (at system init time) */ - X86_S5S3, /* S5 -> S3 */ - X86_S3S0, /* S3 -> S0 */ - X86_S0S3, /* S0 -> S3 */ - X86_S3S5, /* S3 -> S5 */ - X86_S5G3, /* S5 -> G3 */ -}; - -static const char * const state_names[] = { - "G3", - "S5", - "S3", - "S0", - "G3->S5", - "S5->S3", - "S3->S0", - "S0->S3", - "S3->S5", - "S5->G3", -}; - /* Input state flags */ -#define IN_PGOOD_5VALW 0x0001 -#define IN_PGOOD_1_5V_DDR 0x0002 -#define IN_PGOOD_1_5V_PCH 0x0004 -#define IN_PGOOD_1_8VS 0x0008 -#define IN_PGOOD_VCCP 0x0010 -#define IN_PGOOD_VCCSA 0x0020 -#define IN_PGOOD_CPU_CORE 0x0040 -#define IN_PGOOD_VGFX_CORE 0x0080 -#define IN_PCH_SLP_S3n_DEASSERTED 0x0100 -#define IN_PCH_SLP_S4n_DEASSERTED 0x0200 -#define IN_PCH_SLP_S5n_DEASSERTED 0x0400 -#define IN_PCH_SLP_An_DEASSERTED 0x0800 -#define IN_PCH_SLP_SUSn_DEASSERTED 0x1000 -#define IN_PCH_SLP_MEn_DEASSERTED 0x2000 -#define IN_PCH_SUSWARNn_DEASSERTED 0x4000 +#define IN_PGOOD_5VALW X86_SIGNAL_MASK(X86_PGOOD_5VALW) +#define IN_PGOOD_1_5V_DDR X86_SIGNAL_MASK(X86_PGOOD_1_5V_DDR) +#define IN_PGOOD_1_5V_PCH X86_SIGNAL_MASK(X86_PGOOD_1_5V_PCH) +#define IN_PGOOD_1_8VS X86_SIGNAL_MASK(X86_PGOOD_1_8VS) +#define IN_PGOOD_VCCP X86_SIGNAL_MASK(X86_PGOOD_VCCP) +#define IN_PGOOD_VCCSA X86_SIGNAL_MASK(X86_PGOOD_VCCSA) +#define IN_PGOOD_CPU_CORE X86_SIGNAL_MASK(X86_PGOOD_CPU_CORE) +#define IN_PGOOD_VGFX_CORE X86_SIGNAL_MASK(X86_PGOOD_VGFX_CORE) +#define IN_PCH_SLP_S3n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S3n_DEASSERTED) +#define IN_PCH_SLP_S4n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S4n_DEASSERTED) +#define IN_PCH_SLP_S5n_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_S5n_DEASSERTED) +#define IN_PCH_SLP_An_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_An_DEASSERTED) +#define IN_PCH_SLP_SUSn_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_SUSn_DEASSERTED) +#define IN_PCH_SLP_MEn_DEASSERTED X86_SIGNAL_MASK(X86_PCH_SLP_MEn_DEASSERTED) + /* All always-on supplies */ #define IN_PGOOD_ALWAYS_ON (IN_PGOOD_5VALW) /* All non-core power rails */ @@ -102,122 +57,8 @@ static const char * const state_names[] = { #define IN_ALL_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE | \ IN_PGOOD_CPU_CORE | IN_ALL_PM_SLP_DEASSERTED) -static enum x86_state state = X86_G3; /* Current state */ -static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ -static uint32_t in_want; /* Input signal state we're waiting for */ -static uint32_t in_debug; /* Signal values which print debug output */ -static int want_g3_exit; /* Should we exit the G3 state? */ static int throttle_cpu; /* Throttle CPU? */ -/* When did we enter G3? */ -static uint64_t last_shutdown_time; -/* Delay before go into hibernation in seconds*/ -static uint32_t hibernate_delay = 3600; /* 1 Hour */ - -/** - * Update input signal state. - */ -static void update_in_signals(void) -{ - uint32_t inew = 0; - int v; - - if (gpio_get_level(GPIO_PGOOD_5VALW)) - inew |= IN_PGOOD_5VALW; - - if (gpio_get_level(GPIO_PGOOD_1_5V_DDR)) - inew |= IN_PGOOD_1_5V_DDR; - if (gpio_get_level(GPIO_PGOOD_1_5V_PCH)) - inew |= IN_PGOOD_1_5V_PCH; - if (gpio_get_level(GPIO_PGOOD_1_8VS)) - inew |= IN_PGOOD_1_8VS; - if (gpio_get_level(GPIO_PGOOD_VCCP)) - inew |= IN_PGOOD_VCCP; - if (gpio_get_level(GPIO_PGOOD_VCCSA)) - inew |= IN_PGOOD_VCCSA; - - if (gpio_get_level(GPIO_PGOOD_CPU_CORE)) - inew |= IN_PGOOD_CPU_CORE; - if (gpio_get_level(GPIO_PGOOD_VGFX_CORE)) - inew |= IN_PGOOD_VGFX_CORE; - - if (gpio_get_level(GPIO_PCH_SLP_A_L)) - inew |= IN_PCH_SLP_An_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_S3_L)) - inew |= IN_PCH_SLP_S3n_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_S4_L)) - inew |= IN_PCH_SLP_S4n_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_S5_L)) - inew |= IN_PCH_SLP_S5n_DEASSERTED; - - if (gpio_get_level(GPIO_PCH_SLP_SUS_L)) - inew |= IN_PCH_SLP_SUSn_DEASSERTED; - if (gpio_get_level(GPIO_PCH_SLP_ME_CSW_DEV_L)) - inew |= IN_PCH_SLP_MEn_DEASSERTED; - - v = gpio_get_level(GPIO_PCH_SUSWARN_L); - if (v) - inew |= IN_PCH_SUSWARNn_DEASSERTED; - /* Copy SUSWARN# signal from PCH to SUSACK# */ - gpio_set_level(GPIO_PCH_SUSACK_L, v); - - if ((in_signals & in_debug) != (inew & in_debug)) - CPRINTF("[%T x86 in 0x%04x]\n", inew); - - in_signals = inew; -} - -/** - * Check for required inputs - * - * @param want Input flags which must be present (IN_*) - * - * @return Non-zero if all present; zero if a required signal is missing. - */ -static int have_all_in_signals(uint32_t want) -{ - if ((in_signals & want) == want) - return 1; - - CPRINTF("[%T x86 power lost input; wanted 0x%04x, got 0x%04x]\n", - want, in_signals & want); - - return 0; -} - -/** - * Wait for inputs to be present - * - * @param want Input flags which must be present (IN_*) - * - * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout - * before reaching the desired state. - */ -static int wait_in_signals(uint32_t want) -{ - in_want = want; - - while ((in_signals & in_want) != in_want) { - if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { - update_in_signals(); - CPRINTF("[%T x86 power timeout on input; " - "wanted 0x%04x, got 0x%04x]\n", - in_want, in_signals & in_want); - return EC_ERROR_TIMEOUT; - } - /* - * TODO: should really shrink the remaining timeout if we woke - * up but didn't have all the signals we wanted. Also need to - * handle aborts if we're no longer in the same state we were - * when we started waiting. - */ - } - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Chipset interface */ - void chipset_force_shutdown(void) { CPRINTF("[%T chipset force shutdown]\n"); @@ -264,115 +105,26 @@ void chipset_reset(int cold_reset) } } -int chipset_in_state(int state_mask) -{ - int need_mask = 0; - - /* - * TODO: what to do about state transitions? If the caller wants - * HARD_OFF|SOFT_OFF and we're in G3S5, we could still return - * non-zero. - */ - switch (state) { - case X86_G3: - need_mask = CHIPSET_STATE_HARD_OFF; - break; - case X86_G3S5: - case X86_S5G3: - /* - * In between hard and soft off states. Match only if caller - * will accept both. - */ - need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF; - break; - case X86_S5: - need_mask = CHIPSET_STATE_SOFT_OFF; - break; - case X86_S5S3: - case X86_S3S5: - need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; - break; - case X86_S3: - need_mask = CHIPSET_STATE_SUSPEND; - break; - case X86_S3S0: - case X86_S0S3: - need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; - break; - case X86_S0: - need_mask = CHIPSET_STATE_ON; - break; - } - - /* Return non-zero if all needed bits are present */ - return (state_mask & need_mask) == need_mask; -} - -void chipset_exit_hard_off(void) -{ - /* If not in the hard-off state nor headed there, nothing to do */ - if (state != X86_G3 && state != X86_S5G3) - return; - - /* Set a flag to leave G3, then wake the task */ - want_g3_exit = 1; - - if (task_start_called()) - task_wake(TASK_ID_CHIPSET); -} - void chipset_throttle_cpu(int throttle) { throttle_cpu = throttle; /* Immediately set throttling if CPU is on */ - if (state == X86_S0) + if (chipset_in_state(CHIPSET_STATE_ON)) gpio_set_level(GPIO_CPU_PROCHOT, throttle); } -/*****************************************************************************/ -/* Hooks */ - -static void x86_lid_change(void) +enum x86_state x86_chipset_init(void) { - /* Wake up the task to update power state */ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); - -static void x86_power_ac_change(void) -{ - if (extpower_is_present()) { - CPRINTF("[%T x86 AC on]\n"); - } else { - CPRINTF("[%T x86 AC off]\n"); - - if (state == X86_G3) { - last_shutdown_time = get_time().val; - task_wake(TASK_ID_CHIPSET); - } - } -} -DECLARE_HOOK(HOOK_AC_CHANGE, x86_power_ac_change, HOOK_PRIO_DEFAULT); - -static void x86_power_init(void) -{ - /* Update input state */ - update_in_signals(); - in_want = 0; - - /* The initial state is G3. Set shut down timestamp to now. */ - last_shutdown_time = get_time().val; - /* * If we're switching between images without rebooting, see if the x86 * is already powered on; if so, leave it there instead of cycling * through G3. */ if (system_jumped_to_this_image()) { - if ((in_signals & IN_ALL_S0) == IN_ALL_S0) { + if ((x86_get_signals() & IN_ALL_S0) == IN_ALL_S0) { CPRINTF("[%T x86 already in S0]\n"); - state = X86_S0; + return X86_S0; } else { /* Force all signals to their G3 states */ CPRINTF("[%T x86 forcing G3]\n"); @@ -387,382 +139,211 @@ static void x86_power_init(void) } } - /* Enable interrupts for our GPIOs */ - gpio_enable_interrupt(GPIO_PCH_BKLTEN); - gpio_enable_interrupt(GPIO_PCH_SLP_A_L); - gpio_enable_interrupt(GPIO_PCH_SLP_ME_CSW_DEV_L); - gpio_enable_interrupt(GPIO_PCH_SLP_S3_L); - gpio_enable_interrupt(GPIO_PCH_SLP_S4_L); - gpio_enable_interrupt(GPIO_PCH_SLP_S5_L); - gpio_enable_interrupt(GPIO_PCH_SLP_SUS_L); - gpio_enable_interrupt(GPIO_PCH_SUSWARN_L); - gpio_enable_interrupt(GPIO_PGOOD_1_5V_DDR); - gpio_enable_interrupt(GPIO_PGOOD_1_5V_PCH); - gpio_enable_interrupt(GPIO_PGOOD_1_8VS); - gpio_enable_interrupt(GPIO_PGOOD_5VALW); - gpio_enable_interrupt(GPIO_PGOOD_CPU_CORE); - gpio_enable_interrupt(GPIO_PGOOD_VCCP); - gpio_enable_interrupt(GPIO_PGOOD_VCCSA); - gpio_enable_interrupt(GPIO_PGOOD_VGFX_CORE); -} -DECLARE_HOOK(HOOK_INIT, x86_power_init, HOOK_PRIO_INIT_CHIPSET); - -/*****************************************************************************/ -/* Interrupts */ - -void x86_power_interrupt(enum gpio_signal signal) -{ - /* Shadow signals and compare with our desired signal state. */ - update_in_signals(); - - /* Wake up the task */ - task_wake(TASK_ID_CHIPSET); + return X86_G3; } -/*****************************************************************************/ -/* Task function */ - -void chipset_task(void) +enum x86_state x86_handle_state(enum x86_state state) { - uint64_t time_now; + switch (state) { + case X86_G3: + break; - while (1) { - CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", - state, state_names[state], in_signals); + case X86_S5: + if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) { + /* Power up to next state */ + return X86_S5S3; + } + break; - switch (state) { - case X86_G3: - if (want_g3_exit) { - want_g3_exit = 0; - state = X86_G3S5; - break; - } + case X86_S3: + /* + * If lid is closed; hold touchscreen in reset to cut power + * usage. If lid is open, take touchscreen out of reset so it + * can wake the processor. + */ + gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, lid_is_open()); - in_want = 0; - if (extpower_is_present()) - task_wait_event(-1); - else { - uint64_t target_time = last_shutdown_time + - hibernate_delay * 1000000ull; - time_now = get_time().val; - if (time_now > target_time) { - /* - * Time's up. Hibernate until wake pin - * asserted. - */ - CPRINTF("[%T x86 hibernating]\n"); - system_hibernate(0, 0); - } else { - uint64_t wait = target_time - time_now; - if (wait > TASK_MAX_WAIT_US) - wait = TASK_MAX_WAIT_US; + /* Check for state transitions */ + if (!x86_has_signals(IN_PGOOD_S3)) { + /* Required rail went away */ + chipset_force_shutdown(); + return X86_S3S5; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { + /* Power up to next state */ + return X86_S3S0; + } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { + /* Power down to next state */ + return X86_S3S5; + } + break; - /* Wait for a message */ - task_wait_event(wait); - } - } + case X86_S0: + if (!x86_has_signals(IN_PGOOD_S0)) { + /* Required rail went away */ + chipset_force_shutdown(); + return X86_S0S3; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { + /* Power down to next state */ + return X86_S0S3; + } + break; - break; + case X86_G3S5: + /* + * Wait 10ms after +3VALW good, since that powers VccDSW and + * VccSUS. + */ + msleep(10); - case X86_S5: - if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) { - /* Power up to next state */ - state = X86_S5S3; - break; - } + /* Assert DPWROK, deassert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 1); + gpio_set_level(GPIO_PCH_RSMRST_L, 1); - /* Wait for inactivity timeout */ - in_want = 0; - if (task_wait_event(S5_INACTIVITY_TIMEOUT) == - TASK_EVENT_TIMER) { - /* Drop to G3; wake not requested yet */ - want_g3_exit = 0; - state = X86_S5G3; - } - break; + /* Wait 5ms for SUSCLK to stabilize */ + msleep(5); + return X86_S5; - case X86_S3: - /* - * If lid is closed; hold touchscreen in reset to cut - * power usage. If lid is open, take touchscreen out - * of reset so it can wake the processor. - */ - gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, lid_is_open()); + case X86_S5S3: + /* Wait for the always-on rails to be good */ + if (x86_wait_signals(IN_PGOOD_ALWAYS_ON)) { + chipset_force_shutdown(); + return X86_S5; + } - /* Check for state transitions */ - if (!have_all_in_signals(IN_PGOOD_S3)) { - /* Required rail went away */ - chipset_force_shutdown(); - state = X86_S3S5; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { - /* Power up to next state */ - state = X86_S3S0; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { - /* Power down to next state */ - state = X86_S3S5; - break; - } + /* + * 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); - /* Otherwise, steady state; wait for a message */ - in_want = 0; - task_wait_event(-1); - break; + /* Turn on power to RAM */ + gpio_set_level(GPIO_ENABLE_1_5V_DDR, 1); + if (x86_wait_signals(IN_PGOOD_S3)) { + chipset_force_shutdown(); + return X86_S5; + } - case X86_S0: - if (!have_all_in_signals(IN_PGOOD_S0)) { - /* Required rail went away */ - chipset_force_shutdown(); - state = X86_S0S3; - break; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { - /* Power down to next state */ - state = X86_S0S3; - break; - } + /* + * Enable touchpad power so it can wake the system from + * suspend. + */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); - /* Otherwise, steady state; wait for a message */ - in_want = 0; - task_wait_event(-1); - break; + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_STARTUP); + return X86_S3; - case X86_G3S5: - /* - * Wait 10ms after +3VALW good, since that powers - * VccDSW and VccSUS. - */ - msleep(10); + case X86_S3S0: + /* Turn on power rails */ + gpio_set_level(GPIO_ENABLE_VS, 1); - /* Assert DPWROK, deassert RSMRST# */ - gpio_set_level(GPIO_PCH_DPWROK, 1); - gpio_set_level(GPIO_PCH_RSMRST_L, 1); + /* Enable WLAN */ + gpio_set_level(GPIO_ENABLE_WLAN, 1); + gpio_set_level(GPIO_RADIO_ENABLE_WLAN, 1); + gpio_set_level(GPIO_RADIO_ENABLE_BT, 1); - /* Wait 5ms for SUSCLK to stabilize */ - msleep(5); + /* + * Make sure touchscreen is out if reset (even if the lid is + * still closed); it may have been turned off if the lid was + * closed in S3. + */ + gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 1); - state = X86_S5; - break; - - case X86_S5S3: - /* Wait for the always-on rails to be good */ - if (wait_in_signals(IN_PGOOD_ALWAYS_ON)) { - chipset_force_shutdown(); - state = X86_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); - - /* Turn on power to RAM */ - gpio_set_level(GPIO_ENABLE_1_5V_DDR, 1); - if (wait_in_signals(IN_PGOOD_S3)) { - chipset_force_shutdown(); - state = X86_S5; - } - - /* - * Enable touchpad power so it can wake the system from - * suspend. - */ - gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - - state = X86_S3; - break; - - case X86_S3S0: - /* Turn on power rails */ - gpio_set_level(GPIO_ENABLE_VS, 1); - - /* Enable WLAN */ - gpio_set_level(GPIO_ENABLE_WLAN, 1); - gpio_set_level(GPIO_RADIO_ENABLE_WLAN, 1); - gpio_set_level(GPIO_RADIO_ENABLE_BT, 1); - - /* - * Make sure touchscreen is out if reset (even if the - * lid is still closed); it may have been turned off if - * the lid was closed in S3. - */ - gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 1); - - /* Wait for non-core power rails good */ - if (wait_in_signals(IN_PGOOD_S0)) { - chipset_force_shutdown(); - gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0); - gpio_set_level(GPIO_ENABLE_WLAN, 0); - gpio_set_level(GPIO_RADIO_ENABLE_WLAN, 0); - gpio_set_level(GPIO_RADIO_ENABLE_BT, 0); - gpio_set_level(GPIO_ENABLE_VS, 0); - state = X86_S3; - } - - /* - * Enable +CPU_CORE and +VGFX_CORE regulator. The CPU - * itself will request the supplies when it's ready. - */ - gpio_set_level(GPIO_ENABLE_VCORE, 1); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* 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); - - state = X86_S0; - break; - - case X86_S0S3: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SUSPEND); - - /* Clear PCH_PWROK */ - gpio_set_level(GPIO_PCH_PWROK, 0); - - /* Wait 40ns */ - udelay(1); - - /* Disable +CPU_CORE and +VGFX_CORE */ - gpio_set_level(GPIO_ENABLE_VCORE, 0); - - /* Disable WLAN */ + /* Wait for non-core power rails good */ + if (x86_wait_signals(IN_PGOOD_S0)) { + chipset_force_shutdown(); + gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0); gpio_set_level(GPIO_ENABLE_WLAN, 0); gpio_set_level(GPIO_RADIO_ENABLE_WLAN, 0); gpio_set_level(GPIO_RADIO_ENABLE_BT, 0); - - /* - * Deassert prochot since CPU is off and we're about - * to drop +VCCP. - */ - gpio_set_level(GPIO_CPU_PROCHOT, 0); - - /* Turn off power rails */ gpio_set_level(GPIO_ENABLE_VS, 0); - - state = X86_S3; - break; - - case X86_S3S5: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /* Disable touchpad power */ - gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); - - /* Turn off power to RAM */ - gpio_set_level(GPIO_ENABLE_1_5V_DDR, 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); - - state = X86_S5; - break; - - case X86_S5G3: - /* Deassert DPWROK, assert RSMRST# */ - gpio_set_level(GPIO_PCH_DPWROK, 0); - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - /* Record the time we go into G3 */ - last_shutdown_time = get_time().val; - - state = X86_G3; - break; + return X86_S3; } + + /* + * Enable +CPU_CORE and +VGFX_CORE regulator. The CPU itself + * will request the supplies when it's ready. + */ + gpio_set_level(GPIO_ENABLE_VCORE, 1); + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_RESUME); + + /* 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); + return X86_S0; + + case X86_S0S3: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SUSPEND); + + /* Clear PCH_PWROK */ + gpio_set_level(GPIO_PCH_PWROK, 0); + + /* Wait 40ns */ + udelay(1); + + /* Disable +CPU_CORE and +VGFX_CORE */ + gpio_set_level(GPIO_ENABLE_VCORE, 0); + + /* Disable WLAN */ + gpio_set_level(GPIO_ENABLE_WLAN, 0); + gpio_set_level(GPIO_RADIO_ENABLE_WLAN, 0); + gpio_set_level(GPIO_RADIO_ENABLE_BT, 0); + + /* + * Deassert prochot since CPU is off and we're about to drop + * +VCCP. + */ + gpio_set_level(GPIO_CPU_PROCHOT, 0); + + /* Turn off power rails */ + gpio_set_level(GPIO_ENABLE_VS, 0); + return X86_S3; + + case X86_S3S5: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + + /* Disable touchpad power */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); + + /* Turn off power to RAM */ + gpio_set_level(GPIO_ENABLE_1_5V_DDR, 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 X86_S5; + + case X86_S5G3: + /* Deassert DPWROK, assert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); + return X86_G3; } + + return state; } -/*****************************************************************************/ -/* Console commands */ - -static int command_powerinfo(int argc, char **argv) +void ivybridge_interrupt(enum gpio_signal signal) { - /* - * Print x86 power state in same format as state machine. This is - * used by FAFT tests, so must match exactly. - */ - ccprintf("[%T x86 power state %d = %s, in 0x%04x]\n", - state, state_names[state], in_signals); - - return EC_SUCCESS; + /* Route SUSWARN# back to SUSACK# */ + gpio_set_level(GPIO_PCH_SUSACK_L, gpio_get_level(GPIO_PCH_SUSWARN_L)); } -DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, - NULL, - "Show current x86 power state", - NULL); - -static int command_x86indebug(int argc, char **argv) -{ - char *e; - - /* If one arg, set the mask */ - if (argc == 2) { - int m = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - in_debug = m; - } - - /* Print the mask */ - ccprintf("x86 in: 0x%04x\n", in_signals); - ccprintf("debug mask: 0x%04x\n", in_debug); - return EC_SUCCESS; -}; -DECLARE_CONSOLE_COMMAND(x86indebug, command_x86indebug, - "[mask]", - "Get/set x86 input debug mask", - NULL); - -static int command_hibernation_delay(int argc, char **argv) -{ - char *e; - uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) - / SECOND; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - hibernate_delay = s; - } - - /* Print the current setting */ - ccprintf("Hibernation delay: %d s\n", hibernate_delay); - if (state == X86_G3 && !extpower_is_present()) { - ccprintf("Time G3: %d s\n", time_g3); - ccprintf("Time left: %d s\n", hibernate_delay - time_g3); - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, - "[sec]", - "Set the delay before going into hibernation", - NULL); diff --git a/common/chipset_x86_common.c b/common/chipset_x86_common.c new file mode 100644 index 0000000000..63fecfdda9 --- /dev/null +++ b/common/chipset_x86_common.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Common functionality across x86 chipsets */ + +#include "chipset.h" +#include "chipset_x86_common.h" +#include "common.h" +#include "console.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) +#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) + +/* + * Default timeout in us; if we've been waiting this long for an input + * transition, just jump to the next state. + */ +#define DEFAULT_TIMEOUT SECOND + +/* Timeout for dropping back from S5 to G3 */ +#define S5_INACTIVITY_TIMEOUT (10 * SECOND) + +static const char * const state_names[] = { + "G3", + "S5", + "S3", + "S0", + "G3->S5", + "S5->S3", + "S3->S0", + "S0->S3", + "S3->S5", + "S5->G3", +}; + +static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ +static uint32_t in_want; /* Input signal state we're waiting for */ +static uint32_t in_debug; /* Signal values which print debug output */ + +static enum x86_state state = X86_G3; /* Current state */ +static int want_g3_exit; /* Should we exit the G3 state? */ +static uint64_t last_shutdown_time; /* When did we enter G3? */ + +/* Delay before hibernating, in seconds */ +static uint32_t hibernate_delay = 3600; + +/** + * Update input signals mask + */ +static void x86_update_signals(void) +{ + uint32_t inew = 0; + const struct x86_signal_info *s = x86_signal_list; + int i; + + for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) { + if (gpio_get_level(s->gpio) == s->level) + inew |= 1 << i; + } + + if ((in_signals & in_debug) != (inew & in_debug)) + CPRINTF("[%T x86 in 0x%04x]\n", inew); + + in_signals = inew; +} + +uint32_t x86_get_signals(void) +{ + return in_signals; +} + +int x86_has_signals(uint32_t want) +{ + if ((in_signals & want) == want) + return 1; + + CPRINTF("[%T x86 power lost input; wanted 0x%04x, got 0x%04x]\n", + want, in_signals & want); + + return 0; +} + +int x86_wait_signals(uint32_t want) +{ + in_want = want; + if (!want) + return EC_SUCCESS; + + while ((in_signals & in_want) != in_want) { + if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { + x86_update_signals(); + CPRINTF("[%T x86 power timeout on input; " + "wanted 0x%04x, got 0x%04x]\n", + in_want, in_signals & in_want); + return EC_ERROR_TIMEOUT; + } + /* + * TODO: should really shrink the remaining timeout if we woke + * up but didn't have all the signals we wanted. Also need to + * handle aborts if we're no longer in the same state we were + * when we started waiting. + */ + } + return EC_SUCCESS; +} + +/** + * Set the low-level x86 chipset state. + * + * @param new_state New chipset state. + */ +void x86_set_state(enum x86_state new_state) +{ + /* Record the time we go into G3 */ + if (state == X86_G3) + last_shutdown_time = get_time().val; + + state = new_state; +} + +/** + * Common handler for x86 steady states + * + * @param state Current x86 state + * @return Updated x86 state + */ +static enum x86_state x86_common_state(enum x86_state state) +{ + switch (state) { + case X86_G3: + if (want_g3_exit) { + want_g3_exit = 0; + return X86_G3S5; + } + + in_want = 0; + if (extpower_is_present()) + task_wait_event(-1); + else { + uint64_t target_time = last_shutdown_time + + hibernate_delay * 1000000ull; + uint64_t time_now = get_time().val; + if (time_now > target_time) { + /* + * Time's up. Hibernate until wake pin + * asserted. + */ + CPRINTF("[%T x86 hibernating]\n"); + system_hibernate(0, 0); + } else { + uint64_t wait = target_time - time_now; + if (wait > TASK_MAX_WAIT_US) + wait = TASK_MAX_WAIT_US; + + /* Wait for a message */ + task_wait_event(wait); + } + } + break; + + case X86_S5: + /* Wait for inactivity timeout */ + x86_wait_signals(0); + if (task_wait_event(S5_INACTIVITY_TIMEOUT) == + TASK_EVENT_TIMER) { + /* Drop to G3; wake not requested yet */ + want_g3_exit = 0; + return X86_S5G3; + } + break; + + case X86_S3: + /* Wait for a message */ + x86_wait_signals(0); + task_wait_event(-1); + break; + + case X86_S0: + /* Wait for a message */ + x86_wait_signals(0); + task_wait_event(-1); + break; + + default: + /* No common functionality for transition states */ + break; + } + + return state; +} + +/*****************************************************************************/ +/* Chipset interface */ + +int chipset_in_state(int state_mask) +{ + int need_mask = 0; + + /* + * TODO: what to do about state transitions? If the caller wants + * HARD_OFF|SOFT_OFF and we're in G3S5, we could still return + * non-zero. + */ + switch (state) { + case X86_G3: + need_mask = CHIPSET_STATE_HARD_OFF; + break; + case X86_G3S5: + case X86_S5G3: + /* + * In between hard and soft off states. Match only if caller + * will accept both. + */ + need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF; + break; + case X86_S5: + need_mask = CHIPSET_STATE_SOFT_OFF; + break; + case X86_S5S3: + case X86_S3S5: + need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; + break; + case X86_S3: + need_mask = CHIPSET_STATE_SUSPEND; + break; + case X86_S3S0: + case X86_S0S3: + need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; + break; + case X86_S0: + need_mask = CHIPSET_STATE_ON; + break; + } + + /* Return non-zero if all needed bits are present */ + return (state_mask & need_mask) == need_mask; +} + +void chipset_exit_hard_off(void) +{ + /* If not in the hard-off state nor headed there, nothing to do */ + if (state != X86_G3 && state != X86_S5G3) + return; + + /* Set a flag to leave G3, then wake the task */ + want_g3_exit = 1; + + if (task_start_called()) + task_wake(TASK_ID_CHIPSET); +} + +/*****************************************************************************/ +/* Task function */ + +void chipset_task(void) +{ + enum x86_state new_state; + + while (1) { + CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", + state, state_names[state], in_signals); + + /* Always let the specific chipset handle the state first */ + new_state = x86_handle_state(state); + + /* + * If the state hasn't changed, run common steady-state + * handler. + */ + if (new_state == state) + new_state = x86_common_state(state); + + /* Handle state changes */ + if (new_state != state) + x86_set_state(new_state); + } +} + +/*****************************************************************************/ +/* Hooks */ + +static void x86_common_init(void) +{ + const struct x86_signal_info *s = x86_signal_list; + int i; + + /* Update input state */ + x86_update_signals(); + + /* Call chipset-specific init to set initial state */ + x86_set_state(x86_chipset_init()); + + /* Enable interrupts for input signals */ + for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) + gpio_enable_interrupt(s->gpio); +} +DECLARE_HOOK(HOOK_INIT, x86_common_init, HOOK_PRIO_INIT_CHIPSET); + +static void x86_lid_change(void) +{ + /* Wake up the task to update power state */ + task_wake(TASK_ID_CHIPSET); +} +DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); + +static void x86_ac_change(void) +{ + if (extpower_is_present()) { + CPRINTF("[%T x86 AC on]\n"); + } else { + CPRINTF("[%T x86 AC off]\n"); + + if (state == X86_G3) { + last_shutdown_time = get_time().val; + task_wake(TASK_ID_CHIPSET); + } + } +} +DECLARE_HOOK(HOOK_AC_CHANGE, x86_ac_change, HOOK_PRIO_DEFAULT); + +/*****************************************************************************/ +/* Interrupts */ + +void x86_interrupt(enum gpio_signal signal) +{ + /* Shadow signals and compare with our desired signal state. */ + x86_update_signals(); + + /* Wake up the task */ + task_wake(TASK_ID_CHIPSET); +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_powerinfo(int argc, char **argv) +{ + /* + * Print x86 power state in same format as state machine. This is + * used by FAFT tests, so must match exactly. + */ + ccprintf("[%T x86 power state %d = %s, in 0x%04x]\n", + state, state_names[state], in_signals); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, + NULL, + "Show current x86 power state", + NULL); + +static int command_x86indebug(int argc, char **argv) +{ + char *e; + + /* If one arg, set the mask */ + if (argc == 2) { + int m = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; + + in_debug = m; + } + + /* Print the mask */ + ccprintf("x86 in: 0x%04x\n", in_signals); + ccprintf("debug mask: 0x%04x\n", in_debug); + return EC_SUCCESS; +}; +DECLARE_CONSOLE_COMMAND(x86indebug, command_x86indebug, + "[mask]", + "Get/set x86 input debug mask", + NULL); + +static int command_hibernation_delay(int argc, char **argv) +{ + char *e; + uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) + / SECOND; + + if (argc >= 2) { + uint32_t s = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; + + hibernate_delay = s; + } + + /* Print the current setting */ + ccprintf("Hibernation delay: %d s\n", hibernate_delay); + if (state == X86_G3 && !extpower_is_present()) { + ccprintf("Time G3: %d s\n", time_g3); + ccprintf("Time left: %d s\n", hibernate_delay - time_g3); + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, + "[sec]", + "Set the delay before going into hibernation", + NULL); diff --git a/common/mock_x86_power.c b/common/mock_x86_power.c index bbd19c06e7..daceee57b1 100644 --- a/common/mock_x86_power.c +++ b/common/mock_x86_power.c @@ -6,12 +6,12 @@ /* Mock X86 chipset power control module for Chrome EC */ #include "chipset.h" +#include "chipset_x86_common.h" #include "console.h" #include "lpc.h" #include "timer.h" #include "uart.h" #include "util.h" -#include "x86_power.h" static int mock_power_on = 0; @@ -59,7 +59,7 @@ int chipset_in_state(int state_mask) } -void x86_power_interrupt(enum gpio_signal signal) +void x86_interrupt(enum gpio_signal signal) { /* Not implemented */ return; diff --git a/include/chipset_haswell.h b/include/chipset_haswell.h new file mode 100644 index 0000000000..ef61922008 --- /dev/null +++ b/include/chipset_haswell.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Haswell chipset interface */ + +#ifndef __CROS_EC_CHIPSET_HASWELL_H +#define __CROS_EC_CHIPSET_HASWELL_H + +/** + * Interrupt handler for Haswell-specific GPIOs. + */ +void haswell_interrupt(enum gpio_signal signal); + +#endif diff --git a/include/chipset_ivybridge.h b/include/chipset_ivybridge.h new file mode 100644 index 0000000000..d88566948c --- /dev/null +++ b/include/chipset_ivybridge.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Ivy bridge chipset interface */ + +#ifndef __CROS_EC_CHIPSET_IVYBRIDGE_H +#define __CROS_EC_CHIPSET_IVYBRIDGE_H + +/** + * Interrupt handler for Ivy Bridge-specific GPIOs. + */ +void ivybridge_interrupt(enum gpio_signal signal); + +#endif diff --git a/include/chipset_x86_common.h b/include/chipset_x86_common.h new file mode 100644 index 0000000000..a23b11661f --- /dev/null +++ b/include/chipset_x86_common.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Common interface for x86 chipsets */ + +#ifndef __CROS_EC_CHIPSET_X86_COMMON_H +#define __CROS_EC_CHIPSET_X86_COMMON_H + +#include "common.h" +#include "gpio.h" + +enum x86_state { + /* Steady states */ + X86_G3 = 0, /* + * System is off (not technically all the way into G3, + * which means totally unpowered...) + */ + X86_S5, /* System is soft-off */ + X86_S3, /* Suspend; RAM on, processor is asleep */ + X86_S0, /* System is on */ + + /* Transitions */ + X86_G3S5, /* G3 -> S5 (at system init time) */ + X86_S5S3, /* S5 -> S3 */ + X86_S3S0, /* S3 -> S0 */ + X86_S0S3, /* S0 -> S3 */ + X86_S3S5, /* S3 -> S5 */ + X86_S5G3, /* S5 -> G3 */ +}; + +/* Information on an x86 signal */ +struct x86_signal_info { + enum gpio_signal gpio; /* GPIO for signal */ + int level; /* GPIO level which sets signal bit */ + const char *name; /* Name of signal */ +}; + +/* + * Each board must provide its signal list and a corresponding enum x86_signal. + */ +extern const struct x86_signal_info x86_signal_list[]; + +/* Convert enum x86_signal to a mask for signal functions */ +#define X86_SIGNAL_MASK(signal) (1 << (signal)) + +/** + * Return current input signal state (one or more X86_SIGNAL_MASK()s). + */ +uint32_t x86_get_signals(void); + +/** + * Check for required inputs + * + * @param want Mask of signals which must be present (one or more + * X86_SIGNAL_MASK()s). + * + * @return Non-zero if all present; zero if a required signal is missing. + */ +int x86_has_signals(uint32_t want); + +/** + * Wait for x86 input signals to be present + * + * @param want Mask of signals which must be present (one or more + * X86_SIGNAL_MASK()s). If want=0, stops waiting for + * signals. + * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout + * before reaching the desired state. + */ +int x86_wait_signals(uint32_t want); + +/** + * Chipset-specific initialization + * + * @return The state the chipset should start in. Usually X86_G3, but may + * be X86_G0 if the chipset was already on and we've jumped to this image. + */ +enum x86_state x86_chipset_init(void); + +/** + * Chipset-specific state handler + * + * @return The updated state for the x86 chipset. + */ +enum x86_state x86_handle_state(enum x86_state state); + +/** + * Interrupt handler for x86 chipset GPIOs. + */ +void x86_interrupt(enum gpio_signal signal); + +#endif /* __CROS_EC_CHIPSET_X86_COMMON_H */ diff --git a/include/x86_power.h b/include/x86_power.h deleted file mode 100644 index afdbd1b33b..0000000000 --- a/include/x86_power.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* x86 power module for Chrome EC */ - -#ifndef __CROS_EC_X86_POWER_H -#define __CROS_EC_X86_POWER_H - -#include "gpio.h" - -/** - * Interrupt handler for x86 chipset GPIOs. - */ -void x86_power_interrupt(enum gpio_signal signal); - -#endif /* __CROS_EC_X86_POWER_H */