[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