@@ -1,7 +1,11 @@
/*
* Common ACPI functions for hot plug platforms
*
- * Copyright (C) 2006 Intel Corporation
+ * Copyright (C) 2003-2004, 2006 Intel Corporation
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001 IBM Corp.
+ * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
*
* All rights reserved.
*
@@ -21,7 +25,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <kristen.c.accardi@intel.com>
- *
*/
#include <linux/module.h>
@@ -364,6 +367,166 @@ int acpi_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
}
EXPORT_SYMBOL_GPL(acpi_get_hp_params);
+static struct hpp_type0 pci_default_type0 = {
+ .revision = 1,
+ .cache_line_size = 8,
+ .latency_timer = 0x40,
+ .enable_serr = 0,
+ .enable_perr = 0,
+};
+
+static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
+{
+ u16 pci_cmd, pci_bctl;
+
+ if (hpp->revision > 1) {
+ dev_warn(&dev->dev,
+ "PCI setting record rev %d not supported\n",
+ hpp->revision);
+ return;
+ }
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer);
+ pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
+ if (hpp->enable_serr)
+ pci_cmd |= PCI_COMMAND_SERR;
+ else
+ pci_cmd &= ~PCI_COMMAND_SERR;
+ if (hpp->enable_perr)
+ pci_cmd |= PCI_COMMAND_PARITY;
+ else
+ pci_cmd &= ~PCI_COMMAND_PARITY;
+ pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
+
+ /* Program bridge control value */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER,
+ hpp->latency_timer);
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl);
+ if (hpp->enable_serr)
+ pci_bctl |= PCI_BRIDGE_CTL_SERR;
+ else
+ pci_bctl &= ~PCI_BRIDGE_CTL_SERR;
+ if (hpp->enable_perr)
+ pci_bctl |= PCI_BRIDGE_CTL_PARITY;
+ else
+ pci_bctl &= ~PCI_BRIDGE_CTL_PARITY;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl);
+ }
+}
+
+static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
+{
+ int pos;
+ u16 reg16;
+ u32 reg32;
+
+ /* Find PCI Express capability */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return;
+
+ if (hpp->revision > 1) {
+ dev_warn(&dev->dev,
+ "PCI Express setting record rev %d not supported\n",
+ hpp->revision);
+ return;
+ }
+
+ /* Initialize Device Control Register */
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16);
+ reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or;
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
+
+ /* Initialize Link Control Register */
+ if (dev->subordinate) {
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, ®16);
+ reg16 = (reg16 & hpp->pci_exp_lnkctl_and)
+ | hpp->pci_exp_lnkctl_or;
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16);
+ }
+
+ /* Find Advanced Error Reporting Enhanced Capability */
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+
+ /* Initialize Uncorrectable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32);
+ reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32);
+
+ /* Initialize Uncorrectable Error Severity Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32);
+ reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32);
+
+ /* Initialize Correctable Error Mask Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32);
+ reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32);
+
+ /* Initialize Advanced Error Capabilities and Control Register */
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
+ reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or;
+ pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32);
+
+ /*
+ * FIXME: The following two registers are not supported yet.
+ *
+ * o Secondary Uncorrectable Error Severity Register
+ * o Secondary Uncorrectable Error Mask Register
+ */
+}
+
+void pci_configure_slot(struct pci_dev *dev)
+{
+ struct pci_dev *cdev;
+ struct hotplug_params hpp;
+ struct hpp_type0 *type0 = NULL;
+ int ret;
+
+ if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL ||
+ (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
+ return;
+
+ memset(&hpp, 0, sizeof(hpp));
+ ret = acpi_get_hp_params(dev, &hpp);
+ if (ret == 0 && hpp.t0)
+ type0 = hpp.t0;
+ else {
+ /*
+ * Maybe we *should* use default PCI settings for PCIe devices;
+ * this merely preserves the behavior of pciehp, which didn't
+ * supply any default Type 0 settings.
+ */
+ if (!dev->is_pcie) {
+ type0 = &pci_default_type0;
+ dev_warn(&dev->dev,
+ "no PCI setting record; using defaults\n");
+ }
+ }
+
+ if (hpp.t2)
+ program_hpp_type2(dev, hpp.t2);
+
+ if (hpp.t1)
+ dev_info(&dev->dev,
+ "PCI-X setting record not supported\n");
+
+ if (type0)
+ program_hpp_type0(dev, type0);
+
+ if (dev->subordinate) {
+ list_for_each_entry(cdev, &dev->subordinate->devices,
+ bus_list)
+ pci_configure_slot(cdev);
+ }
+}
+EXPORT_SYMBOL_GPL(pci_configure_slot);
+
/**
* acpi_get_hp_hw_control_from_firmware
* @dev: the pci_dev of the bridge that has a hotplug controller
new file mode 100644
@@ -230,6 +230,9 @@ int acpi_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp);
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
int acpi_pci_detect_ejectable(struct pci_bus *pbus);
+void pci_configure_slot(struct pci_dev *dev);
+#else
+static inline void pci_configure_slot(struct pci_dev *dev) { }
#endif
#endif