From: Jonathan Yong <jonathan.y...@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.y...@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

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to