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 
*/
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to