diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2014-09-23 10:03:18 -0600 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-09-23 10:03:18 -0600 |
commit | 2f419d76599e2fd1a2c2cf31730c80e952201af1 (patch) | |
tree | f1302a51b310d255480931c95d9ba42b6b8e1a14 | |
parent | ef39ab79f7401f99212e2ae8a2399d88d6c77c64 (diff) | |
parent | bceee4a97eb58bd0e80e39eff11b506ddd9e7ad3 (diff) | |
download | op-kernel-dev-2f419d76599e2fd1a2c2cf31730c80e952201af1.zip op-kernel-dev-2f419d76599e2fd1a2c2cf31730c80e952201af1.tar.gz |
Merge branch 'pci/hotplug' into next
* pci/hotplug:
PCI: pciehp: Prevent NULL dereference during probe
PCI: pciehp: Reduce PCIe slot_ctrl to 16 bits
PCI: Configure *all* devices, not just hot-added ones
PCI: Preserve MPS and MRRS when applying _HPX settings
PCI: Apply _HPP settings to all hot-added PCI devices
PCI: Preserve BIOS PCI_COMMAND_SERR and PCI_COMMAND_PARITY settings
PCI: Apply _HPP settings to PCIe devices as well as PCI and PCI-X
PCI: Remove unused pci_configure_slot()
ACPI / hotplug / PCI: Remove pci_configure_slot() usage
PCI: shpchp: Remove pci_configure_slot() usage
PCI: pciehp: Remove pci_configure_slot() usage
PCI: Add pci_configure_device() during enumeration
PCI: Move pci_configure_slot() to drivers/pci/probe.c
PCI: Shuffle pci-acpi.c functions to group them logically
PCI: Whitespace cleanup in pci-acpi.c
PCI: Move pci_get_hp_params() to drivers/pci/pci-acpi.c
PCI: pciehp: Configure hot-added display devices
PCI: Remove "no hotplug settings from platform" warning
-rw-r--r-- | drivers/pci/hotplug/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpi_pcihp.c | 251 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 11 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 7 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 9 | ||||
-rw-r--r-- | drivers/pci/hotplug/pcihp_slot.c | 180 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_pci.c | 8 | ||||
-rw-r--r-- | drivers/pci/pci-acpi.c | 276 | ||||
-rw-r--r-- | drivers/pci/probe.c | 134 | ||||
-rw-r--r-- | include/linux/pci_hotplug.h | 2 |
12 files changed, 409 insertions, 475 deletions
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 3e6532b..4a9aa08 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o -pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o +pci_hotplug-objs := pci_hotplug_core.o ifdef CONFIG_HOTPLUG_PCI_CPCI pci_hotplug-objs += cpci_hotplug_core.o \ diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index a94d850..2ac9a28 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -46,215 +46,6 @@ static bool debug_acpi; -static acpi_status -decode_type0_hpx_record(union acpi_object *record, struct hotplug_params *hpx) -{ - int i; - union acpi_object *fields = record->package.elements; - u32 revision = fields[1].integer.value; - - switch (revision) { - case 1: - if (record->package.count != 6) - return AE_ERROR; - for (i = 2; i < 6; i++) - if (fields[i].type != ACPI_TYPE_INTEGER) - return AE_ERROR; - hpx->t0 = &hpx->type0_data; - hpx->t0->revision = revision; - hpx->t0->cache_line_size = fields[2].integer.value; - hpx->t0->latency_timer = fields[3].integer.value; - hpx->t0->enable_serr = fields[4].integer.value; - hpx->t0->enable_perr = fields[5].integer.value; - break; - default: - printk(KERN_WARNING - "%s: Type 0 Revision %d record not supported\n", - __func__, revision); - return AE_ERROR; - } - return AE_OK; -} - -static acpi_status -decode_type1_hpx_record(union acpi_object *record, struct hotplug_params *hpx) -{ - int i; - union acpi_object *fields = record->package.elements; - u32 revision = fields[1].integer.value; - - switch (revision) { - case 1: - if (record->package.count != 5) - return AE_ERROR; - for (i = 2; i < 5; i++) - if (fields[i].type != ACPI_TYPE_INTEGER) - return AE_ERROR; - hpx->t1 = &hpx->type1_data; - hpx->t1->revision = revision; - hpx->t1->max_mem_read = fields[2].integer.value; - hpx->t1->avg_max_split = fields[3].integer.value; - hpx->t1->tot_max_split = fields[4].integer.value; - break; - default: - printk(KERN_WARNING - "%s: Type 1 Revision %d record not supported\n", - __func__, revision); - return AE_ERROR; - } - return AE_OK; -} - -static acpi_status -decode_type2_hpx_record(union acpi_object *record, struct hotplug_params *hpx) -{ - int i; - union acpi_object *fields = record->package.elements; - u32 revision = fields[1].integer.value; - - switch (revision) { - case 1: - if (record->package.count != 18) - return AE_ERROR; - for (i = 2; i < 18; i++) - if (fields[i].type != ACPI_TYPE_INTEGER) - return AE_ERROR; - hpx->t2 = &hpx->type2_data; - hpx->t2->revision = revision; - hpx->t2->unc_err_mask_and = fields[2].integer.value; - hpx->t2->unc_err_mask_or = fields[3].integer.value; - hpx->t2->unc_err_sever_and = fields[4].integer.value; - hpx->t2->unc_err_sever_or = fields[5].integer.value; - hpx->t2->cor_err_mask_and = fields[6].integer.value; - hpx->t2->cor_err_mask_or = fields[7].integer.value; - hpx->t2->adv_err_cap_and = fields[8].integer.value; - hpx->t2->adv_err_cap_or = fields[9].integer.value; - hpx->t2->pci_exp_devctl_and = fields[10].integer.value; - hpx->t2->pci_exp_devctl_or = fields[11].integer.value; - hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value; - hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value; - hpx->t2->sec_unc_err_sever_and = fields[14].integer.value; - hpx->t2->sec_unc_err_sever_or = fields[15].integer.value; - hpx->t2->sec_unc_err_mask_and = fields[16].integer.value; - hpx->t2->sec_unc_err_mask_or = fields[17].integer.value; - break; - default: - printk(KERN_WARNING - "%s: Type 2 Revision %d record not supported\n", - __func__, revision); - return AE_ERROR; - } - return AE_OK; -} - -static acpi_status -acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) -{ - acpi_status status; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *package, *record, *fields; - u32 type; - int i; - - /* Clear the return buffer with zeros */ - memset(hpx, 0, sizeof(struct hotplug_params)); - - status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); - if (ACPI_FAILURE(status)) - return status; - - package = (union acpi_object *)buffer.pointer; - if (package->type != ACPI_TYPE_PACKAGE) { - status = AE_ERROR; - goto exit; - } - - for (i = 0; i < package->package.count; i++) { - record = &package->package.elements[i]; - if (record->type != ACPI_TYPE_PACKAGE) { - status = AE_ERROR; - goto exit; - } - - fields = record->package.elements; - if (fields[0].type != ACPI_TYPE_INTEGER || - fields[1].type != ACPI_TYPE_INTEGER) { - status = AE_ERROR; - goto exit; - } - - type = fields[0].integer.value; - switch (type) { - case 0: - status = decode_type0_hpx_record(record, hpx); - if (ACPI_FAILURE(status)) - goto exit; - break; - case 1: - status = decode_type1_hpx_record(record, hpx); - if (ACPI_FAILURE(status)) - goto exit; - break; - case 2: - status = decode_type2_hpx_record(record, hpx); - if (ACPI_FAILURE(status)) - goto exit; - break; - default: - printk(KERN_ERR "%s: Type %d record not supported\n", - __func__, type); - status = AE_ERROR; - goto exit; - } - } - exit: - kfree(buffer.pointer); - return status; -} - -static acpi_status -acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) -{ - acpi_status status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *package, *fields; - int i; - - memset(hpp, 0, sizeof(struct hotplug_params)); - - status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); - if (ACPI_FAILURE(status)) - return status; - - package = (union acpi_object *) buffer.pointer; - if (package->type != ACPI_TYPE_PACKAGE || - package->package.count != 4) { - status = AE_ERROR; - goto exit; - } - - fields = package->package.elements; - for (i = 0; i < 4; i++) { - if (fields[i].type != ACPI_TYPE_INTEGER) { - status = AE_ERROR; - goto exit; - } - } - - hpp->t0 = &hpp->type0_data; - hpp->t0->revision = 1; - hpp->t0->cache_line_size = fields[0].integer.value; - hpp->t0->latency_timer = fields[1].integer.value; - hpp->t0->enable_serr = fields[2].integer.value; - hpp->t0->enable_perr = fields[3].integer.value; - -exit: - kfree(buffer.pointer); - return status; -} - - - /* acpi_run_oshp - get control of hotplug from the firmware * * @handle - the handle of the hotplug controller. @@ -283,48 +74,6 @@ static acpi_status acpi_run_oshp(acpi_handle handle) return status; } -/* pci_get_hp_params - * - * @dev - the pci_dev for which we want parameters - * @hpp - allocated by the caller - */ -int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) -{ - acpi_status status; - acpi_handle handle, phandle; - struct pci_bus *pbus; - - handle = NULL; - for (pbus = dev->bus; pbus; pbus = pbus->parent) { - handle = acpi_pci_get_bridge_handle(pbus); - if (handle) - break; - } - - /* - * _HPP settings apply to all child buses, until another _HPP is - * encountered. If we don't find an _HPP for the input pci dev, - * look for it in the parent device scope since that would apply to - * this pci dev. - */ - while (handle) { - status = acpi_run_hpx(handle, hpp); - if (ACPI_SUCCESS(status)) - return 0; - status = acpi_run_hpp(handle, hpp); - if (ACPI_SUCCESS(status)) - return 0; - if (acpi_is_root_bridge(handle)) - break; - status = acpi_get_parent(handle, &phandle); - if (ACPI_FAILURE(status)) - break; - handle = phandle; - } - return -ENODEV; -} -EXPORT_SYMBOL_GPL(pci_get_hp_params); - /** * acpi_get_hp_hw_control_from_firmware * @dev: the pci_dev of the bridge that has a hotplug controller diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 70741c8..a6f8e0b 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -61,7 +61,6 @@ static DEFINE_MUTEX(bridge_mutex); static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type); static void acpiphp_post_dock_fixup(struct acpi_device *adev); static void acpiphp_sanitize_bus(struct pci_bus *bus); -static void acpiphp_set_hpp_values(struct pci_bus *bus); static void hotplug_event(u32 type, struct acpiphp_context *context); static void free_bridge(struct kref *kref); @@ -510,7 +509,7 @@ static void enable_slot(struct acpiphp_slot *slot) __pci_bus_assign_resources(bus, &add_list, NULL); acpiphp_sanitize_bus(bus); - acpiphp_set_hpp_values(bus); + pcie_bus_configure_settings(bus); acpiphp_set_acpi_region(slot); list_for_each_entry(dev, &bus->devices, bus_list) { @@ -702,14 +701,6 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) } } -static void acpiphp_set_hpp_values(struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) - pci_configure_slot(dev); -} - /* * Remove devices for which we could not assign resources, call * arch specific code to fix-up the bus diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 9e5a9fb..b115219 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -92,7 +92,7 @@ struct controller { struct slot *slot; wait_queue_head_t queue; /* sleep & wake process */ u32 slot_cap; - u32 slot_ctrl; + u16 slot_ctrl; struct timer_list poll_timer; unsigned long cmd_started; /* jiffies */ unsigned int cmd_busy:1; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 07aa722..3a5e7e2 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -262,6 +262,13 @@ static int pciehp_probe(struct pcie_device *dev) goto err_out_none; } + if (!dev->port->subordinate) { + /* Can happen if we run out of bus numbers during probe */ + dev_err(&dev->device, + "Hotplug bridge without secondary bus, ignoring\n"); + goto err_out_none; + } + ctrl = pcie_init(dev); if (!ctrl) { dev_err(&dev->device, "Controller initialization failed\n"); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 9da84b8..9e0f4ae 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -171,7 +171,7 @@ static void pcie_wait_cmd(struct controller *ctrl) * interrupts. */ if (!rc) - ctrl_info(ctrl, "Timeout on hotplug command %#010x (issued %u msec ago)\n", + ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n", ctrl->slot_ctrl, jiffies_to_msecs(now - ctrl->cmd_started)); } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 5f871f4..9e69403 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -65,14 +65,7 @@ int pciehp_configure_device(struct slot *p_slot) pci_hp_add_bridge(dev); pci_assign_unassigned_bridge_resources(bridge); - - list_for_each_entry(dev, &parent->devices, bus_list) { - if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) - continue; - - pci_configure_slot(dev); - } - + pcie_bus_configure_settings(parent); pci_bus_add_devices(parent); out: diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c deleted file mode 100644 index e246a10..0000000 --- a/drivers/pci/hotplug/pcihp_slot.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 1995,2001 Compaq Computer Corporation - * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2001 IBM Corp. - * Copyright (C) 2003-2004 Intel Corporation - * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/pci.h> -#include <linux/export.h> -#include <linux/pci_hotplug.h> - -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) { - /* - * Perhaps we *should* use default settings for PCIe, but - * pciehp didn't, so we won't either. - */ - if (pci_is_pcie(dev)) - return; - dev_info(&dev->dev, "using default PCI settings\n"); - hpp = &pci_default_type0; - } - - if (hpp->revision > 1) { - dev_warn(&dev->dev, - "PCI settings rev %d not supported; using defaults\n", - hpp->revision); - hpp = &pci_default_type0; - } - - 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_type1(struct pci_dev *dev, struct hpp_type1 *hpp) -{ - if (hpp) - dev_warn(&dev->dev, "PCI-X settings not supported\n"); -} - -static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) -{ - int pos; - u32 reg32; - - if (!hpp) - return; - - if (hpp->revision > 1) { - dev_warn(&dev->dev, "PCIe settings rev %d not supported\n", - hpp->revision); - return; - } - - /* Initialize Device Control Register */ - pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, - ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or); - - /* Initialize Link Control Register */ - if (dev->subordinate) - pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, - ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or); - - /* 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; - 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; - - pcie_bus_configure_settings(dev->bus); - - memset(&hpp, 0, sizeof(hpp)); - ret = pci_get_hp_params(dev, &hpp); - if (ret) - dev_warn(&dev->dev, "no hotplug settings from platform\n"); - - program_hpp_type2(dev, hpp.t2); - program_hpp_type1(dev, hpp.t1); - program_hpp_type0(dev, hpp.t0); - - if (dev->subordinate) { - list_for_each_entry(cdev, &dev->subordinate->devices, - bus_list) - pci_configure_slot(cdev); - } -} -EXPORT_SYMBOL_GPL(pci_configure_slot); diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index 469454e..f8cd3a2 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -69,13 +69,7 @@ int shpchp_configure_device(struct slot *p_slot) } pci_assign_unassigned_bridge_resources(bridge); - - list_for_each_entry(dev, &parent->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != p_slot->device) - continue; - pci_configure_slot(dev); - } - + pcie_bus_configure_settings(parent); pci_bus_add_devices(parent); out: diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 37263b0..6ebf8ed 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/pci_hotplug.h> #include <linux/module.h> #include <linux/pci-aspm.h> #include <linux/pci-acpi.h> @@ -17,6 +18,267 @@ #include <linux/pm_qos.h> #include "pci.h" +phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) +{ + acpi_status status = AE_NOT_EXIST; + unsigned long long mcfg_addr; + + if (handle) + status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, + NULL, &mcfg_addr); + if (ACPI_FAILURE(status)) + return 0; + + return (phys_addr_t)mcfg_addr; +} + +static acpi_status decode_type0_hpx_record(union acpi_object *record, + struct hotplug_params *hpx) +{ + int i; + union acpi_object *fields = record->package.elements; + u32 revision = fields[1].integer.value; + + switch (revision) { + case 1: + if (record->package.count != 6) + return AE_ERROR; + for (i = 2; i < 6; i++) + if (fields[i].type != ACPI_TYPE_INTEGER) + return AE_ERROR; + hpx->t0 = &hpx->type0_data; + hpx->t0->revision = revision; + hpx->t0->cache_line_size = fields[2].integer.value; + hpx->t0->latency_timer = fields[3].integer.value; + hpx->t0->enable_serr = fields[4].integer.value; + hpx->t0->enable_perr = fields[5].integer.value; + break; + default: + printk(KERN_WARNING + "%s: Type 0 Revision %d record not supported\n", + __func__, revision); + return AE_ERROR; + } + return AE_OK; +} + +static acpi_status decode_type1_hpx_record(union acpi_object *record, + struct hotplug_params *hpx) +{ + int i; + union acpi_object *fields = record->package.elements; + u32 revision = fields[1].integer.value; + + switch (revision) { + case 1: + if (record->package.count != 5) + return AE_ERROR; + for (i = 2; i < 5; i++) + if (fields[i].type != ACPI_TYPE_INTEGER) + return AE_ERROR; + hpx->t1 = &hpx->type1_data; + hpx->t1->revision = revision; + hpx->t1->max_mem_read = fields[2].integer.value; + hpx->t1->avg_max_split = fields[3].integer.value; + hpx->t1->tot_max_split = fields[4].integer.value; + break; + default: + printk(KERN_WARNING + "%s: Type 1 Revision %d record not supported\n", + __func__, revision); + return AE_ERROR; + } + return AE_OK; +} + +static acpi_status decode_type2_hpx_record(union acpi_object *record, + struct hotplug_params *hpx) +{ + int i; + union acpi_object *fields = record->package.elements; + u32 revision = fields[1].integer.value; + + switch (revision) { + case 1: + if (record->package.count != 18) + return AE_ERROR; + for (i = 2; i < 18; i++) + if (fields[i].type != ACPI_TYPE_INTEGER) + return AE_ERROR; + hpx->t2 = &hpx->type2_data; + hpx->t2->revision = revision; + hpx->t2->unc_err_mask_and = fields[2].integer.value; + hpx->t2->unc_err_mask_or = fields[3].integer.value; + hpx->t2->unc_err_sever_and = fields[4].integer.value; + hpx->t2->unc_err_sever_or = fields[5].integer.value; + hpx->t2->cor_err_mask_and = fields[6].integer.value; + hpx->t2->cor_err_mask_or = fields[7].integer.value; + hpx->t2->adv_err_cap_and = fields[8].integer.value; + hpx->t2->adv_err_cap_or = fields[9].integer.value; + hpx->t2->pci_exp_devctl_and = fields[10].integer.value; + hpx->t2->pci_exp_devctl_or = fields[11].integer.value; + hpx->t2->pci_exp_lnkctl_and = fields[12].integer.value; + hpx->t2->pci_exp_lnkctl_or = fields[13].integer.value; + hpx->t2->sec_unc_err_sever_and = fields[14].integer.value; + hpx->t2->sec_unc_err_sever_or = fields[15].integer.value; + hpx->t2->sec_unc_err_mask_and = fields[16].integer.value; + hpx->t2->sec_unc_err_mask_or = fields[17].integer.value; + break; + default: + printk(KERN_WARNING + "%s: Type 2 Revision %d record not supported\n", + __func__, revision); + return AE_ERROR; + } + return AE_OK; +} + +static acpi_status acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) +{ + acpi_status status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *package, *record, *fields; + u32 type; + int i; + + /* Clear the return buffer with zeros */ + memset(hpx, 0, sizeof(struct hotplug_params)); + + status = acpi_evaluate_object(handle, "_HPX", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + package = (union acpi_object *)buffer.pointer; + if (package->type != ACPI_TYPE_PACKAGE) { + status = AE_ERROR; + goto exit; + } + + for (i = 0; i < package->package.count; i++) { + record = &package->package.elements[i]; + if (record->type != ACPI_TYPE_PACKAGE) { + status = AE_ERROR; + goto exit; + } + + fields = record->package.elements; + if (fields[0].type != ACPI_TYPE_INTEGER || + fields[1].type != ACPI_TYPE_INTEGER) { + status = AE_ERROR; + goto exit; + } + + type = fields[0].integer.value; + switch (type) { + case 0: + status = decode_type0_hpx_record(record, hpx); + if (ACPI_FAILURE(status)) + goto exit; + break; + case 1: + status = decode_type1_hpx_record(record, hpx); + if (ACPI_FAILURE(status)) + goto exit; + break; + case 2: + status = decode_type2_hpx_record(record, hpx); + if (ACPI_FAILURE(status)) + goto exit; + break; + default: + printk(KERN_ERR "%s: Type %d record not supported\n", + __func__, type); + status = AE_ERROR; + goto exit; + } + } + exit: + kfree(buffer.pointer); + return status; +} + +static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package, *fields; + int i; + + memset(hpp, 0, sizeof(struct hotplug_params)); + + status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + package = (union acpi_object *) buffer.pointer; + if (package->type != ACPI_TYPE_PACKAGE || + package->package.count != 4) { + status = AE_ERROR; + goto exit; + } + + fields = package->package.elements; + for (i = 0; i < 4; i++) { + if (fields[i].type != ACPI_TYPE_INTEGER) { + status = AE_ERROR; + goto exit; + } + } + + hpp->t0 = &hpp->type0_data; + hpp->t0->revision = 1; + hpp->t0->cache_line_size = fields[0].integer.value; + hpp->t0->latency_timer = fields[1].integer.value; + hpp->t0->enable_serr = fields[2].integer.value; + hpp->t0->enable_perr = fields[3].integer.value; + +exit: + kfree(buffer.pointer); + return status; +} + +/* pci_get_hp_params + * + * @dev - the pci_dev for which we want parameters + * @hpp - allocated by the caller + */ +int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) +{ + acpi_status status; + acpi_handle handle, phandle; + struct pci_bus *pbus; + + handle = NULL; + for (pbus = dev->bus; pbus; pbus = pbus->parent) { + handle = acpi_pci_get_bridge_handle(pbus); + if (handle) + break; + } + + /* + * _HPP settings apply to all child buses, until another _HPP is + * encountered. If we don't find an _HPP for the input pci dev, + * look for it in the parent device scope since that would apply to + * this pci dev. + */ + while (handle) { + status = acpi_run_hpx(handle, hpp); + if (ACPI_SUCCESS(status)) + return 0; + status = acpi_run_hpp(handle, hpp); + if (ACPI_SUCCESS(status)) + return 0; + if (acpi_is_root_bridge(handle)) + break; + status = acpi_get_parent(handle, &phandle); + if (ACPI_FAILURE(status)) + break; + handle = phandle; + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(pci_get_hp_params); + /** * pci_acpi_wake_bus - Root bus wakeup notification fork function. * @work: Work item to handle. @@ -84,20 +346,6 @@ acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev); } -phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) -{ - acpi_status status = AE_NOT_EXIST; - unsigned long long mcfg_addr; - - if (handle) - status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, - NULL, &mcfg_addr); - if (ACPI_FAILURE(status)) - return 0; - - return (phys_addr_t)mcfg_addr; -} - /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e02cdaa..c99c4d6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -6,6 +6,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/pci_hotplug.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/cpumask.h> @@ -1249,6 +1250,137 @@ int pci_setup_device(struct pci_dev *dev) return 0; } +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) + hpp = &pci_default_type0; + + if (hpp->revision > 1) { + dev_warn(&dev->dev, + "PCI settings rev %d not supported; using defaults\n", + hpp->revision); + hpp = &pci_default_type0; + } + + 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; + if (hpp->enable_perr) + 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; + if (hpp->enable_perr) + pci_bctl |= PCI_BRIDGE_CTL_PARITY; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); + } +} + +static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp) +{ + if (hpp) + dev_warn(&dev->dev, "PCI-X settings not supported\n"); +} + +static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) +{ + int pos; + u32 reg32; + + if (!hpp) + return; + + if (hpp->revision > 1) { + dev_warn(&dev->dev, "PCIe settings rev %d not supported\n", + hpp->revision); + return; + } + + /* + * Don't allow _HPX to change MPS or MRRS settings. We manage + * those to make sure they're consistent with the rest of the + * platform. + */ + hpp->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD | + PCI_EXP_DEVCTL_READRQ; + hpp->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD | + PCI_EXP_DEVCTL_READRQ); + + /* Initialize Device Control Register */ + pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, + ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or); + + /* Initialize Link Control Register */ + if (dev->subordinate) + pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, + ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or); + + /* 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 + */ +} + +static void pci_configure_device(struct pci_dev *dev) +{ + struct hotplug_params hpp; + int ret; + + memset(&hpp, 0, sizeof(hpp)); + ret = pci_get_hp_params(dev, &hpp); + if (ret) + return; + + program_hpp_type2(dev, hpp.t2); + program_hpp_type1(dev, hpp.t1); + program_hpp_type0(dev, hpp.t0); +} + static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); @@ -1391,6 +1523,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { int ret; + pci_configure_device(dev); + device_initialize(&dev->dev); dev->dev.release = pci_release_dev; diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 5f2e559..2706ee9 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -187,6 +187,4 @@ static inline int pci_get_hp_params(struct pci_dev *dev, return -ENODEV; } #endif - -void pci_configure_slot(struct pci_dev *dev); #endif |