1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2013 PALO, Richard. All rights reserved.
  25  */
  26 
  27 /*
  28  *      Library file that has miscellaneous support for npe(7d)
  29  */
  30 
  31 #include <sys/conf.h>
  32 #include <sys/pci.h>
  33 #include <sys/sunndi.h>
  34 #include <acpica/include/acpi.h>
  35 #include <sys/acpica.h>
  36 #include <sys/pci_cap.h>
  37 #include <sys/pcie_impl.h>
  38 #include <sys/x86_archext.h>
  39 #include <io/pciex/pcie_nvidia.h>
  40 #include <io/pciex/pcie_nb5000.h>
  41 #include <sys/pci_cfgacc_x86.h>
  42 #include <sys/cpuvar.h>
  43 
  44 /*
  45  * Prototype declaration
  46  */
  47 void    npe_query_acpi_mcfg(dev_info_t *dip);
  48 void    npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl);
  49 int     npe_disable_empty_bridges_workaround(dev_info_t *child);
  50 void    npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl);
  51 void    npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl);
  52 boolean_t npe_is_child_pci(dev_info_t *dip);
  53 int     npe_enable_htmsi(ddi_acc_handle_t cfg_hdl);
  54 void    npe_enable_htmsi_children(dev_info_t *dip);
  55 
  56 int     npe_enable_htmsi_flag = 1;
  57 
  58 /*
  59  * Default ecfga base address
  60  */
  61 int64_t npe_default_ecfga_base = 0xE0000000;
  62 
  63 extern uint32_t npe_aer_uce_mask;
  64 
  65 /*
  66  * Query the MCFG table using ACPI.  If MCFG is found, setup the
  67  * 'ecfg' property accordingly.  Otherwise, set the values
  68  * to the default values.
  69  */
  70 void
  71 npe_query_acpi_mcfg(dev_info_t *dip)
  72 {
  73         ACPI_TABLE_HEADER *mcfgp;
  74         ACPI_MCFG_ALLOCATION *cfg_baap;
  75         char *cfg_baa_endp;
  76         int64_t ecfginfo[4];
  77         int ecfg_found = 0;
  78 
  79         /* Query the MCFG table using ACPI */
  80         if (AcpiGetTable(ACPI_SIG_MCFG, 1, &mcfgp) == AE_OK) {
  81 
  82             cfg_baap = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)mcfgp + 1);
  83             cfg_baa_endp = ((char *)mcfgp) + mcfgp->Length;
  84 
  85                 while ((char *)cfg_baap < cfg_baa_endp) {
  86                         if (cfg_baap->Address != (uint64_t)0 &&
  87                             cfg_baap->PciSegment == 0) {
  88                                 /*
  89                                  * Set up the 'ecfg' property to hold
  90                                  * base_addr, segment, and first/last bus.
  91                                  * We only do the first entry that maps
  92                                  * segment 0; nonzero segments are not yet
  93                                  * known, or handled.  If they appear,
  94                                  * we'll need to figure out which bus node
  95                                  * should have which entry by examining the
  96                                  * ACPI _SEG method on each bus node.
  97                                  */
  98                                 ecfginfo[0] = cfg_baap->Address;
  99                                 ecfginfo[1] = cfg_baap->PciSegment;
 100                                 ecfginfo[2] = cfg_baap->StartBusNumber;
 101                                 ecfginfo[3] = cfg_baap->EndBusNumber;
 102                                 (void) ndi_prop_update_int64_array(
 103                                     DDI_DEV_T_NONE, dip, "ecfg",
 104                                     ecfginfo, 4);
 105                                 ecfg_found = 1;
 106                                 break;
 107                         }
 108                         cfg_baap++;
 109                 }
 110         }
 111         if (ecfg_found)
 112                 return;
 113         /*
 114          * If MCFG is not found or ecfga_base is not found in MCFG table,
 115          * set the property to the default values.
 116          */
 117         ecfginfo[0] = npe_default_ecfga_base;
 118         ecfginfo[1] = 0;                /* segment 0 */
 119         ecfginfo[2] = 0;                /* first bus 0 */
 120         ecfginfo[3] = 0xff;             /* last bus ff */
 121         (void) ndi_prop_update_int64_array(DDI_DEV_T_NONE, dip,
 122             "ecfg", ecfginfo, 4);
 123 }
 124 
 125 
 126 /*
 127  * Enable reporting of AER capability next pointer.
 128  * This needs to be done only for CK8-04 devices
 129  * by setting NV_XVR_VEND_CYA1 (offset 0xf40) bit 13
 130  * NOTE: BIOS is disabling this, it needs to be enabled temporarily
 131  */
 132 void
 133 npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl)
 134 {
 135         ushort_t cya1;
 136 
 137         if ((pci_config_get16(cfg_hdl, PCI_CONF_VENID) == NVIDIA_VENDOR_ID) &&
 138             (pci_config_get16(cfg_hdl, PCI_CONF_DEVID) ==
 139             NVIDIA_CK804_DEVICE_ID) &&
 140             (pci_config_get8(cfg_hdl, PCI_CONF_REVID) >=
 141             NVIDIA_CK804_AER_VALID_REVID)) {
 142                 cya1 =  pci_config_get16(cfg_hdl, NVIDIA_CK804_VEND_CYA1_OFF);
 143                 if (!(cya1 & ~NVIDIA_CK804_VEND_CYA1_ERPT_MASK))
 144                         (void) pci_config_put16(cfg_hdl,
 145                             NVIDIA_CK804_VEND_CYA1_OFF,
 146                             cya1 | NVIDIA_CK804_VEND_CYA1_ERPT_VAL);
 147         }
 148 }
 149 
 150 /*
 151  * If the bridge is empty, disable it
 152  */
 153 int
 154 npe_disable_empty_bridges_workaround(dev_info_t *child)
 155 {
 156         /*
 157          * Do not bind drivers to empty bridges.
 158          * Fail above, if the bridge is found to be hotplug capable
 159          */
 160         if (ddi_driver_major(child) == ddi_name_to_major("pcieb") &&
 161             ddi_get_child(child) == NULL &&
 162             ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
 163             "pci-hotplug-type", INBAND_HPC_NONE) == INBAND_HPC_NONE)
 164                 return (1);
 165 
 166         return (0);
 167 }
 168 
 169 void
 170 npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl) {
 171         uint32_t regs;
 172         uint16_t vendor_id = pci_config_get16(cfg_hdl, PCI_CONF_VENID);
 173         uint16_t dev_id = pci_config_get16(cfg_hdl, PCI_CONF_DEVID);
 174 
 175         if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(dev_id)) {
 176                 /* Disable ECRC for all devices */
 177                 regs = pcie_get_aer_uce_mask() | npe_aer_uce_mask |
 178                     PCIE_AER_UCE_ECRC;
 179                 pcie_set_aer_uce_mask(regs);
 180 
 181                 /*
 182                  * Turn full scan on since the Error Source ID register may not
 183                  * have the correct ID.
 184                  */
 185                 pcie_force_fullscan();
 186         }
 187 }
 188 
 189 void
 190 npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl) {
 191         uint32_t regs;
 192         uint16_t vendor_id = pci_config_get16(cfg_hdl, PCI_CONF_VENID);
 193         uint16_t dev_id = pci_config_get16(cfg_hdl, PCI_CONF_DEVID);
 194 
 195         if (vendor_id == INTEL_VENDOR_ID) {
 196                 /*
 197                  * Due to an errata in Intel's ESB2 southbridge, all ECRCs
 198                  * generation/checking need to be disabled.  There is a
 199                  * workaround by setting a proprietary bit in the ESB2, but it
 200                  * is not well documented or understood.  If that bit is set in
 201                  * the future, then ECRC generation/checking should be enabled
 202                  * again.
 203                  *
 204                  * Disable ECRC generation/checking by masking ECRC in the AER
 205                  * UE Mask.  The pcie misc module would then automatically
 206                  * disable ECRC generation/checking in the AER Control register.
 207                  */
 208                 regs = pcie_get_aer_uce_mask() | PCIE_AER_UCE_ECRC;
 209                 pcie_set_aer_uce_mask(regs);
 210 
 211                 if (INTEL_NB5500_PCIE_DEV_ID(dev_id) ||
 212                     INTEL_NB5520_PCIE_DEV_ID(dev_id)) {
 213                         /*
 214                          * Turn full scan on since the Error Source ID register
 215                          * may not have the correct ID. See Intel 5520 and
 216                          * Intel 5500 Chipsets errata #34 and #54 in the August
 217                          * 2009 specification update, document number
 218                          * 321329-006.
 219                          */
 220                         pcie_force_fullscan();
 221                 }
 222         }
 223 }
 224 
 225 /*
 226  * Check's if this child is a PCI device.
 227  * Child is a PCI device if:
 228  * parent has a dev_type of "pci"
 229  * -and-
 230  * child does not have a dev_type of "pciex"
 231  *
 232  * If the parent is not of dev_type "pci", then assume it is "pciex" and all
 233  * children should support using PCIe style MMCFG access.
 234  *
 235  * If parent's dev_type is "pci" and child is "pciex", then also enable using
 236  * PCIe style MMCFG access.  This covers the case where NPE is "pci" and a PCIe
 237  * RP is beneath.
 238  */
 239 boolean_t
 240 npe_child_is_pci(dev_info_t *dip) {
 241         char *dev_type;
 242         boolean_t parent_is_pci, child_is_pciex;
 243 
 244         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(dip),
 245             DDI_PROP_DONTPASS, "device_type", &dev_type) ==
 246             DDI_PROP_SUCCESS) {
 247                 parent_is_pci = (strcmp(dev_type, "pci") == 0);
 248                 ddi_prop_free(dev_type);
 249         } else {
 250                 parent_is_pci = B_FALSE;
 251         }
 252 
 253         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 254             "device_type", &dev_type) == DDI_PROP_SUCCESS) {
 255                 child_is_pciex = (strcmp(dev_type, "pciex") == 0);
 256                 ddi_prop_free(dev_type);
 257         } else {
 258                 child_is_pciex = B_FALSE;
 259         }
 260 
 261         return (parent_is_pci && !child_is_pciex);
 262 }
 263 
 264 /*
 265  * Checks to see if MMCFG is supported.
 266  * Returns: TRUE if MMCFG is supported, FALSE if not.
 267  *
 268  * If a device is attached to a parent whose "dev_type" is "pciex",
 269  * the device will support MMCFG access.  Otherwise, use legacy IOCFG access.
 270  *
 271  * Enable Legacy PCI config space access for AMD K8 north bridges.
 272  *      Host bridge: AMD HyperTransport Technology Configuration
 273  *      Host bridge: AMD Address Map
 274  *      Host bridge: AMD DRAM Controller
 275  *      Host bridge: AMD Miscellaneous Control
 276  * These devices do not support MMCFG access.
 277  */
 278 boolean_t
 279 npe_is_mmcfg_supported(dev_info_t *dip)
 280 {
 281         int vendor_id, device_id;
 282 
 283         vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 284             "vendor-id", -1);
 285         device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 286             "device-id", -1);
 287 
 288         return !(npe_child_is_pci(dip) ||
 289             IS_BAD_AMD_NTBRIDGE(vendor_id, device_id));
 290 }
 291 
 292 int
 293 npe_enable_htmsi(ddi_acc_handle_t cfg_hdl)
 294 {
 295         uint16_t ptr;
 296         uint16_t reg;
 297 
 298         if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
 299             PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
 300                 return (DDI_FAILURE);
 301 
 302         reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
 303         reg |= PCI_HTCAP_MSIMAP_ENABLE;
 304 
 305         pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
 306         return (DDI_SUCCESS);
 307 }
 308 
 309 void
 310 npe_enable_htmsi_children(dev_info_t *dip)
 311 {
 312         dev_info_t *cdip = ddi_get_child(dip);
 313         ddi_acc_handle_t cfg_hdl;
 314 
 315         if (!npe_enable_htmsi_flag)
 316                 return;
 317 
 318         /*
 319          * Hypertransport MSI remapping only applies to AMD CPUs using
 320          * Hypertransport (K8 and above) and not other platforms with non-AMD
 321          * CPUs that may be using Hypertransport internally in the chipset(s)
 322          */
 323         if (!(cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
 324             cpuid_getfamily(CPU) >= 0xf))
 325                 return;
 326 
 327         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 328                 if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS) {
 329                         cmn_err(CE_NOTE, "!npe_enable_htmsi_children: "
 330                             "pci_config_setup failed for %s",
 331                             ddi_node_name(cdip));
 332                         return;
 333                 }
 334 
 335                 (void) npe_enable_htmsi(cfg_hdl);
 336                 pci_config_teardown(&cfg_hdl);
 337         }
 338 }
 339 
 340 /*
 341  * save config regs for HyperTransport devices without drivers of classes:
 342  * memory controller and hostbridge
 343  */
 344 int
 345 npe_save_htconfig_children(dev_info_t *dip)
 346 {
 347         dev_info_t *cdip = ddi_get_child(dip);
 348         ddi_acc_handle_t cfg_hdl;
 349         uint16_t ptr;
 350         int rval = DDI_SUCCESS;
 351         uint8_t cl, scl;
 352 
 353         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 354                 if (ddi_driver_major(cdip) != DDI_MAJOR_T_NONE)
 355                         continue;
 356 
 357                 if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS)
 358                         return (DDI_FAILURE);
 359 
 360                 cl = pci_config_get8(cfg_hdl, PCI_CONF_BASCLASS);
 361                 scl = pci_config_get8(cfg_hdl, PCI_CONF_SUBCLASS);
 362 
 363                 if (((cl == PCI_CLASS_MEM && scl == PCI_MEM_RAM) ||
 364                     (cl == PCI_CLASS_BRIDGE && scl == PCI_BRIDGE_HOST)) &&
 365                     pci_htcap_locate(cfg_hdl, 0, 0, &ptr) == DDI_SUCCESS) {
 366 
 367                         if (pci_save_config_regs(cdip) != DDI_SUCCESS) {
 368                                 cmn_err(CE_WARN, "Failed to save HT config "
 369                                     "regs for %s\n", ddi_node_name(cdip));
 370                                 rval = DDI_FAILURE;
 371 
 372                         } else if (ddi_prop_update_int(DDI_DEV_T_NONE, cdip,
 373                             "htconfig-saved", 1) != DDI_SUCCESS) {
 374                                 cmn_err(CE_WARN, "Failed to set htconfig-saved "
 375                                     "property for %s\n", ddi_node_name(cdip));
 376                                 rval = DDI_FAILURE;
 377                         }
 378                 }
 379 
 380                 pci_config_teardown(&cfg_hdl);
 381         }
 382 
 383         return (rval);
 384 }
 385 
 386 int
 387 npe_restore_htconfig_children(dev_info_t *dip)
 388 {
 389         dev_info_t *cdip = ddi_get_child(dip);
 390         int rval = DDI_SUCCESS;
 391 
 392         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 393                 if (ddi_prop_get_int(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
 394                     "htconfig-saved", 0) == 0)
 395                         continue;
 396 
 397                 if (pci_restore_config_regs(cdip) != DDI_SUCCESS) {
 398                         cmn_err(CE_WARN, "Failed to restore HT config "
 399                             "regs for %s\n", ddi_node_name(cdip));
 400                         rval = DDI_FAILURE;
 401                 }
 402         }
 403 
 404         return (rval);
 405 }