blob: 58e1220d230484518d294b01345b325cb2561eda [file] [log] [blame]
From ltsi-dev-bounces@lists.linuxfoundation.org Wed Nov 5 10:39:57 2014
From: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
Date: Wed, 5 Nov 2014 09:39:12 +0800
Subject: [LTSI-dev] [PATCH 07/16] mfd: lpc_ich: Add support for iTCO v3
To: LTSI Mailing List <ltsi-dev@lists.linuxfoundation.org>
Cc: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
Message-ID: <1415151561-16047-8-git-send-email-rebecca.swee.fun.chang@intel.com>
From: Peter Tyser <ptyser@xes-inc.com>
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>
(cherry picked from commit eb71d4dec4a5e010e34b9d7afdb5af41884c388e)
Signed-off-by: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
---
drivers/mfd/lpc_ich.c | 81 ++++++++++++++++++++++++++++++++++++--------
include/linux/mfd/lpc_ich.h | 8 ++--
2 files changed, 71 insertions(+), 18 deletions(-)
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -71,9 +71,11 @@
#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 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(st
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(st
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_d
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_d
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
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) {
--- a/include/linux/mfd/lpc_ich.h
+++ b/include/linux/mfd/lpc_ich.h
@@ -21,10 +21,10 @@
#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