From 09115a92f6ac6123a6f1ed435c05fe60dc01d58c Mon Sep 17 00:00:00 2001 From: Shaunak Saha Date: Sun, 24 Jul 2016 20:50:12 -0700 Subject: soc/apollolake: add GPIO SMI support GPIOs which trigger SMIs set the GPIO_SMI_STS status bits in SMI_STS register. This patch also sets the SMI_EN bit in enable register for each community based on GPIOROUTSMI bit in gpio pad. When SMI on a gpio happens status needs to be gathered on gpio number which is done by reading the GPI_SMI_STS and GPI_SMI_EN registers. BUG=chrome-os-partner:54977 TEST=When system is in firmware mode executing the command lidclose from ec console shuts down the system. Change-Id: Id89a526106d1989c2bd3416ab81913e6cf743d17 Signed-off-by: Shaunak Saha Reviewed-on: https://review.coreboot.org/15833 Reviewed-by: Aaron Durbin Tested-by: build bot (Jenkins) --- src/soc/intel/apollolake/Makefile.inc | 1 + src/soc/intel/apollolake/gpio.c | 114 +++++++++++++++++++++++ src/soc/intel/apollolake/include/soc/gpio.h | 19 ++++ src/soc/intel/apollolake/include/soc/gpio_defs.h | 27 ++++++ src/soc/intel/apollolake/include/soc/pm.h | 1 + src/soc/intel/apollolake/include/soc/smm.h | 3 + src/soc/intel/apollolake/pmc.c | 11 +++ src/soc/intel/apollolake/smi.c | 2 +- src/soc/intel/apollolake/smihandler.c | 16 ++++ 9 files changed, 193 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc index 9e30df8d7e..6eb9d9a70a 100644 --- a/src/soc/intel/apollolake/Makefile.inc +++ b/src/soc/intel/apollolake/Makefile.inc @@ -40,6 +40,7 @@ romstage-y += spi.c smm-y += mmap_boot.c smm-y += pmutil.c +smm-y += gpio.c smm-y += smihandler.c smm-y += spi.c smm-y += tsc_freq.c diff --git a/src/soc/intel/apollolake/gpio.c b/src/soc/intel/apollolake/gpio.c index 8ac00cde36..ac72203ed5 100644 --- a/src/soc/intel/apollolake/gpio.c +++ b/src/soc/intel/apollolake/gpio.c @@ -26,19 +26,34 @@ static const struct pad_community { uint16_t first_pad; uint8_t port; + uint8_t num_gpi_regs; + uint8_t gpi_offset; + const char *grp_name; } gpio_communities[] = { { .port = GPIO_SOUTHWEST, .first_pad = SW_OFFSET, + .num_gpi_regs = NUM_SW_GPI_REGS, + .gpi_offset = 0, + .grp_name = "GPIO_GPE_SW", }, { .port = GPIO_WEST, .first_pad = W_OFFSET, + .num_gpi_regs = NUM_W_GPI_REGS, + .gpi_offset = NUM_SW_GPI_REGS, + .grp_name = "GPIO_GPE_W", }, { .port = GPIO_NORTHWEST, .first_pad = NW_OFFSET, + .num_gpi_regs = NUM_NW_GPI_REGS, + .gpi_offset = NUM_W_GPI_REGS + NUM_SW_GPI_REGS, + .grp_name = "GPIO_GPE_NW", }, { .port = GPIO_NORTH, .first_pad = N_OFFSET, + .num_gpi_regs = NUM_N_GPI_REGS, + .gpi_offset = NUM_NW_GPI_REGS+ NUM_W_GPI_REGS + NUM_SW_GPI_REGS, + .grp_name = "GPIO_GPE_N", } }; @@ -104,6 +119,30 @@ static void gpio_configure_owner(const struct pad_config *cfg, iosf_write(port, hostsw_reg, val); } +static void gpi_enable_smi(const struct pad_config *cfg, uint16_t port, int pin) +{ + uint32_t value; + uint16_t sts_reg; + uint16_t en_reg; + int group; + + if (((cfg->config0) & PAD_CFG0_ROUTE_SMI) != PAD_CFG0_ROUTE_SMI) + return; + + group = pin / GPIO_MAX_NUM_PER_GROUP; + + sts_reg = GPI_SMI_STS_OFFSET(group); + value = iosf_read(port, sts_reg); + /* Write back 1 to reset the sts bits */ + iosf_write(port, sts_reg, value); + + /* Set enable bits */ + en_reg = GPI_SMI_EN_OFFSET(group); + value = iosf_read(port, en_reg ); + value |= 1 << (pin % GPIO_MAX_NUM_PER_GROUP); + iosf_write(port, en_reg , value); +} + void gpio_configure_pad(const struct pad_config *cfg) { uint32_t dw1; @@ -123,6 +162,7 @@ void gpio_configure_pad(const struct pad_config *cfg) gpio_configure_itss(cfg, comm->port, config_offset); gpio_configure_owner(cfg, comm->port, cfg->pad - comm->first_pad); + gpi_enable_smi(cfg, comm->port, cfg->pad - comm->first_pad); } void gpio_configure_pads(const struct pad_config *cfg, size_t num_pads) @@ -216,6 +256,80 @@ uint16_t gpio_acpi_pin(gpio_t gpio_num) return gpio_num; } +static void print_gpi_status(const struct gpi_status *sts) +{ + int i; + int group; + int index = 0; + int bit_set; + int num_groups; + int abs_bit; + const struct pad_community *comm; + + for (i = 0; i < ARRAY_SIZE(gpio_communities); i++) { + comm = &gpio_communities[i]; + num_groups = comm->num_gpi_regs; + index = comm->gpi_offset; + for (group = 0; group < num_groups; group++, index++) { + for (bit_set = 31; bit_set >= 0; bit_set--) { + if (!(sts->grp[index] & (1 << bit_set))) + continue; + + abs_bit = bit_set; + abs_bit += group * GPIO_MAX_NUM_PER_GROUP; + printk(BIOS_DEBUG, "%s %d \n",comm->grp_name, + abs_bit); + } + } + } +} + +void gpi_clear_get_smi_status(struct gpi_status *sts) +{ + int i; + int group; + int index = 0; + uint32_t sts_value; + uint32_t en_value; + int num_groups; + const struct pad_community *comm; + + for (i = 0; i < ARRAY_SIZE(gpio_communities); i++) { + comm = &gpio_communities[i]; + num_groups = comm->num_gpi_regs; + index = comm->gpi_offset; + for (group = 0; group < num_groups; group++, index++) { + sts_value = iosf_read(gpio_communities[i].port, + GPI_SMI_STS_OFFSET(group)); + en_value = iosf_read(gpio_communities[i].port, + GPI_SMI_EN_OFFSET(group)); + sts->grp[index] = sts_value & en_value; + /* Clear the set status bits. */ + iosf_write(gpio_communities[i].port, + GPI_SMI_STS_OFFSET(group), sts->grp[index]); + } + } + + if (IS_ENABLED(CONFIG_DEBUG_SMI)) + print_gpi_status(sts); + +} + +int gpi_status_get(const struct gpi_status *sts, gpio_t gpi) +{ + uint8_t sts_index; + const struct pad_community *comm = gpio_get_community(gpi); + + /* Check if valid gpi */ + if (comm == NULL) + return 0; + + sts_index = comm->gpi_offset + (gpi - (comm->first_pad) / + GPIO_MAX_NUM_PER_GROUP); + + return !!(sts->grp[sts_index] & (1 << (gpi % GPIO_MAX_NUM_PER_GROUP))); +} + /* Helper function to map PMC register groups to tier1 sci groups */ static int pmc_gpe_route_to_gpio(int route) { diff --git a/src/soc/intel/apollolake/include/soc/gpio.h b/src/soc/intel/apollolake/include/soc/gpio.h index 8ef74bb70a..00894448c8 100644 --- a/src/soc/intel/apollolake/include/soc/gpio.h +++ b/src/soc/intel/apollolake/include/soc/gpio.h @@ -25,6 +25,25 @@ typedef uint32_t gpio_t; +/* + * Structure to represent GPI status for GPE and SMI. Use helper + * functions for interrogating particular GPIs. Here the number of + * array elements is total number of groups that can be present in all + * the communities. + */ +struct gpi_status { + uint32_t grp[NUM_GPI_STATUS_REGS]; +}; + +/* + * Clear GPI SMI status and fill in the structure representing enabled + * and set status. + */ +void gpi_clear_get_smi_status(struct gpi_status *sts); + +/* Return 1 if gpio is set in the gpi_status struct. Otherwise 0. */ +int gpi_status_get(const struct gpi_status *sts, gpio_t gpi); + #define PAD_FUNC(value) PAD_CFG0_MODE_##value #define PAD_RESET(value) PAD_CFG0_RESET_##value #define PAD_PULL(value) PAD_CFG1_PULL_##value diff --git a/src/soc/intel/apollolake/include/soc/gpio_defs.h b/src/soc/intel/apollolake/include/soc/gpio_defs.h index d85f844823..88fa475b5c 100644 --- a/src/soc/intel/apollolake/include/soc/gpio_defs.h +++ b/src/soc/intel/apollolake/include/soc/gpio_defs.h @@ -38,6 +38,8 @@ #define GPIO_GPE_N_31_0 7 /* NORTH GPIO# 0 ~ 31 belong to GROUP7 */ #define GPIO_GPE_N_63_32 8 /* NORTH GPIO# 32 ~ 61 belong to GROUP8 */ +#define GPIO_MAX_NUM_PER_GROUP 32 + #define MISCCFG_GPE0_DW0_SHIFT 8 #define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT) #define MISCCFG_GPE0_DW1_SHIFT 12 @@ -134,6 +136,31 @@ #define GPIO_NORTH 0xc5 #define GPIO_WEST 0xc7 +#define GPI_SMI_STS_0 0x140 +#define GPI_SMI_EN_0 0x150 +#define GPI_SMI_STS_OFFSET(group) (GPI_SMI_STS_0 + ((group) * 4)) +#define GPI_SMI_EN_OFFSET(group) (GPI_SMI_EN_0 + ((group) * 4)) + +#define NUM_N_PADS (PAD_N(SVID0_CLK) + 1) +#define NUM_NW_PADS (PAD_NW(GPIO_123) + 1) +#define NUM_W_PADS (PAD_W(SUSPWRDNACK) + 1) +#define NUM_SW_PADS (PAD_SW(LPC_FRAMEB) + 1) + +#define NUM_N_GPI_REGS \ + (ALIGN_UP(NUM_N_PADS, GPIO_MAX_NUM_PER_GROUP) / GPIO_MAX_NUM_PER_GROUP) + +#define NUM_NW_GPI_REGS \ + (ALIGN_UP(NUM_NW_PADS, GPIO_MAX_NUM_PER_GROUP) / GPIO_MAX_NUM_PER_GROUP) + +#define NUM_W_GPI_REGS \ + (ALIGN_UP(NUM_W_PADS, GPIO_MAX_NUM_PER_GROUP) / GPIO_MAX_NUM_PER_GROUP) + +#define NUM_SW_GPI_REGS \ + (ALIGN_UP(NUM_SW_PADS, GPIO_MAX_NUM_PER_GROUP) / GPIO_MAX_NUM_PER_GROUP) + +#define NUM_GPI_STATUS_REGS (NUM_N_GPI_REGS + NUM_NW_GPI_REGS \ + + NUM_W_GPI_REGS + NUM_SW_GPI_REGS) + /* North community pads */ #define GPIO_0 0 #define GPIO_1 1 diff --git a/src/soc/intel/apollolake/include/soc/pm.h b/src/soc/intel/apollolake/include/soc/pm.h index 467c8f159c..5641e54c50 100644 --- a/src/soc/intel/apollolake/include/soc/pm.h +++ b/src/soc/intel/apollolake/include/soc/pm.h @@ -70,6 +70,7 @@ #define USB_EN (1 << SMI_XHCI) /* Legacy USB2 SMI logic */ #define PERIODIC_EN (1 << SMI_PERIODIC) /* SMI on PERIODIC_STS in SMI_STS */ #define TCO_EN (1 << SMI_TCO) /* Enable TCO Logic (BIOSWE et al) */ +#define GPIO_EN (1 << SMI_GPIO) /* Enable GPIO SMI */ #define BIOS_RLS (1 << SMI_BIOS_RLS) /* asserts SCI on bit set */ #define SWSMI_TMR_EN (1 << SMI_SWSMI_TMR) /* start software smi timer on bit set */ #define APMC_EN (1 << SMI_APMC) /* Writes to APM_CNT cause SMI# */ diff --git a/src/soc/intel/apollolake/include/soc/smm.h b/src/soc/intel/apollolake/include/soc/smm.h index 07749745d9..7a9846eea2 100644 --- a/src/soc/intel/apollolake/include/soc/smm.h +++ b/src/soc/intel/apollolake/include/soc/smm.h @@ -19,6 +19,7 @@ #define _SOC_SMM_H_ #include +#include /* These helpers are for performing SMM relocation. */ void southbridge_clear_smi_status(void); @@ -31,6 +32,8 @@ void southbridge_clear_smi_status(void); void southbridge_smm_clear_state(void); void southbridge_smm_enable_smi(void); +/* Mainboard handler for GPI SMIs*/ +void mainboard_smi_gpi_handler(const struct gpi_status *sts); /* Fills in the arguments for the entire SMM region covered by chipset * protections. e.g. TSEG. */ diff --git a/src/soc/intel/apollolake/pmc.c b/src/soc/intel/apollolake/pmc.c index f303632cb2..92a150ddf4 100644 --- a/src/soc/intel/apollolake/pmc.c +++ b/src/soc/intel/apollolake/pmc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -122,10 +123,20 @@ static void pmc_gpe_init(void) fixup_power_state(); } +static void pch_set_acpi_mode(void) +{ + if (IS_ENABLED(CONFIG_HAVE_SMI_HANDLER) && !acpi_is_wakeup_s3()) { + printk(BIOS_DEBUG, "Disabling ACPI via APMC:"); + outb(APM_CNT_ACPI_DISABLE, APM_CNT); + printk(BIOS_DEBUG, "Done.\n"); + } +} + static void pmc_init(struct device *dev) { /* Set up GPE configuration */ pmc_gpe_init(); + pch_set_acpi_mode(); } static const struct device_operations device_ops = { diff --git a/src/soc/intel/apollolake/smi.c b/src/soc/intel/apollolake/smi.c index 4f151feb2a..3ad33fde9d 100644 --- a/src/soc/intel/apollolake/smi.c +++ b/src/soc/intel/apollolake/smi.c @@ -52,7 +52,7 @@ void southbridge_smm_enable_smi(void) disable_gpe(PME_B0_EN); /* Enable SMI generation */ - enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS); + enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS | GPIO_EN); } void southbridge_clear_smi_status(void) diff --git a/src/soc/intel/apollolake/smihandler.c b/src/soc/intel/apollolake/smihandler.c index 1521920fa1..b84e5cb296 100644 --- a/src/soc/intel/apollolake/smihandler.c +++ b/src/soc/intel/apollolake/smihandler.c @@ -30,6 +30,7 @@ #include #include #include +#include int smm_disable_busmaster(device_t dev) { @@ -43,10 +44,25 @@ const struct smm_save_state_ops *get_smm_save_state_ops(void) return &em64t100_smm_ops; } +void __attribute__((weak)) +mainboard_smi_gpi_handler(const struct gpi_status *sts) { } + +static void southbridge_smi_gpi(const struct smm_save_state_ops *save_state_ops) +{ + struct gpi_status smi_sts; + + gpi_clear_get_smi_status(&smi_sts); + mainboard_smi_gpi_handler(&smi_sts); + + /* Clear again after mainboard handler */ + gpi_clear_get_smi_status(&smi_sts); +} + const smi_handler_t southbridge_smi[32] = { [SLP_SMI_STS] = southbridge_smi_sleep, [APM_SMI_STS] = southbridge_smi_apmc, [FAKE_PM1_SMI_STS] = southbridge_smi_pm1, + [GPIO_SMI_STS] = southbridge_smi_gpi, [TCO_SMI_STS] = southbridge_smi_tco, [PERIODIC_SMI_STS] = southbridge_smi_periodic, }; -- cgit v1.2.3