[linux-yocto] [PATCH 07/11] x86: Move Watchdog loader for Apollo Lake
Yong, Jonathan
jonathan.yong at intel.com
Mon Jun 27 18:28:22 PDT 2016
From: Jonathan Yong <jonathan.yong at intel.com>
The watchdog timer on Apollo Lake is on the Power Management
Controller instead of the SMBUS. The LPC driver will also load
the Apollo Lake Sideband Interface platform driver.
Signed-off-by: Jonathan Yong <jonathan.yong at intel.com>
---
drivers/mfd/lpc_ich.c | 154 +++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 139 insertions(+), 15 deletions(-)
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 231a372..20a176b 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -64,9 +64,11 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci.h>
+#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/lpc_ich.h>
#include <linux/platform_data/itco_wdt.h>
+#include <linux/platform_data/sbi_apl.h>
#define ACPIBASE 0x40
#define ACPIBASE_GPE_OFF 0x28
@@ -102,8 +104,9 @@ struct lpc_ich_priv {
int gctrl; /* GPIO control */
int abase_save; /* Cached ACPI base value */
- int actrl_pbase_save; /* Cached ACPI control or PMC base value */
+ int actrl_pbase_save; /* Cached ACPI control or PMC base value */
int gctrl_save; /* Cached GPIO control value */
+ struct mutex lock; /* Device hide/unhide control */
};
static struct resource wdt_ich_res[] = {
@@ -135,8 +138,11 @@ static struct resource gpio_ich_res[] = {
enum lpc_cells {
LPC_WDT = 0,
LPC_GPIO,
+ LPC_P2SB_APL,
};
+static struct sbi_platform_data sbi_apl_data;
+
static struct mfd_cell lpc_ich_cells[] = {
[LPC_WDT] = {
.name = "iTCO_wdt",
@@ -150,6 +156,12 @@ static struct mfd_cell lpc_ich_cells[] = {
.resources = gpio_ich_res,
.ignore_resource_conflicts = true,
},
+ [LPC_P2SB_APL] = {
+ .name = "sbi_apl",
+ .num_resources = 0,
+ .platform_data = &sbi_apl_data,
+ .pdata_size = sizeof(sbi_apl_data),
+ }
};
/* chipset related info */
@@ -220,6 +232,7 @@ enum lpc_chipsets {
LPC_WPT_LP, /* Wildcat Point-LP */
LPC_BRASWELL, /* Braswell SoC */
LPC_9S, /* 9 Series */
+ LPC_APL, /* Apollo Lake SoC */
};
static struct lpc_ich_info lpc_chipset_info[] = {
@@ -531,6 +544,10 @@ static struct lpc_ich_info lpc_chipset_info[] = {
.name = "9 Series",
.iTCO_version = 2,
},
+ [LPC_APL] = {
+ .name = "Apollo Lake SoC",
+ .iTCO_version = 5,
+ },
};
/*
@@ -679,6 +696,7 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
{ PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
{ PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
+ { PCI_VDEVICE(INTEL, 0x5ae8), LPC_APL},
{ PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
@@ -794,6 +812,9 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
u8 reg_save;
switch (lpc_chipset_info[priv->chipset].iTCO_version) {
+ case 5:
+ /* Doesn't apply for APL */
+ break;
case 3:
/*
* Some chipsets (eg Avoton) enable the ACPI space in the
@@ -967,32 +988,73 @@ gpio_done:
return ret;
}
-static int lpc_ich_init_wdt(struct pci_dev *dev)
+static unsigned long lpc_ich_res(resource_size_t *start, struct pci_bus *bus,
+ unsigned int devfn, unsigned int bar)
+{
+ u32 dword;
+ unsigned long flag = 0;
+ unsigned int addr = PCI_BASE_ADDRESS_0 + 4 * bar;
+
+ pci_bus_read_config_dword(bus, devfn, addr, &dword);
+ if ((dword & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ flag = IORESOURCE_IO;
+ *start = dword & PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ flag = IORESOURCE_MEM;
+ *start = dword & PCI_BASE_ADDRESS_MEM_MASK;
+ if (dword & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ flag |= IORESOURCE_MEM_64;
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ addr += 4;
+ pci_bus_read_config_dword(bus, devfn, addr, &dword);
+ *start |= ((u64) dword) << 32;
+#endif
+ }
+ }
+ return flag;
+}
+
+static int lpc_ich_init_wdt_pmc(struct pci_dev *dev, struct lpc_ich_priv *priv,
+ u32 *base_addr_cfg, u32 *base_addr)
{
- struct lpc_ich_priv *priv = pci_get_drvdata(dev);
- u32 base_addr_cfg;
- u32 base_addr;
- int ret;
struct resource *res;
+ /* Not applicable for APL */
+ if (lpc_chipset_info[priv->chipset].iTCO_version == 5)
+ return 0;
+
/* Setup power management base register */
- pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
- base_addr = base_addr_cfg & 0x0000ff80;
- if (!base_addr) {
+ pci_read_config_dword(dev, priv->abase, base_addr_cfg);
+ *base_addr = *base_addr_cfg & 0x0000ff80;
+ if (!*base_addr) {
dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n");
- ret = -ENODEV;
- goto wdt_done;
+ return -ENODEV;
}
res = wdt_io_res(ICH_RES_IO_TCO);
- res->start = base_addr + ACPIBASE_TCO_OFF;
- res->end = base_addr + ACPIBASE_TCO_END;
+ res->start = *base_addr + ACPIBASE_TCO_OFF;
+ res->end = *base_addr + ACPIBASE_TCO_END;
res = wdt_io_res(ICH_RES_IO_SMI);
- res->start = base_addr + ACPIBASE_SMI_OFF;
- res->end = base_addr + ACPIBASE_SMI_END;
+ res->start = *base_addr + ACPIBASE_SMI_OFF;
+ res->end = *base_addr + ACPIBASE_SMI_END;
lpc_ich_enable_acpi_space(dev);
+ return 0;
+}
+
+static int lpc_ich_init_wdt(struct pci_dev *dev)
+{
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+ u32 base_addr_cfg;
+ u32 base_addr;
+ int ret;
+ struct resource *res, *smi, *tco;
+ unsigned int p2sb, pmc;
+
+ ret = lpc_ich_init_wdt_pmc(dev, priv, &base_addr_cfg, &base_addr);
+ if (ret)
+ goto wdt_done;
/*
* iTCO v2:
@@ -1032,6 +1094,44 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
res->start = base_addr + ACPIBASE_PMC_OFF;
res->end = base_addr + ACPIBASE_PMC_END;
break;
+ case 5:
+ /* TCO may not necessarily be in IO space */
+ p2sb = PCI_DEVFN(13, 0);
+ pmc = PCI_DEVFN(13, 1);
+ smi = wdt_io_res(ICH_RES_IO_SMI);
+ tco = wdt_io_res(ICH_RES_IO_TCO);
+ res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
+ mutex_lock(&priv->lock);
+ pci_bus_write_config_byte(dev->bus, p2sb, 0xe1, 0x0);
+ pci_bus_read_config_dword(dev->bus, pmc, 0,
+ &base_addr_cfg);
+ if (base_addr_cfg == ~0 || base_addr_cfg == 0) {
+ ret = -ENODEV;
+ goto end_case_5;
+ } else {
+ ret = 0;
+ }
+ res->flags = lpc_ich_res(&res->start, dev->bus, pmc, 0);
+ res->end = res->start + 0x100c;
+ res->start += 0x1008;
+
+ /* We should really get the ACPI base off the 3rd BAR,
+ but it is not programmed in the PCI config space */
+ tco->flags = smi->flags = IORESOURCE_IO;
+ smi->start = 0x400;
+ tco->start = smi->start + ACPIBASE_TCO_OFF;
+ tco->end = smi->start + ACPIBASE_TCO_END;
+ /* Apollo Lake is unusual for using 0x40-0x43 for the
+ SMI_EN registers instead of the usual 0x30 */
+ smi->end = smi->start + 0x43;
+ smi->start += 0x40;
+
+end_case_5:
+ pci_bus_write_config_byte(dev->bus, p2sb, 0xe1, 0x1);
+ mutex_unlock(&priv->lock);
+ if (ret)
+ goto wdt_done;
+ break;
default:
dev_notice(&dev->dev, "Unknown TCO v%u\n",
lpc_chipset_info[priv->chipset].iTCO_version);
@@ -1048,6 +1148,25 @@ wdt_done:
return ret;
}
+static int lpc_ich_misc(struct pci_dev *dev, enum lpc_chipsets e,
+ struct lpc_ich_priv *priv)
+{
+ switch (e) {
+ case LPC_APL:
+ sbi_apl_data.name = lpc_ich_cells[LPC_P2SB_APL].name;
+ sbi_apl_data.version = 1;
+ sbi_apl_data.bus = 0;
+ sbi_apl_data.p2sb = PCI_DEVFN(0x0d, 0);
+ sbi_apl_data.lock = &priv->lock;
+ return mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+ &lpc_ich_cells[LPC_P2SB_APL], 1, NULL, 0, NULL);
+ break;
+ default:
+ break;
+ }
+ return -ENODEV;
+}
+
static int lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
@@ -1077,6 +1196,8 @@ static int lpc_ich_probe(struct pci_dev *dev,
priv->gctrl = GPIOCTRL_ICH6;
}
+ mutex_init(&priv->lock);
+
pci_set_drvdata(dev, priv);
if (lpc_chipset_info[priv->chipset].iTCO_version) {
@@ -1091,6 +1212,9 @@ static int lpc_ich_probe(struct pci_dev *dev,
cell_added = true;
}
+ if (!lpc_ich_misc(dev, priv->chipset, priv))
+ cell_added = true;
+
/*
* We only care if at least one or none of the cells registered
* successfully.
--
2.7.3
More information about the linux-yocto
mailing list