mfd: lpc_ich: Add support for iTCO v3
authorPeter Tyser <ptyser@xes-inc.com>
Mon, 10 Mar 2014 21:34:54 +0000 (16:34 -0500)
committerLee Jones <lee.jones@linaro.org>
Wed, 19 Mar 2014 09:00:02 +0000 (09:00 +0000)
Some newer Atom CPUs, eg Avoton and Bay Trail, use slightly different
register layouts for the iTCO than the current v1 and v2 iTCO.
Differences from previous iTCO versions include:
- The ACPI space is enabled in the "ACPI base address" register instead
  of the "ACPI control register"

- The "no reboot" functionality is set in the "Power Management
  Configuration" register instead of the "General Control and Status"
  (GCS) register or PCI configuration space.

- The "ACPI Control Register" is not present on v3.  The "Power
  Management Configuration Base Address" register resides at the same
  address is Avoton/Bay Trail.

To differentiate these newer chipsets create a new v3 iTCO version and
update the MFD driver to support them.

Signed-off-by: Peter Tyser <ptyser@xes-inc.com>
Tested-by: Rajat Jain <rajatjain@juniper.net>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
drivers/mfd/lpc_ich.c
include/linux/mfd/lpc_ich.h

index b24bae2bcdea777406595cd29f6b0216f7c4027c..c0683370abbfafd1ed4c2ac414ec6b7711d3d8fe 100644 (file)
 #define ACPIBASE_GPE_END       0x2f
 #define ACPIBASE_SMI_OFF       0x30
 #define ACPIBASE_SMI_END       0x33
+#define ACPIBASE_PMC_OFF       0x08
+#define ACPIBASE_PMC_END       0x0c
 #define ACPIBASE_TCO_OFF       0x60
 #define ACPIBASE_TCO_END       0x7f
-#define ACPICTRL               0x44
+#define ACPICTRL_PMCBASE       0x44
 
 #define ACPIBASE_GCS_OFF       0x3410
 #define ACPIBASE_GCS_END       0x3414
@@ -93,11 +95,12 @@ struct lpc_ich_priv {
        int chipset;
 
        int abase;              /* ACPI base */
-       int actrl;              /* ACPI control or PMC base */
+       int actrl_pbase;        /* ACPI control or PMC base */
        int gbase;              /* GPIO base */
        int gctrl;              /* GPIO control */
 
-       int actrl_save;         /* Cached ACPI control base value */
+       int abase_save;         /* Cached ACPI base value */
+       int actrl_pbase_save;           /* Cached ACPI control or PMC base value */
        int gctrl_save;         /* Cached GPIO control value */
 };
 
