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:
parent
a46ee4d34d
commit
09115a92f6
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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# */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue