svn commit: r340182 - stable/11/sys/dev/ichwd
Andriy Gapon
avg at FreeBSD.org
Tue Nov 6 13:54:26 UTC 2018
Author: avg
Date: Tue Nov 6 13:54:24 2018
New Revision: 340182
URL: https://svnweb.freebsd.org/changeset/base/340182
Log:
MFC r339591: ichwd: add support for TCO watchdog timer in Lewisburg PCH (C620)
PR: 222079
Relnotes: maybe
Sponsored by: Panzura
Modified:
stable/11/sys/dev/ichwd/ichwd.c
stable/11/sys/dev/ichwd/ichwd.h
Directory Properties:
stable/11/ (props changed)
Modified: stable/11/sys/dev/ichwd/ichwd.c
==============================================================================
--- stable/11/sys/dev/ichwd/ichwd.c Tue Nov 6 13:51:08 2018 (r340181)
+++ stable/11/sys/dev/ichwd/ichwd.c Tue Nov 6 13:54:24 2018 (r340182)
@@ -287,6 +287,11 @@ static struct ichwd_device ichwd_devices[] = {
{ 0, NULL, 0, 0 },
};
+static struct ichwd_device ichwd_smb_devices[] = {
+ { DEVICEID_LEWISBURG_SMB, "Lewisburg watchdog timer", 10, 4 },
+ { 0, NULL, 0, 0 },
+};
+
static devclass_t ichwd_devclass;
#define ichwd_read_tco_1(sc, off) \
@@ -372,7 +377,8 @@ ichwd_sts_reset(struct ichwd_softc *sc)
* be done in two separate operations.
*/
ichwd_write_tco_2(sc, TCO2_STS, TCO_SECOND_TO_STS);
- ichwd_write_tco_2(sc, TCO2_STS, TCO_BOOT_STS);
+ if (sc->tco_version < 4)
+ ichwd_write_tco_2(sc, TCO2_STS, TCO_BOOT_STS);
}
/*
@@ -486,6 +492,11 @@ ichwd_clear_noreboot(struct ichwd_softc *sc)
if (status & ICH_PMC_NO_REBOOT)
rc = EIO;
break;
+ case 4:
+ /*
+ * TODO. This needs access to a hidden PCI device at 31:1.
+ */
+ break;
default:
ichwd_verbose_printf(sc->device,
"Unknown TCO Version: %d, can't set NO_REBOOT.\n",
@@ -558,6 +569,36 @@ ichwd_find_ich_lpc_bridge(device_t isa, struct ichwd_d
return (NULL);
}
+static device_t
+ichwd_find_smb_dev(device_t isa, struct ichwd_device **id_p)
+{
+ struct ichwd_device *id;
+ device_t isab, smb;
+ uint16_t devid;
+
+ /*
+ * Check if SMBus controller provides TCO configuration.
+ * The controller's device and function are fixed and we expect
+ * it to be on the same bus as ISA bridge.
+ */
+ isab = device_get_parent(isa);
+ smb = pci_find_dbsf(pci_get_domain(isab), pci_get_bus(isab), 31, 4);
+ if (smb == NULL)
+ return (NULL);
+ if (pci_get_vendor(smb) != VENDORID_INTEL)
+ return (NULL);
+ devid = pci_get_device(smb);
+ for (id = ichwd_smb_devices; id->desc != NULL; ++id) {
+ if (devid == id->device) {
+ if (id_p != NULL)
+ *id_p = id;
+ return (smb);
+ }
+ }
+
+ return (NULL);
+}
+
/*
* Look for an ICH LPC interface bridge. If one is found, register an
* ichwd device. There can be only one.
@@ -566,14 +607,18 @@ static void
ichwd_identify(driver_t *driver, device_t parent)
{
struct ichwd_device *id_p;
- device_t ich = NULL;
+ device_t ich, smb;
device_t dev;
uint32_t base_address;
+ uint32_t ctl;
int rc;
ich = ichwd_find_ich_lpc_bridge(parent, &id_p);
- if (ich == NULL)
- return;
+ if (ich == NULL) {
+ smb = ichwd_find_smb_dev(parent, &id_p);
+ if (smb == NULL)
+ return;
+ }
/* good, add child to bus */
if ((dev = device_find_child(parent, driver->name, 0)) == NULL)
@@ -607,6 +652,24 @@ ichwd_identify(driver_t *driver, device_t parent)
"Can not set TCO v%d memory resource for PBASE\n",
id_p->tco_version);
break;
+ case 4:
+ /* Get TCO base address. */
+ ctl = pci_read_config(smb, ICH_TCOCTL, 4);
+ if ((ctl & ICH_TCOCTL_TCO_BASE_EN) == 0) {
+ ichwd_verbose_printf(dev,
+ "TCO v%d decoding is not enabled\n",
+ id_p->tco_version);
+ break;
+ }
+ base_address = pci_read_config(smb, ICH_TCOBASE, 4);
+ rc = bus_set_resource(dev, SYS_RES_IOPORT, 0,
+ base_address & ICH_TCOBASE_ADDRMASK, ICH_TCOBASE_SIZE);
+ if (rc != 0) {
+ ichwd_verbose_printf(dev,
+ "Can not set TCO v%d I/O resource (err = %d)\n",
+ id_p->tco_version, rc);
+ }
+ break;
default:
ichwd_verbose_printf(dev,
"Can not set unknown TCO v%d memory resource for unknown base address\n",
@@ -624,7 +687,8 @@ ichwd_probe(device_t dev)
if (isa_get_logicalid(dev) != 0)
return (ENXIO);
- if (ichwd_find_ich_lpc_bridge(device_get_parent(dev), &id_p) == NULL)
+ if (ichwd_find_ich_lpc_bridge(device_get_parent(dev), &id_p) == NULL &&
+ ichwd_find_smb_dev(device_get_parent(dev), &id_p) == NULL)
return (ENXIO);
device_set_desc_copy(dev, id_p->desc);
@@ -632,21 +696,71 @@ ichwd_probe(device_t dev)
}
static int
-ichwd_attach(device_t dev)
+ichwd_smb_attach(device_t dev)
{
struct ichwd_softc *sc;
struct ichwd_device *id_p;
+ device_t isab, pmdev;
+ device_t smb;
+ uint32_t acpi_base;
+
+ sc = device_get_softc(dev);
+ smb = ichwd_find_smb_dev(device_get_parent(dev), &id_p);
+ if (smb == NULL)
+ return (ENXIO);
+
+ sc->ich_version = id_p->ich_version;
+ sc->tco_version = id_p->tco_version;
+
+ /* Allocate TCO control I/O register space. */
+ sc->tco_rid = 0;
+ sc->tco_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->tco_rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->tco_res == NULL) {
+ device_printf(dev, "unable to reserve TCO registers\n");
+ return (ENXIO);
+ }
+
+ /* Get ACPI base address. */
+ isab = device_get_parent(device_get_parent(dev));
+ pmdev = pci_find_dbsf(pci_get_domain(isab), pci_get_bus(isab), 31, 2);
+ if (pmdev == NULL) {
+ device_printf(dev, "unable to find Power Management device\n");
+ return (ENXIO);
+ }
+ acpi_base = pci_read_config(pmdev, ICH_PMBASE, 4) & 0xffffff00;
+ if (acpi_base == 0) {
+ device_printf(dev, "ACPI base address is not set\n");
+ return (ENXIO);
+ }
+
+ /* Allocate SMI control I/O register space. */
+ sc->smi_rid = 1;
+ sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid,
+ acpi_base + SMI_BASE, acpi_base + SMI_BASE + SMI_LEN - 1, SMI_LEN,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->smi_res == NULL) {
+ device_printf(dev, "unable to reserve SMI registers\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+ichwd_lpc_attach(device_t dev)
+{
+ struct ichwd_softc *sc;
+ struct ichwd_device *id_p;
device_t ich;
unsigned int pmbase = 0;
sc = device_get_softc(dev);
- sc->device = dev;
ich = ichwd_find_ich_lpc_bridge(device_get_parent(dev), &id_p);
- if (ich == NULL) {
- device_printf(sc->device, "Can not find ICH device.\n");
- goto fail;
- }
+ if (ich == NULL)
+ return (ENXIO);
+
sc->ich = ich;
sc->ich_version = id_p->ich_version;
sc->tco_version = id_p->tco_version;
@@ -655,7 +769,7 @@ ichwd_attach(device_t dev)
pmbase = pci_read_config(ich, ICH_PMBASE, 2) & ICH_PMBASE_MASK;
if (pmbase == 0) {
device_printf(dev, "ICH PMBASE register is empty\n");
- goto fail;
+ return (ENXIO);
}
/* allocate I/O register space */
@@ -665,7 +779,7 @@ ichwd_attach(device_t dev)
RF_ACTIVE | RF_SHAREABLE);
if (sc->smi_res == NULL) {
device_printf(dev, "unable to reserve SMI registers\n");
- goto fail;
+ return (ENXIO);
}
sc->tco_rid = 1;
@@ -674,7 +788,7 @@ ichwd_attach(device_t dev)
RF_ACTIVE | RF_SHAREABLE);
if (sc->tco_res == NULL) {
device_printf(dev, "unable to reserve TCO registers\n");
- goto fail;
+ return (ENXIO);
}
sc->gcs_rid = 0;
@@ -683,10 +797,24 @@ ichwd_attach(device_t dev)
&sc->gcs_rid, RF_ACTIVE|RF_SHAREABLE);
if (sc->gcs_res == NULL) {
device_printf(dev, "unable to reserve GCS registers\n");
- goto fail;
+ return (ENXIO);
}
}
+ return (0);
+}
+
+static int
+ichwd_attach(device_t dev)
+{
+ struct ichwd_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->device = dev;
+
+ if (ichwd_lpc_attach(dev) != 0 && ichwd_smb_attach(dev) != 0)
+ goto fail;
+
if (ichwd_clear_noreboot(sc) != 0)
goto fail;
@@ -722,7 +850,7 @@ ichwd_attach(device_t dev)
bus_release_resource(dev, SYS_RES_IOPORT,
sc->smi_rid, sc->smi_res);
if (sc->gcs_res != NULL)
- bus_release_resource(ich, SYS_RES_MEMORY,
+ bus_release_resource(sc->ich, SYS_RES_MEMORY,
sc->gcs_rid, sc->gcs_res);
return (ENXIO);
Modified: stable/11/sys/dev/ichwd/ichwd.h
==============================================================================
--- stable/11/sys/dev/ichwd/ichwd.h Tue Nov 6 13:51:08 2018 (r340181)
+++ stable/11/sys/dev/ichwd/ichwd.h Tue Nov 6 13:54:24 2018 (r340182)
@@ -270,6 +270,7 @@ struct ichwd_softc {
#define DEVICEID_WCPT_LP6 0x9cc6
#define DEVICEID_WCPT_LP7 0x9cc7
#define DEVICEID_WCPT_LP9 0x9cc9
+#define DEVICEID_LEWISBURG_SMB 0xa1a3
/* ICH LPC Interface Bridge Registers (ICH5 and older) */
#define ICH_GEN_STA 0xd4
@@ -288,6 +289,14 @@ struct ichwd_softc {
#define ICH_PMC_OFFSET 0x08
#define ICH_PMC_SIZE 0x4
#define ICH_PMC_NO_REBOOT 0x10
+
+/* Lewisburg configration registers in SMBus controller. */
+#define ICH_TCOBASE 0x50 /* TCO Base Addr */
+#define ICH_TCOBASE_ADDRMASK 0xffe0
+#define ICH_TCOBASE_SIZE 32
+#define ICH_TCOCTL 0x54 /* TCO Control */
+#define ICH_TCOCTL_TCO_BASE_EN 0x0100 /* TCO Base decoding enabled */
+#define ICH_TCOCTL_TCO_BASE_LOCK 0x0001 /* TCOBASE is locked */
/* register names and locations (relative to PMBASE) */
#define SMI_BASE 0x30 /* base address for SMI registers */
More information about the svn-src-stable
mailing list