Shaunak Saha (shaunak.saha@intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15833
-gerrit
commit 6bd3063826501d4e82800b72d4f02adc08f31908 Author: Shaunak Saha shaunak.saha@intel.com Date: Sun Jul 24 20:50:12 2016 -0700
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 shaunak.saha@intel.com --- src/soc/intel/apollolake/Makefile.inc | 1 + src/soc/intel/apollolake/gpio.c | 158 +++++++++++++++++++++++ src/soc/intel/apollolake/include/soc/gpio.h | 22 ++++ src/soc/intel/apollolake/include/soc/gpio_defs.h | 16 +++ 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, 229 insertions(+), 1 deletion(-)
diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc index 9e30df8..6eb9d9a 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 a3ffb3d..58a6c20 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_of_grps; /* Groups in last community + groups in this community */ + const char *grp_name; + uint32_t pad_in_grp; /* We are using 8 bits for each group */ } gpio_communities[] = { { .port = GPIO_SOUTHWEST, .first_pad = SW_OFFSET, + .num_of_grps = 2, + .grp_name = "GPIO_GPE_SW", + .pad_in_grp = 0xB20, /* SW: 2 groups with 32 and 11 pads */ }, { .port = GPIO_WEST, .first_pad = W_OFFSET, + .num_of_grps = 3, + .grp_name = "GPIO_GPE_W", + .pad_in_grp = 0x1A, /* West: 1 group with 26 pads */ }, { .port = GPIO_NORTHWEST, .first_pad = NW_OFFSET, + .num_of_grps = 6, + .grp_name = "GPIO_GPE_NW", + .pad_in_grp = 0xD1D12, /* NW:3 groups with 18,29,13 pads */ }, { .port = GPIO_NORTH, .first_pad = N_OFFSET, + .num_of_grps = 8, + .grp_name = "GPIO_GPE_N", + .pad_in_grp = 0x1E20, /* North: 2 groups with 30,32 pads */ } };
@@ -83,6 +98,40 @@ static void gpio_configure_itss(const struct pad_config *cfg, itss_set_irq_polarity(irq, !!(cfg->config0 & PAD_CFG0_RX_POL_INVERT)); }
+static void gpi_enable_smi(gpio_t pad) +{ + uint8_t i; + uint8_t grp_index = 0; + uint8_t set_offset=0; + const struct pad_community *comm; + uint32_t pad_mask; + + comm = gpio_get_community(pad); + if (comm == NULL) + return; + + for (i = 0 ; i < ARRAY_SIZE(gpio_communities); i++) + { + if (comm->port == gpio_communities[i].port) { + if (0 == set_offset) { + pad = pad - (gpio_communities[i].first_pad); + set_offset = 1; + } + if (pad > (((gpio_communities[i].pad_in_grp) >> + (8 * grp_index)) & GRP_MASK)) { + pad = pad - GPIO_MAX_NUM_PER_GROUP; + grp_index++; + } + } + } + + pad_mask = iosf_read(comm->port, GPI_SMI_STS_OFFSET(grp_index)); + /* Write back 1 to reset the sts bits */ + iosf_write(comm->port, GPI_SMI_STS_OFFSET(grp_index), pad_mask); + /* Set enable bits */ + iosf_write(comm->port, GPI_SMI_EN_OFFSET(grp_index), 1 << pad); +} + void gpio_configure_pad(const struct pad_config *cfg) { const struct pad_community *comm = gpio_get_community(cfg->pad); @@ -91,6 +140,11 @@ void gpio_configure_pad(const struct pad_config *cfg) iosf_write(comm->port, config_offset + sizeof(uint32_t), cfg->config1);
gpio_configure_itss(cfg, comm->port, config_offset); + + if (((cfg->config0) & PAD_FIELD(GPIROUTSMI, MASK)) == + PAD_FIELD(GPIROUTSMI, YES)) { + gpi_enable_smi(cfg->pad); + } }
void gpio_configure_pads(const struct pad_config *cfg, size_t num_pads) @@ -184,6 +238,110 @@ uint16_t gpio_acpi_pin(gpio_t gpio_num) return gpio_num; }
+static size_t community_clr_get_smi_sts(const struct pad_community *comm, + uint32_t *sts) +{ + uint8_t i; + uint8_t num_grps = 0; + + for (i = 0 ; i < ARRAY_SIZE(gpio_communities); i++) { + if (i == 0) + num_grps = gpio_communities[i].num_of_grps; + else + num_grps = gpio_communities[i].num_of_grps - + gpio_communities[i-1].num_of_grps; + } + + /* Not all groups can be routed to SMI. However, the registers + * read as 0. In order to simplify the logic read everything from + * each community. */ + for (i=0 ; i < num_grps; i++) { + sts[i] = iosf_read(comm->port, GPI_SMI_STS_OFFSET(i)) & + iosf_read(comm->port, GPI_SMI_EN_OFFSET(i)); + /* Clear the set status bits. */ + iosf_write(comm->port, GPI_SMI_STS_OFFSET(i), sts[i]); + } + return num_grps; +} + +static void print_gpi_status(uint32_t status, int offset) +{ + int i; + static int grp_index = 0; + + if (gpio_communities[grp_index].num_of_grps <= offset) + grp_index++; + + if (!status) + return; + + for (i = 31; i >= 0; i--) { + if (status & (1 << i)) + printk(BIOS_DEBUG, "%s %d ", + gpio_communities[grp_index].grp_name, i); + } +} + +void gpi_clear_get_smi_status(struct gpi_status *sts) +{ + int i; + int do_print; + size_t sts_index = 0; + + for (i = 0; i < ARRAY_SIZE(gpio_communities); i++) { + const struct pad_community *comm = &gpio_communities[i]; + sts_index += community_clr_get_smi_sts(comm, + &sts->grp[sts_index]); + } + + do_print = 0; + for (i = 0; i < ARRAY_SIZE(sts->grp); i++) { + if (sts->grp[i] == 0) + continue; + do_print = 1; + break; + } + + if (!do_print) + return; + + printk(BIOS_DEBUG, "GPI_SMI_STS: "); + for (i = 0; i < ARRAY_SIZE(sts->grp); i++) + print_gpi_status(sts->grp[i], i); + printk(BIOS_DEBUG, "\n"); +} + +int gpi_status_get(const struct gpi_status *sts, gpio_t gpi) +{ + const uint32_t *gpi_sts = NULL; + uint8_t i, + uint8_t sts_index = 0; + uint8_t gpi_offset = 0; + uint8_t grp_index = 0; + + const struct pad_community *comm = gpio_get_community(gpi); + + /* Check if valid gpi */ + if (comm == NULL) + return 0; + + for (i = 0 ; i < ARRAY_SIZE(gpio_communities); i++) { + if (comm->port == gpio_communities[i].port) { + sts_index = gpio_communities[i].num_of_grps - 2; + if (gpi > (((gpio_communities[i].pad_in_grp) >> + (8 * grp_index)) & GRP_MASK)){ + gpi_offset = gpi - GPIO_MAX_NUM_PER_GROUP; + grp_index ++; + sts_index++; + } + } + } + if (sts->grp[sts_index] == (1 << gpi_offset)) + gpi_sts = &sts->grp[sts_index];; + + return !!(*gpi_sts & (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 1ebac2d..56191f5 100644 --- a/src/soc/intel/apollolake/include/soc/gpio.h +++ b/src/soc/intel/apollolake/include/soc/gpio.h @@ -25,6 +25,21 @@
typedef uint32_t gpio_t;
+/* Structure to represent GPI status for GPE and SMI. Use helper + * functions for interrogating particular GPIs. */ +struct gpi_status { + uint32_t grp[GPIO_NUM_GROUPS]; +}; + +/* + * 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 @@ -114,6 +129,13 @@ struct pad_config { uint16_t pad; };
+#define PAD_FIELD_VAL(field_, val_) \ + (((val_) & field_ ## _MASK) << field_ ## _SHIFT) + +#define PAD_FIELD(field_, setting_) \ + PAD_FIELD_VAL(field_, field_ ## _ ## setting_) + + /* * Configuration for raw pads. Some pads are designated as only special function * pins, and don't have an associated GPIO number, so we need to expose the raw diff --git a/src/soc/intel/apollolake/include/soc/gpio_defs.h b/src/soc/intel/apollolake/include/soc/gpio_defs.h index 48e08e7..3756bc2 100644 --- a/src/soc/intel/apollolake/include/soc/gpio_defs.h +++ b/src/soc/intel/apollolake/include/soc/gpio_defs.h @@ -38,6 +38,10 @@ #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_NUM_GROUPS 8 +#define GPIO_MAX_NUM_PER_GROUP 32 +#define GRP_MASK (0xff << 0) + #define MISCCFG_GPE0_DW0_SHIFT 8 #define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT) #define MISCCFG_GPE0_DW1_SHIFT 12 @@ -97,6 +101,18 @@ #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(pad) (GPI_SMI_STS_0 + ((pad) * 4)) +#define GPI_SMI_EN_OFFSET(pad) (GPI_SMI_EN_0 + ((pad) * 4)) + +/* GPIROUTSMI - route to SMI */ +#define GPIROUTSMI_SHIFT 18 +#define GPIROUTSMI_MASK 0x1 +#define GPIROUTSMI_NO 0 +#define GPIROUTSMI_YES 1 + + /* 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 d8eb50b..801fba7 100644 --- a/src/soc/intel/apollolake/include/soc/pm.h +++ b/src/soc/intel/apollolake/include/soc/pm.h @@ -69,6 +69,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 0774974..7a9846e 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 <stdint.h> +#include <soc/gpio.h>
/* 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 94040c3..97b1eab 100644 --- a/src/soc/intel/apollolake/pmc.c +++ b/src/soc/intel/apollolake/pmc.c @@ -19,6 +19,7 @@ #include <device/pci.h> #include <device/pci_ids.h> #include <console/console.h> +#include <cpu/x86/smm.h> #include <soc/iomap.h> #include <soc/pci_ids.h> #include <soc/gpio.h> @@ -119,10 +120,20 @@ static void pmc_gpe_init(void) gpio_route_gpe(dw1, dw2, dw3); }
+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 29bab55..0566c73 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 1521920..b84e5cb 100644 --- a/src/soc/intel/apollolake/smihandler.c +++ b/src/soc/intel/apollolake/smihandler.c @@ -30,6 +30,7 @@ #include <spi-generic.h> #include <stdint.h> #include <stdlib.h> +#include <soc/smm.h>
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, };