aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/intel/skylake/chip.h21
-rw-r--r--src/soc/intel/skylake/gpio.c23
-rw-r--r--src/soc/intel/skylake/include/soc/gpio.h9
-rw-r--r--src/soc/intel/skylake/include/soc/gpio_defs.h31
-rw-r--r--src/soc/intel/skylake/include/soc/pmc.h5
-rw-r--r--src/soc/intel/skylake/pmc.c43
6 files changed, 118 insertions, 14 deletions
diff --git a/src/soc/intel/skylake/chip.h b/src/soc/intel/skylake/chip.h
index 96389a9471..873342ae47 100644
--- a/src/soc/intel/skylake/chip.h
+++ b/src/soc/intel/skylake/chip.h
@@ -19,14 +19,16 @@
* Foundation, Inc.
*/
+
+#ifndef _SOC_CHIP_H_
+#define _SOC_CHIP_H_
+
#include <stdint.h>
+#include <soc/gpio_defs.h>
#include <soc/pci_devs.h>
#include <soc/pmc.h>
#include <soc/serialio.h>
-#ifndef _SOC_CHIP_H_
-#define _SOC_CHIP_H_
-
struct soc_intel_skylake_config {
/*
* Interrupt Routing configuration
@@ -42,10 +44,15 @@ struct soc_intel_skylake_config {
uint8_t pirqh_routing;
/* GPE configuration */
- uint32_t gpe0_en_1;
- uint32_t gpe0_en_2;
- uint32_t gpe0_en_3;
- uint32_t gpe0_en_4;
+ uint32_t gpe0_en_1; /* GPE0_EN_31_0 */
+ uint32_t gpe0_en_2; /* GPE0_EN_63_32 */
+ uint32_t gpe0_en_3; /* GPE0_EN_95_64 */
+ uint32_t gpe0_en_4; /* GPE0_EN_127_96 / GPE_STD */
+ /* Gpio group routed to each dword of the GPE0 block. Values are
+ * of the form GPP_[A:G] or GPD. */
+ uint8_t gpe0_dw0; /* GPE0_31_0 STS/EN */
+ uint8_t gpe0_dw1; /* GPE0_63_32 STS/EN */
+ uint8_t gpe0_dw2; /* GPE0_95_64 STS/EN */
/* GPIO SMI configuration */
uint32_t ec_smi_gpio;
diff --git a/src/soc/intel/skylake/gpio.c b/src/soc/intel/skylake/gpio.c
index 28ed07ebd3..18638873f7 100644
--- a/src/soc/intel/skylake/gpio.c
+++ b/src/soc/intel/skylake/gpio.c
@@ -71,6 +71,29 @@ static const struct gpio_community *gpio_get_community(gpio_t pad)
return NULL;
}
+void gpio_route_gpe(uint16_t gpe0_route)
+{
+ int i;
+ uint32_t misc_cfg;
+ const uint32_t misc_cfg_reg_mask = GPE_DW_MASK;
+
+ misc_cfg = (uint32_t)gpe0_route << GPE_DW_SHIFT;
+ misc_cfg &= misc_cfg_reg_mask;
+
+ for (i = 0; i < ARRAY_SIZE(communities); i++) {
+ uint8_t *regs;
+ uint32_t reg;
+ const struct gpio_community *comm = &communities[i];
+
+ regs = pcr_port_regs(comm->port_id);
+
+ reg = read32(regs + MISCCFG_OFFSET);
+ reg &= ~misc_cfg_reg_mask;
+ reg |= misc_cfg;
+ write32(regs + MISCCFG_OFFSET, reg);
+ }
+}
+
static void *gpio_dw_regs(gpio_t pad)
{
const struct gpio_community *comm;
diff --git a/src/soc/intel/skylake/include/soc/gpio.h b/src/soc/intel/skylake/include/soc/gpio.h
index a7d9158ed5..321f04c5d3 100644
--- a/src/soc/intel/skylake/include/soc/gpio.h
+++ b/src/soc/intel/skylake/include/soc/gpio.h
@@ -43,6 +43,15 @@ void gpio_enable_all_smi(void);
/* Enable GPIO individual Group SMI */
void gpio_enable_groupsmi(gpio_t gpio_num, u32 mask);
+/*
+ * Set the GPIO groups for the GPE blocks. The gpe0_route is interpreted
+ * as the packed configuration for GPE0_DW[2:0]:
+ * dw0 = gpe0_route[3:0]
+ * dw1 = gpe0_route[7:4]
+ * dw2 = gpe0_route[11:8].
+ */
+void gpio_route_gpe(uint16_t gpe0_route);
+
/* Configure the pads according to the pad_config array. */
struct pad_config;
void gpio_configure_pads(const struct pad_config *cfgs, size_t num);
diff --git a/src/soc/intel/skylake/include/soc/gpio_defs.h b/src/soc/intel/skylake/include/soc/gpio_defs.h
index 625acdb74e..09f50199f6 100644
--- a/src/soc/intel/skylake/include/soc/gpio_defs.h
+++ b/src/soc/intel/skylake/include/soc/gpio_defs.h
@@ -21,6 +21,23 @@
#define _SOC_GPIO_DEFS_H_
/*
+ * There are 8 GPIO groups. GPP_A -> GPP_G and GPD. GPD is the special case
+ * where that group is not so generic. So most of the fixed numbers and macros
+ * are based on the GPP groups. The GPIO groups are accessed through register
+ * blocks called communities.
+ */
+#define GPP_A 0
+#define GPP_B 1
+#define GPP_C 2
+#define GPP_D 3
+#define GPP_E 4
+#define GPP_F 5
+#define GPP_G 6
+#define GPD 7
+#define GPIO_NUM_GROUPS 8
+#define GPIO_MAX_NUM_PER_GROUP 24
+
+/*
* GPIOs are ordered monotonically increasing to match ACPI/OS driver.
*/
@@ -375,6 +392,12 @@
#define GPD11_IRQ 0x5b
/* Register defines. */
+#define MISCCFG_OFFSET 0x10
+#define GPIO_DRIVER_IRQ_ROUTE_MASK 8
+#define GPIO_DRIVER_IRQ_ROUTE_IRQ14 0
+#define GPIO_DRIVER_IRQ_ROUTE_IRQ15 8
+#define GPE_DW_SHIFT 8
+#define GPE_DW_MASK 0xfff00
#define PAD_OWN_REG_OFFSET 0x20
#define PAD_OWN_PADS_PER 8
#define PAD_OWN_WIDTH_PER 4
@@ -476,9 +499,9 @@
#define PAD_TERM_667_PU 13
#define PAD_TERM_NATIVE 15
-#define MISCCFG_OFFSET 0x10
-#define GPIO_DRIVER_IRQ_ROUTE_MASK 8
-#define GPIO_DRIVER_IRQ_ROUTE_IRQ14 0
-#define GPIO_DRIVER_IRQ_ROUTE_IRQ15 8
+#define GPI_GPE_STS_OFFSET 0x140
+#define GPI_GPE_EN_OFFSET 0x160
+#define GPI_SMI_STS_OFFSET 0x180
+#define GPI_SMI_EN_OFFSET 0x1a0
#endif /* _SOC_GPIO_DEFS_H_ */
diff --git a/src/soc/intel/skylake/include/soc/pmc.h b/src/soc/intel/skylake/include/soc/pmc.h
index 5774d46a4b..9c9b175285 100644
--- a/src/soc/intel/skylake/include/soc/pmc.h
+++ b/src/soc/intel/skylake/include/soc/pmc.h
@@ -91,6 +91,11 @@
#define DSX_EN_LAN_WAKE_PIN (1 << 0)
#define PMSYNC_TPR_CFG 0xc4
#define PMSYNC_LOCK (1 << 31)
+#define GPIO_CFG 0x120
+#define GPE0_DWX_MASK 0xf
+#define GPE0_DW0_SHIFT 0
+#define GPE0_DW1_SHIFT 4
+#define GPE0_DW2_SHIFT 8
#define GBLRST_CAUSE0 0x124
#define GBLRST_CAUSE1 0x128
diff --git a/src/soc/intel/skylake/pmc.c b/src/soc/intel/skylake/pmc.c
index 2c794cabea..b6c35ebb4f 100644
--- a/src/soc/intel/skylake/pmc.c
+++ b/src/soc/intel/skylake/pmc.c
@@ -27,6 +27,7 @@
#include <pc80/mc146818rtc.h>
#include <reg_script.h>
#include <string.h>
+#include <soc/gpio.h>
#include <soc/iomap.h>
#include <soc/pci_devs.h>
#include <soc/pmc.h>
@@ -147,6 +148,42 @@ static void pch_rtc_init(void)
#endif
}
+static void pmc_gpe_init(config_t *config)
+{
+ uint8_t *pmc_regs;
+ uint32_t gpio_cfg;
+ uint32_t gpio_cfg_reg;
+ const uint32_t gpio_cfg_mask =
+ (GPE0_DWX_MASK << GPE0_DW0_SHIFT) |
+ (GPE0_DWX_MASK << GPE0_DW1_SHIFT) |
+ (GPE0_DWX_MASK << GPE0_DW2_SHIFT);
+
+ pmc_regs = pmc_mmio_regs();
+ gpio_cfg = 0;
+
+ /* Route the GPIOs to the GPE0 block. Determine that all values
+ * are different, and if they aren't use the reset values. */
+ if (config->gpe0_dw0 == config->gpe0_dw1 ||
+ config->gpe0_dw1 == config->gpe0_dw2) {
+ printk(BIOS_INFO, "PMC: Using default GPE route.\n");
+ gpio_cfg = read32(pmc_regs + GPIO_CFG);
+ } else {
+ gpio_cfg |= (uint32_t)config->gpe0_dw0 << GPE0_DW0_SHIFT;
+ gpio_cfg |= (uint32_t)config->gpe0_dw1 << GPE0_DW1_SHIFT;
+ gpio_cfg |= (uint32_t)config->gpe0_dw2 << GPE0_DW2_SHIFT;
+ }
+ gpio_cfg_reg = read32(pmc_regs + GPIO_CFG) & ~gpio_cfg_mask;
+ gpio_cfg_reg |= gpio_cfg & gpio_cfg_mask;
+ write32(pmc_regs + GPIO_CFG, gpio_cfg_reg);
+
+ /* Set the routes in the GPIO communities as well. */
+ gpio_route_gpe(gpio_cfg_reg >> GPE0_DW0_SHIFT);
+
+ /* Set GPE enables based on devictree. */
+ enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2,
+ config->gpe0_en_3, config->gpe0_en_4);
+}
+
static void pch_power_options(void)
{
u16 reg16;
@@ -187,9 +224,9 @@ static void pch_power_options(void)
}
pci_write_config16(dev, GEN_PMCON_B, reg16);
printk(BIOS_INFO, "Set power %s after power failure.\n", state);
- /* GPE setup based on device tree configuration */
- enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2,
- config->gpe0_en_3, config->gpe0_en_4);
+
+ /* Set up GPE configuration. */
+ pmc_gpe_init(config);
/* SMI setup based on device tree configuration */
enable_alt_smi(config->ec_smi_gpio, config->alt_gp_smi_en);