aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/apollolake
diff options
context:
space:
mode:
authorShaunak Saha <shaunak.saha@intel.com>2016-07-24 20:50:12 -0700
committerMartin Roth <martinroth@google.com>2016-08-10 21:10:59 +0200
commit09115a92f6ac6123a6f1ed435c05fe60dc01d58c (patch)
tree9c6b16087c872cb140c5b447c33ebae3a78e2e23 /src/soc/intel/apollolake
parenta46ee4d34d6b7ce7d47882779c7fb36270ccbda1 (diff)
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> Reviewed-on: https://review.coreboot.org/15833 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/soc/intel/apollolake')
-rw-r--r--src/soc/intel/apollolake/Makefile.inc1
-rw-r--r--src/soc/intel/apollolake/gpio.c114
-rw-r--r--src/soc/intel/apollolake/include/soc/gpio.h19
-rw-r--r--src/soc/intel/apollolake/include/soc/gpio_defs.h27
-rw-r--r--src/soc/intel/apollolake/include/soc/pm.h1
-rw-r--r--src/soc/intel/apollolake/include/soc/smm.h3
-rw-r--r--src/soc/intel/apollolake/pmc.c11
-rw-r--r--src/soc/intel/apollolake/smi.c2
-rw-r--r--src/soc/intel/apollolake/smihandler.c16
9 files changed, 193 insertions, 1 deletions
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 <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 f303632cb2..92a150ddf4 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>
@@ -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 <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,
};