From b3bac8e57c82e8d3e05f4abcb18c4f0a40656655 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 23 Jan 2014 11:30:37 -0800 Subject: PCI/MSI: Check kmalloc() return value, fix leak of name Coverity reported that I forgot to check the return value of kmalloc() when creating the MSI attribute name, so fix that up and properly free it if there is an error when allocating the msi_dev_attr variable. Found by Coverity (CID 1163315 and 1163316). Fixes: 1c51b50c2995 ("PCI/MSI: Export MSI mode using attributes, not kobjects") Signed-off-by: Greg Kroah-Hartman Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 7a0fec6ce571..39dff3fe57af 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -545,9 +545,15 @@ static int populate_msi_sysfs(struct pci_dev *pdev) return -ENOMEM; list_for_each_entry(entry, &pdev->msi_list, list) { char *name = kmalloc(20, GFP_KERNEL); + if (!name) + goto error_attrs; + msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); - if (!msi_dev_attr) + if (!msi_dev_attr) { + kfree(name); goto error_attrs; + } + sprintf(name, "%d", entry->irq); sysfs_attr_init(&msi_dev_attr->attr); msi_dev_attr->attr.name = name; -- cgit v1.2.3 From 13f81c099bee8141f764fd41a1f4b68b93be3296 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 23 Jan 2014 12:01:12 -0800 Subject: PCI/MSI: Fix leak of msi_attrs Coverity reported that I forgot to clean up some allocated memory on the error path in populate_msi_sysfs(), so this patch fixes that. Thanks to Dave Jones for pointing out where the error was, I obviously can't read code this morning... Found by Coverity (CID 1163317). Fixes: 1c51b50c2995 ("PCI/MSI: Export MSI mode using attributes, not kobjects") Signed-off-by: Greg Kroah-Hartman Signed-off-by: Bjorn Helgaas Cc: Dave Jones --- drivers/pci/msi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 39dff3fe57af..6f0474ebe420 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -595,6 +595,7 @@ error_attrs: ++count; msi_attr = msi_attrs[count]; } + kfree(msi_attrs); return ret; } -- cgit v1.2.3 From 39af663b5fd25c9dd427e12e106763b6b4f9572c Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 21 Jan 2014 00:42:55 +0900 Subject: PCI/MSI: Fix pci_msix_vec_count() htmldocs failure An empty line in msi.c caused "make htmldocs" failure: Warning(/home/iida/Repo/linux-next//drivers/pci/msi.c:962): bad line: Fixes: ff1aa430a2fa ("PCI/MSI: Add pci_msix_vec_count()") Signed-off-by: Masanari Iida Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 6f0474ebe420..955ab7990c5b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -966,7 +966,6 @@ EXPORT_SYMBOL(pci_disable_msi); /** * pci_msix_vec_count - return the number of device's MSI-X table entries * @dev: pointer to the pci_dev data structure of MSI-X device function - * This function returns the number of device's MSI-X table entries and * therefore the number of MSI-X vectors device is capable of sending. * It returns a negative errno if the device is not capable of sending MSI-X -- cgit v1.2.3 From ce300008023fc4ddb561c48bf7e157c8e6279f46 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 24 Jan 2014 09:51:06 -0700 Subject: PCI: Remove unnecessary list_empty(&pci_pme_list) check list_for_each_entry() handles empty lists just fine, so there's no need to check whether the list is empty first. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1febe90831b4..52e10e1181d0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1611,29 +1611,27 @@ static void pci_pme_list_scan(struct work_struct *work) struct pci_pme_device *pme_dev, *n; mutex_lock(&pci_pme_list_mutex); - if (!list_empty(&pci_pme_list)) { - list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { - if (pme_dev->dev->pme_poll) { - struct pci_dev *bridge; - - bridge = pme_dev->dev->bus->self; - /* - * If bridge is in low power state, the - * configuration space of subordinate devices - * may be not accessible - */ - if (bridge && bridge->current_state != PCI_D0) - continue; - pci_pme_wakeup(pme_dev->dev, NULL); - } else { - list_del(&pme_dev->list); - kfree(pme_dev); - } + list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { + if (pme_dev->dev->pme_poll) { + struct pci_dev *bridge; + + bridge = pme_dev->dev->bus->self; + /* + * If bridge is in low power state, the + * configuration space of subordinate devices + * may be not accessible + */ + if (bridge && bridge->current_state != PCI_D0) + continue; + pci_pme_wakeup(pme_dev->dev, NULL); + } else { + list_del(&pme_dev->list); + kfree(pme_dev); } - if (!list_empty(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); } + if (!list_empty(&pci_pme_list)) + schedule_delayed_work(&pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } -- cgit v1.2.3 From 9a4d7d87197e3ff9138981e196aa5021d13a51a8 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:21 +0100 Subject: PCI: Increment max correctly in pci_scan_bridge() This patch fixes two small issues: - If pci_add_new_bus() fails, max must not be incremented. Otherwise an incorrect value is returned from pci_scan_bridge(). - If the bus is already present, max must be incremented. I think that this case should only be hit if we trigger a manual rescan of a CardBus bridge. Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6e34498ec9f0..f340c947d8cb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -852,11 +852,12 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) * this case we only re-scan this bus. */ child = pci_find_bus(pci_domain_nr(bus), max+1); if (!child) { - child = pci_add_new_bus(bus, dev, ++max); + child = pci_add_new_bus(bus, dev, max+1); if (!child) goto out; - pci_bus_insert_busn_res(child, max, 0xff); + pci_bus_insert_busn_res(child, max+1, 0xff); } + max++; buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) -- cgit v1.2.3 From 2ed8582341f651ca14d00ab0ada4b46f493e1fcb Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:22 +0100 Subject: PCI: Clarify the "scan anyway" comment in pci_scan_bridge() Initially when we encountered a bus that was already present we skipped it. Since 74710ded8e16 'PCI: always scan child buses' we continue scanning in order to allow user triggered rescans of already existing busses. The old comment suggested that the reason for continuing the scan is a bug in the i450NX chipset. This is not the case. Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f340c947d8cb..511a8f6d7636 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -805,11 +805,10 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; /* - * If we already got to this bus through a different bridge, - * don't re-add it. This can happen with the i450NX chipset. - * - * However, we continue to descend down the hierarchy and - * scan remaining child buses. + * The bus might already exist for two reasons: Either we are + * rescanning the bus or the bus is reachable through more than + * one bridge. The second case can happen with the i450NX + * chipset. */ child = pci_find_bus(pci_domain_nr(bus), secondary); if (!child) { -- cgit v1.2.3 From 619c8c310f7f21e59a7e2b53795183c34401a599 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:23 +0100 Subject: PCI: Assign CardBus bus number only during the second pass Right now the CardBus code in pci_scan_bridge() is executed during both passes. Since we always allocate the bus number ourselves it makes sense to put it into the second pass. Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 511a8f6d7636..5dc8e1a31e4f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -831,7 +831,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken) + if (pcibios_assign_all_busses() || broken || is_cardbus) /* Temporarily disable forwarding of the configuration cycles on all bridges in this bus segment to avoid possible -- cgit v1.2.3 From ced04d15519a15d38b46162b94a1f26b4022116e Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:24 +0100 Subject: PCI: Use request_resource_conflict() instead of insert_ for bus numbers If a conflict happens during insert_resource_conflict() and all conflicts fit within the newly inserted resource then they will become children of the new resource. This is almost certainly not what we want for bus numbers. Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5dc8e1a31e4f..21f162c5c7bb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1835,7 +1835,7 @@ int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) res->flags |= IORESOURCE_PCI_FIXED; } - conflict = insert_resource_conflict(parent_res, res); + conflict = request_resource_conflict(parent_res, res); if (conflict) dev_printk(KERN_DEBUG, &b->dev, -- cgit v1.2.3 From 1820ffdccb9b4398c5f0f70360edc68e039c3c72 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:25 +0100 Subject: PCI: Make sure bus number resources stay within their parents bounds Right now we use 0xff for busn_res.end when probing and later reduce it to the value that is actually used. This does not work if a parent bridge has already a lower subordinate value. For example during hotplug of a new bridge below an already-configured bridge the following message is printed from pci_bus_insert_busn_res(): pci_bus 0000:06: busn_res: can not insert [bus 06-ff] under [bus 05-9b] (conflicts with (null) [bus 05-9b]) This patch clamps the bus range to that of the parent and also ensures that we do not exceed the parents range when assigning the final subordinate value. We also check that busses configured by the firmware fit into their parents bounds. [bhelgaas: reword dev_warn() and fix printk format warning] Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 21f162c5c7bb..9a641cc4275d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -782,7 +782,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number || - secondary > subordinate)) { + secondary > subordinate || subordinate > bus->busn_res.end)) { dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n", secondary, subordinate); broken = 1; @@ -854,7 +854,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) child = pci_add_new_bus(bus, dev, max+1); if (!child) goto out; - pci_bus_insert_busn_res(child, max+1, 0xff); + pci_bus_insert_busn_res(child, max+1, + bus->busn_res.end); } max++; buses = (buses & 0xff000000) @@ -927,6 +928,11 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) /* * Set the subordinate bus number to its real value. */ + if (max > bus->busn_res.end) { + dev_warn(&dev->dev, "max busn %02x is outside %pR\n", + max, &bus->busn_res); + max = bus->busn_res.end; + } pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } -- cgit v1.2.3 From 4703389f7df70518cc4ea584f3e64cb11f28aa7c Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:28:43 -0800 Subject: PCI: pciehp: Make check_link_active() non-static check_link_active() functionality needs to be used by subsequent patches (that introduce link state change based hotplug). Thus make the function non-static, and rename it to pciehp_check_link_active() so as to be consistent with other non-static functions. Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 1 + drivers/pci/hotplug/pciehp_hpc.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 88b37cad4b35..f9524592da0f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -153,6 +153,7 @@ void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); void pciehp_green_led_blink(struct slot *slot); int pciehp_check_link_status(struct controller *ctrl); +bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); int pciehp_reset_slot(struct slot *slot, int probe); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 14acfccb7670..aed6ab43cc13 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -206,7 +206,7 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) mutex_unlock(&ctrl->ctrl_lock); } -static bool check_link_active(struct controller *ctrl) +bool pciehp_check_link_active(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_status; @@ -225,12 +225,12 @@ static void __pcie_wait_link_active(struct controller *ctrl, bool active) { int timeout = 1000; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; while (timeout > 0) { msleep(10); timeout -= 10; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; } ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", -- cgit v1.2.3 From e48f1b67f668762003e8888eccd7acb71109e874 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:29:10 -0800 Subject: PCI: pciehp: Use link change notifications for hot-plug and removal A lot of systems do not have the fancy buttons and LEDs, and instead want to rely only on the Link state change events to drive the hotplug and removal state machinery. (http://www.spinics.net/lists/hotplug/msg05802.html) This patch adds support for that functionality. Here are the details about the patch itself: * Define and use interrupt events for linkup / linkdown. * Make the pcie_isr() also look at link events, and direct control to corresponding (new) link state change handler function. * Introduce the functions to handle link-up and link-down events and queue the add / removal work in the slot->wq to be processed by pciehp_power_thread() As a side note, this patch also fixes the bug https://bugzilla.kernel.org/show_bug.cgi?id=65521 "pciehp ignores Data Link Layer State Changed bit." Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 3 ++ drivers/pci/hotplug/pciehp_ctrl.c | 88 +++++++++++++++++++++++++++++++++++++++ drivers/pci/hotplug/pciehp_hpc.c | 6 ++- 3 files changed, 96 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index f9524592da0f..d8d033619a77 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -109,6 +109,8 @@ struct controller { #define INT_BUTTON_PRESS 7 #define INT_BUTTON_RELEASE 8 #define INT_BUTTON_CANCEL 9 +#define INT_LINK_UP 10 +#define INT_LINK_DOWN 11 #define STATIC_STATE 0 #define BLINKINGON_STATE 1 @@ -132,6 +134,7 @@ u8 pciehp_handle_attention_button(struct slot *p_slot); u8 pciehp_handle_switch_change(struct slot *p_slot); u8 pciehp_handle_presence_change(struct slot *p_slot); u8 pciehp_handle_power_fault(struct slot *p_slot); +void pciehp_handle_linkstate_change(struct slot *p_slot); int pciehp_configure_device(struct slot *p_slot); int pciehp_unconfigure_device(struct slot *p_slot); void pciehp_queue_pushbutton_work(struct work_struct *work); diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 50628487597d..56082842b265 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -150,6 +150,27 @@ u8 pciehp_handle_power_fault(struct slot *p_slot) return 1; } +void pciehp_handle_linkstate_change(struct slot *p_slot) +{ + u32 event_type; + struct controller *ctrl = p_slot->ctrl; + + /* Link Status Change */ + ctrl_dbg(ctrl, "Data Link Layer State change\n"); + + if (pciehp_check_link_active(ctrl)) { + ctrl_info(ctrl, "slot(%s): Link Up event\n", + slot_name(p_slot)); + event_type = INT_LINK_UP; + } else { + ctrl_info(ctrl, "slot(%s): Link Down event\n", + slot_name(p_slot)); + event_type = INT_LINK_DOWN; + } + + queue_interrupt_event(p_slot, event_type); +} + /* The following routines constitute the bulk of the hotplug controller logic */ @@ -415,6 +436,69 @@ static void handle_surprise_event(struct slot *p_slot) queue_work(p_slot->wq, &info->work); } +/* + * Note: This function must be called with slot->lock held + */ +static void handle_link_event(struct slot *p_slot, u32 event) +{ + struct controller *ctrl = p_slot->ctrl; + struct power_work_info *info; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", + __func__); + return; + } + info->p_slot = p_slot; + INIT_WORK(&info->work, pciehp_power_thread); + + switch (p_slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&p_slot->work); + /* Fall through */ + case STATIC_STATE: + p_slot->state = event == INT_LINK_UP ? + POWERON_STATE : POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + break; + case POWERON_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event ignored on slot(%s): already powering on\n", + slot_name(p_slot)); + kfree(info); + } else { + ctrl_info(ctrl, + "Link Down event queued on slot(%s): currently getting powered on\n", + slot_name(p_slot)); + p_slot->state = POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + } + break; + case POWEROFF_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event queued on slot(%s): currently getting powered off\n", + slot_name(p_slot)); + p_slot->state = POWERON_STATE; + queue_work(p_slot->wq, &info->work); + } else { + ctrl_info(ctrl, + "Link Down event ignored on slot(%s): already powering off\n", + slot_name(p_slot)); + kfree(info); + } + break; + default: + ctrl_err(ctrl, "Not a valid state on slot(%s)\n", + slot_name(p_slot)); + kfree(info); + break; + } +} + static void interrupt_event_handler(struct work_struct *work) { struct event_info *info = container_of(work, struct event_info, work); @@ -439,6 +523,10 @@ static void interrupt_event_handler(struct work_struct *work) ctrl_dbg(ctrl, "Surprise Removal\n"); handle_surprise_event(p_slot); break; + case INT_LINK_UP: + case INT_LINK_DOWN: + handle_link_event(p_slot, info->event_type); + break; default: break; } diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index aed6ab43cc13..b413dce8f194 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -540,7 +540,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | - PCI_EXP_SLTSTA_CC); + PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); detected &= ~intr_loc; intr_loc |= detected; if (!intr_loc) @@ -579,6 +579,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ctrl->power_fault_detected = 1; pciehp_handle_power_fault(slot); } + + if (intr_loc & PCI_EXP_SLTSTA_DLLSC) + pciehp_handle_linkstate_change(slot); + return IRQ_HANDLED; } -- cgit v1.2.3 From 2c74424470a0f98df8d6540642aff96d1a0b94b3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 3 Feb 2014 14:27:33 -0700 Subject: PCI: Add device-specific PCI ACS enable Some devices support PCI ACS-like features, but don't report it using the standard PCIe capabilities. We already provide hooks for device-specific testing of ACS, but not for device-specific enabling of ACS. This provides that setup hook. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 26 ++++++++++++++++++++------ drivers/pci/quirks.c | 25 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1febe90831b4..b89502ff3139 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2180,21 +2180,18 @@ void pci_request_acs(void) } /** - * pci_enable_acs - enable ACS if hardware support it + * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites * @dev: the PCI device */ -void pci_enable_acs(struct pci_dev *dev) +static int pci_std_enable_acs(struct pci_dev *dev) { int pos; u16 cap; u16 ctrl; - if (!pci_acs_enable) - return; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); if (!pos) - return; + return -ENODEV; pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); @@ -2212,6 +2209,23 @@ void pci_enable_acs(struct pci_dev *dev) ctrl |= (cap & PCI_ACS_UF); pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); + + return 0; +} + +/** + * pci_enable_acs - enable ACS if hardware support it + * @dev: the PCI device + */ +void pci_enable_acs(struct pci_dev *dev) +{ + if (!pci_acs_enable) + return; + + if (!pci_std_enable_acs(dev)) + return; + + pci_dev_specific_enable_acs(dev); } static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5cb726c193de..f681fb08f5e4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3461,3 +3461,28 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) return -ENOTTY; } + +static const struct pci_dev_enable_acs { + u16 vendor; + u16 device; + int (*enable_acs)(struct pci_dev *dev); +} pci_dev_enable_acs[] = { + { 0 } +}; + +void pci_dev_specific_enable_acs(struct pci_dev *dev) +{ + const struct pci_dev_enable_acs *i; + int ret; + + for (i = pci_dev_enable_acs; i->enable_acs; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->enable_acs(dev); + if (ret >= 0) + return; + } + } +} -- cgit v1.2.3 From d99321b63b1f2cd386cdf20aefe4d8d3d414cb5b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 3 Feb 2014 14:27:46 -0700 Subject: PCI: Enable quirks for PCIe ACS on Intel PCH root ports Many of the currently available Intel PCH-based root ports do not provide PCIe ACS capabilities. Without this, we must assume that peer-to-peer traffic between multifunction root ports and between devices behind root ports is possible. This lack of isolation is exposed by grouping the devices together in the same IOMMU group. If we want to expose these devices to userspace, vfio uses IOMMU groups as the unit of ownership, thus making it very difficult to assign individual devices to separate users. The good news is that the chipset does provide ACS-like isolation capabilities, but we do need to verify and enable those capabilities if the BIOS has not done so. This patch implements the device specific enabling and testing of equivalent ACS function for these devices. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Acked-by: Don Dugger --- drivers/pci/quirks.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f681fb08f5e4..ed2ed867c34c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3423,6 +3423,61 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags) #endif } +/* + * Many Intel PCH root ports do provide ACS-like features to disable peer + * transactions and validate bus numbers in requests, but do not provide an + * actual PCIe ACS capability. This is the list of device IDs known to fall + * into that category as provided by Intel in Red Hat bugzilla 1037684. + */ +static const u16 pci_quirk_intel_pch_acs_ids[] = { + /* Ibexpeak PCH */ + 0x3b42, 0x3b43, 0x3b44, 0x3b45, 0x3b46, 0x3b47, 0x3b48, 0x3b49, + 0x3b4a, 0x3b4b, 0x3b4c, 0x3b4d, 0x3b4e, 0x3b4f, 0x3b50, 0x3b51, + /* Cougarpoint PCH */ + 0x1c10, 0x1c11, 0x1c12, 0x1c13, 0x1c14, 0x1c15, 0x1c16, 0x1c17, + 0x1c18, 0x1c19, 0x1c1a, 0x1c1b, 0x1c1c, 0x1c1d, 0x1c1e, 0x1c1f, + /* Pantherpoint PCH */ + 0x1e10, 0x1e11, 0x1e12, 0x1e13, 0x1e14, 0x1e15, 0x1e16, 0x1e17, + 0x1e18, 0x1e19, 0x1e1a, 0x1e1b, 0x1e1c, 0x1e1d, 0x1e1e, 0x1e1f, + /* Lynxpoint-H PCH */ + 0x8c10, 0x8c11, 0x8c12, 0x8c13, 0x8c14, 0x8c15, 0x8c16, 0x8c17, + 0x8c18, 0x8c19, 0x8c1a, 0x8c1b, 0x8c1c, 0x8c1d, 0x8c1e, 0x8c1f, + /* Lynxpoint-LP PCH */ + 0x9c10, 0x9c11, 0x9c12, 0x9c13, 0x9c14, 0x9c15, 0x9c16, 0x9c17, + 0x9c18, 0x9c19, 0x9c1a, 0x9c1b, + /* Wildcat PCH */ + 0x9c90, 0x9c91, 0x9c92, 0x9c93, 0x9c94, 0x9c95, 0x9c96, 0x9c97, + 0x9c98, 0x9c99, 0x9c9a, 0x9c9b, +}; + +static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev) +{ + int i; + + /* Filter out a few obvious non-matches first */ + if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) + return false; + + for (i = 0; i < ARRAY_SIZE(pci_quirk_intel_pch_acs_ids); i++) + if (pci_quirk_intel_pch_acs_ids[i] == dev->device) + return true; + + return false; +} + +#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV) + +static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) +{ + u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ? + INTEL_PCH_ACS_FLAGS : 0; + + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + return acs_flags & ~flags ? 0 : 1; +} + static const struct pci_dev_acs_enabled { u16 vendor; u16 device; @@ -3434,6 +3489,7 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0 } }; @@ -3462,11 +3518,115 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) return -ENOTTY; } +/* Config space offset of Root Complex Base Address register */ +#define INTEL_LPC_RCBA_REG 0xf0 +/* 31:14 RCBA address */ +#define INTEL_LPC_RCBA_MASK 0xffffc000 +/* RCBA Enable */ +#define INTEL_LPC_RCBA_ENABLE (1 << 0) + +/* Backbone Scratch Pad Register */ +#define INTEL_BSPR_REG 0x1104 +/* Backbone Peer Non-Posted Disable */ +#define INTEL_BSPR_REG_BPNPD (1 << 8) +/* Backbone Peer Posted Disable */ +#define INTEL_BSPR_REG_BPPD (1 << 9) + +/* Upstream Peer Decode Configuration Register */ +#define INTEL_UPDCR_REG 0x1114 +/* 5:0 Peer Decode Enable bits */ +#define INTEL_UPDCR_REG_MASK 0x3f + +static int pci_quirk_enable_intel_lpc_acs(struct pci_dev *dev) +{ + u32 rcba, bspr, updcr; + void __iomem *rcba_mem; + + /* + * Read the RCBA register from the LPC (D31:F0). PCH root ports + * are D28:F* and therefore get probed before LPC, thus we can't + * use pci_get_slot/pci_read_config_dword here. + */ + pci_bus_read_config_dword(dev->bus, PCI_DEVFN(31, 0), + INTEL_LPC_RCBA_REG, &rcba); + if (!(rcba & INTEL_LPC_RCBA_ENABLE)) + return -EINVAL; + + rcba_mem = ioremap_nocache(rcba & INTEL_LPC_RCBA_MASK, + PAGE_ALIGN(INTEL_UPDCR_REG)); + if (!rcba_mem) + return -ENOMEM; + + /* + * The BSPR can disallow peer cycles, but it's set by soft strap and + * therefore read-only. If both posted and non-posted peer cycles are + * disallowed, we're ok. If either are allowed, then we need to use + * the UPDCR to disable peer decodes for each port. This provides the + * PCIe ACS equivalent of PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF + */ + bspr = readl(rcba_mem + INTEL_BSPR_REG); + bspr &= INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD; + if (bspr != (INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD)) { + updcr = readl(rcba_mem + INTEL_UPDCR_REG); + if (updcr & INTEL_UPDCR_REG_MASK) { + dev_info(&dev->dev, "Disabling UPDCR peer decodes\n"); + updcr &= ~INTEL_UPDCR_REG_MASK; + writel(updcr, rcba_mem + INTEL_UPDCR_REG); + } + } + + iounmap(rcba_mem); + return 0; +} + +/* Miscellaneous Port Configuration register */ +#define INTEL_MPC_REG 0xd8 +/* MPC: Invalid Receive Bus Number Check Enable */ +#define INTEL_MPC_REG_IRBNCE (1 << 26) + +static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) +{ + u32 mpc; + + /* + * When enabled, the IRBNCE bit of the MPC register enables the + * equivalent of PCI ACS Source Validation (PCI_ACS_SV), which + * ensures that requester IDs fall within the bus number range + * of the bridge. Enable if not already. + */ + pci_read_config_dword(dev, INTEL_MPC_REG, &mpc); + if (!(mpc & INTEL_MPC_REG_IRBNCE)) { + dev_info(&dev->dev, "Enabling MPC IRBNCE\n"); + mpc |= INTEL_MPC_REG_IRBNCE; + pci_write_config_word(dev, INTEL_MPC_REG, mpc); + } +} + +static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) +{ + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + if (pci_quirk_enable_intel_lpc_acs(dev)) { + dev_warn(&dev->dev, "Failed to enable Intel PCH ACS quirk\n"); + return 0; + } + + pci_quirk_enable_intel_rp_mpc_acs(dev); + + dev->dev_flags |= PCI_DEV_FLAGS_ACS_ENABLED_QUIRK; + + dev_info(&dev->dev, "Intel PCH root port ACS workaround enabled\n"); + + return 0; +} + static const struct pci_dev_enable_acs { u16 vendor; u16 device; int (*enable_acs)(struct pci_dev *dev); } pci_dev_enable_acs[] = { + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs }, { 0 } }; -- cgit v1.2.3 From f5fb40700fc9a52944fbe07148c858a5025908b1 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:26 +0100 Subject: PCI: Remove pci_fixup_parent_subordinate_busnr() The function has no effect. If pcibios_assign_all_busses() is not set then the function does nothing. If it is set then in pci_scan_bridge we are always in the branch where we assign the bus numbers ourselves and the subordinate values of all parent busses will be set to 0xff since that is what they inherited from their parent bus and ultimately from the root bus. Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9a641cc4275d..e5df03669470 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -731,22 +731,6 @@ struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *de return child; } -static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) -{ - struct pci_bus *parent = child->parent; - - /* Attempts to fix that up are really dangerous unless - we're going to re-assign all bus numbers. */ - if (!pcibios_assign_all_busses()) - return; - - while (parent->parent && parent->busn_res.end < max) { - parent->busn_res.end = max; - pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); - parent = parent->parent; - } -} - /* * If it's a bridge, configure it and scan the bus behind it. * For CardBus bridges, we don't scan behind as the devices will @@ -879,20 +863,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) if (!is_cardbus) { child->bridge_ctl = bctl; - /* - * Adjust subordinate busnr in parent buses. - * We do this before scanning for children because - * some devices may not be detected if the bios - * was lazy. - */ - pci_fixup_parent_subordinate_busnr(child, max); - /* Now we can scan all subordinate buses... */ max = pci_scan_child_bus(child); - /* - * now fix it up again since we have found - * the real value of max. - */ - pci_fixup_parent_subordinate_busnr(child, max); } else { /* * For CardBus bridges, we leave 4 bus numbers @@ -923,7 +894,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } } max += i; - pci_fixup_parent_subordinate_busnr(child, max); } /* * Set the subordinate bus number to its real value. -- cgit v1.2.3 From c95b0bd6ca3dbb1abf8394c38d26df65d890cb9a Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:27 +0100 Subject: PCI: Check for child busses which use more bus numbers than allocated pci_scan_child_bus can (potentially) return a bus number higher than the subordinate value of the child bus. Possible reasons are that bus numbers are reserved for SR-IOV or for CardBus (SR-IOV is done without checks and the CardBus checks are sketchy at best). We clamp the returned value to the actual subordinate value and print a warning if too many bus numbers are reserved. [bhelgaas: whitespace] Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e5df03669470..1436288924c3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -805,10 +805,12 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } cmax = pci_scan_child_bus(child); - if (cmax > max) - max = cmax; - if (child->busn_res.end > max) - max = child->busn_res.end; + if (cmax > subordinate) + dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n", + subordinate, cmax); + /* subordinate should equal child->busn_res.end */ + if (subordinate > max) + max = subordinate; } else { /* * We need to assign a number to this bus which we always -- cgit v1.2.3 From fc1b253141b360f9c669d391b4ff663b984ef0c9 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Thu, 23 Jan 2014 21:59:28 +0100 Subject: PCI: Don't scan random busses in pci_scan_bridge() When assigning a new bus number in pci_scan_bridge we check whether max+1 is free by calling pci_find_bus. If it does already exist then we assume that we are rescanning and that this is the right bus to scan. This is fragile. If max+1 lies outside of bus->busn_res.end then we will rescan some random bus from somewhere else in the hierachy. This patch checks for this case and prints a warning. [bhelgaas: add parent/child bus number info to dev_warn()] Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 1436288924c3..509494381a7a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -829,12 +829,16 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; } + if (max >= bus->busn_res.end) { + dev_warn(&dev->dev, "can't allocate child bus %02x from %pR\n", + max, &bus->busn_res); + goto out; + } + /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); - /* Prevent assigning a bus number that already exists. - * This can happen when a bridge is hot-plugged, so in - * this case we only re-scan this bus. */ + /* The bus will already exist if we are rescanning */ child = pci_find_bus(pci_domain_nr(bus), max+1); if (!child) { child = pci_add_new_bus(bus, dev, max+1); -- cgit v1.2.3 From 4f854f2a2a4414248f82239e8d86b98489a1cf40 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:29:23 -0800 Subject: PCI: pciehp: Enable link state change notifications Enable the Link state notifications unconditionally. Enable the presence detection notification only if attention button is absent. This was discussed at this thread: https://lkml.kernel.org/r/529E5C0E.80903@gmail.com Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b413dce8f194..245a3cb5d2f3 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -600,9 +600,17 @@ void pcie_enable_notification(struct controller *ctrl) * when it is cleared in the interrupt service routine, and * next power fault detected interrupt was notified again. */ - cmd = PCI_EXP_SLTCTL_PDCE; + + /* + * Always enable link events: thus link-up and link-down shall + * always be treated as hotplug and unplug respectively. Enable + * presence detect only if Attention Button is not present. + */ + cmd = PCI_EXP_SLTCTL_DLLSCE; if (ATTN_BUTTN(ctrl)) cmd |= PCI_EXP_SLTCTL_ABPE; + else + cmd |= PCI_EXP_SLTCTL_PDCE; if (MRL_SENS(ctrl)) cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) @@ -610,7 +618,8 @@ void pcie_enable_notification(struct controller *ctrl) mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | - PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); pcie_write_cmd(ctrl, cmd, mask); } -- cgit v1.2.3 From b1811d2455f32754cc3d8725bf2e961c5eda2a72 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:30:04 -0800 Subject: PCI: pciehp: Don't disable the link permanently during removal We need future link up events for hot-add, thus don't disable the link permanently during device removal. Also, remove the static functions that are now left unused. This reverts part of 2debd9289997 ("PCI: pciehp: Disable/enable link during slot power off/on"). This was discussed at the URL below, where it was revealed that it was done for a bug in a PCIe repeater chip on that particular platform. Link: https://lkml.kernel.org/r/CAErSpo72KZ-a2OSQLWoK71GCgwBt676XZdGt4tEYm-6UYnLmPw@mail.gmail.com Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 245a3cb5d2f3..15ca3a1cad8d 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -242,11 +242,6 @@ static void pcie_wait_link_active(struct controller *ctrl) __pcie_wait_link_active(ctrl, true); } -static void pcie_wait_link_not_active(struct controller *ctrl) -{ - __pcie_wait_link_active(ctrl, false); -} - static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) { u32 l; @@ -332,11 +327,6 @@ static int pciehp_link_enable(struct controller *ctrl) return __pciehp_link_set(ctrl, true); } -static int pciehp_link_disable(struct controller *ctrl) -{ - return __pciehp_link_set(ctrl, false); -} - void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; @@ -508,14 +498,6 @@ void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - /* Disable the link at first */ - pciehp_link_disable(ctrl); - /* wait the link is down */ - if (ctrl->link_active_reporting) - pcie_wait_link_not_active(ctrl); - else - msleep(1000); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, -- cgit v1.2.3 From 02e93a8a7c1dcecc1a33ea762a0c041cbb6a0a66 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:30:21 -0800 Subject: PCI: pciehp: Don't check adapter or latch status while disabling It does not make much sense to refuse to disable a slot if an adapter is not present or the latch is open. If an adapter is not present, it provides an even better reason to disable the device slot. This is specially a problem for link state hot-plug, because some ports use in band mechanism for presence detection. Thus when link goes down, presence detect also goes down. We _want_ that the removal should take place in such case. Thus remove the checks for adapter and latch in pciehp_disable_slot() Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_ctrl.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 56082842b265..b418e3b09aa4 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -582,24 +582,6 @@ int pciehp_disable_slot(struct slot *p_slot) if (!p_slot->ctrl) return 1; - if (!HP_SUPR_RM(p_slot->ctrl)) { - pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) { - ctrl_info(ctrl, "No adapter on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - - if (MRL_SENS(p_slot->ctrl)) { - pciehp_get_latch_status(p_slot, &getstatus); - if (getstatus) { - ctrl_info(ctrl, "Latch open on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - if (POWER_CTRL(p_slot->ctrl)) { pciehp_get_power_status(p_slot, &getstatus); if (!getstatus) { -- cgit v1.2.3 From 06a8d89af551b6151fa2289dd8660647ea9d2faa Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:30:40 -0800 Subject: PCI: pciehp: Disable link notification across slot reset Disable the link notification (in addition to presence detect notifications) across the slot reset since the reset could flap the link, and we don't want to treat it as hot unplug followed by a hotplug. Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 15ca3a1cad8d..6433e73aa2df 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -619,33 +619,37 @@ static void pcie_disable_notification(struct controller *ctrl) /* * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary - * bus reset of the bridge, but if the slot supports surprise removal we need - * to disable presence detection around the bus reset and clear any spurious + * bus reset of the bridge, but if the slot supports surprise removal (or + * link state change based hotplug), we need to disable presence detection + * (or link state notifications) around the bus reset and clear any spurious * events after. */ int pciehp_reset_slot(struct slot *slot, int probe) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; if (probe) return 0; - if (HP_SUPR_RM(ctrl)) { - pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - del_timer_sync(&ctrl->poll_timer); + if (HP_SUPR_RM(ctrl) && !ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; } + ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; + stat_mask |= PCI_EXP_SLTSTA_DLLSC; + + pcie_write_cmd(ctrl, 0, ctrl_mask); + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); pci_reset_bridge_secondary_bus(ctrl->pcie->port); - if (HP_SUPR_RM(ctrl)) { - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - int_poll_timeout(ctrl->poll_timer.data); - } + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); + pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask); + if (pciehp_poll_mode) + int_poll_timeout(ctrl->poll_timer.data); return 0; } -- cgit v1.2.3 From c4f2f5e4981073a5aa0739f93b6733060cd37648 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:30:56 -0800 Subject: PCI: pciehp: Ensure very fast hotplug events are also processed Today, this is how all the hotplug and unplug events work: Hotplug / Removal needs to be done => Set slot->state (protected by slot->lock) to either POWERON_STATE (for enabling) or POWEROFF_STATE (for disabling). => Submit the work item for pciehp_power_thread() to slot->wq. Problem: There is a problem if the hotplug events can happen fast enough that they do not give SW enough time to add or remove the new devices. => Assume: Event for unplug comes (e.g. surprise removal). But before the pciehp_power_thread() work item was executed, the card was replaced by another card, causing surprise hotplug event. => What goes wrong: => The hot-removal event sets slot->state to POWEROFF_STATE, and schedules the pciehp_power_thread(). => The hot-add event sets slot->state to POWERON_STATE, and schedules the pciehp_power_thread(). => Now the pciehp_power_thread() is scheduled twice, and on both occasions it will find POWERON_STATE and will try to add the devices on the slot, and will fail complaining that the devices already exist. => Why this is a problem: If the device was replaced between the hot removal and hot-add, then we should unload the old driver and reload the new one. This does not happen today. The kernel or the driver is not even aware that the device was replaced. The problem is that the pciehp_power_thread() only looks at the slot->state which would only contain the *latest* state - not the actual event (add / remove) that was the intent of the IRQ handler who submitted the work. What this patch does: => Hotplug events pass on an actual request (for addition or removal) to pciehp_power_thread() which is local to that work item submission. => pciehp_power_thread() does not need to look at slote->state and hence no locks needed in that. => Essentially this results in all the hotplug and unplug events "replayed" by pciehp_power_thread(). Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_ctrl.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index b418e3b09aa4..3e40ec0f7da1 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -276,6 +276,9 @@ static int remove_board(struct slot *p_slot) struct power_work_info { struct slot *p_slot; struct work_struct work; + unsigned int req; +#define DISABLE_REQ 0 +#define ENABLE_REQ 1 }; /** @@ -291,10 +294,8 @@ static void pciehp_power_thread(struct work_struct *work) container_of(work, struct power_work_info, work); struct slot *p_slot = info->p_slot; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { - case POWEROFF_STATE: - mutex_unlock(&p_slot->lock); + switch (info->req) { + case DISABLE_REQ: ctrl_dbg(p_slot->ctrl, "Disabling domain:bus:device=%04x:%02x:00\n", pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), @@ -302,18 +303,22 @@ static void pciehp_power_thread(struct work_struct *work) pciehp_disable_slot(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; - break; - case POWERON_STATE: mutex_unlock(&p_slot->lock); + break; + case ENABLE_REQ: + ctrl_dbg(p_slot->ctrl, + "Enabling domain:bus:device=%04x:%02x:00\n", + pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), + p_slot->ctrl->pcie->port->subordinate->number); if (pciehp_enable_slot(p_slot)) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; + mutex_unlock(&p_slot->lock); break; default: break; } - mutex_unlock(&p_slot->lock); kfree(info); } @@ -336,9 +341,11 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) switch (p_slot->state) { case BLINKINGOFF_STATE: p_slot->state = POWEROFF_STATE; + info->req = DISABLE_REQ; break; case BLINKINGON_STATE: p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; break; default: kfree(info); @@ -428,10 +435,13 @@ static void handle_surprise_event(struct slot *p_slot) INIT_WORK(&info->work, pciehp_power_thread); pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) + if (!getstatus) { p_slot->state = POWEROFF_STATE; - else + info->req = DISABLE_REQ; + } else { p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; + } queue_work(p_slot->wq, &info->work); } @@ -451,6 +461,7 @@ static void handle_link_event(struct slot *p_slot, u32 event) return; } info->p_slot = p_slot; + info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ; INIT_WORK(&info->work, pciehp_power_thread); switch (p_slot->state) { -- cgit v1.2.3 From 50b52fdee050745935d92e7026373edea2647e60 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 4 Feb 2014 18:31:11 -0800 Subject: PCI: pciehp: Add hotplug_lock to serialize hotplug events Today it is there is no protection around pciehp_enable_slot() and pciehp_disable_slot() to ensure that they complete before another hot-plug operation can be done on that particular slot. This patch introduces the slot->hotplug_lock to ensure that any hotplug operations (add / remove) complete before another hotplug event can begin processing on that particular slot. Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 1 + drivers/pci/hotplug/pciehp_core.c | 7 ++++++- drivers/pci/hotplug/pciehp_ctrl.c | 17 +++++++++++++++-- drivers/pci/hotplug/pciehp_hpc.c | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index d8d033619a77..8a66866b8cf1 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -76,6 +76,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct mutex hotplug_lock; struct workqueue_struct *wq; }; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 53b58debc288..23b4bde8aad3 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -283,8 +283,11 @@ static int pciehp_probe(struct pcie_device *dev) slot = ctrl->slot; pciehp_get_adapter_status(slot, &occupied); pciehp_get_power_status(slot, &poweron); - if (occupied && pciehp_force) + if (occupied && pciehp_force) { + mutex_lock(&slot->hotplug_lock); pciehp_enable_slot(slot); + mutex_unlock(&slot->hotplug_lock); + } /* If empty slot's power status is on, turn power off */ if (!occupied && poweron && POWER_CTRL(ctrl)) pciehp_power_off_slot(slot); @@ -328,10 +331,12 @@ static int pciehp_resume (struct pcie_device *dev) /* Check if slot is occupied */ pciehp_get_adapter_status(slot, &status); + mutex_lock(&slot->hotplug_lock); if (status) pciehp_enable_slot(slot); else pciehp_disable_slot(slot); + mutex_unlock(&slot->hotplug_lock); return 0; } #endif /* PM */ diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 3e40ec0f7da1..fec99a164d93 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -293,6 +293,7 @@ static void pciehp_power_thread(struct work_struct *work) struct power_work_info *info = container_of(work, struct power_work_info, work); struct slot *p_slot = info->p_slot; + int ret; switch (info->req) { case DISABLE_REQ: @@ -300,7 +301,9 @@ static void pciehp_power_thread(struct work_struct *work) "Disabling domain:bus:device=%04x:%02x:00\n", pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), p_slot->ctrl->pcie->port->subordinate->number); + mutex_lock(&p_slot->hotplug_lock); pciehp_disable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; mutex_unlock(&p_slot->lock); @@ -310,7 +313,10 @@ static void pciehp_power_thread(struct work_struct *work) "Enabling domain:bus:device=%04x:%02x:00\n", pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), p_slot->ctrl->pcie->port->subordinate->number); - if (pciehp_enable_slot(p_slot)) + mutex_lock(&p_slot->hotplug_lock); + ret = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); + if (ret) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; @@ -546,6 +552,9 @@ static void interrupt_event_handler(struct work_struct *work) kfree(info); } +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_enable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -584,7 +593,9 @@ int pciehp_enable_slot(struct slot *p_slot) return rc; } - +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -617,7 +628,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) case STATIC_STATE: p_slot->state = POWERON_STATE; mutex_unlock(&p_slot->lock); + mutex_lock(&p_slot->hotplug_lock); retval = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 6433e73aa2df..da4b0204b4f7 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -686,6 +686,7 @@ static int pcie_init_slot(struct controller *ctrl) slot->ctrl = ctrl; mutex_init(&slot->lock); + mutex_init(&slot->hotplug_lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; -- cgit v1.2.3 From a760d2fb2c700469f2578f980e30423bcba316ac Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 5 Feb 2014 11:55:49 +0100 Subject: PCI: mvebu: Use Device ID and revision from underlying endpoint Marvell SoCs place the SoC number into the PCIe endpoint device ID. The SoC stepping is placed into the PCIe revision. The old plat-orion PCIe driver allowed this information to be seen in user space with a simple lspci command. The new driver places a virtual PCI-PCI bridge on top of these endpoints. It has its own hard coded PCI device ID. Thus it is no longer possible to see what the SoC is using lspci. When initializing the PCI-PCI bridge, set its device ID and revision from the underlying endpoint, thus restoring this functionality. Debian would like to use this in order to aid installing the correct DTB file. Fixes: 45361a4fe4464 ("pci: PCIe driver for Marvell Armada 370/XP systems") Signed-off-by: Andrew Lunn Signed-off-by: Bjorn Helgaas Acked-by: Thomas Petazzoni Acked-by: Jason Cooper Cc: stable@vger.kernel.org # v3.11+ --- drivers/pci/host/pci-mvebu.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 13478ecd4113..0e79665afd44 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -60,14 +60,6 @@ #define PCIE_DEBUG_CTRL 0x1a60 #define PCIE_DEBUG_SOFT_RESET BIT(20) -/* - * This product ID is registered by Marvell, and used when the Marvell - * SoC is not the root complex, but an endpoint on the PCIe bus. It is - * therefore safe to re-use this PCI ID for our emulated PCI-to-PCI - * bridge. - */ -#define MARVELL_EMULATED_PCI_PCI_BRIDGE_ID 0x7846 - /* PCI configuration space of a PCI-to-PCI bridge */ struct mvebu_sw_pci_bridge { u16 vendor; @@ -388,7 +380,8 @@ static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port) bridge->class = PCI_CLASS_BRIDGE_PCI; bridge->vendor = PCI_VENDOR_ID_MARVELL; - bridge->device = MARVELL_EMULATED_PCI_PCI_BRIDGE_ID; + bridge->device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; + bridge->revision = mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; bridge->header_type = PCI_HEADER_TYPE_BRIDGE; bridge->cache_line_size = 0x10; -- cgit v1.2.3 From 357fe8576374652be3dfe1c829c3c0a4dd40c24f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 10 Feb 2014 18:23:50 +0300 Subject: PCI: cpqphp: Fix hex vs decimal typo in cpqhpc_probe() This is a static checker fix and I can't test it, but from the context it definitely looks like hexadecimal 0x20 was intended here instead of decimal 20. Signed-off-by: Dan Carpenter Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 31273e155e6c..037e2612c5bd 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -920,12 +920,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; break; } - if (bus_cap & 20) { + if (bus_cap & 0x20) { dbg("bus max supports 66MHz PCI-X\n"); bus->max_bus_speed = PCI_SPEED_66MHz_PCIX; break; } - if (bus_cap & 10) { + if (bus_cap & 0x10) { dbg("bus max supports 66MHz PCI\n"); bus->max_bus_speed = PCI_SPEED_66MHz; break; -- cgit v1.2.3 From 39656f29f665f4c57e6f7c2566b0008b2f9f63f0 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Mon, 3 Feb 2014 17:38:36 +0000 Subject: PCI: Cleanup per-arch list of object files setup-bus.o is now included unconditionally as of commit 7dc303033425 ("PCI: Always build setup-bus when PCI is enabled"). Remove it from the per-arch list of object files. Signed-off-by: Liviu Dudau Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 17d2b07ee67c..e2501ac6fe84 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -33,21 +33,15 @@ obj-$(CONFIG_PCI_IOV) += iov.o # # Some architectures use the generic PCI setup functions # -obj-$(CONFIG_X86) += setup-bus.o -obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o -obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o -obj-$(CONFIG_UNICORE32) += setup-bus.o setup-irq.o -obj-$(CONFIG_PARISC) += setup-bus.o -obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o -obj-$(CONFIG_PPC) += setup-bus.o -obj-$(CONFIG_FRV) += setup-bus.o -obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o +obj-$(CONFIG_ALPHA) += setup-irq.o +obj-$(CONFIG_ARM) += setup-irq.o +obj-$(CONFIG_UNICORE32) += setup-irq.o +obj-$(CONFIG_SUPERH) += setup-irq.o +obj-$(CONFIG_MIPS) += setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o -obj-$(CONFIG_MN10300) += setup-bus.o -obj-$(CONFIG_MICROBLAZE) += setup-bus.o -obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o -obj-$(CONFIG_SPARC_LEON) += setup-bus.o setup-irq.o -obj-$(CONFIG_M68K) += setup-bus.o setup-irq.o +obj-$(CONFIG_TILE) += setup-irq.o +obj-$(CONFIG_SPARC_LEON) += setup-irq.o +obj-$(CONFIG_M68K) += setup-irq.o # # ACPI Related PCI FW Functions -- cgit v1.2.3 From 50277c8b06d56f2345e1a0693db46db29fc6d063 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 11 Feb 2014 17:36:51 -0700 Subject: PCI: pciehp: Don't turn slot off when hot-added device already exists If we found device already exists during hot add device, we should leave it, not turn the slot off. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_ctrl.c | 3 ++- drivers/pci/hotplug/pciehp_pci.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index fec99a164d93..b4a4ac150e61 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -233,7 +233,8 @@ static int board_added(struct slot *p_slot) if (retval) { ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", pci_domain_nr(parent), parent->number); - goto err_exit; + if (retval != -EEXIST) + goto err_exit; } pciehp_green_led_on(p_slot); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index b07d7cc2d697..1b533060ce65 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -50,7 +50,7 @@ int pciehp_configure_device(struct slot *p_slot) "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), pci_domain_nr(parent), parent->number); pci_dev_put(dev); - ret = -EINVAL; + ret = -EEXIST; goto out; } -- cgit v1.2.3 From c6f0d5adc21e2d9623fe757f35a0a642a1d863c4 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 13 Feb 2014 21:13:58 +0800 Subject: ACPI / hotplug / PCI: Use list_for_each_entry() for bus traversal Replace list_for_each() + pci_bus_b() with list_for_each_entry(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/hotplug/acpiphp_glue.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index cd929aed3613..aee6a0acbbe9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -450,7 +450,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) */ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; /* @@ -463,8 +463,8 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) */ max = bus->busn_res.start; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if (n > max) max = n; } -- cgit v1.2.3 From 4f4bde1df33bde076f53325bdf2c6430cf85e1bb Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 14 Feb 2014 11:46:15 -0700 Subject: PCI: mvebu: Fix potential issue in range parsing The second parameter of of_read_number() is not the index, but a size. As it happens, in this case it may work just fine because of the conversion to u32 and the favorable endianness on this architecture. Fixes: 11be65472a427 ("PCI: mvebu: Adapt to the new device tree layout") Tested-by: Thomas Petazzoni Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Bjorn Helgaas Acked-by: Thomas Petazzoni Acked-by: Jason Cooper Cc: stable@vger.kernel.org # v3.12+ --- drivers/pci/host/pci-mvebu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 0e79665afd44..05e352889868 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -797,7 +797,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, for (i = 0; i < nranges; i++) { u32 flags = of_read_number(range, 1); - u32 slot = of_read_number(range, 2); + u32 slot = of_read_number(range + 1, 1); u64 cpuaddr = of_read_number(range + na, pna); unsigned long rtype; -- cgit v1.2.3 From 94e6a9b93064b49024b8701d2d81fcb4a821fa09 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 13 Feb 2014 21:14:03 +0800 Subject: PCI: Remove pci_bus_b() and use list_for_each_entry() directly Replace list_for_each() with list_for_each_entry(), which means we no longer need pci_bus_b() and can remove it. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 6 +++--- drivers/pci/search.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1febe90831b4..6f5ed88ca126 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -108,12 +108,12 @@ static bool pcie_ari_disabled; */ unsigned char pci_bus_max_busnr(struct pci_bus* bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; max = bus->busn_res.end; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if(n > max) max = n; } diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 3ff2ac7c14e2..4a1b972efe7f 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -54,14 +54,14 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev) static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr) { - struct pci_bus* child; - struct list_head *tmp; + struct pci_bus *child; + struct pci_bus *tmp; if(bus->number == busnr) return bus; - list_for_each(tmp, &bus->children) { - child = pci_do_find_bus(pci_bus_b(tmp), busnr); + list_for_each_entry(tmp, &bus->children, node) { + child = pci_do_find_bus(tmp, busnr); if(child) return child; } @@ -111,7 +111,7 @@ pci_find_next_bus(const struct pci_bus *from) down_read(&pci_bus_sem); n = from ? from->node.next : pci_root_buses.next; if (n != &pci_root_buses) - b = pci_bus_b(n); + b = list_entry(n, struct pci_bus, node); up_read(&pci_bus_sem); return b; } -- cgit v1.2.3 From ed65b78881c713b41051310780f94bde3c010db9 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 18 Feb 2014 11:10:51 +0900 Subject: PCI: rcar: Check platform_get_irq() return code The current code does not check the return from platform_get_irq() so add an error check and return if this call does fail. Signed-off-by: Ben Dooks Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/pci-rcar-gen2.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index ceec147baec3..201e4a450340 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -308,6 +308,11 @@ static int __init rcar_pci_probe(struct platform_device *pdev) priv->reg = reg; priv->dev = &pdev->dev; + if (priv->irq < 0) { + dev_err(&pdev->dev, "no valid irq found\n"); + return priv->irq; + } + return rcar_pci_add_controller(priv); } -- cgit v1.2.3 From 80a595d941a2ce2953e6c77d822d8a4c3d8fa5a0 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 18 Feb 2014 11:11:01 +0900 Subject: PCI: rcar: Add error interrupt handling Add option to enable interrupts to report any errors from the AHB-PCI bridge to help find any issues with the bridge when in use. Signed-off-by: Ben Dooks Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/pci-rcar-gen2.c | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 201e4a450340..11e444e79f45 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -39,9 +39,26 @@ #define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) #define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_SIGTABORT (1 << 0) +#define RCAR_PCI_INT_SIGRETABORT (1 << 1) +#define RCAR_PCI_INT_REMABORT (1 << 2) +#define RCAR_PCI_INT_PERR (1 << 3) +#define RCAR_PCI_INT_SIGSERR (1 << 4) +#define RCAR_PCI_INT_RESERR (1 << 5) +#define RCAR_PCI_INT_WIN1ERR (1 << 12) +#define RCAR_PCI_INT_WIN2ERR (1 << 13) #define RCAR_PCI_INT_A (1 << 16) #define RCAR_PCI_INT_B (1 << 17) #define RCAR_PCI_INT_PME (1 << 19) +#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_REMABORT | \ + RCAR_PCI_INT_PERR | \ + RCAR_PCI_INT_SIGSERR | \ + RCAR_PCI_INT_RESERR | \ + RCAR_PCI_INT_WIN1ERR | \ + RCAR_PCI_INT_WIN2ERR) #define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) #define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) @@ -164,6 +181,46 @@ static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return priv->irq; } +#ifdef CONFIG_PCI_DEBUG +/* if debug enabled, then attach an error handler irq to the bridge */ + +static irqreturn_t rcar_pci_err_irq(int irq, void *pw) +{ + struct rcar_pci_priv *priv = pw; + u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); + + if (status & RCAR_PCI_INT_ALLERRORS) { + dev_err(priv->dev, "error irq: status %08x\n", status); + + /* clear the error(s) */ + iowrite32(status & RCAR_PCI_INT_ALLERRORS, + priv->reg + RCAR_PCI_INT_STATUS_REG); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) +{ + int ret; + u32 val; + + ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq, + IRQF_SHARED, "error irq", priv); + if (ret) { + dev_err(priv->dev, "cannot claim IRQ for error handling\n"); + return; + } + + val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG); + val |= RCAR_PCI_INT_ALLERRORS; + iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG); +} +#else +static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } +#endif + /* PCI host controller setup */ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) { @@ -224,6 +281,9 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, reg + RCAR_PCI_INT_ENABLE_REG); + if (priv->irq > 0) + rcar_pci_setup_errirq(priv); + /* Add PCI resources */ pci_add_resource(&sys->resources, &priv->io_res); pci_add_resource(&sys->resources, &priv->mem_res); -- cgit v1.2.3 From e64a2a973e179613489a0ddb1bb550dff108164e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 18 Feb 2014 11:11:11 +0900 Subject: PCI: rcar: Fix bridge logic configuration accesses The bridge logic at slot 0 only supports reads up to 0x40 and the rest of the PCI configuration space for this slot is marked as reserved in the manual. Trying a read from offset 0x100 is producing an error from the bridge. With error interrupts enabled, the following is printed: pci-rcar-gen2 ee0d0000.pci: error irq: status 00000014 Signed-off-by: Ben Dooks Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/pci-rcar-gen2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 11e444e79f45..6288476576a7 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -119,6 +119,10 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, if (slot > 2) return NULL; + /* bridge logic only has registers to 0x40 */ + if (slot == 0x0 && where >= 0x40) + return NULL; + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; -- cgit v1.2.3 From 546cadda3575153bdd0af4febfe958cc5945f95a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 18 Feb 2014 11:11:21 +0900 Subject: PCI: rcar: Register each instance independently Convert the code to allow per-device probe() like other device drivers. This also delays driver registration due to change from subsys_initcall() to regular module_platform_driver(). Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/pci-rcar-gen2.c | 80 ++++++++++------------------------------ 1 file changed, 20 insertions(+), 60 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 6288476576a7..967997b2890b 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -91,9 +91,6 @@ #define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) -/* Number of internal PCI controllers */ -#define RCAR_PCI_NR_CONTROLLERS 3 - struct rcar_pci_priv { struct device *dev; void __iomem *reg; @@ -177,7 +174,7 @@ static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, } /* PCI interrupt mapping */ -static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pci_sys_data *sys = dev->bus->sysdata; struct rcar_pci_priv *priv = sys->private_data; @@ -226,7 +223,7 @@ static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } #endif /* PCI host controller setup */ -static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) +static int rcar_pci_setup(int nr, struct pci_sys_data *sys) { struct rcar_pci_priv *priv = sys->private_data; void __iomem *reg = priv->reg; @@ -292,6 +289,8 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) pci_add_resource(&sys->resources, &priv->io_res); pci_add_resource(&sys->resources, &priv->mem_res); + /* Setup bus number based on platform device id */ + sys->busnr = to_platform_device(priv->dev)->id; return 1; } @@ -300,48 +299,13 @@ static struct pci_ops rcar_pci_ops = { .write = rcar_pci_write_config, }; -static struct hw_pci rcar_hw_pci __initdata = { - .map_irq = rcar_pci_map_irq, - .ops = &rcar_pci_ops, - .setup = rcar_pci_setup, -}; - -static int rcar_pci_count __initdata; - -static int __init rcar_pci_add_controller(struct rcar_pci_priv *priv) -{ - void **private_data; - int count; - - if (rcar_hw_pci.nr_controllers < rcar_pci_count) - goto add_priv; - - /* (Re)allocate private data pointer array if needed */ - count = rcar_pci_count + RCAR_PCI_NR_CONTROLLERS; - private_data = kzalloc(count * sizeof(void *), GFP_KERNEL); - if (!private_data) - return -ENOMEM; - - rcar_pci_count = count; - if (rcar_hw_pci.private_data) { - memcpy(private_data, rcar_hw_pci.private_data, - rcar_hw_pci.nr_controllers * sizeof(void *)); - kfree(rcar_hw_pci.private_data); - } - - rcar_hw_pci.private_data = private_data; - -add_priv: - /* Add private data pointer to the array */ - rcar_hw_pci.private_data[rcar_hw_pci.nr_controllers++] = priv; - return 0; -} - -static int __init rcar_pci_probe(struct platform_device *pdev) +static int rcar_pci_probe(struct platform_device *pdev) { struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; void __iomem *reg; + struct hw_pci hw; + void *hw_private[1]; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); @@ -377,31 +341,27 @@ static int __init rcar_pci_probe(struct platform_device *pdev) return priv->irq; } - return rcar_pci_add_controller(priv); + hw_private[0] = priv; + memset(&hw, 0, sizeof(hw)); + hw.nr_controllers = ARRAY_SIZE(hw_private); + hw.private_data = hw_private; + hw.map_irq = rcar_pci_map_irq; + hw.ops = &rcar_pci_ops; + hw.setup = rcar_pci_setup; + pci_common_init_dev(&pdev->dev, &hw); + return 0; } static struct platform_driver rcar_pci_driver = { .driver = { .name = "pci-rcar-gen2", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, + .probe = rcar_pci_probe, }; -static int __init rcar_pci_init(void) -{ - int retval; - - retval = platform_driver_probe(&rcar_pci_driver, rcar_pci_probe); - if (!retval) - pci_common_init(&rcar_hw_pci); - - /* Private data pointer array is not needed any more */ - kfree(rcar_hw_pci.private_data); - rcar_hw_pci.private_data = NULL; - - return retval; -} - -subsys_initcall(rcar_pci_init); +module_platform_driver(rcar_pci_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI"); -- cgit v1.2.3 From 33966fd9b5bcae325a283d7d46156bab25bc162b Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 18 Feb 2014 11:11:32 +0900 Subject: PCI: rcar: Break out window size handling Break out the hard coded window size code to allow dynamic setup. The window size is still left at 1GiB but with this patch changing window size is easy for testing. Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/pci-rcar-gen2.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 967997b2890b..fd3e3ab56509 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /* AHB-PCI Bridge PCI communication registers */ @@ -98,6 +99,7 @@ struct rcar_pci_priv { struct resource mem_res; struct resource *cfg_res; int irq; + unsigned long window_size; }; /* PCI configuration space operations */ @@ -241,10 +243,31 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(val, reg + RCAR_USBCTR_REG); udelay(4); - /* De-assert reset and set PCIAHB window1 size to 1GB */ + /* De-assert reset and reset PCIAHB window1 size */ val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); - iowrite32(val | RCAR_USBCTR_PCIAHB_WIN1_1G, reg + RCAR_USBCTR_REG); + + /* Setup PCIAHB window1 size */ + switch (priv->window_size) { + case SZ_2G: + val |= RCAR_USBCTR_PCIAHB_WIN1_2G; + break; + case SZ_1G: + val |= RCAR_USBCTR_PCIAHB_WIN1_1G; + break; + case SZ_512M: + val |= RCAR_USBCTR_PCIAHB_WIN1_512M; + break; + default: + pr_warn("unknown window size %ld - defaulting to 256M\n", + priv->window_size); + priv->window_size = SZ_256M; + /* fall-through */ + case SZ_256M: + val |= RCAR_USBCTR_PCIAHB_WIN1_256M; + break; + } + iowrite32(val, reg + RCAR_USBCTR_REG); /* Configure AHB master and slave modes */ iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); @@ -255,7 +278,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) RCAR_PCI_ARBITER_PCIBP_MODE; iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); - /* PCI-AHB mapping: 0x40000000-0x80000000 */ + /* PCI-AHB mapping: 0x40000000 base */ iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, reg + RCAR_PCIAHB_WIN1_CTR_REG); @@ -341,6 +364,8 @@ static int rcar_pci_probe(struct platform_device *pdev) return priv->irq; } + priv->window_size = SZ_1G; + hw_private[0] = priv; memset(&hw, 0, sizeof(hw)); hw.nr_controllers = ARRAY_SIZE(hw_private); -- cgit v1.2.3 From 2613ba480fb7b40c67eea36d03c9946977828623 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 12 Feb 2014 15:57:08 -0700 Subject: PCI: mvebu: Call request_resource() on the apertures It is typical for host drivers to request a resource for the aperture; once this is done the PCI core will properly populate resources for all BARs in the system. With this patch cat /proc/iomem will now show: e0000000-efffffff : PCI MEM 0000 e0000000-e00fffff : PCI Bus 0000:01 e0000000-e001ffff : 0000:01:00.0 Tested on Kirkwood. Signed-off-by: Arnd Bergmann Signed-off-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 05e352889868..d3d1cfd51e09 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -101,7 +101,9 @@ struct mvebu_pcie { struct mvebu_pcie_port *ports; struct msi_chip *msi; struct resource io; + char io_name[30]; struct resource realio; + char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -672,10 +674,30 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; + int domain = 0; - if (resource_size(&pcie->realio) != 0) +#ifdef CONFIG_PCI_DOMAINS + domain = sys->domain; +#endif + + snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", + domain); + pcie->mem.name = pcie->mem_name; + + snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); + pcie->realio.name = pcie->io_name; + + if (request_resource(&iomem_resource, &pcie->mem)) + return 0; + + if (resource_size(&pcie->realio) != 0) { + if (request_resource(&ioport_resource, &pcie->realio)) { + release_resource(&pcie->mem); + return 0; + } pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); + } pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); -- cgit v1.2.3 From 1e2571a78138361f74afeb4c5df859b9536b2d8e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 29 Jan 2014 16:13:51 -0700 Subject: PCI: Enable INTx if BIOS left them disabled Some firmware leaves the Interrupt Disable bit set even if the device uses INTx interrupts. Clear Interrupt Disable so we get those interrupts. Based on the report mentioned below, if the user selects the "EHCI only" option in the Intel Baytrail BIOS, the EHCI device is handed off to the OS with the PCI_COMMAND_INTX_DISABLE bit set. Link: http://lkml.kernel.org/r/20140114181721.GC12126@xanatos Link: https://bugzilla.kernel.org/show_bug.cgi?id=70601 Reported-by: Chris Cheng Reported-and-tested-by: Jamie Chen Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org CC: Sarah Sharp --- drivers/pci/pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 52e10e1181d0..8dc3e701ec57 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1181,6 +1181,8 @@ EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state); static int do_pci_enable_device(struct pci_dev *dev, int bars) { int err; + u16 cmd; + u8 pin; err = pci_set_power_state(dev, PCI_D0); if (err < 0 && err != -EIO) @@ -1190,6 +1192,14 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars) return err; pci_fixup_device(pci_fixup_enable, dev); + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_INTX_DISABLE) + pci_write_config_word(dev, PCI_COMMAND, + cmd & ~PCI_COMMAND_INTX_DISABLE); + } + return 0; } -- cgit v1.2.3 From 46cb7b1bd86fc227a5325ba8fa4c8be70fade4e8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 30 Jan 2014 11:28:15 -0700 Subject: PCI: Remove unused SR-IOV VF Migration support This reverts commit 74bb1bcc7dbb ("PCI: handle SR-IOV Virtual Function Migration"), removing this exported interface: pci_sriov_migration() Since pci_sriov_migration() is unused, it is impossible to schedule sriov_migration_task() or use any of the other migration infrastructure. This is based on Stephen Hemminger's patch (see link below), but goes a bit further. Link: http://lkml.kernel.org/r/20131227132710.7190647c@nehalam.linuxnetplumber.net Signed-off-by: Bjorn Helgaas CC: Stephen Hemminger --- drivers/pci/iov.c | 119 ------------------------------------------------------ drivers/pci/pci.h | 4 -- 2 files changed, 123 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9dce7c5e2a77..de7a74782f92 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -170,97 +170,6 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) pci_dev_put(dev); } -static int sriov_migration(struct pci_dev *dev) -{ - u16 status; - struct pci_sriov *iov = dev->sriov; - - if (!iov->num_VFs) - return 0; - - if (!(iov->cap & PCI_SRIOV_CAP_VFM)) - return 0; - - pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); - if (!(status & PCI_SRIOV_STATUS_VFM)) - return 0; - - schedule_work(&iov->mtask); - - return 1; -} - -static void sriov_migration_task(struct work_struct *work) -{ - int i; - u8 state; - u16 status; - struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); - - for (i = iov->initial_VFs; i < iov->num_VFs; i++) { - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_MI) { - writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 1); - } else if (state == PCI_SRIOV_VFM_MO) { - virtfn_remove(iov->self, i, 1); - writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 0); - } - } - - pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); - status &= ~PCI_SRIOV_STATUS_VFM; - pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); -} - -static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) -{ - int bir; - u32 table; - resource_size_t pa; - struct pci_sriov *iov = dev->sriov; - - if (nr_virtfn <= iov->initial_VFs) - return 0; - - pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); - bir = PCI_SRIOV_VFM_BIR(table); - if (bir > PCI_STD_RESOURCE_END) - return -EIO; - - table = PCI_SRIOV_VFM_OFFSET(table); - if (table + nr_virtfn > pci_resource_len(dev, bir)) - return -EIO; - - pa = pci_resource_start(dev, bir) + table; - iov->mstate = ioremap(pa, nr_virtfn); - if (!iov->mstate) - return -ENOMEM; - - INIT_WORK(&iov->mtask, sriov_migration_task); - - iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - return 0; -} - -static void sriov_disable_migration(struct pci_dev *dev) -{ - struct pci_sriov *iov = dev->sriov; - - iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - cancel_work_sync(&iov->mtask); - iounmap(iov->mstate); -} - static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -351,12 +260,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto failed; } - if (iov->cap & PCI_SRIOV_CAP_VFM) { - rc = sriov_enable_migration(dev, nr_virtfn); - if (rc) - goto failed; - } - kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->num_VFs = nr_virtfn; @@ -387,9 +290,6 @@ static void sriov_disable(struct pci_dev *dev) if (!iov->num_VFs) return; - if (iov->cap & PCI_SRIOV_CAP_VFM) - sriov_disable_migration(dev); - for (i = 0; i < iov->num_VFs; i++) virtfn_remove(dev, i, 0); @@ -687,25 +587,6 @@ void pci_disable_sriov(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_disable_sriov); -/** - * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration - * @dev: the PCI device - * - * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. - * - * Physical Function driver is responsible to register IRQ handler using - * VF Migration Interrupt Message Number, and call this function when the - * interrupt is generated by the hardware. - */ -irqreturn_t pci_sriov_migration(struct pci_dev *dev) -{ - if (!dev->is_physfn) - return IRQ_NONE; - - return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; -} -EXPORT_SYMBOL_GPL(pci_sriov_migration); - /** * pci_num_vf - return number of VFs associated with a PF device_release_driver * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4df38df224f4..6bd082299e31 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1,8 +1,6 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H -#include - #define PCI_CFG_SPACE_SIZE 256 #define PCI_CFG_SPACE_EXP_SIZE 4096 @@ -240,8 +238,6 @@ struct pci_sriov { struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ - struct work_struct mtask; /* VF Migration task */ - u8 __iomem *mstate; /* VF Migration State Array */ }; #ifdef CONFIG_PCI_ATS -- cgit v1.2.3 From dbffdd6862e67d60703f2df66c558bf448f81d6e Mon Sep 17 00:00:00 2001 From: Mohit Kumar Date: Wed, 19 Feb 2014 17:34:35 +0530 Subject: PCI: designware: Fix RC BAR to be single 64-bit non-prefetchable memory BAR The Synopsys PCIe core provides one pair of 32-bit BARs (BAR 0 and BAR 1). The BARs can be configured as follows: - One 64-bit BAR: BARs 0 and 1 are combined to form a single 64-bit BAR - Two 32-bit BARs: BARs 0 and 1 are two independent 32-bit BARs This patch corrects 64-bit, non-prefetchable memory BAR configuration implemented in dw driver. Signed-off-by: Mohit Kumar Signed-off-by: Bjorn Helgaas Cc: Pratyush Anand Cc: Jingoo Han Cc: Arnd Bergmann Cc: stable@vger.kernel.org # v3.12+ --- drivers/pci/host/pcie-designware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 17ce88f79d2b..6d23d8c893cd 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -800,7 +800,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* setup RC BARs */ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); - dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_1); + dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); /* setup interrupt pins */ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val); -- cgit v1.2.3 From f95d3ae771916c8c7024fecfb6c420e5dfeced05 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 19 Feb 2014 13:22:18 -0700 Subject: PCI: imx6: Wait for retraining This patch handles the case where the PCIe link is up and running, yet drops into the LTSSM training mode. The link spends short time in the LTSSM training mode, but the current code can misinterpret it as the link being stalled. Waiting for the LTSSM training to complete fixes the issue. Quoting Sascha: This is broken since commit 7f9f40c01cce ('PCI: imx6: Report "link up" only after link training completes'). The designware driver changes the PORT_LOGIC_SPEED_CHANGE bit in dw_pcie_host_init() which causes the link to be retrained. During the next call to dw_pcie_rd_conf() the link is then reported being down and the function returns PCIBIOS_DEVICE_NOT_FOUND resulting in nonfunctioning PCIe. Fixes: 7f9f40c01cce (PCI: imx6: Report "link up" only after link training completes) Tested-by: Troy Kisky Tested-by: Sascha Hauer Signed-off-by: Marek Vasut Signed-off-by: Troy Kisky Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo --- drivers/pci/host/pci-imx6.c | 47 ++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index e8663a8c3406..ee082509b0ba 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -424,20 +424,40 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp) static int imx6_pcie_link_up(struct pcie_port *pp) { - u32 rc, ltssm, rx_valid; + u32 rc, debug_r0, rx_valid; + int count = 5; /* - * Test if the PHY reports that the link is up and also that - * the link training finished. It might happen that the PHY - * reports the link is already up, but the link training bit - * is still set, so make sure to check the training is done - * as well here. + * Test if the PHY reports that the link is up and also that the LTSSM + * training finished. There are three possible states of the link when + * this code is called: + * 1) The link is DOWN (unlikely) + * The link didn't come up yet for some reason. This usually means + * we have a real problem somewhere. Reset the PHY and exit. This + * state calls for inspection of the DEBUG registers. + * 2) The link is UP, but still in LTSSM training + * Wait for the training to finish, which should take a very short + * time. If the training does not finish, we have a problem and we + * need to inspect the DEBUG registers. If the training does finish, + * the link is up and operating correctly. + * 3) The link is UP and no longer in LTSSM training + * The link is up and operating correctly. */ - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); - if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && - !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) - return 1; - + while (1) { + rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP)) + break; + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) + return 1; + if (!count--) + break; + dev_dbg(pp->dev, "Link is up, but still in training\n"); + /* + * Wait a little bit, then re-check if the link finished + * the training. + */ + usleep_range(1000, 2000); + } /* * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). @@ -446,15 +466,16 @@ static int imx6_pcie_link_up(struct pcie_port *pp) * to gen2 is stuck */ pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); - ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F; + debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0); if (rx_valid & 0x01) return 0; - if (ltssm != 0x0d) + if ((debug_r0 & 0x3f) != 0x0d) return 0; dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); + dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc); imx6_pcie_reset_phy(pp); -- cgit v1.2.3 From 2b3940b60626ac4932fa048cc74f2a872cc4bfb4 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 18 Feb 2014 18:53:19 -0800 Subject: PCI: pciehp: Remove a non-existent card, regardless of "surprise" capability In case a card is physically yanked out, it should immediately be removed, regardless of the "surprise" capability bit. Thus: - Always handle the physical removal - regardless of the "surprise" bit. - Don't use "surprise" capability when making decisions about enabling presence detect notifications. - Reword the comments to indicate the intent. Signed-off-by: Rajat Jain Signed-off-by: Rajat Jain Signed-off-by: Guenter Roeck Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_ctrl.c | 9 ++++++++- drivers/pci/hotplug/pciehp_hpc.c | 9 +++++---- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index b4a4ac150e61..0c2e524a1cf0 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -535,9 +535,16 @@ static void interrupt_event_handler(struct work_struct *work) pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: - case INT_PRESENCE_OFF: if (!HP_SUPR_RM(ctrl)) break; + ctrl_dbg(ctrl, "Surprise Insertion\n"); + handle_surprise_event(p_slot); + break; + case INT_PRESENCE_OFF: + /* + * Regardless of surprise capability, we need to + * definitely remove a card that has been pulled out! + */ ctrl_dbg(ctrl, "Surprise Removal\n"); handle_surprise_event(p_slot); break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index da4b0204b4f7..d7d058fa19a4 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -619,9 +619,10 @@ static void pcie_disable_notification(struct controller *ctrl) /* * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary - * bus reset of the bridge, but if the slot supports surprise removal (or - * link state change based hotplug), we need to disable presence detection - * (or link state notifications) around the bus reset and clear any spurious + * bus reset of the bridge, but at the same time we want to ensure that it is + * not seen as a hot-unplug, followed by the hot-plug of the device. Thus, + * disable link state notification and presence detection change notification + * momentarily, if we see that they could interfere. Also, clear any spurious * events after. */ int pciehp_reset_slot(struct slot *slot, int probe) @@ -633,7 +634,7 @@ int pciehp_reset_slot(struct slot *slot, int probe) if (probe) return 0; - if (HP_SUPR_RM(ctrl) && !ATTN_BUTTN(ctrl)) { + if (!ATTN_BUTTN(ctrl)) { ctrl_mask |= PCI_EXP_SLTCTL_PDCE; stat_mask |= PCI_EXP_SLTSTA_PDC; } -- cgit v1.2.3 From 9cad7f582055513fe13a93fee3ddb213656a6a5d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 11 Feb 2014 15:26:29 -0700 Subject: PCI: pciehp: Cleanup whitespace Minor whitespace cleanup; no functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_acpi.c | 1 + drivers/pci/hotplug/pciehp_core.c | 1 + drivers/pci/hotplug/pciehp_ctrl.c | 9 ++++----- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index eddddd447d0d..20fea57d2149 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -112,6 +112,7 @@ static struct pcie_port_service_driver __initdata dummy_driver = { static int __init select_detection_mode(void) { struct dummy_slot *slot, *tmp; + if (pcie_port_service_register(&dummy_driver)) return PCIEHP_DETECT_ACPI; pcie_port_service_unregister(&dummy_driver); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 23b4bde8aad3..0e0a2fff20a3 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -108,6 +108,7 @@ static int init_slot(struct controller *ctrl) ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) goto out; + ops->enable_slot = enable_slot; ops->disable_slot = disable_slot; ops->get_power_status = get_power_status; diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 0c2e524a1cf0..c75e6a678dcc 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -399,11 +399,10 @@ static void handle_button_press_event(struct slot *p_slot) */ ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) { + if (p_slot->state == BLINKINGOFF_STATE) pciehp_green_led_on(p_slot); - } else { + else pciehp_green_led_off(p_slot); - } pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); @@ -595,9 +594,9 @@ int pciehp_enable_slot(struct slot *p_slot) pciehp_get_latch_status(p_slot, &getstatus); rc = board_added(p_slot); - if (rc) { + if (rc) pciehp_get_latch_status(p_slot, &getstatus); - } + return rc; } -- cgit v1.2.3 From ef4741e90c4e6523795f4375659b5097293d87db Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 18 Feb 2014 11:12:01 +0900 Subject: PCI: rcar: Make the Kconfig dependencies more generic Update the R-Car Generation 2 PCI driver Kconfig dependencies to follow same style as other drivers - no SoC dependencies. Also, update the COMPILE_TEST bits to depend on ARM. This since the DMA bounce buffer and dma_ops handling code is ARM specific. [bhelgaas: adjust context after dropping DMABOUNCE patches] Signed-off-by: Magnus Damm Signed-off-by: Bjorn Helgaas Acked-by: Simon Horman --- drivers/pci/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 47d46c6d8468..a6f67ec8882f 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -27,7 +27,7 @@ config PCI_TEGRA config PCI_RCAR_GEN2 bool "Renesas R-Car Gen2 Internal PCI controller" - depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST) help Say Y here if you want internal PCI support on R-Car Gen2 SoC. There are 3 internal PCI controllers available with a single -- cgit v1.2.3 From 5edb93b89f6cc3089ee283656555e7a9ad36a8a0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 4 Feb 2014 19:32:28 -0800 Subject: resource: Add resource_contains() We have two identical copies of resource_contains() already, and more places that could use it. This moves it to ioport.h where it can be shared. resource_contains(struct resource *r1, struct resource *r2) returns true iff r1 and r2 are the same type (most callers already checked this separately) and the r1 address range completely contains r2. In addition, the new resource_contains() checks that both r1 and r2 have addresses assigned to them. If a resource is IORESOURCE_UNSET, it doesn't have a valid address and can't contain or be contained by another resource. Some callers already check this or for res->start. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/host-bridge.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 06ace6248c61..47aaf22d814e 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -32,11 +32,6 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, bridge->release_data = release_data; } -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { @@ -45,9 +40,6 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, resource_size_t offset = 0; list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - if (resource_contains(window->res, res)) { offset = window->offset; break; -- cgit v1.2.3 From f44116ae881868ab72274df1eff48fdbde9898af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:25:58 -0700 Subject: PCI: Remove pci_find_parent_resource() use for allocation If the resource hasn't been allocated yet, pci_find_parent_resource() is documented as returning the region "where it should be allocated from." This is impossible in general because there may be several candidates: a prefetchable BAR can be put in either a prefetchable or non-prefetchable window, a transparent bridge may have overlapping positively- and subtractively-decoded windows, and a root bus may have several windows of the same type. Allocation should be done by pci_bus_alloc_resource(), which iterates through all bus resources and looks for the best match, e.g., one with the desired prefetchability attributes, and falls back to less-desired possibilities. The only valid use of pci_find_parent_resource() is to find the parent of an already-allocated resource so we can claim it via request_resource(), and all we need for that is a bus region of the correct type that contains the resource. Note that like 8c8def26bfaa ("PCI: allow matching of prefetchable resources to non-prefetchable windows"), this depends on pci_bus_for_each_resource() iterating through positively-decoded regions before subtractively-decoded ones. We prefer not to return a subtractively-decoded region because requesting from it will likely conflict with the overlapping positively- decoded window (see Launchpad report below). Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/424142 Signed-off-by: Bjorn Helgaas CC: Linus Torvalds --- drivers/pci/pci.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1febe90831b4..99293fa40db9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -401,33 +401,40 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability); * @res: child resource record for which parent is sought * * For given resource region of given device, return the resource - * region of parent bus the given region is contained in or where - * it should be allocated from. + * region of parent bus the given region is contained in. */ struct resource * pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) { const struct pci_bus *bus = dev->bus; + struct resource *r; int i; - struct resource *best = NULL, *r; pci_bus_for_each_resource(bus, r, i) { if (!r) continue; - if (res->start && !(res->start >= r->start && res->end <= r->end)) - continue; /* Not contained */ - if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) - continue; /* Wrong type */ - if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) - return r; /* Exact match */ - /* We can't insert a non-prefetch resource inside a prefetchable parent .. */ - if (r->flags & IORESOURCE_PREFETCH) - continue; - /* .. but we can put a prefetchable resource inside a non-prefetchable one */ - if (!best) - best = r; + if (res->start && resource_contains(r, res)) { + + /* + * If the window is prefetchable but the BAR is + * not, the allocator made a mistake. + */ + if (r->flags & IORESOURCE_PREFETCH && + !(res->flags & IORESOURCE_PREFETCH)) + return NULL; + + /* + * If we're below a transparent bridge, there may + * be both a positively-decoded aperture and a + * subtractively-decoded region that contain the BAR. + * We want the positively-decoded one, so this depends + * on pci_bus_for_each_resource() giving us those + * first. + */ + return r; + } } - return best; + return NULL; } /** -- cgit v1.2.3 From bd064f0a231af336218838474ea45a64f1672190 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:25:58 -0700 Subject: PCI: Mark resources as IORESOURCE_UNSET if we can't assign them When assigning addresses to resources, mark them with IORESOURCE_UNSET before we start and clear IORESOURCE_UNSET if assignment is successful. That means that if we print the resource during assignment, we will show the size, not a meaningless address. Also, clear IORESOURCE_UNSET if we do assign an address, so we print the address when it is valid. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 ++ drivers/pci/quirks.c | 5 +++++ drivers/pci/rom.c | 2 ++ drivers/pci/setup-res.c | 4 ++++ 4 files changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 99293fa40db9..dc9ce62be7aa 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4244,6 +4244,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) "Rounding up size of resource #%d to %#llx.\n", i, (unsigned long long)size); } + r->flags |= IORESOURCE_UNSET; r->end = size - 1; r->start = 0; } @@ -4257,6 +4258,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) r = &dev->resource[i]; if (!(r->flags & IORESOURCE_MEM)) continue; + r->flags |= IORESOURCE_UNSET; r->end = resource_size(r) - 1; r->start = 0; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5cb726c193de..6e596ab77fb9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -296,6 +296,7 @@ static void quirk_s3_64M(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0x3ffffff; } @@ -937,6 +938,8 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C static void quirk_dunord(struct pci_dev *dev) { struct resource *r = &dev->resource [1]; + + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xffffff; } @@ -1740,6 +1743,7 @@ static void quirk_tc86c001_ide(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if (r->start & 0x8) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xf; } @@ -1769,6 +1773,7 @@ static void quirk_plx_pci9050(struct pci_dev *dev) dev_info(&dev->dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n", bar); + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xff; } diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 5d595724e5f4..c1839450d4d6 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -197,8 +197,10 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) void pci_cleanup_rom(struct pci_dev *pdev) { struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + if (res->flags & IORESOURCE_ROM_COPY) { kfree((void*)(unsigned long)res->start); + res->flags |= IORESOURCE_UNSET; res->flags &= ~IORESOURCE_ROM_COPY; res->start = 0; res->end = 0; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 5c060b152ce6..0474b0217fdf 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -263,6 +263,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) resource_size_t align, size; int ret; + res->flags |= IORESOURCE_UNSET; align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't assign %pR " @@ -282,6 +283,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) ret = pci_revert_fw_address(res, dev, resno, size); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) @@ -297,6 +299,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz resource_size_t new_size; int ret; + res->flags |= IORESOURCE_UNSET; if (!res->parent) { dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR " "\n", resno, res); @@ -307,6 +310,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz new_size = resource_size(res) + addsize; ret = _pci_assign_resource(dev, resno, new_size, min_align); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) -- cgit v1.2.3 From 434aafc1aefb5eb6e8c8d15284c8f929be756521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:25:59 -0700 Subject: PCI: Don't clear IORESOURCE_UNSET when updating BAR Clear IORESOURCE_UNSET when we assign an address to a resource, not when we write the address to the BAR. Also, drop the "BAR %d: set to %pR" message; this is mostly redundant with the "BAR %d: assigned %pR" message from pci_assign_resource(). Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 0474b0217fdf..725d5b28398c 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -101,11 +101,6 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (disable) pci_write_config_word(dev, PCI_COMMAND, cmd); - - res->flags &= ~IORESOURCE_UNSET; - dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", - resno, res, (unsigned long long)region.start, - (unsigned long long)region.end); } int pci_claim_resource(struct pci_dev *dev, int resource) -- cgit v1.2.3 From cd8a4d3657c3f2cf9ce3780707be1debb8fea6e2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:25:59 -0700 Subject: PCI: Check IORESOURCE_UNSET before updating BAR Check to make sure we don't update a BAR with an address we haven't assigned. If we haven't assigned an address to a resource, we shouldn't write it to a BAR. This isn't a problem for the usual path via pci_assign_resource(), which clears IORESOURCE_UNSET before calling pci_update_resource(), but paths like pci_restore_bars() can call this for resources we haven't assigned. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 725d5b28398c..7f7652176fc5 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -44,6 +44,9 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (!res->flags) return; + if (res->flags & IORESOURCE_UNSET) + return; + /* * Ignore non-moveable resources. This might be legacy resources for * which no functional BAR register exists or another important -- cgit v1.2.3 From 29003beb7f15d3daa5a8f9afb8d007b64baa2357 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:25:59 -0700 Subject: PCI: Don't try to claim IORESOURCE_UNSET resources If the IORESOURCE_UNSET bit is set, it means we haven't assigned an address yet, so don't try to claim the region. Also, make the error messages more uniform and add info about which BAR is involved. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 7f7652176fc5..6e443135ba24 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -111,18 +111,23 @@ int pci_claim_resource(struct pci_dev *dev, int resource) struct resource *res = &dev->resource[resource]; struct resource *root, *conflict; + if (res->flags & IORESOURCE_UNSET) { + dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n", + resource, res); + return -EINVAL; + } + root = pci_find_parent_resource(dev, res); if (!root) { - dev_info(&dev->dev, "no compatible bridge window for %pR\n", - res); + dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", + resource, res); return -EINVAL; } conflict = request_resource_conflict(root, res); if (conflict) { - dev_info(&dev->dev, - "address space collision: %pR conflicts with %s %pR\n", - res, conflict->name, conflict); + dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", + resource, res, conflict->name, conflict); return -EBUSY; } -- cgit v1.2.3 From c83bd900aac38552b0d903588bbb084d3b26fe71 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:26:00 -0700 Subject: PCI: Mark 64-bit resource as IORESOURCE_UNSET if we only support 32-bit If we don't support 64-bit addresses, i.e., CONFIG_PHYS_ADDR_T_64BIT is not set, we can't deal with BARs above 4GB. In this case we already pretend the BAR contained zero; this patch also sets IORESOURCE_UNSET so we can try to reallocate it later. I don't think this is exactly correct: what we care about here are *bus* addresses, not CPU addresses, so the tests of sizeof(resource_size_t) probably should be on sizeof(dma_addr_t) instead. But this is what's been in -next, so we'll fix that later. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6e34498ec9f0..78335efbbb74 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -252,6 +252,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); + res->flags |= IORESOURCE_UNSET; region.start = 0; region.end = sz64; bar_disabled = true; -- cgit v1.2.3 From 866d54177b4e671cd52bed1fb487d140d7b691f5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 7 Mar 2014 16:06:05 -0700 Subject: PCI: Enable INTx in pci_reenable_device() only when MSI/MSI-X not enabled Andreas reported that after 1f42db786b14 ("PCI: Enable INTx if BIOS left them disabled"), pciehp surprise removal stopped working. This happens because pci_reenable_device() on the hotplug bridge (used in the pciehp_configure_device() path) clears the Interrupt Disable bit, which apparently breaks the bridge's MSI hotplug event reporting. Previously we cleared the Interrupt Disable bit in do_pci_enable_device(), which is used by both pci_enable_device() and pci_reenable_device(). But we use pci_reenable_device() after the driver may have enabled MSI or MSI-X, and we *set* Interrupt Disable as part of enabling MSI/MSI-X. This patch clears Interrupt Disable only when MSI/MSI-X has not been enabled. Fixes: 1f42db786b14 PCI: Enable INTx if BIOS left them disabled Link: https://bugzilla.kernel.org/show_bug.cgi?id=71691 Reported-and-tested-by: Andreas Noever Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org CC: Sarah Sharp --- drivers/pci/pci.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8dc3e701ec57..79fc89c6c3f3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1192,6 +1192,9 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars) return err; pci_fixup_device(pci_fixup_enable, dev); + if (dev->msi_enabled || dev->msix_enabled) + return 0; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (pin) { pci_read_config_word(dev, PCI_COMMAND, &cmd); -- cgit v1.2.3 From 3cedcc3621289d41bd21c5dbe0b886d57c83a1ea Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:26:00 -0700 Subject: PCI: Don't enable decoding if BAR hasn't been assigned an address Don't enable memory or I/O decoding if we haven't assigned or claimed the BAR's resource. If we enable decoding for a BAR that hasn't been assigned an address, we'll likely cause bus conflicts. This declines to enable decoding for resources with IORESOURCE_UNSET. Note that drivers can use pci_enable_device_io() or pci_enable_device_mem() if they only care about specific types of BARs. In that case, we don't bother checking whether the corresponding resources are assigned or claimed. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 6e443135ba24..7eed671d5586 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -343,9 +343,15 @@ int pci_enable_resources(struct pci_dev *dev, int mask) (!(r->flags & IORESOURCE_ROM_ENABLE))) continue; + if (r->flags & IORESOURCE_UNSET) { + dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n", + i, r); + return -EINVAL; + } + if (!r->parent) { - dev_err(&dev->dev, "device not available " - "(can't reserve %pR)\n", r); + dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n", + i, r); return -EINVAL; } -- cgit v1.2.3 From 8a9d56097c142d0716234eb1cf7c8150c6dc1588 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 26 Feb 2014 11:26:00 -0700 Subject: PCI: Add "weak" generic pcibios_enable_device() implementation Many architectures implement pcibios_enable_device() the same way, so provide a default implementation in the core. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index dc9ce62be7aa..c3ce3d61091c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1185,6 +1185,11 @@ int pci_load_and_free_saved_state(struct pci_dev *dev, } EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state); +int __weak pcibios_enable_device(struct pci_dev *dev, int bars) +{ + return pci_enable_resources(dev, bars); +} + static int do_pci_enable_device(struct pci_dev *dev, int bars) { int err; -- cgit v1.2.3 From e20fa6609a0076def469aeb799b1c25558e70042 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 10 Mar 2014 10:46:56 -0600 Subject: PCI: Don't check resource_size() in pci_bus_alloc_resource() Paul reported that after f75b99d5a77d ("PCI: Enforce bus address limits in resource allocation") on a 32-bit kernel (CONFIG_PHYS_ADDR_T_64BIT not set), intel-gtt complained "can't ioremap flush page - no chipset flushing". In addition, other PCI resource allocations, e.g., for bridge windows, failed. This happens because we incorrectly skip bus resources of [mem 0x00000000-0xffffffff] because we think they are of size zero. When resource_size_t is 32 bits wide, resource_size() on [mem 0x00000000-0xffffffff] returns 0 because (r->end - r->start + 1) overflows. Therefore, we can't use "resource_size() == 0" to decide that allocation from this resource will fail. allocate_resource() should fail anyway if it can't satisfy the address constraints, so we should just depend on that. A [mem 0x00000000-0xffffffff] bus resource is obviously not really valid, but we do fall back to it as a default when we don't have information about host bridge apertures. Link: https://bugzilla.kernel.org/show_bug.cgi?id=71611 Fixes: f75b99d5a77d PCI: Enforce bus address limits in resource allocation Reported-and-tested-by: Paul Bolle Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 00660cc502c5..38901665c770 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -162,8 +162,6 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, avail = *r; pci_clip_resource_to_region(bus, &avail, region); - if (!resource_size(&avail)) - continue; /* * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to -- cgit v1.2.3 From aa11fc58dc71c27701b1f9a529a36a38d4337722 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 7 Mar 2014 13:39:01 -0700 Subject: PCI: Check all IORESOURCE_TYPE_BITS in pci_bus_alloc_from_region() When allocating space from a bus resource, i.e., from apertures leading to this bus, make sure the entire resource type matches. The previous code assumed the IORESOURCE_TYPE_BITS field was a bitmask with only a single bit set, but this is not true. IORESOURCE_TYPE_BITS is really an enumeration, and we have to check all the bits. See 72dcb1197228 ("resources: Add register address resource type"). No functional change. If we used this path for allocating IRQs, DMA channels, or bus numbers, this would fix a bug because those types are indistinguishable when masked by IORESOURCE_IO | IORESOURCE_MEM. But we don't, so this shouldn't make any difference. Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 38901665c770..e3bdc88668ec 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -144,7 +144,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, struct resource *r, avail; resource_size_t max; - type_mask |= IORESOURCE_IO | IORESOURCE_MEM; + type_mask |= IORESOURCE_TYPE_BITS; pci_bus_for_each_resource(bus, r, i) { if (!r) -- cgit v1.2.3 From 664c28480c90fb8541bcdd1d4b349e9436165ec7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 7 Mar 2014 13:51:12 -0700 Subject: PCI: Change pci_bus_alloc_resource() type_mask to unsigned long The pci_bus_alloc_resource() "type_mask" parameter is used to compare with the "flags" member of a struct resource, so it should be the same type, namely "unsigned long". No functional change because all current IORESOURCE_* flags fit in 32 bits. Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index e3bdc88668ec..fb8aed307c28 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -132,7 +132,7 @@ static void pci_clip_resource_to_region(struct pci_bus *bus, static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, @@ -200,7 +200,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, */ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, -- cgit v1.2.3 From 075eb9e35578c23ee2414f87d97d2e5065aa1bc1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 5 Mar 2014 14:07:03 -0700 Subject: PCI: Log IDE resource quirk in dmesg Make a note in dmesg when we overwrite legacy IDE BAR info. We previously logged something like this: pci 0000:00:1f.1: reg 0x10: [io 0x0000-0x0007] and then silently overwrote the resource. There's an example in the bugzilla below. This doesn't fix the bugzilla; it just makes what's going on more obvious. No functional change; merely adds some dev_info() calls. Link: https://bugzilla.kernel.org/show_bug.cgi?id=48451 Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 78335efbbb74..93dad114eaef 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1126,10 +1126,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); /* - * Do the ugly legacy mode stuff here rather than broken chip - * quirk code. Legacy mode ATA controllers have fixed - * addresses. These are not always echoed in BAR0-3, and - * BAR0-3 in a few cases contain junk! + * Do the ugly legacy mode stuff here rather than broken chip + * quirk code. Legacy mode ATA controllers have fixed + * addresses. These are not always echoed in BAR0-3, and + * BAR0-3 in a few cases contain junk! */ if (class == PCI_CLASS_STORAGE_IDE) { u8 progif; @@ -1140,11 +1140,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x10: %pR\n", + res); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x14: %pR\n", + res); } if ((progif & 4) == 0) { region.start = 0x170; @@ -1152,11 +1156,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x18: %pR\n", + res); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x1c: %pR\n", + res); } } break; -- cgit v1.2.3