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)
This commit is contained in:
Shaunak Saha 2016-07-24 20:50:12 -07:00 committed by Martin Roth
parent a46ee4d34d
commit 09115a92f6
9 changed files with 193 additions and 1 deletions

View File

@ -40,6 +40,7 @@ romstage-y += spi.c
smm-y += mmap_boot.c smm-y += mmap_boot.c
smm-y += pmutil.c smm-y += pmutil.c
smm-y += gpio.c
smm-y += smihandler.c smm-y += smihandler.c
smm-y += spi.c smm-y += spi.c
smm-y += tsc_freq.c smm-y += tsc_freq.c

View File

@ -26,19 +26,34 @@
static const struct pad_community { static const struct pad_community {
uint16_t first_pad; uint16_t first_pad;
uint8_t port; uint8_t port;
uint8_t num_gpi_regs;
uint8_t gpi_offset;
const char *grp_name;
} gpio_communities[] = { } gpio_communities[] = {
{ {
.port = GPIO_SOUTHWEST, .port = GPIO_SOUTHWEST,
.first_pad = SW_OFFSET, .first_pad = SW_OFFSET,
.num_gpi_regs = NUM_SW_GPI_REGS,
.gpi_offset = 0,
.grp_name = "GPIO_GPE_SW",
}, { }, {
.port = GPIO_WEST, .port = GPIO_WEST,
.first_pad = W_OFFSET, .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, .port = GPIO_NORTHWEST,
.first_pad = NW_OFFSET, .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, .port = GPIO_NORTH,
.first_pad = N_OFFSET, .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); 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) void gpio_configure_pad(const struct pad_config *cfg)
{ {
uint32_t dw1; 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_itss(cfg, comm->port, config_offset);
gpio_configure_owner(cfg, comm->port, cfg->pad - comm->first_pad); 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) 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; 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 */ /* Helper function to map PMC register groups to tier1 sci groups */
static int pmc_gpe_route_to_gpio(int route) static int pmc_gpe_route_to_gpio(int route)
{ {

View File

@ -25,6 +25,25 @@
typedef uint32_t gpio_t; 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_FUNC(value) PAD_CFG0_MODE_##value
#define PAD_RESET(value) PAD_CFG0_RESET_##value #define PAD_RESET(value) PAD_CFG0_RESET_##value
#define PAD_PULL(value) PAD_CFG1_PULL_##value #define PAD_PULL(value) PAD_CFG1_PULL_##value

View File

@ -38,6 +38,8 @@
#define GPIO_GPE_N_31_0 7 /* NORTH GPIO# 0 ~ 31 belong to GROUP7 */ #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_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_SHIFT 8
#define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT) #define MISCCFG_GPE0_DW0_MASK (0xf << MISCCFG_GPE0_DW0_SHIFT)
#define MISCCFG_GPE0_DW1_SHIFT 12 #define MISCCFG_GPE0_DW1_SHIFT 12
@ -134,6 +136,31 @@
#define GPIO_NORTH 0xc5 #define GPIO_NORTH 0xc5
#define GPIO_WEST 0xc7 #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 */ /* North community pads */
#define GPIO_0 0 #define GPIO_0 0
#define GPIO_1 1 #define GPIO_1 1

View File

@ -70,6 +70,7 @@
#define USB_EN (1 << SMI_XHCI) /* Legacy USB2 SMI logic */ #define USB_EN (1 << SMI_XHCI) /* Legacy USB2 SMI logic */
#define PERIODIC_EN (1 << SMI_PERIODIC) /* SMI on PERIODIC_STS in SMI_STS */ #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 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 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 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# */ #define APMC_EN (1 << SMI_APMC) /* Writes to APM_CNT cause SMI# */

View File

@ -19,6 +19,7 @@
#define _SOC_SMM_H_ #define _SOC_SMM_H_
#include <stdint.h> #include <stdint.h>
#include <soc/gpio.h>
/* These helpers are for performing SMM relocation. */ /* These helpers are for performing SMM relocation. */
void southbridge_clear_smi_status(void); 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_clear_state(void);
void southbridge_smm_enable_smi(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 /* Fills in the arguments for the entire SMM region covered by chipset
* protections. e.g. TSEG. */ * protections. e.g. TSEG. */

View File

@ -19,6 +19,7 @@
#include <device/pci.h> #include <device/pci.h>
#include <device/pci_ids.h> #include <device/pci_ids.h>
#include <console/console.h> #include <console/console.h>
#include <cpu/x86/smm.h>
#include <soc/iomap.h> #include <soc/iomap.h>
#include <soc/pci_ids.h> #include <soc/pci_ids.h>
#include <soc/gpio.h> #include <soc/gpio.h>
@ -122,10 +123,20 @@ static void pmc_gpe_init(void)
fixup_power_state(); 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) static void pmc_init(struct device *dev)
{ {
/* Set up GPE configuration */ /* Set up GPE configuration */
pmc_gpe_init(); pmc_gpe_init();
pch_set_acpi_mode();
} }
static const struct device_operations device_ops = { static const struct device_operations device_ops = {

View File

@ -52,7 +52,7 @@ void southbridge_smm_enable_smi(void)
disable_gpe(PME_B0_EN); disable_gpe(PME_B0_EN);
/* Enable SMI generation */ /* 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) void southbridge_clear_smi_status(void)

View File

@ -30,6 +30,7 @@
#include <spi-generic.h> #include <spi-generic.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <soc/smm.h>
int smm_disable_busmaster(device_t dev) 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; 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] = { const smi_handler_t southbridge_smi[32] = {
[SLP_SMI_STS] = southbridge_smi_sleep, [SLP_SMI_STS] = southbridge_smi_sleep,
[APM_SMI_STS] = southbridge_smi_apmc, [APM_SMI_STS] = southbridge_smi_apmc,
[FAKE_PM1_SMI_STS] = southbridge_smi_pm1, [FAKE_PM1_SMI_STS] = southbridge_smi_pm1,
[GPIO_SMI_STS] = southbridge_smi_gpi,
[TCO_SMI_STS] = southbridge_smi_tco, [TCO_SMI_STS] = southbridge_smi_tco,
[PERIODIC_SMI_STS] = southbridge_smi_periodic, [PERIODIC_SMI_STS] = southbridge_smi_periodic,
}; };