[coreboot-gerrit] New patch to review for coreboot: soc/apollolake: add GPIO SMI support
Shaunak Saha (shaunak.saha@intel.com)
gerrit at coreboot.org
Mon Jul 25 07:28:39 CEST 2016
Shaunak Saha (shaunak.saha at intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15833
-gerrit
commit 54d2c81039c347478f9f2bc59ca77bdf304a73f7
Author: Shaunak Saha <shaunak.saha at 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
Change-Id: Id89a526106d1989c2bd3416ab81913e6cf743d17
Signed-off-by: Shaunak Saha <shaunak.saha at intel.com>
---
src/soc/intel/apollolake/Makefile.inc | 1 +
src/soc/intel/apollolake/gpio.c | 141 +++++++++++++++++++++++
src/soc/intel/apollolake/include/soc/gpio.h | 22 ++++
src/soc/intel/apollolake/include/soc/gpio_defs.h | 15 +++
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, 211 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..aadbf0b 100644
--- a/src/soc/intel/apollolake/gpio.c
+++ b/src/soc/intel/apollolake/gpio.c
@@ -42,6 +42,26 @@ static const struct pad_community {
}
};
+/*
+ * Structure for storing information about registers offset, community,
+ * maximal pad number for available groups
+ */
+static const struct GPIO_GROUP_INFO{
+ const char *grp_name;
+ uint8_t Community;
+ uint16_t first_pad;
+ uint8_t PadPerGroup;
+} GpioGroupInfo[] = {
+ {"GPIO_GPE_SW_31_0" , GPIO_SOUTHWEST, SW_OFFSET, 32},
+ {"GPIO_GPE_SW_63_32", GPIO_SOUTHWEST, SW_OFFSET, 11},
+ {"GPIO_GPE_W_31_0", GPIO_WEST, W_OFFSET, 26},
+ {"GPIO_GPE_NW_31_0", GPIO_NORTHWEST, NW_OFFSET, 18},
+ {"GPIO_GPE_NW_63_32", GPIO_NORTHWEST, NW_OFFSET, 29},
+ {"GPIO_GPE_NW_95_64", GPIO_NORTHWEST, NW_OFFSET, 13},
+ {"GPIO_GPE_N_31_0", GPIO_NORTH, N_OFFSET, 32},
+ {"GPIO_GPE_N_63_32", GPIO_NORTH, N_OFFSET, 30},
+};
+
static const struct pad_community *gpio_get_community(uint16_t pad)
{
const struct pad_community *map = gpio_communities;
@@ -83,6 +103,38 @@ 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 num_grps = 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(GpioGroupInfo); i++)
+ {
+ if(comm->port == GpioGroupInfo[i].Community) {
+ if(0 == set_offset) {
+ pad = pad - (GpioGroupInfo[i].first_pad);
+ set_offset = 1;
+ }
+ if( pad > (GpioGroupInfo[i].PadPerGroup)) {
+ pad = pad - GPIO_MAX_NUM_PER_GROUP;
+ num_grps++;
+ }
+ }
+ }
+ pad_mask = iosf_read(comm->port, GPI_SMI_STS_OFFSET(num_grps));
+ /* Write back 1 to reset the sts bits */
+ iosf_write(comm->port, GPI_SMI_STS_OFFSET(num_grps), pad_mask);
+ /* Set enable bits */
+ iosf_write(comm->port, GPI_SMI_EN_OFFSET(num_grps), 1 << pad) ;
+}
+
void gpio_configure_pad(const struct pad_config *cfg)
{
const struct pad_community *comm = gpio_get_community(cfg->pad);
@@ -98,7 +150,12 @@ void gpio_configure_pads(const struct pad_config *cfg, size_t num_pads)
uint32_t i;
for (i = 0; i < num_pads; i++)
+ {
gpio_configure_pad(cfg + i);
+ if ((((cfg + i)->config0) & PAD_FIELD(GPIROUTSMI, MASK)) ==
+ PAD_FIELD(GPIROUTSMI, YES))
+ gpi_enable_smi((cfg + i)->pad);
+ }
}
void gpio_input_pulldown(gpio_t gpio)
@@ -184,6 +241,90 @@ 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(GpioGroupInfo); i++)
+ {
+ if(comm->port == GpioGroupInfo[i].Community)
+ num_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, const char *grp_name)
+{
+ int i;
+
+ if (!status)
+ return;
+
+ for (i = 31; i >= 0; i--) {
+ if (status & (1 << i))
+ printk(BIOS_DEBUG, "%s:: %d ", 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], GpioGroupInfo[i].grp_name);
+ printk(BIOS_DEBUG, "\n");
+}
+
+int gpi_status_get(const struct gpi_status *sts, gpio_t gpi)
+{
+ const uint32_t *gpi_sts;
+ int i;
+
+ /* Check if valid gpi */
+ if (gpio_get_community(gpi) == NULL)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(sts->grp); i++) {
+ if (sts->grp[i] == 0)
+ continue;
+ gpi_sts = &sts->grp[i];;
+ break;
+ }
+
+ 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..e6610b0 100644
--- a/src/soc/intel/apollolake/include/soc/gpio_defs.h
+++ b/src/soc/intel/apollolake/include/soc/gpio_defs.h
@@ -38,6 +38,9 @@
#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 MISCCFG_GPE0_DW0_SHIFT 8
#define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT)
#define MISCCFG_GPE0_DW1_SHIFT 12
@@ -97,6 +100,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..7f9f945 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:\n");
+ 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,
};
More information about the coreboot-gerrit
mailing list