[coreboot-gerrit] Patch set updated for coreboot: soc/apollolake: add GPIO SMI support

Shaunak Saha (shaunak.saha@intel.com) gerrit at coreboot.org
Fri Jul 29 08:51:08 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 6bd3063826501d4e82800b72d4f02adc08f31908
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
    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 at 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,
 };



More information about the coreboot-gerrit mailing list