soc/intel/apollolake: Add GPE routing code

This patch adds the basic framework for SCI to GPE routing code.

BUG = chrome-os-partner:53438
TEST = Toogle pch_sci_l from ec console using gpioset command and
	see that the sci counter increases in /sys/firmware/acpi/interrupt
	and also 9 in /proc/interrupts.

Change-Id: I3b3198276530bf6513d94e9bea02ab9751212adf
Signed-off-by: Shaunak Saha <shaunak.saha@intel.com>
Reviewed-on: https://review.coreboot.org/15324
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Tested-by: build bot (Jenkins)
This commit is contained in:
Shaunak Saha 2016-06-07 02:06:28 -07:00 committed by Aaron Durbin
parent 0b806285a7
commit 5b6c5a500e
7 changed files with 291 additions and 2 deletions

View file

@ -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_ */

View file

@ -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);
}
}

View file

@ -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

View file

@ -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_ */

View file

@ -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)

View file

@ -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

View file

@ -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 = {