aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/intel/apollolake/chip.h5
-rw-r--r--src/soc/intel/apollolake/gpio.c68
-rw-r--r--src/soc/intel/apollolake/include/soc/gpe.h102
-rw-r--r--src/soc/intel/apollolake/include/soc/gpio.h11
-rw-r--r--src/soc/intel/apollolake/include/soc/gpio_defs.h22
-rw-r--r--src/soc/intel/apollolake/include/soc/pm.h19
-rw-r--r--src/soc/intel/apollolake/pmc.c68
7 files changed, 292 insertions, 3 deletions
diff --git a/src/soc/intel/apollolake/chip.h b/src/soc/intel/apollolake/chip.h
index c83f9739fd..aabd42df42 100644
--- a/src/soc/intel/apollolake/chip.h
+++ b/src/soc/intel/apollolake/chip.h
@@ -21,6 +21,7 @@
#include <soc/gpio.h>
#include <soc/intel/common/lpss_i2c.h>
#include <device/i2c.h>
+#include <soc/pm.h>
#define CLKREQ_DISABLED 0xf
#define APOLLOLAKE_I2C_DEV_MAX 8
@@ -96,6 +97,10 @@ struct soc_intel_apollolake_config {
/* I2C bus configuration */
struct apollolake_i2c_config i2c[APOLLOLAKE_I2C_DEV_MAX];
+
+ uint8_t gpe0_dw1; /* GPE0_63_32 STS/EN */
+ uint8_t gpe0_dw2; /* GPE0_95_64 STS/EN */
+ uint8_t gpe0_dw3; /* GPE0_127_96 STS/EN */
};
#endif /* _SOC_APOLLOLAKE_CHIP_H_ */
diff --git a/src/soc/intel/apollolake/gpio.c b/src/soc/intel/apollolake/gpio.c
index ac0d83b810..525c972b5c 100644
--- a/src/soc/intel/apollolake/gpio.c
+++ b/src/soc/intel/apollolake/gpio.c
@@ -19,6 +19,7 @@
#include <gpio.h>
#include <soc/gpio.h>
#include <soc/iosf.h>
+#include <soc/pm.h>
/* This list must be in order, from highest pad numbers, to lowest pad numbers*/
static const struct pad_community {
@@ -150,3 +151,70 @@ uint16_t gpio_acpi_pin(gpio_t gpio_num)
return gpio_num;
}
+
+/* Helper function to map PMC register groups to tier1 sci groups */
+static int pmc_gpe_route_to_gpio(int route)
+{
+ switch(route) {
+ case PMC_GPE_SW_31_0:
+ return GPIO_GPE_SW_31_0;
+ case PMC_GPE_SW_63_32:
+ return GPIO_GPE_SW_63_32;
+ case PMC_GPE_NW_31_0:
+ return GPIO_GPE_NW_31_0;
+ case PMC_GPE_NW_63_32:
+ return GPIO_GPE_NW_63_32;
+ case PMC_GPE_NW_95_64:
+ return GPIO_GPE_NW_95_64;
+ case PMC_GPE_N_31_0:
+ return GPIO_GPE_N_31_0;
+ case PMC_GPE_N_63_32:
+ return GPIO_GPE_N_63_32;
+ case PMC_GPE_W_31_0:
+ return GPIO_GPE_W_31_0;
+ default:
+ return -1;
+ }
+}
+
+void gpio_route_gpe(uint8_t gpe0b, uint8_t gpe0c, uint8_t gpe0d)
+{
+ int i;
+ uint32_t misccfg_mask;
+ uint32_t misccfg_value;
+ uint32_t value;
+
+ /* Get the group here for community specific MISCCFG register.
+ * If any of these returns -1 then there is some error in devicetree
+ * where the group is probably hardcoded and does not comply with the
+ * PMC group defines. So we return from here and MISCFG is set to
+ * default.
+ */
+ gpe0b = pmc_gpe_route_to_gpio(gpe0b);
+ if(gpe0b == -1)
+ return;
+ gpe0c = pmc_gpe_route_to_gpio(gpe0c);
+ if(gpe0c == -1)
+ return;
+ gpe0d = pmc_gpe_route_to_gpio(gpe0d);
+ if(gpe0d == -1)
+ return;
+
+ misccfg_value = gpe0b << MISCCFG_GPE0_DW0_SHIFT;
+ misccfg_value |= gpe0c << MISCCFG_GPE0_DW1_SHIFT;
+ misccfg_value |= gpe0d << MISCCFG_GPE0_DW2_SHIFT;
+
+ /* Program GPIO_MISCCFG */
+ misccfg_mask = ~(MISCCFG_GPE0_DW2_MASK |
+ MISCCFG_GPE0_DW1_MASK |
+ MISCCFG_GPE0_DW0_MASK);
+
+ for (i = 0; i < ARRAY_SIZE(gpio_communities); i++) {
+ const struct pad_community *comm = &gpio_communities[i];
+
+ value = iosf_read(comm->port, GPIO_MISCCFG);
+ value &= misccfg_mask;
+ value |= misccfg_value;
+ iosf_write(comm->port, GPIO_MISCCFG, value);
+ }
+}
diff --git a/src/soc/intel/apollolake/include/soc/gpe.h b/src/soc/intel/apollolake/include/soc/gpe.h
index 8abbad86c6..5482ed379f 100644
--- a/src/soc/intel/apollolake/include/soc/gpe.h
+++ b/src/soc/intel/apollolake/include/soc/gpe.h
@@ -34,4 +34,106 @@
#define GPE0A_SMB_WAK_STS 16
#define GPE0A_SATA_PME_STS 17
+/* Group DW0 is reserved in Apollolake */
+
+/* GPE_63_32 */
+#define GPE0_DW1_00 32
+#define GPE0_DW1_01 33
+#define GPE0_DW1_02 34
+#define GPE0_DW1_03 36
+#define GPE0_DW1_04 36
+#define GPE0_DW1_05 37
+#define GPE0_DW1_06 38
+#define GPE0_DW1_07 39
+#define GPE0_DW1_08 40
+#define GPE0_DW1_09 41
+#define GPE0_DW1_10 42
+#define GPE0_DW1_11 43
+#define GPE0_DW1_12 44
+#define GPE0_DW1_13 45
+#define GPE0_DW1_14 46
+#define GPE0_DW1_15 47
+#define GPE0_DW1_16 48
+#define GPE0_DW1_17 49
+#define GPE0_DW1_18 50
+#define GPE0_DW1_19 51
+#define GPE0_DW1_20 52
+#define GPE0_DW1_21 53
+#define GPE0_DW1_22 54
+#define GPE0_DW1_23 55
+#define GPE0_DW1_24 56
+#define GPE0_DW1_25 57
+#define GPE0_DW1_26 58
+#define GPE0_DW1_27 59
+#define GPE0_DW1_28 60
+#define GPE0_DW1_29 61
+#define GPE0_DW1_30 62
+#define GPE0_DW1_31 63
+/* GPE_95_64 */
+#define GPE0_DW2_00 64
+#define GPE0_DW2_01 65
+#define GPE0_DW2_02 66
+#define GPE0_DW2_03 67
+#define GPE0_DW2_04 68
+#define GPE0_DW2_05 69
+#define GPE0_DW2_06 70
+#define GPE0_DW2_07 71
+#define GPE0_DW2_08 72
+#define GPE0_DW2_09 73
+#define GPE0_DW2_10 74
+#define GPE0_DW2_11 75
+#define GPE0_DW2_12 76
+#define GPE0_DW2_13 77
+#define GPE0_DW2_14 78
+#define GPE0_DW2_15 79
+#define GPE0_DW2_16 80
+#define GPE0_DW2_17 81
+#define GPE0_DW2_18 82
+#define GPE0_DW2_19 83
+#define GPE0_DW2_20 84
+#define GPE0_DW2_21 85
+#define GPE0_DW2_22 86
+#define GPE0_DW2_23 87
+#define GPE0_DW2_24 88
+#define GPE0_DW2_25 89
+#define GPE0_DW2_26 90
+#define GPE0_DW2_27 91
+#define GPE0_DW2_28 92
+#define GPE0_DW2_29 93
+#define GPE0_DW2_30 94
+#define GPE0_DW2_31 95
+/* GPE_127_96 */
+#define GPE0_DW3_00 96
+#define GPE0_DW3_01 97
+#define GPE0_DW3_02 98
+#define GPE0_DW3_03 99
+#define GPE0_DW3_04 100
+#define GPE0_DW3_05 101
+#define GPE0_DW3_06 102
+#define GPE0_DW3_07 103
+#define GPE0_DW3_08 104
+#define GPE0_DW3_09 105
+#define GPE0_DW3_10 106
+#define GPE0_DW3_11 107
+#define GPE0_DW3_12 108
+#define GPE0_DW3_13 109
+#define GPE0_DW3_14 110
+#define GPE0_DW3_15 111
+#define GPE0_DW3_16 112
+#define GPE0_DW3_17 113
+#define GPE0_DW3_18 114
+#define GPE0_DW3_19 115
+#define GPE0_DW3_20 116
+#define GPE0_DW3_21 117
+#define GPE0_DW3_22 118
+#define GPE0_DW3_23 119
+#define GPE0_DW3_24 120
+#define GPE0_DW3_25 121
+#define GPE0_DW3_26 122
+#define GPE0_DW3_27 123
+#define GPE0_DW3_28 124
+#define GPE0_DW3_29 125
+#define GPE0_DW3_30 126
+#define GPE0_DW3_31 127
+
#endif
diff --git a/src/soc/intel/apollolake/include/soc/gpio.h b/src/soc/intel/apollolake/include/soc/gpio.h
index c9d32ccf56..763cdefa4e 100644
--- a/src/soc/intel/apollolake/include/soc/gpio.h
+++ b/src/soc/intel/apollolake/include/soc/gpio.h
@@ -99,5 +99,16 @@ struct pad_config {
void gpio_configure_pad(const struct pad_config *cfg);
void gpio_configure_pads(const struct pad_config *cfg, size_t num_pads);
+/*
+ * Set the GPIO groups for the GPE blocks. The values from PMC register GPE_CFG
+ * are passed which is then mapped to proper groups for MISCCFG. This basically
+ * sets the MISCCFG register bits:
+ * dw0 = gpe0_route[11:8]. This is ACPI GPE0b.
+ * dw1 = gpe0_route[15:12]. This is ACPI GPE0c.
+ * dw2 = gpe0_route[19:16]. This is ACPI GPE0d.
+ */
+void gpio_route_gpe(uint8_t gpe0b, uint8_t gpe0c, uint8_t gpe0d);
+
#endif /* __ACPI__ */
+
#endif /* _SOC_APOLLOLAKE_GPIO_H_ */
diff --git a/src/soc/intel/apollolake/include/soc/gpio_defs.h b/src/soc/intel/apollolake/include/soc/gpio_defs.h
index 30c4bdb272..c6e2c3cf91 100644
--- a/src/soc/intel/apollolake/include/soc/gpio_defs.h
+++ b/src/soc/intel/apollolake/include/soc/gpio_defs.h
@@ -23,6 +23,28 @@
#ifndef _SOC_APOLLOLAKE_GPIO_DEFS_H_
#define _SOC_APOLLOLAKE_GPIO_DEFS_H_
+/*
+ * Miscellaneous Configuration register(MISCCFG).These are community specific
+ * registers and are meant to house miscellaneous configuration fields per
+ * community. There are 8 GPIO groups: GPP_0 -> GPP_8 (Group 3 is absent)
+ */
+#define GPIO_MISCCFG 0x10 /* Miscellaneous Configuration offset */
+#define GPIO_GPE_SW_31_0 0 /* SOUTHWEST GPIO# 0 ~ 31 belong to GROUP0 */
+#define GPIO_GPE_SW_63_32 1 /* SOUTHWEST GPIO# 32 ~ 42 belong to GROUP1 */
+#define GPIO_GPE_W_31_0 2 /* WEST GPIO# 0 ~ 25 belong to GROUP2 */
+#define GPIO_GPE_NW_31_0 4 /* NORTHWEST GPIO# 0 ~ 17 belong to GROUP4 */
+#define GPIO_GPE_NW_63_32 5 /* NORTHWEST GPIO# 32 ~ 63 belong to GROUP5 */
+#define GPIO_GPE_NW_95_64 6 /* NORTHWEST GPIO# 64 ~ 76 belong to GROUP6 */
+#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 MISCCFG_GPE0_DW0_SHIFT 8
+#define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT)
+#define MISCCFG_GPE0_DW1_SHIFT 12
+#define MISCCFG_GPE0_DW1_MASK (0xf << MISCCFG_GPE0_DW1_SHIFT)
+#define MISCCFG_GPE0_DW2_SHIFT 16
+#define MISCCFG_GPE0_DW2_MASK (0xf << MISCCFG_GPE0_DW2_SHIFT)
+
#define PAD_CFG0_TX_STATE (1 << 0)
#define PAD_CFG0_RX_STATE (1 << 1)
#define PAD_CFG0_TX_DISABLE (1 << 8)
diff --git a/src/soc/intel/apollolake/include/soc/pm.h b/src/soc/intel/apollolake/include/soc/pm.h
index 72f74a669c..99f922f244 100644
--- a/src/soc/intel/apollolake/include/soc/pm.h
+++ b/src/soc/intel/apollolake/include/soc/pm.h
@@ -129,9 +129,22 @@
# define RPS (1 << 2)
#define GEN_PMCON3 0x1028
#define ETR 0x1048
-# define CF9_LOCK (1 << 31)
-# define CF9_GLB_RST (1 << 20)
-
+# define CF9_LOCK (1 << 31)
+# define CF9_GLB_RST (1 << 20)
+#define GPIO_GPE_CFG 0x1050
+#define GPE0_DWX_MASK 0xf
+#define GPE0_DW1_SHIFT 4
+#define GPE0_DW2_SHIFT 8
+#define GPE0_DW3_SHIFT 12
+
+#define PMC_GPE_SW_31_0 0
+#define PMC_GPE_SW_63_32 1
+#define PMC_GPE_NW_31_0 3
+#define PMC_GPE_NW_63_32 4
+#define PMC_GPE_NW_95_64 5
+#define PMC_GPE_N_31_0 6
+#define PMC_GPE_N_63_32 7
+#define PMC_GPE_W_31_0 9
/* Generic sleep state types */
#define SLEEP_STATE_S0 0
diff --git a/src/soc/intel/apollolake/pmc.c b/src/soc/intel/apollolake/pmc.c
index c88e14c4c0..94040c3650 100644
--- a/src/soc/intel/apollolake/pmc.c
+++ b/src/soc/intel/apollolake/pmc.c
@@ -18,8 +18,13 @@
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
+#include <console/console.h>
#include <soc/iomap.h>
#include <soc/pci_ids.h>
+#include <soc/gpio.h>
+#include <soc/pci_devs.h>
+#include <soc/pm.h>
+#include "chip.h"
/*
* The ACPI IO BAR (offset 0x20) is not PCI compliant. We've observed cases
@@ -58,10 +63,73 @@ static void set_resources(device_t dev)
report_resource_stored(dev, res, " ACPI BAR");
}
+static void pmc_gpe_init(void)
+{
+ uint32_t gpio_cfg = 0;
+ uint32_t gpio_cfg_reg;
+ uint8_t dw1, dw2, dw3;
+ const struct soc_intel_apollolake_config *config;
+
+ struct device *dev = NB_DEV_ROOT;
+ if (!dev || !dev->chip_info) {
+ printk(BIOS_ERR, "BUG! Could not find SOC devicetree config\n");
+ return;
+ }
+ config = dev->chip_info;
+
+ uintptr_t pmc_bar = get_pmc_mmio_bar();
+
+ const uint32_t gpio_cfg_mask =
+ (GPE0_DWX_MASK << GPE0_DW1_SHIFT) |
+ (GPE0_DWX_MASK << GPE0_DW2_SHIFT) |
+ (GPE0_DWX_MASK << GPE0_DW3_SHIFT);
+
+ /* Assign to local variable */
+ dw1 = config->gpe0_dw1;
+ dw2 = config->gpe0_dw2;
+ dw3 = config->gpe0_dw3;
+
+ /* Making sure that bad values don't bleed into the other fields */
+ dw1 &= GPE0_DWX_MASK;
+ dw2 &= GPE0_DWX_MASK;
+ dw3 &= GPE0_DWX_MASK;
+
+ /* Route the GPIOs to the GPE0 block. Determine that all values
+ * are different, and if they aren't use the reset values.
+ * DW0 is reserved/unused */
+ if (dw1 == dw2 || dw2 == dw3) {
+ printk(BIOS_INFO, "PMC: Using default GPE route.\n");
+ gpio_cfg = read32((void *)pmc_bar + GPIO_GPE_CFG);
+
+ dw1 = (gpio_cfg >> GPE0_DW1_SHIFT) & GPE0_DWX_MASK;
+ dw2 = (gpio_cfg >> GPE0_DW2_SHIFT) & GPE0_DWX_MASK;
+ dw3 = (gpio_cfg >> GPE0_DW3_SHIFT) & GPE0_DWX_MASK;
+ } else {
+ gpio_cfg |= (uint32_t)dw1 << GPE0_DW1_SHIFT;
+ gpio_cfg |= (uint32_t)dw2 << GPE0_DW2_SHIFT;
+ gpio_cfg |= (uint32_t)dw3 << GPE0_DW3_SHIFT;
+ }
+
+ gpio_cfg_reg = read32((void *)pmc_bar + GPIO_GPE_CFG) & ~gpio_cfg_mask;
+ gpio_cfg_reg |= gpio_cfg & gpio_cfg_mask;
+
+ write32((void *)pmc_bar + GPIO_GPE_CFG, gpio_cfg_reg);
+
+ /* Set the routes in the GPIO communities as well. */
+ gpio_route_gpe(dw1, dw2, dw3);
+}
+
+static void pmc_init(struct device *dev)
+{
+ /* Set up GPE configuration */
+ pmc_gpe_init();
+}
+
static const struct device_operations device_ops = {
.read_resources = read_resources,
.set_resources = set_resources,
.enable_resources = pci_dev_enable_resources,
+ .init = &pmc_init,
};
static const struct pci_driver pmc __pci_driver = {