Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 6 Nov 2018 17:31:09 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r340190 - stable/12/sys/dev/ichwd
Message-ID:  <201811061731.wA6HV9NE065619@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Tue Nov  6 17:31:09 2018
New Revision: 340190
URL: https://svnweb.freebsd.org/changeset/base/340190

Log:
  MFC r339591: ichwd: add support for TCO watchdog timer in Lewisburg PCH (C620)
  
  PR:		222079
  Approved by:	re (rgrimes)
  Relnotes:	maybe
  Sponsored by:	Panzura

Modified:
  stable/12/sys/dev/ichwd/ichwd.c
  stable/12/sys/dev/ichwd/ichwd.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/dev/ichwd/ichwd.c
==============================================================================
--- stable/12/sys/dev/ichwd/ichwd.c	Tue Nov  6 16:56:49 2018	(r340189)
+++ stable/12/sys/dev/ichwd/ichwd.c	Tue Nov  6 17:31:09 2018	(r340190)
@@ -289,6 +289,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) \
@@ -374,7 +379,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);
 }
 
 /*
@@ -488,6 +494,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",
@@ -560,6 +571,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.
@@ -568,14 +609,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)
@@ -609,6 +654,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",
@@ -626,7 +689,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);
@@ -634,21 +698,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;
@@ -657,7 +771,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 */
@@ -667,7 +781,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;
@@ -676,7 +790,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;
@@ -685,10 +799,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;
 
@@ -724,7 +852,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/12/sys/dev/ichwd/ichwd.h
==============================================================================
--- stable/12/sys/dev/ichwd/ichwd.h	Tue Nov  6 16:56:49 2018	(r340189)
+++ stable/12/sys/dev/ichwd/ichwd.h	Tue Nov  6 17:31:09 2018	(r340190)
@@ -272,6 +272,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
@@ -290,6 +291,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 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811061731.wA6HV9NE065619>