@@ -110,7 +113,7 @@ static struct resource wdt_ich_res[] = {
        {
                .flags = IORESOURCE_IO,
        },
-       /* GCS */
+       /* GCS or PMC */
        {
                .flags = IORESOURCE_MEM,
        },
@@ -742,9 +745,15 @@ static void lpc_ich_restore_config_space(struct pci_dev *dev)
 {
        struct lpc_ich_priv *priv = pci_get_drvdata(dev);
 
-       if (priv->actrl_save >= 0) {
-               pci_write_config_byte(dev, priv->actrl, priv->actrl_save);
-               priv->actrl_save = -1;
+       if (priv->abase_save >= 0) {
+               pci_write_config_byte(dev, priv->abase, priv->abase_save);
+               priv->abase_save = -1;
+       }
+
+       if (priv->actrl_pbase_save >= 0) {
+               pci_write_config_byte(dev, priv->actrl_pbase,
+                       priv->actrl_pbase_save);
+               priv->actrl_pbase_save = -1;
        }
 
        if (priv->gctrl_save >= 0) {
@@ -758,9 +767,26 @@ static void lpc_ich_enable_acpi_space(struct pci_dev *dev)
        struct lpc_ich_priv *priv = pci_get_drvdata(dev);
        u8 reg_save;
 
-       pci_read_config_byte(dev, priv->actrl, &reg_save);
-       pci_write_config_byte(dev, priv->actrl, reg_save | 0x80);
-       priv->actrl_save = reg_save;
+       switch (lpc_chipset_info[priv->chipset].iTCO_version) {
+       case 3:
+               /*
+                * Some chipsets (eg Avoton) enable the ACPI space in the
+                * ACPI BASE register.
+                */
+               pci_read_config_byte(dev, priv->abase, &reg_save);
+               pci_write_config_byte(dev, priv->abase, reg_save | 0x2);
+               priv->abase_save = reg_save;
+               break;
+       default:
+               /*
+                * Most chipsets enable the ACPI space in the ACPI control
+                * register.
+                */
+               pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+               pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x80);
+               priv->actrl_pbase_save = reg_save;
+               break;
+       }
 }
 
 static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
@@ -773,6 +799,17 @@ static void lpc_ich_enable_gpio_space(struct pci_dev *dev)
        priv->gctrl_save = reg_save;
 }
 
+static void lpc_ich_enable_pmc_space(struct pci_dev *dev)
+{
+       struct lpc_ich_priv *priv = pci_get_drvdata(dev);
+       u8 reg_save;
+
+       pci_read_config_byte(dev, priv->actrl_pbase, &reg_save);
+       pci_write_config_byte(dev, priv->actrl_pbase, reg_save | 0x2);
+
+       priv->actrl_pbase_save = reg_save;
+}
+
 static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
 {
        struct lpc_ich_priv *priv = pci_get_drvdata(dev);
@@ -910,14 +947,20 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
        lpc_ich_enable_acpi_space(dev);
 
        /*
+        * iTCO v2:
         * Get the Memory-Mapped GCS register. To get access to it
         * we have to read RCBA from PCI Config space 0xf0 and use
         * it as base. GCS = RCBA + ICH6_GCS(0x3410).
+        *
+        * iTCO v3:
+        * Get the Power Management Configuration register.  To get access
+        * to it we have to read the PMC BASE from config space and address
+        * the register at offset 0x8.
         */
        if (lpc_chipset_info[priv->chipset].iTCO_version == 1) {
                /* Don't register iomem for TCO ver 1 */
                lpc_ich_cells[LPC_WDT].num_resources--;
-       } else {
+       } else if (lpc_chipset_info[priv->chipset].iTCO_version == 2) {
                pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
                base_addr = base_addr_cfg & 0xffffc000;
                if (!(base_addr_cfg & 1)) {
@@ -926,9 +969,17 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
                        ret = -ENODEV;
                        goto wdt_done;
                }
-               res = wdt_mem_res(ICH_RES_MEM_GCS);
+               res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
                res->start = base_addr + ACPIBASE_GCS_OFF;
                res->end = base_addr + ACPIBASE_GCS_END;
+       } else if (lpc_chipset_info[priv->chipset].iTCO_version == 3) {
+               lpc_ich_enable_pmc_space(dev);
+               pci_read_config_dword(dev, ACPICTRL_PMCBASE, &base_addr_cfg);
+               base_addr = base_addr_cfg & 0xfffffe00;
+
+               res = wdt_mem_res(ICH_RES_MEM_GCS_PMC);
+               res->start = base_addr + ACPIBASE_PMC_OFF;
+               res->end = base_addr + ACPIBASE_PMC_END;
        }
 
        lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
@@ -953,9 +1004,11 @@ static int lpc_ich_probe(struct pci_dev *dev,
 
        priv->chipset = id->driver_data;
 
-       priv->actrl_save = -1;
+       priv->actrl_pbase_save = -1;
+       priv->abase_save = -1;
+
        priv->abase = ACPIBASE;
-       priv->actrl = ACPICTRL;
+       priv->actrl_pbase = ACPICTRL_PMCBASE;
 
        priv->gctrl_save = -1;
        if (priv->chipset <= LPC_ICH5) {
index b2364dd026a69b87ef6d25bc392947962ae08fde..8feac782fa835e8a78c6b6b6ae65e5e4da6602eb 100644 (file)
 #define LPC_ICH_H
 
 /* Watchdog resources */
-#define ICH_RES_IO_TCO 0
-#define ICH_RES_IO_SMI 1
-#define ICH_RES_MEM_OFF        2
-#define ICH_RES_MEM_GCS        0
+#define ICH_RES_IO_TCO         0
+#define ICH_RES_IO_SMI         1
+#define ICH_RES_MEM_OFF                2
+#define ICH_RES_MEM_GCS_PMC    0
 
 /* GPIO resources */
 #define ICH_RES_GPIO   0