Skip to content

Commit

Permalink
i2c: i801: Create iTCO device on newer Intel PCHs
Browse files Browse the repository at this point in the history
Starting from Intel Sunrisepoint (Skylake PCH) the iTCO watchdog resources
have been moved to reside under the i801 SMBus host controller whereas
previously they were under the LPC device.

In order to support the iTCO watchdog on newer PCHs we need to create the
platform device here in the SMBus driver and pass all known resources using
platform data.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
  • Loading branch information
westeri authored and Lee Jones committed Aug 11, 2015
1 parent 420b54d commit 9424693
Showing 1 changed file with 119 additions and 1 deletion.
120 changes: 119 additions & 1 deletion drivers/i2c/busses/i2c-i801.c
Expand Up @@ -88,12 +88,13 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>

#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
defined CONFIG_DMI
#include <linux/gpio.h>
#include <linux/i2c-mux-gpio.h>
#include <linux/platform_device.h>
#endif

/* I801 SMBus address offsets */
Expand All @@ -113,6 +114,16 @@
#define SMBPCICTL 0x004
#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
#define TCOBASE 0x050
#define TCOCTL 0x054

#define ACPIBASE 0x040
#define ACPIBASE_SMI_OFF 0x030
#define ACPICTRL 0x044
#define ACPICTRL_EN 0x080

#define SBREG_BAR 0x10
#define SBREG_SMBCTRL 0xc6000c

/* Host status bits for SMBPCISTS */
#define SMBPCISTS_INTS 0x08
Expand All @@ -125,6 +136,9 @@
#define SMBHSTCFG_SMB_SMI_EN 2
#define SMBHSTCFG_I2C_EN 4

/* TCO configuration bits for TCOCTL */
#define TCOCTL_EN 0x0100

/* Auxiliary control register bits, ICH4+ only */
#define SMBAUXCTL_CRC 1
#define SMBAUXCTL_E32B 2
Expand Down Expand Up @@ -221,6 +235,7 @@ struct i801_priv {
const struct i801_mux_config *mux_drvdata;
struct platform_device *mux_pdev;
#endif
struct platform_device *tco_pdev;
};

#define FEATURE_SMBUS_PEC (1 << 0)
Expand All @@ -230,6 +245,7 @@ struct i801_priv {
#define FEATURE_IRQ (1 << 4)
/* Not really a feature, but it's convenient to handle it as such */
#define FEATURE_IDF (1 << 15)
#define FEATURE_TCO (1 << 16)

static const char *i801_feature_names[] = {
"SMBus PEC",
Expand Down Expand Up @@ -1132,6 +1148,95 @@ static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
}
#endif

static const struct itco_wdt_platform_data tco_platform_data = {
.name = "Intel PCH",
.version = 4,
};

static DEFINE_SPINLOCK(p2sb_spinlock);

static void i801_add_tco(struct i801_priv *priv)
{
struct pci_dev *pci_dev = priv->pci_dev;
struct resource tco_res[3], *res;
struct platform_device *pdev;
unsigned int devfn;
u32 tco_base, tco_ctl;
u32 base_addr, ctrl_val;
u64 base64_addr;

if (!(priv->features & FEATURE_TCO))
return;

pci_read_config_dword(pci_dev, TCOBASE, &tco_base);
pci_read_config_dword(pci_dev, TCOCTL, &tco_ctl);
if (!(tco_ctl & TCOCTL_EN))
return;

memset(tco_res, 0, sizeof(tco_res));

res = &tco_res[ICH_RES_IO_TCO];
res->start = tco_base & ~1;
res->end = res->start + 32 - 1;
res->flags = IORESOURCE_IO;

/*
* Power Management registers.
*/
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 2);
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPIBASE, &base_addr);

res = &tco_res[ICH_RES_IO_SMI];
res->start = (base_addr & ~1) + ACPIBASE_SMI_OFF;
res->end = res->start + 3;
res->flags = IORESOURCE_IO;

/*
* Enable the ACPI I/O space.
*/
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPICTRL, &ctrl_val);
ctrl_val |= ACPICTRL_EN;
pci_bus_write_config_dword(pci_dev->bus, devfn, ACPICTRL, ctrl_val);

/*
* We must access the NO_REBOOT bit over the Primary to Sideband
* bridge (P2SB). The BIOS prevents the P2SB device from being
* enumerated by the PCI subsystem, so we need to unhide/hide it
* to lookup the P2SB BAR.
*/
spin_lock(&p2sb_spinlock);

devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);

/* Unhide the P2SB device */
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);

pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
base64_addr = base_addr & 0xfffffff0;

pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
base64_addr |= (u64)base_addr << 32;

/* Hide the P2SB device */
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x1);
spin_unlock(&p2sb_spinlock);

res = &tco_res[ICH_RES_MEM_OFF];
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
res->end = res->start + 3;
res->flags = IORESOURCE_MEM;

pdev = platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
tco_res, 3, &tco_platform_data,
sizeof(tco_platform_data));
if (IS_ERR(pdev)) {
dev_warn(&pci_dev->dev, "failed to create iTCO device\n");
return;
}

priv->tco_pdev = pdev;
}

static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned char temp;
Expand All @@ -1149,6 +1254,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)

priv->pci_dev = dev;
switch (dev->device) {
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
priv->features |= FEATURE_I2C_BLOCK_READ;
priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC;
priv->features |= FEATURE_BLOCK_BUFFER;
priv->features |= FEATURE_TCO;
break;

case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
Expand Down Expand Up @@ -1265,6 +1379,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_info(&dev->dev, "SMBus using %s\n",
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");

i801_add_tco(priv);

/* set up the sysfs linkage to our parent device */
priv->adapter.dev.parent = &dev->dev;

Expand Down Expand Up @@ -1296,6 +1412,8 @@ static void i801_remove(struct pci_dev *dev)
i2c_del_adapter(&priv->adapter);
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);

platform_device_unregister(priv->tco_pdev);

/*
* do not call pci_disable_device(dev) since it can cause hard hangs on
* some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
Expand Down

0 comments on commit 9424693

Please sign in to comment.