From f07852d6442c46c50b59c7e2acc8a1b291f9ab6d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:10:02 -0500 Subject: [PATCH 01/48] PCI: Rewrite pci_scan_slot The Alternate Routing-ID Interpretation capability allows a single device to have up to 256 functions. They can be populated sparsely, so the current technique of scanning every eighth function is not guaranteed to find them all. By introducing a 'next_fn' function pointer, we can use the linked list of functions in the ARI capability to scan all the functions which exist. We can then speed up the pci_scan_slot by skipping the scan of subsequent devfns for PCIe devices which are the direct children of Root Ports or Downstream Ports. These devices are only permitted to implement device 0, unless they are ARI devices, in which case they'll be scanned by the ARI code above. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 56 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 446e4a94d7d3..dd64310a735e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1081,6 +1081,37 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) } EXPORT_SYMBOL(pci_scan_single_device); +static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn) +{ + u16 cap; + unsigned pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); + if (!pos) + return 0; + pci_read_config_word(dev, pos + 4, &cap); + return cap >> 8; +} + +static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn) +{ + return (fn + 1) % 8; +} + +static unsigned no_next_fn(struct pci_dev *dev, unsigned fn) +{ + return 0; +} + +static int only_one_child(struct pci_bus *bus) +{ + struct pci_dev *parent = bus->self; + if (!parent || !pci_is_pcie(parent)) + return 0; + if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) + return 1; + return 0; +} + /** * pci_scan_slot - scan a PCI slot on a bus for devices. * @bus: PCI bus to scan @@ -1094,21 +1125,28 @@ EXPORT_SYMBOL(pci_scan_single_device); */ int pci_scan_slot(struct pci_bus *bus, int devfn) { - int fn, nr = 0; + unsigned fn, nr = 0; struct pci_dev *dev; + unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn; + + if (only_one_child(bus) && (devfn > 0)) + return 0; /* Already scanned the entire slot */ dev = pci_scan_single_device(bus, devfn); if (dev && !dev->is_added) /* new device? */ nr++; - if (dev && dev->multifunction) { - for (fn = 1; fn < 8; fn++) { - dev = pci_scan_single_device(bus, devfn + fn); - if (dev) { - if (!dev->is_added) - nr++; - dev->multifunction = 1; - } + if (pci_ari_enabled(bus)) + next_fn = next_ari_fn; + else if (dev && dev->multifunction) + next_fn = next_trad_fn; + + for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) { + dev = pci_scan_single_device(bus, devfn + fn); + if (dev) { + if (!dev->is_added) + nr++; + dev->multifunction = 1; } } From 536c8cb49eccd4f753b4782e7e975ef87359cb44 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:31 -0500 Subject: [PATCH 02/48] PCI: Unify pcie_link_speed and pci_bus_speed These enums must not overlap anyway, since we only have a single pci_bus_speed_strings array. Use a single enum, and move it to pci.h. Add 'SPEED' to the pcie names to make it clear what they are. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_hpc.c | 16 ++++++++-------- include/linux/pci.h | 21 +++++++++++++++++++++ include/linux/pci_hotplug.h | 26 -------------------------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 10040d58c8ef..6744ca1d8d01 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -613,7 +613,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *value) { struct controller *ctrl = slot->ctrl; - enum pcie_link_speed lnk_speed; + enum pci_bus_speed lnk_speed; u32 lnk_cap; int retval = 0; @@ -625,13 +625,13 @@ int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *value) switch (lnk_cap & 0x000F) { case 1: - lnk_speed = PCIE_2_5GB; + lnk_speed = PCIE_SPEED_2_5GT; break; case 2: - lnk_speed = PCIE_5_0GB; + lnk_speed = PCIE_SPEED_5_0GT; break; default: - lnk_speed = PCIE_LNK_SPEED_UNKNOWN; + lnk_speed = PCI_SPEED_UNKNOWN; break; } @@ -694,7 +694,7 @@ int pciehp_get_max_lnk_width(struct slot *slot, int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *value) { struct controller *ctrl = slot->ctrl; - enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; + enum pci_bus_speed lnk_speed = PCI_SPEED_UNKNOWN; int retval = 0; u16 lnk_status; @@ -707,13 +707,13 @@ int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *value) switch (lnk_status & PCI_EXP_LNKSTA_CLS) { case 1: - lnk_speed = PCIE_2_5GB; + lnk_speed = PCIE_SPEED_2_5GT; break; case 2: - lnk_speed = PCIE_5_0GB; + lnk_speed = PCIE_SPEED_5_0GT; break; default: - lnk_speed = PCIE_LNK_SPEED_UNKNOWN; + lnk_speed = PCI_SPEED_UNKNOWN; break; } diff --git a/include/linux/pci.h b/include/linux/pci.h index c1968f464c38..d76a8a0b6b53 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -187,6 +187,27 @@ enum pci_bus_flags { PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2, }; +/* Based on the PCI Hotplug Spec, but some values are made up by us */ +enum pci_bus_speed { + PCI_SPEED_33MHz = 0x00, + PCI_SPEED_66MHz = 0x01, + PCI_SPEED_66MHz_PCIX = 0x02, + PCI_SPEED_100MHz_PCIX = 0x03, + PCI_SPEED_133MHz_PCIX = 0x04, + PCI_SPEED_66MHz_PCIX_ECC = 0x05, + PCI_SPEED_100MHz_PCIX_ECC = 0x06, + PCI_SPEED_133MHz_PCIX_ECC = 0x07, + PCI_SPEED_66MHz_PCIX_266 = 0x09, + PCI_SPEED_100MHz_PCIX_266 = 0x0a, + PCI_SPEED_133MHz_PCIX_266 = 0x0b, + PCI_SPEED_66MHz_PCIX_533 = 0x11, + PCI_SPEED_100MHz_PCIX_533 = 0x12, + PCI_SPEED_133MHz_PCIX_533 = 0x13, + PCIE_SPEED_2_5GT = 0x14, + PCIE_SPEED_5_0GT = 0x15, + PCI_SPEED_UNKNOWN = 0xff, +}; + struct pci_cap_saved_state { struct hlist_node next; char cap_nr; diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 652ba797696d..56251ac385e9 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -28,26 +28,6 @@ #ifndef _PCI_HOTPLUG_H #define _PCI_HOTPLUG_H - -/* These values come from the PCI Hotplug Spec */ -enum pci_bus_speed { - PCI_SPEED_33MHz = 0x00, - PCI_SPEED_66MHz = 0x01, - PCI_SPEED_66MHz_PCIX = 0x02, - PCI_SPEED_100MHz_PCIX = 0x03, - PCI_SPEED_133MHz_PCIX = 0x04, - PCI_SPEED_66MHz_PCIX_ECC = 0x05, - PCI_SPEED_100MHz_PCIX_ECC = 0x06, - PCI_SPEED_133MHz_PCIX_ECC = 0x07, - PCI_SPEED_66MHz_PCIX_266 = 0x09, - PCI_SPEED_100MHz_PCIX_266 = 0x0a, - PCI_SPEED_133MHz_PCIX_266 = 0x0b, - PCI_SPEED_66MHz_PCIX_533 = 0x11, - PCI_SPEED_100MHz_PCIX_533 = 0x12, - PCI_SPEED_133MHz_PCIX_533 = 0x13, - PCI_SPEED_UNKNOWN = 0xff, -}; - /* These values come from the PCI Express Spec */ enum pcie_link_width { PCIE_LNK_WIDTH_RESRV = 0x00, @@ -61,12 +41,6 @@ enum pcie_link_width { PCIE_LNK_WIDTH_UNKNOWN = 0xFF, }; -enum pcie_link_speed { - PCIE_2_5GB = 0x14, - PCIE_5_0GB = 0x15, - PCIE_LNK_SPEED_UNKNOWN = 0xFF, -}; - /** * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use * @owner: The module owner of this structure From 3749c51ac6c1560aa1cb1520066bed84c6f8152a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:32 -0500 Subject: [PATCH 03/48] PCI: Make current and maximum bus speeds part of the PCI core Move the max_bus_speed and cur_bus_speed into the pci_bus. Expose the values through the PCI slot driver instead of the hotplug slot driver. Update all the hotplug drivers to use the pci_bus instead of their own data structures. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_core.c | 2 - drivers/pci/hotplug/cpqphp.h | 2 - drivers/pci/hotplug/cpqphp_core.c | 57 +++------- drivers/pci/hotplug/cpqphp_ctrl.c | 27 +++-- drivers/pci/hotplug/ibmphp_core.c | 106 +++++------------- drivers/pci/hotplug/pci_hotplug_core.c | 132 ---------------------- drivers/pci/hotplug/pciehp_core.c | 25 ----- drivers/pci/hotplug/pciehp_hpc.c | 72 ++---------- drivers/pci/hotplug/rpaphp_core.c | 23 ++-- drivers/pci/hotplug/shpchp.h | 2 - drivers/pci/hotplug/shpchp_core.c | 35 ------ drivers/pci/hotplug/shpchp_ctrl.c | 13 +-- drivers/pci/hotplug/shpchp_hpc.c | 149 +++++++++++++------------ drivers/pci/probe.c | 27 +++++ drivers/pci/slot.c | 54 +++++++++ include/linux/pci.h | 3 + include/linux/pci_hotplug.h | 15 +-- 17 files changed, 246 insertions(+), 498 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 4dd7114964ac..efa9f2de51c1 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -332,8 +332,6 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot) slot->hotplug_slot->info->attention_status = 0; slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot); slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot); - slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; - slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; acpiphp_slot->slot = slot; snprintf(name, SLOT_NAME_SIZE, "%llu", slot->acpi_slot->sun); diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index 9c6a9fd26812..d8ffc7366801 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -310,8 +310,6 @@ struct controller { u8 first_slot; u8 add_support; u8 push_flag; - enum pci_bus_speed speed; - enum pci_bus_speed speed_capability; u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */ u8 slot_switch_type; /* 0 = no switch, 1 = switch present */ u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */ diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 075b4f4b6e0d..f184d1d2ecbe 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -583,30 +583,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = hotplug_slot->private; - struct controller *ctrl = slot->ctrl; - - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); - - *value = ctrl->speed_capability; - - return 0; -} - -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = hotplug_slot->private; - struct controller *ctrl = slot->ctrl; - - dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); - - *value = ctrl->speed; - - return 0; -} - static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { .set_attention_status = set_attention_status, .enable_slot = process_SI, @@ -616,8 +592,6 @@ static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, }; #define SLOT_NAME_SIZE 10 @@ -629,6 +603,7 @@ static int ctrl_slot_setup(struct controller *ctrl, struct slot *slot; struct hotplug_slot *hotplug_slot; struct hotplug_slot_info *hotplug_slot_info; + struct pci_bus *bus = ctrl->pci_bus; u8 number_of_slots; u8 slot_device; u8 slot_number; @@ -694,7 +669,7 @@ static int ctrl_slot_setup(struct controller *ctrl, slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; if (is_slot66mhz(slot)) slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; - if (ctrl->speed == PCI_SPEED_66MHz) + if (bus->cur_bus_speed == PCI_SPEED_66MHz) slot->capabilities |= PCISLOT_66_MHZ_OPERATION; ctrl_slot = @@ -844,6 +819,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) u32 rc; struct controller *ctrl; struct pci_func *func; + struct pci_bus *bus; int err; err = pci_enable_device(pdev); @@ -852,6 +828,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_name(pdev), err); return err; } + bus = pdev->subordinate; /* Need to read VID early b/c it's used to differentiate CPQ and INTC * discovery @@ -929,22 +906,22 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_read_config_byte(pdev, 0x41, &bus_cap); if (bus_cap & 0x80) { dbg("bus max supports 133MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; + bus->max_bus_speed = PCI_SPEED_133MHz_PCIX; break; } if (bus_cap & 0x40) { dbg("bus max supports 100MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; break; } if (bus_cap & 20) { dbg("bus max supports 66MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; + bus->max_bus_speed = PCI_SPEED_66MHz_PCIX; break; } if (bus_cap & 10) { dbg("bus max supports 66MHz PCI\n"); - ctrl->speed_capability = PCI_SPEED_66MHz; + bus->max_bus_speed = PCI_SPEED_66MHz; break; } @@ -955,7 +932,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case PCI_SUB_HPC_ID: /* Original 6500/7000 implementation */ ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; + bus->max_bus_speed = PCI_SPEED_33MHz; ctrl->push_button = 0; ctrl->pci_config_space = 1; ctrl->defeature_PHP = 1; @@ -966,7 +943,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* First Pushbutton implementation */ ctrl->push_flag = 1; ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; + bus->max_bus_speed = PCI_SPEED_33MHz; ctrl->push_button = 1; ctrl->pci_config_space = 1; ctrl->defeature_PHP = 1; @@ -976,7 +953,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case PCI_SUB_HPC_ID_INTC: /* Third party (6500/7000) */ ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; + bus->max_bus_speed = PCI_SPEED_33MHz; ctrl->push_button = 0; ctrl->pci_config_space = 1; ctrl->defeature_PHP = 1; @@ -987,7 +964,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* First 66 Mhz implementation */ ctrl->push_flag = 1; ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_66MHz; + bus->max_bus_speed = PCI_SPEED_66MHz; ctrl->push_button = 1; ctrl->pci_config_space = 1; ctrl->defeature_PHP = 1; @@ -998,7 +975,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* First PCI-X implementation, 100MHz */ ctrl->push_flag = 1; ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; ctrl->push_button = 1; ctrl->pci_config_space = 1; ctrl->defeature_PHP = 1; @@ -1015,9 +992,9 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) case PCI_VENDOR_ID_INTEL: /* Check for speed capability (0=33, 1=66) */ if (subsystem_deviceid & 0x0001) - ctrl->speed_capability = PCI_SPEED_66MHz; + bus->max_bus_speed = PCI_SPEED_66MHz; else - ctrl->speed_capability = PCI_SPEED_33MHz; + bus->max_bus_speed = PCI_SPEED_33MHz; /* Check for push button */ if (subsystem_deviceid & 0x0002) @@ -1079,7 +1056,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pdev->bus->number); dbg("Hotplug controller capabilities:\n"); - dbg(" speed_capability %d\n", ctrl->speed_capability); + dbg(" speed_capability %d\n", bus->max_bus_speed); dbg(" slot_switch_type %s\n", ctrl->slot_switch_type ? "switch present" : "no switch"); dbg(" defeature_PHP %s\n", ctrl->defeature_PHP ? @@ -1142,7 +1119,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Check for 66Mhz operation */ - ctrl->speed = get_controller_speed(ctrl); + bus->cur_bus_speed = get_controller_speed(ctrl); /******************************************************** diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 0ff689afa757..e43908d9b5df 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1130,12 +1130,13 @@ static int is_bridge(struct pci_func * func) static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot) { struct slot *slot; + struct pci_bus *bus = ctrl->pci_bus; u8 reg; u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); u16 reg16; u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); - if (ctrl->speed == adapter_speed) + if (bus->cur_bus_speed == adapter_speed) return 0; /* We don't allow freq/mode changes if we find another adapter running @@ -1152,7 +1153,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ * lower speed/mode, we allow the new adapter to function at * this rate if supported */ - if (ctrl->speed < adapter_speed) + if (bus->cur_bus_speed < adapter_speed) return 0; return 1; @@ -1161,20 +1162,20 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ /* If the controller doesn't support freq/mode changes and the * controller is running at a higher mode, we bail */ - if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability)) + if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability)) return 1; /* But we allow the adapter to run at a lower rate if possible */ - if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability)) + if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability)) return 0; /* We try to set the max speed supported by both the adapter and * controller */ - if (ctrl->speed_capability < adapter_speed) { - if (ctrl->speed == ctrl->speed_capability) + if (bus->max_bus_speed < adapter_speed) { + if (bus->cur_bus_speed == bus->max_bus_speed) return 0; - adapter_speed = ctrl->speed_capability; + adapter_speed = bus->max_bus_speed; } writel(0x0L, ctrl->hpc_reg + LED_CONTROL); @@ -1229,8 +1230,8 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ pci_write_config_byte(ctrl->pci_dev, 0x43, reg); /* Only if mode change...*/ - if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || - ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) + if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || + ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) set_SOGO(ctrl); wait_for_ctrl_irq(ctrl); @@ -1243,7 +1244,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ set_SOGO(ctrl); wait_for_ctrl_irq(ctrl); - ctrl->speed = adapter_speed; + bus->cur_bus_speed = adapter_speed; slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); info("Successfully changed frequency/mode for adapter in slot %d\n", @@ -1269,6 +1270,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ */ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) { + struct pci_bus *bus = ctrl->pci_bus; u8 hp_slot; u8 temp_byte; u8 adapter_speed; @@ -1309,7 +1311,7 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) wait_for_ctrl_irq (ctrl); adapter_speed = get_adapter_speed(ctrl, hp_slot); - if (ctrl->speed != adapter_speed) + if (bus->cur_bus_speed != adapter_speed) if (set_controller_speed(ctrl, adapter_speed, hp_slot)) rc = WRONG_BUS_FREQUENCY; @@ -1426,6 +1428,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) u32 temp_register = 0xFFFFFFFF; u32 rc = 0; struct pci_func *new_slot = NULL; + struct pci_bus *bus = ctrl->pci_bus; struct slot *p_slot; struct resource_lists res_lists; @@ -1456,7 +1459,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) wait_for_ctrl_irq (ctrl); adapter_speed = get_adapter_speed(ctrl, hp_slot); - if (ctrl->speed != adapter_speed) + if (bus->cur_bus_speed != adapter_speed) if (set_controller_speed(ctrl, adapter_speed, hp_slot)) rc = WRONG_BUS_FREQUENCY; diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 7485ffda950c..d934dd4fa873 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -395,89 +395,40 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value) return rc; } -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static int get_max_bus_speed(struct slot *slot) { - int rc = -ENODEV; - struct slot *pslot; + int rc; u8 mode = 0; + enum pci_bus_speed speed; + struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus; - debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __func__, - hotplug_slot, value); + debug("%s - Entry slot[%p]\n", __func__, slot); ibmphp_lock_operations(); + mode = slot->supported_bus_mode; + speed = slot->supported_speed; + ibmphp_unlock_operations(); - if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - rc = 0; - mode = pslot->supported_bus_mode; - *value = pslot->supported_speed; - switch (*value) { - case BUS_SPEED_33: - break; - case BUS_SPEED_66: - if (mode == BUS_MODE_PCIX) - *value += 0x01; - break; - case BUS_SPEED_100: - case BUS_SPEED_133: - *value = pslot->supported_speed + 0x01; - break; - default: - /* Note (will need to change): there would be soon 256, 512 also */ - rc = -ENODEV; - } - } + switch (speed) { + case BUS_SPEED_33: + break; + case BUS_SPEED_66: + if (mode == BUS_MODE_PCIX) + speed += 0x01; + break; + case BUS_SPEED_100: + case BUS_SPEED_133: + speed += 0x01; + break; + default: + /* Note (will need to change): there would be soon 256, 512 also */ + rc = -ENODEV; } - ibmphp_unlock_operations(); - debug("%s - Exit rc[%d] value[%x]\n", __func__, rc, *value); - return rc; -} + if (!rc) + bus->max_bus_speed = speed; -static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - int rc = -ENODEV; - struct slot *pslot; - u8 mode = 0; - - debug("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __func__, - hotplug_slot, value); - - ibmphp_lock_operations(); - - if (hotplug_slot) { - pslot = hotplug_slot->private; - if (pslot) { - rc = get_cur_bus_info(&pslot); - if (!rc) { - mode = pslot->bus_on->current_bus_mode; - *value = pslot->bus_on->current_speed; - switch (*value) { - case BUS_SPEED_33: - break; - case BUS_SPEED_66: - if (mode == BUS_MODE_PCIX) - *value += 0x01; - else if (mode == BUS_MODE_PCI) - ; - else - *value = PCI_SPEED_UNKNOWN; - break; - case BUS_SPEED_100: - case BUS_SPEED_133: - *value += 0x01; - break; - default: - /* Note of change: there would also be 256, 512 soon */ - rc = -ENODEV; - } - } - } - } - - ibmphp_unlock_operations(); - debug("%s - Exit rc[%d] value[%x]\n", __func__, rc, *value); + debug("%s - Exit rc[%d] speed[%x]\n", __func__, rc, speed); return rc; } @@ -572,6 +523,7 @@ static int __init init_ops(void) if (slot_cur->bus_on->current_speed == 0xFF) if (get_cur_bus_info(&slot_cur)) return -1; + get_max_bus_speed(slot_cur); if (slot_cur->ctrl->options == 0xFF) if (get_hpc_options(slot_cur, &slot_cur->ctrl->options)) @@ -655,6 +607,7 @@ static int validate(struct slot *slot_cur, int opn) int ibmphp_update_slot_info(struct slot *slot_cur) { struct hotplug_slot_info *info; + struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus; int rc; u8 bus_speed; u8 mode; @@ -700,8 +653,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur) bus_speed = PCI_SPEED_UNKNOWN; } - info->cur_bus_speed = bus_speed; - info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed; + bus->cur_bus_speed = bus_speed; // To do: bus_names rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info); @@ -1326,8 +1278,6 @@ struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_present, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, /* .get_max_adapter_speed = get_max_adapter_speed, .get_bus_name_status = get_bus_name, */ diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 38183a534b65..728b119f71ad 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -64,32 +64,6 @@ static int debug; static LIST_HEAD(pci_hotplug_slot_list); static DEFINE_MUTEX(pci_hp_mutex); -/* these strings match up with the values in pci_bus_speed */ -static char *pci_bus_speed_strings[] = { - "33 MHz PCI", /* 0x00 */ - "66 MHz PCI", /* 0x01 */ - "66 MHz PCI-X", /* 0x02 */ - "100 MHz PCI-X", /* 0x03 */ - "133 MHz PCI-X", /* 0x04 */ - NULL, /* 0x05 */ - NULL, /* 0x06 */ - NULL, /* 0x07 */ - NULL, /* 0x08 */ - "66 MHz PCI-X 266", /* 0x09 */ - "100 MHz PCI-X 266", /* 0x0a */ - "133 MHz PCI-X 266", /* 0x0b */ - NULL, /* 0x0c */ - NULL, /* 0x0d */ - NULL, /* 0x0e */ - NULL, /* 0x0f */ - NULL, /* 0x10 */ - "66 MHz PCI-X 533", /* 0x11 */ - "100 MHz PCI-X 533", /* 0x12 */ - "133 MHz PCI-X 533", /* 0x13 */ - "2.5 GT/s PCIe", /* 0x14 */ - "5.0 GT/s PCIe", /* 0x15 */ -}; - #ifdef CONFIG_HOTPLUG_PCI_CPCI extern int cpci_hotplug_init(int debug); extern void cpci_hotplug_exit(void); @@ -118,8 +92,6 @@ GET_STATUS(power_status, u8) GET_STATUS(attention_status, u8) GET_STATUS(latch_status, u8) GET_STATUS(adapter_status, u8) -GET_STATUS(max_bus_speed, enum pci_bus_speed) -GET_STATUS(cur_bus_speed, enum pci_bus_speed) static ssize_t power_read_file(struct pci_slot *slot, char *buf) { @@ -263,60 +235,6 @@ static struct pci_slot_attribute hotplug_slot_attr_presence = { .show = presence_read_file, }; -static char *unknown_speed = "Unknown bus speed"; - -static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf) -{ - char *speed_string; - int retval; - enum pci_bus_speed value; - - retval = get_max_bus_speed(slot->hotplug, &value); - if (retval) - goto exit; - - if (value == PCI_SPEED_UNKNOWN) - speed_string = unknown_speed; - else - speed_string = pci_bus_speed_strings[value]; - - retval = sprintf (buf, "%s\n", speed_string); - -exit: - return retval; -} - -static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = { - .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, - .show = max_bus_speed_read_file, -}; - -static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf) -{ - char *speed_string; - int retval; - enum pci_bus_speed value; - - retval = get_cur_bus_speed(slot->hotplug, &value); - if (retval) - goto exit; - - if (value == PCI_SPEED_UNKNOWN) - speed_string = unknown_speed; - else - speed_string = pci_bus_speed_strings[value]; - - retval = sprintf (buf, "%s\n", speed_string); - -exit: - return retval; -} - -static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = { - .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, - .show = cur_bus_speed_read_file, -}; - static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { @@ -391,26 +309,6 @@ static bool has_adapter_file(struct pci_slot *pci_slot) return false; } -static bool has_max_bus_speed_file(struct pci_slot *pci_slot) -{ - struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; - if (slot->ops->get_max_bus_speed) - return true; - return false; -} - -static bool has_cur_bus_speed_file(struct pci_slot *pci_slot) -{ - struct hotplug_slot *slot = pci_slot->hotplug; - if ((!slot) || (!slot->ops)) - return false; - if (slot->ops->get_cur_bus_speed) - return true; - return false; -} - static bool has_test_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; @@ -456,20 +354,6 @@ static int fs_add_slot(struct pci_slot *slot) goto exit_adapter; } - if (has_max_bus_speed_file(slot)) { - retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_max_bus_speed.attr); - if (retval) - goto exit_max_speed; - } - - if (has_cur_bus_speed_file(slot)) { - retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_cur_bus_speed.attr); - if (retval) - goto exit_cur_speed; - } - if (has_test_file(slot)) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); @@ -480,14 +364,6 @@ static int fs_add_slot(struct pci_slot *slot) goto exit; exit_test: - if (has_cur_bus_speed_file(slot)) - sysfs_remove_file(&slot->kobj, - &hotplug_slot_attr_cur_bus_speed.attr); -exit_cur_speed: - if (has_max_bus_speed_file(slot)) - sysfs_remove_file(&slot->kobj, - &hotplug_slot_attr_max_bus_speed.attr); -exit_max_speed: if (has_adapter_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); @@ -523,14 +399,6 @@ static void fs_remove_slot(struct pci_slot *slot) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - if (has_max_bus_speed_file(slot)) - sysfs_remove_file(&slot->kobj, - &hotplug_slot_attr_max_bus_speed.attr); - - if (has_cur_bus_speed_file(slot)) - sysfs_remove_file(&slot->kobj, - &hotplug_slot_attr_cur_bus_speed.attr); - if (has_test_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 5674b2075bdc..920f820edf87 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -69,8 +69,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); -static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); /** * release_slot - free up the memory used by a slot @@ -113,8 +111,6 @@ static int init_slot(struct controller *ctrl) ops->disable_slot = disable_slot; ops->get_power_status = get_power_status; ops->get_adapter_status = get_adapter_status; - ops->get_max_bus_speed = get_max_bus_speed; - ops->get_cur_bus_speed = get_cur_bus_speed; if (MRL_SENS(ctrl)) ops->get_latch_status = get_latch_status; if (ATTN_LED(ctrl)) { @@ -227,27 +223,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return pciehp_get_adapter_status(slot, value); } -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, - enum pci_bus_speed *value) -{ - struct slot *slot = hotplug_slot->private; - - ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", - __func__, slot_name(slot)); - - return pciehp_get_max_link_speed(slot, value); -} - -static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = hotplug_slot->private; - - ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", - __func__, slot_name(slot)); - - return pciehp_get_cur_link_speed(slot, value); -} - static int pciehp_probe(struct pcie_device *dev) { int rc; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 6744ca1d8d01..40b48f569b1e 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -492,6 +492,7 @@ int pciehp_power_on_slot(struct slot * slot) u16 slot_cmd; u16 cmd_mask; u16 slot_status; + u16 lnk_status; int retval = 0; /* Clear sticky power-fault bit from previous power failures */ @@ -523,6 +524,14 @@ int pciehp_power_on_slot(struct slot * slot) ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); + if (retval) { + ctrl_err(ctrl, "%s: Cannot read LNKSTA register\n", + __func__); + return retval; + } + pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); + return retval; } @@ -610,37 +619,6 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) return IRQ_HANDLED; } -int pciehp_get_max_link_speed(struct slot *slot, enum pci_bus_speed *value) -{ - struct controller *ctrl = slot->ctrl; - enum pci_bus_speed lnk_speed; - u32 lnk_cap; - int retval = 0; - - retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); - return retval; - } - - switch (lnk_cap & 0x000F) { - case 1: - lnk_speed = PCIE_SPEED_2_5GT; - break; - case 2: - lnk_speed = PCIE_SPEED_5_0GT; - break; - default: - lnk_speed = PCI_SPEED_UNKNOWN; - break; - } - - *value = lnk_speed; - ctrl_dbg(ctrl, "Max link speed = %d\n", lnk_speed); - - return retval; -} - int pciehp_get_max_lnk_width(struct slot *slot, enum pcie_link_width *value) { @@ -691,38 +669,6 @@ int pciehp_get_max_lnk_width(struct slot *slot, return retval; } -int pciehp_get_cur_link_speed(struct slot *slot, enum pci_bus_speed *value) -{ - struct controller *ctrl = slot->ctrl; - enum pci_bus_speed lnk_speed = PCI_SPEED_UNKNOWN; - int retval = 0; - u16 lnk_status; - - retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", - __func__); - return retval; - } - - switch (lnk_status & PCI_EXP_LNKSTA_CLS) { - case 1: - lnk_speed = PCIE_SPEED_2_5GT; - break; - case 2: - lnk_speed = PCIE_SPEED_5_0GT; - break; - default: - lnk_speed = PCI_SPEED_UNKNOWN; - break; - } - - *value = lnk_speed; - ctrl_dbg(ctrl, "Current link speed = %d\n", lnk_speed); - - return retval; -} - int pciehp_get_cur_lnk_width(struct slot *slot, enum pcie_link_width *value) { diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index c159223389ec..b14e5e66a92c 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -130,10 +130,9 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value) return 0; } -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static enum pci_bus_speed get_max_bus_speed(struct slot *slot) { - struct slot *slot = (struct slot *)hotplug_slot->private; - + enum pci_bus_speed speed; switch (slot->type) { case 1: case 2: @@ -141,30 +140,30 @@ static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe case 4: case 5: case 6: - *value = PCI_SPEED_33MHz; /* speed for case 1-6 */ + speed = PCI_SPEED_33MHz; /* speed for case 1-6 */ break; case 7: case 8: - *value = PCI_SPEED_66MHz; + speed = PCI_SPEED_66MHz; break; case 11: case 14: - *value = PCI_SPEED_66MHz_PCIX; + speed = PCI_SPEED_66MHz_PCIX; break; case 12: case 15: - *value = PCI_SPEED_100MHz_PCIX; + speed = PCI_SPEED_100MHz_PCIX; break; case 13: case 16: - *value = PCI_SPEED_133MHz_PCIX; + speed = PCI_SPEED_133MHz_PCIX; break; default: - *value = PCI_SPEED_UNKNOWN; + speed = PCI_SPEED_UNKNOWN; break; - } - return 0; + + return speed; } static int get_children_props(struct device_node *dn, const int **drc_indexes, @@ -408,6 +407,8 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) slot->state = NOT_VALID; return -EINVAL; } + + slot->bus->max_bus_speed = get_max_bus_speed(slot); return 0; } diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 8e210cd76e55..d2627e1c3ac1 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -333,8 +333,6 @@ struct hpc_ops { int (*set_attention_status)(struct slot *slot, u8 status); int (*get_latch_status)(struct slot *slot, u8 *status); int (*get_adapter_status)(struct slot *slot, u8 *status); - int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); - int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed); int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode); int (*get_prog_int)(struct slot *slot, u8 *prog_int); diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 8a520a3d0f59..a5062297f488 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -65,8 +65,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); -static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { .set_attention_status = set_attention_status, @@ -76,8 +74,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, }; /** @@ -279,37 +275,6 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, - enum pci_bus_speed *value) -{ - struct slot *slot = get_slot(hotplug_slot); - int retval; - - ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", - __func__, slot_name(slot)); - - retval = slot->hpc_ops->get_max_bus_speed(slot, value); - if (retval < 0) - *value = PCI_SPEED_UNKNOWN; - - return 0; -} - -static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) -{ - struct slot *slot = get_slot(hotplug_slot); - int retval; - - ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", - __func__, slot_name(slot)); - - retval = slot->hpc_ops->get_cur_bus_speed(slot, value); - if (retval < 0) - *value = PCI_SPEED_UNKNOWN; - - return 0; -} - static int is_shpc_capable(struct pci_dev *dev) { if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index b8ab2796e66a..179b1c1cb99b 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -285,17 +285,8 @@ static int board_added(struct slot *p_slot) return WRONG_BUS_FREQUENCY; } - rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bsp); - if (rc) { - ctrl_err(ctrl, "Can't get bus operation speed\n"); - return WRONG_BUS_FREQUENCY; - } - - rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &msp); - if (rc) { - ctrl_err(ctrl, "Can't get max bus operation speed\n"); - msp = bsp; - } + bsp = ctrl->pci_dev->bus->cur_bus_speed; + msp = ctrl->pci_dev->bus->max_bus_speed; /* Check if there are other slots or devices on the same bus */ if (!list_empty(&ctrl->pci_dev->subordinate->devices)) diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 86dc39847769..5f5e8d2e3552 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -660,6 +660,75 @@ static int hpc_slot_disable(struct slot * slot) return retval; } +static int shpc_get_cur_bus_speed(struct controller *ctrl) +{ + int retval = 0; + struct pci_bus *bus = ctrl->pci_dev->subordinate; + enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; + u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG); + u8 pi = shpc_readb(ctrl, PROG_INTERFACE); + u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7); + + if ((pi == 1) && (speed_mode > 4)) { + retval = -ENODEV; + goto out; + } + + switch (speed_mode) { + case 0x0: + bus_speed = PCI_SPEED_33MHz; + break; + case 0x1: + bus_speed = PCI_SPEED_66MHz; + break; + case 0x2: + bus_speed = PCI_SPEED_66MHz_PCIX; + break; + case 0x3: + bus_speed = PCI_SPEED_100MHz_PCIX; + break; + case 0x4: + bus_speed = PCI_SPEED_133MHz_PCIX; + break; + case 0x5: + bus_speed = PCI_SPEED_66MHz_PCIX_ECC; + break; + case 0x6: + bus_speed = PCI_SPEED_100MHz_PCIX_ECC; + break; + case 0x7: + bus_speed = PCI_SPEED_133MHz_PCIX_ECC; + break; + case 0x8: + bus_speed = PCI_SPEED_66MHz_PCIX_266; + break; + case 0x9: + bus_speed = PCI_SPEED_100MHz_PCIX_266; + break; + case 0xa: + bus_speed = PCI_SPEED_133MHz_PCIX_266; + break; + case 0xb: + bus_speed = PCI_SPEED_66MHz_PCIX_533; + break; + case 0xc: + bus_speed = PCI_SPEED_100MHz_PCIX_533; + break; + case 0xd: + bus_speed = PCI_SPEED_133MHz_PCIX_533; + break; + default: + retval = -ENODEV; + break; + } + + out: + bus->cur_bus_speed = bus_speed; + dbg("Current bus speed = %d\n", bus_speed); + return retval; +} + + static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value) { int retval; @@ -720,6 +789,8 @@ static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value) retval = shpc_write_cmd(slot, 0, cmd); if (retval) ctrl_err(ctrl, "%s: Write command failed!\n", __func__); + else + shpc_get_cur_bus_speed(ctrl); return retval; } @@ -803,10 +874,10 @@ static irqreturn_t shpc_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value) +static int shpc_get_max_bus_speed(struct controller *ctrl) { int retval = 0; - struct controller *ctrl = slot->ctrl; + struct pci_bus *bus = ctrl->pci_dev->subordinate; enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; u8 pi = shpc_readb(ctrl, PROG_INTERFACE); u32 slot_avail1 = shpc_readl(ctrl, SLOT_AVAIL1); @@ -842,79 +913,12 @@ static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value) retval = -ENODEV; } - *value = bus_speed; + bus->max_bus_speed = bus_speed; ctrl_dbg(ctrl, "Max bus speed = %d\n", bus_speed); return retval; } -static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value) -{ - int retval = 0; - struct controller *ctrl = slot->ctrl; - enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; - u16 sec_bus_reg = shpc_readw(ctrl, SEC_BUS_CONFIG); - u8 pi = shpc_readb(ctrl, PROG_INTERFACE); - u8 speed_mode = (pi == 2) ? (sec_bus_reg & 0xF) : (sec_bus_reg & 0x7); - - if ((pi == 1) && (speed_mode > 4)) { - *value = PCI_SPEED_UNKNOWN; - return -ENODEV; - } - - switch (speed_mode) { - case 0x0: - *value = PCI_SPEED_33MHz; - break; - case 0x1: - *value = PCI_SPEED_66MHz; - break; - case 0x2: - *value = PCI_SPEED_66MHz_PCIX; - break; - case 0x3: - *value = PCI_SPEED_100MHz_PCIX; - break; - case 0x4: - *value = PCI_SPEED_133MHz_PCIX; - break; - case 0x5: - *value = PCI_SPEED_66MHz_PCIX_ECC; - break; - case 0x6: - *value = PCI_SPEED_100MHz_PCIX_ECC; - break; - case 0x7: - *value = PCI_SPEED_133MHz_PCIX_ECC; - break; - case 0x8: - *value = PCI_SPEED_66MHz_PCIX_266; - break; - case 0x9: - *value = PCI_SPEED_100MHz_PCIX_266; - break; - case 0xa: - *value = PCI_SPEED_133MHz_PCIX_266; - break; - case 0xb: - *value = PCI_SPEED_66MHz_PCIX_533; - break; - case 0xc: - *value = PCI_SPEED_100MHz_PCIX_533; - break; - case 0xd: - *value = PCI_SPEED_133MHz_PCIX_533; - break; - default: - *value = PCI_SPEED_UNKNOWN; - retval = -ENODEV; - break; - } - - ctrl_dbg(ctrl, "Current bus speed = %d\n", bus_speed); - return retval; -} - static struct hpc_ops shpchp_hpc_ops = { .power_on_slot = hpc_power_on_slot, .slot_enable = hpc_slot_enable, @@ -926,8 +930,6 @@ static struct hpc_ops shpchp_hpc_ops = { .get_latch_status = hpc_get_latch_status, .get_adapter_status = hpc_get_adapter_status, - .get_max_bus_speed = hpc_get_max_bus_speed, - .get_cur_bus_speed = hpc_get_cur_bus_speed, .get_adapter_speed = hpc_get_adapter_speed, .get_mode1_ECC_cap = hpc_get_mode1_ECC_cap, .get_prog_int = hpc_get_prog_int, @@ -1086,6 +1088,9 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) } ctrl_dbg(ctrl, "HPC at %s irq=%x\n", pci_name(pdev), pdev->irq); + shpc_get_max_bus_speed(ctrl); + shpc_get_cur_bus_speed(ctrl); + /* * If this is the first controller to be initialized, * initialize the shpchpd work queue diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dd64310a735e..51cf8982fec7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -387,10 +387,37 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); INIT_LIST_HEAD(&b->slots); + b->max_bus_speed = PCI_SPEED_UNKNOWN; + b->cur_bus_speed = PCI_SPEED_UNKNOWN; } return b; } +static unsigned char pcie_link_speed[] = { + PCI_SPEED_UNKNOWN, /* 0 */ + PCIE_SPEED_2_5GT, /* 1 */ + PCIE_SPEED_5_0GT, /* 2 */ + PCI_SPEED_UNKNOWN, /* 3 */ + PCI_SPEED_UNKNOWN, /* 4 */ + PCI_SPEED_UNKNOWN, /* 5 */ + PCI_SPEED_UNKNOWN, /* 6 */ + PCI_SPEED_UNKNOWN, /* 7 */ + PCI_SPEED_UNKNOWN, /* 8 */ + PCI_SPEED_UNKNOWN, /* 9 */ + PCI_SPEED_UNKNOWN, /* A */ + PCI_SPEED_UNKNOWN, /* B */ + PCI_SPEED_UNKNOWN, /* C */ + PCI_SPEED_UNKNOWN, /* D */ + PCI_SPEED_UNKNOWN, /* E */ + PCI_SPEED_UNKNOWN /* F */ +}; + +void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) +{ + bus->cur_bus_speed = pcie_link_speed[linksta & 0xf]; +} +EXPORT_SYMBOL_GPL(pcie_update_link_speed); + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 8c02b6c53bdb..6f6b8d24786a 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -47,6 +47,54 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf) slot->number); } +/* these strings match up with the values in pci_bus_speed */ +static char *pci_bus_speed_strings[] = { + "33 MHz PCI", /* 0x00 */ + "66 MHz PCI", /* 0x01 */ + "66 MHz PCI-X", /* 0x02 */ + "100 MHz PCI-X", /* 0x03 */ + "133 MHz PCI-X", /* 0x04 */ + NULL, /* 0x05 */ + NULL, /* 0x06 */ + NULL, /* 0x07 */ + NULL, /* 0x08 */ + "66 MHz PCI-X 266", /* 0x09 */ + "100 MHz PCI-X 266", /* 0x0a */ + "133 MHz PCI-X 266", /* 0x0b */ + NULL, /* 0x0c */ + NULL, /* 0x0d */ + NULL, /* 0x0e */ + NULL, /* 0x0f */ + NULL, /* 0x10 */ + "66 MHz PCI-X 533", /* 0x11 */ + "100 MHz PCI-X 533", /* 0x12 */ + "133 MHz PCI-X 533", /* 0x13 */ + "2.5 GT/s PCIe", /* 0x14 */ + "5.0 GT/s PCIe", /* 0x15 */ +}; + +static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) +{ + const char *speed_string; + + if (speed < ARRAY_SIZE(pci_bus_speed_strings)) + speed_string = pci_bus_speed_strings[speed]; + else + speed_string = "Unknown"; + + return sprintf(buf, "%s\n", speed_string); +} + +static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf) +{ + return bus_speed_read(slot->bus->max_bus_speed, buf); +} + +static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf) +{ + return bus_speed_read(slot->bus->cur_bus_speed, buf); +} + static void pci_slot_release(struct kobject *kobj) { struct pci_dev *dev; @@ -66,9 +114,15 @@ static void pci_slot_release(struct kobject *kobj) static struct pci_slot_attribute pci_slot_attr_address = __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL); +static struct pci_slot_attribute pci_slot_attr_max_speed = + __ATTR(max_bus_speed, (S_IFREG | S_IRUGO), max_speed_read_file, NULL); +static struct pci_slot_attribute pci_slot_attr_cur_speed = + __ATTR(cur_bus_speed, (S_IFREG | S_IRUGO), cur_speed_read_file, NULL); static struct attribute *pci_slot_default_attrs[] = { &pci_slot_attr_address.attr, + &pci_slot_attr_max_speed.attr, + &pci_slot_attr_cur_speed.attr, NULL, }; diff --git a/include/linux/pci.h b/include/linux/pci.h index d76a8a0b6b53..a446097a9f68 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -380,6 +380,8 @@ struct pci_bus { unsigned char primary; /* number of primary bridge */ unsigned char secondary; /* number of secondary bridge */ unsigned char subordinate; /* max number of subordinate buses */ + unsigned char max_bus_speed; /* enum pci_bus_speed */ + unsigned char cur_bus_speed; /* enum pci_bus_speed */ char name[48]; @@ -610,6 +612,7 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr); +void pcie_update_link_speed(struct pci_bus *bus, u16 link_status); struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, struct hotplug_slot *hotplug); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 56251ac385e9..5d09cbafa7db 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -63,12 +63,6 @@ enum pcie_link_width { * @get_adapter_status: Called to get see if an adapter is present in the slot or not. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. - * @get_max_bus_speed: Called to get the max bus speed for a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. - * @get_cur_bus_speed: Called to get the current bus speed for a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * * The table of function pointers that is passed to the hotplug pci core by a * hotplug pci driver. These functions are called by the hotplug pci core when @@ -86,17 +80,14 @@ struct hotplug_slot_ops { int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); - int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); - int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); }; /** * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot - * @power: if power is enabled or not (1/0) + * @power_status: if power is enabled or not (1/0) * @attention_status: if the attention light is enabled or not (1/0) * @latch_status: if the latch (if any) is open or closed (1/0) - * @adapter_present: if there is a pci board present in the slot or not (1/0) - * @address: (domain << 16 | bus << 8 | dev) + * @adapter_status: if there is a pci board present in the slot or not (1/0) * * Used to notify the hotplug pci core of the status of a specific slot. */ @@ -105,8 +96,6 @@ struct hotplug_slot_info { u8 attention_status; u8 latch_status; u8 adapter_status; - enum pci_bus_speed max_bus_speed; - enum pci_bus_speed cur_bus_speed; }; /** From 9be60ca0497a2563662fde4c9007841c3b79a742 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:33 -0500 Subject: [PATCH 04/48] PCI: Add support for detection of PCIe and PCI-X bus speeds Both PCIe and PCI-X bridges report their secondary bus speed in their respective capabilities. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 51cf8982fec7..188ee9cf0605 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -393,6 +393,25 @@ static struct pci_bus * pci_alloc_bus(void) return b; } +static unsigned char pcix_bus_speed[] = { + PCI_SPEED_UNKNOWN, /* 0 */ + PCI_SPEED_66MHz_PCIX, /* 1 */ + PCI_SPEED_100MHz_PCIX, /* 2 */ + PCI_SPEED_133MHz_PCIX, /* 3 */ + PCI_SPEED_UNKNOWN, /* 4 */ + PCI_SPEED_66MHz_PCIX_ECC, /* 5 */ + PCI_SPEED_100MHz_PCIX_ECC, /* 6 */ + PCI_SPEED_133MHz_PCIX_ECC, /* 7 */ + PCI_SPEED_UNKNOWN, /* 8 */ + PCI_SPEED_66MHz_PCIX_266, /* 9 */ + PCI_SPEED_100MHz_PCIX_266, /* A */ + PCI_SPEED_133MHz_PCIX_266, /* B */ + PCI_SPEED_UNKNOWN, /* C */ + PCI_SPEED_66MHz_PCIX_533, /* D */ + PCI_SPEED_100MHz_PCIX_533, /* E */ + PCI_SPEED_133MHz_PCIX_533 /* F */ +}; + static unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCIE_SPEED_2_5GT, /* 1 */ @@ -418,6 +437,51 @@ void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) } EXPORT_SYMBOL_GPL(pcie_update_link_speed); +static void pci_set_bus_speed(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + int pos; + + pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); + if (pos) { + u16 status; + enum pci_bus_speed max; + pci_read_config_word(bridge, pos + 2, &status); + + if (status & 0x8000) { + max = PCI_SPEED_133MHz_PCIX_533; + } else if (status & 0x4000) { + max = PCI_SPEED_133MHz_PCIX_266; + } else if (status & 0x0002) { + if (((status >> 12) & 0x3) == 2) { + max = PCI_SPEED_133MHz_PCIX_ECC; + } else { + max = PCI_SPEED_133MHz_PCIX; + } + } else { + max = PCI_SPEED_66MHz_PCIX; + } + + bus->max_bus_speed = max; + bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf]; + + return; + } + + pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); + if (pos) { + u32 linkcap; + u16 linksta; + + pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap); + bus->max_bus_speed = pcie_link_speed[linkcap & 0xf]; + + pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta); + pcie_update_link_speed(bus, linksta); + } +} + + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { @@ -457,6 +521,8 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->self = bridge; child->bridge = get_device(&bridge->dev); + pci_set_bus_speed(child); + /* Set up default resource pointers and names.. */ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; From 45b4cdd57ef0e57555b2ab61b584784819b39365 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:34 -0500 Subject: [PATCH 05/48] PCI: Add support for AGP in cur/max bus speed Take advantage of some gaps in the table to fit in support for AGP speeds. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/slot.c | 10 +++++----- include/linux/pci.h | 5 +++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 188ee9cf0605..2803ab96a98c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -437,11 +437,56 @@ void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) } EXPORT_SYMBOL_GPL(pcie_update_link_speed); +static unsigned char agp_speeds[] = { + AGP_UNKNOWN, + AGP_1X, + AGP_2X, + AGP_4X, + AGP_8X +}; + +static enum pci_bus_speed agp_speed(int agp3, int agpstat) +{ + int index = 0; + + if (agpstat & 4) + index = 3; + else if (agpstat & 2) + index = 2; + else if (agpstat & 1) + index = 1; + else + goto out; + + if (agp3) { + index += 2; + if (index == 5) + index = 0; + } + + out: + return agp_speeds[index]; +} + + static void pci_set_bus_speed(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; int pos; + pos = pci_find_capability(bridge, PCI_CAP_ID_AGP); + if (!pos) + pos = pci_find_capability(bridge, PCI_CAP_ID_AGP3); + if (pos) { + u32 agpstat, agpcmd; + + pci_read_config_dword(bridge, pos + PCI_AGP_STATUS, &agpstat); + bus->max_bus_speed = agp_speed(agpstat & 8, agpstat & 7); + + pci_read_config_dword(bridge, pos + PCI_AGP_COMMAND, &agpcmd); + bus->cur_bus_speed = agp_speed(agpstat & 8, agpcmd & 7); + } + pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); if (pos) { u16 status; diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 6f6b8d24786a..c7260d4e339b 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -61,11 +61,11 @@ static char *pci_bus_speed_strings[] = { "66 MHz PCI-X 266", /* 0x09 */ "100 MHz PCI-X 266", /* 0x0a */ "133 MHz PCI-X 266", /* 0x0b */ - NULL, /* 0x0c */ - NULL, /* 0x0d */ - NULL, /* 0x0e */ - NULL, /* 0x0f */ - NULL, /* 0x10 */ + "Unknown AGP", /* 0x0c */ + "1x AGP", /* 0x0d */ + "2x AGP", /* 0x0e */ + "4x AGP", /* 0x0f */ + "8x AGP", /* 0x10 */ "66 MHz PCI-X 533", /* 0x11 */ "100 MHz PCI-X 533", /* 0x12 */ "133 MHz PCI-X 533", /* 0x13 */ diff --git a/include/linux/pci.h b/include/linux/pci.h index a446097a9f68..842adaad312e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -200,6 +200,11 @@ enum pci_bus_speed { PCI_SPEED_66MHz_PCIX_266 = 0x09, PCI_SPEED_100MHz_PCIX_266 = 0x0a, PCI_SPEED_133MHz_PCIX_266 = 0x0b, + AGP_UNKNOWN = 0x0c, + AGP_1X = 0x0d, + AGP_2X = 0x0e, + AGP_4X = 0x0f, + AGP_8X = 0x10, PCI_SPEED_66MHz_PCIX_533 = 0x11, PCI_SPEED_100MHz_PCIX_533 = 0x12, PCI_SPEED_133MHz_PCIX_533 = 0x13, From 9dfd97fe12f79ec8b68feb63912a4ef2f31f571a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:35 -0500 Subject: [PATCH 06/48] PCI: Add support for reporting PCIe 3.0 speeds Add the 8.0 GT/s speed. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 2 +- drivers/pci/slot.c | 1 + include/linux/pci.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2803ab96a98c..9672760c7ade 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -416,7 +416,7 @@ static unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCIE_SPEED_2_5GT, /* 1 */ PCIE_SPEED_5_0GT, /* 2 */ - PCI_SPEED_UNKNOWN, /* 3 */ + PCIE_SPEED_8_0GT, /* 3 */ PCI_SPEED_UNKNOWN, /* 4 */ PCI_SPEED_UNKNOWN, /* 5 */ PCI_SPEED_UNKNOWN, /* 6 */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index c7260d4e339b..49c9e6c9779a 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -71,6 +71,7 @@ static char *pci_bus_speed_strings[] = { "133 MHz PCI-X 533", /* 0x13 */ "2.5 GT/s PCIe", /* 0x14 */ "5.0 GT/s PCIe", /* 0x15 */ + "8.0 GT/s PCIe", /* 0x16 */ }; static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) diff --git a/include/linux/pci.h b/include/linux/pci.h index 842adaad312e..0c3a29444f89 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -210,6 +210,7 @@ enum pci_bus_speed { PCI_SPEED_133MHz_PCIX_533 = 0x13, PCIE_SPEED_2_5GT = 0x14, PCIE_SPEED_5_0GT = 0x15, + PCIE_SPEED_8_0GT = 0x16, PCI_SPEED_UNKNOWN = 0xff, }; From 3804259475314a50e4d7a8a974a22fddb6ac7dd7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 4 Jan 2010 15:44:10 -0800 Subject: [PATCH 07/48] PCI hotplug: remove obsolete usage of get_bus_speed from rpaphp hotplug ops No longer needed and causes build breakage. Reported-by: Stephen Rothwell Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/rpaphp_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index b14e5e66a92c..dcaae725fd79 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -430,7 +430,6 @@ struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { .get_power_status = get_power_status, .get_attention_status = get_attention_status, .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, }; module_init(rpaphp_init); From 93177a748ba0d4f3d3e51c8e6c785773bf6a70df Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 2 Jan 2010 22:57:24 +0100 Subject: [PATCH 08/48] PCI: Clean up build for CONFIG_PCI_QUIRKS unset Currently, drivers/pci/quirks.c is built unconditionally, but if CONFIG_PCI_QUIRKS is unset, the only things actually built in this file are definitions of global variables and empty functions (due to the #ifdef CONFIG_PCI_QUIRKS embracing all of the code inside the file). This is not particularly nice and if someone overlooks the #ifdef CONFIG_PCI_QUIRKS, build errors are introduced. To clean that up, move the definitions of the global variables in quirks.c that are always built to pci.c, move the definitions of the empty functions (compiled when CONFIG_PCI_QUIRKS is unset) to headers (additionally make these functions static inline) and modify drivers/pci/Makefile so that quirks.c is only built if CONFIG_PCI_QUIRKS is set. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/Makefile | 4 +++- drivers/pci/pci.c | 7 ++++++- drivers/pci/pci.h | 7 +++++++ drivers/pci/quirks.c | 14 ++------------ include/linux/pci.h | 6 +++++- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4df48d58eaa6..adb74253a996 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -2,12 +2,14 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ +obj-y += access.o bus.o probe.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ irq.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o +obj-$(CONFIG_PCI_QUIRKS) += quirks.o + obj-$(CONFIG_PCI_LEGACY) += legacy.o CFLAGS_legacy.o += -Wno-deprecated-declarations diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 315fea47e784..1f9be53c39ba 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -19,7 +19,6 @@ #include #include #include -#include /* isa_dma_bridge_buggy */ #include #include #include "pci.h" @@ -29,6 +28,12 @@ const char *pci_power_names[] = { }; EXPORT_SYMBOL_GPL(pci_power_names); +int isa_dma_bridge_buggy; +EXPORT_SYMBOL(isa_dma_bridge_buggy); + +int pci_pci_problems; +EXPORT_SYMBOL(pci_pci_problems); + unsigned int pci_pm_d3_delay; static void pci_dev_d3_sleep(struct pci_dev *dev) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fbd0e3adbca3..5d169bc3ccd1 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -319,6 +319,13 @@ struct pci_dev_reset_methods { int (*reset)(struct pci_dev *dev, int probe); }; +#ifdef CONFIG_PCI_QUIRKS extern int pci_dev_specific_reset(struct pci_dev *dev, int probe); +#else +static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) +{ + return -ENOTTY; +} +#endif #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d58b94030ef3..790eb69a4aa9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -25,14 +25,9 @@ #include #include #include +#include /* isa_dma_bridge_buggy */ #include "pci.h" -int isa_dma_bridge_buggy; -EXPORT_SYMBOL(isa_dma_bridge_buggy); -int pci_pci_problems; -EXPORT_SYMBOL(pci_pci_problems); - -#ifdef CONFIG_PCI_QUIRKS /* * This quirk function disables memory decoding and releases memory resources * of the device specified by kernel's boot parameter 'pci=resource_alignment='. @@ -2612,6 +2607,7 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) } pci_do_fixups(dev, start, end); } +EXPORT_SYMBOL(pci_fixup_device); static int __init pci_apply_final_quirks(void) { @@ -2723,9 +2719,3 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } - -#else -void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} -int pci_dev_specific_reset(struct pci_dev *dev, int probe) { return -ENOTTY; } -#endif -EXPORT_SYMBOL(pci_fixup_device); diff --git a/include/linux/pci.h b/include/linux/pci.h index 0c3a29444f89..16f48e7506ed 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1271,8 +1271,12 @@ enum pci_fixup_pass { DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ suspend##vendor##device##hook, vendor, device, hook) - +#ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); +#else +static inline void pci_fixup_device(enum pci_fixup_pass pass, + struct pci_dev *dev) {} +#endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr); From 93da6202264ce1256b04db8008a43882ae62d060 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Tue, 12 Jan 2010 16:56:37 -0800 Subject: [PATCH 09/48] x86/PCI: irq and pci_ids patch for Intel Cougar Point DeviceIDs This patch adds the Intel Cougar Point (PCH) LPC and SMBus Controller DeviceIDs. Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 2 ++ include/linux/pci_ids.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 0696d506c4ad..b02f6d8ac922 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -590,6 +590,8 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route case PCI_DEVICE_ID_INTEL_ICH10_1: case PCI_DEVICE_ID_INTEL_ICH10_2: case PCI_DEVICE_ID_INTEL_ICH10_3: + case PCI_DEVICE_ID_INTEL_CPT_LPC1: + case PCI_DEVICE_ID_INTEL_CPT_LPC2: r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index cca8a044e2b6..0be824320580 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2417,6 +2417,9 @@ #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 #define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 +#define PCI_DEVICE_ID_INTEL_CPT_SMBUS 0x1c22 +#define PCI_DEVICE_ID_INTEL_CPT_LPC1 0x1c42 +#define PCI_DEVICE_ID_INTEL_CPT_LPC2 0x1c43 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 From b26b2d494b659f988b4d75eb394dfa0ddac415c9 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 1 Jan 2010 17:40:49 +0100 Subject: [PATCH 10/48] resource/PCI: align functions now return start of resource As suggested by Linus, align functions should return the start of a resource, not void. An update of "res->start" is no longer necessary. Cc: Bjorn Helgaas Cc: Yinghai Lu Signed-off-by: Dominik Brodowski Signed-off-by: Jesse Barnes --- arch/alpha/kernel/pci.c | 4 ++-- arch/arm/kernel/bios32.c | 8 +++++--- arch/cris/arch-v32/drivers/pci/bios.c | 14 ++++++-------- arch/frv/mb93090-mb00/pci-frv.c | 14 ++++++-------- arch/ia64/pci/pci.c | 3 ++- arch/mips/pci/pci.c | 4 ++-- arch/mips/pmc-sierra/yosemite/ht.c | 10 +++++----- arch/mn10300/unit-asb2305/pci-asb2305.c | 16 +++++++--------- arch/parisc/kernel/pci.c | 10 +++++----- arch/powerpc/kernel/pci-common.c | 13 ++++++------- arch/sh/drivers/pci/pci.c | 6 +++--- arch/sparc/kernel/pci.c | 5 +++-- arch/sparc/kernel/pcic.c | 5 +++-- arch/x86/pci/i386.c | 12 +++++------- arch/xtensa/kernel/pci.c | 11 +++++------ drivers/pci/bus.c | 6 ++++-- drivers/pcmcia/rsrc_mgr.c | 12 ++++++------ drivers/pcmcia/rsrc_nonstatic.c | 16 +++++++++------- include/linux/ioport.h | 6 ++++-- include/linux/pci.h | 8 +++++--- kernel/resource.c | 14 +++++++++----- 21 files changed, 102 insertions(+), 95 deletions(-) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index a91ba28999b5..5cf111ed851c 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -126,7 +126,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final); #define MB (1024*KB) #define GB (1024*MB) -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { @@ -184,7 +184,7 @@ pcibios_align_resource(void *data, struct resource *res, } } - res->start = start; + return start; } #undef KB #undef MB diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 809681900ec8..a7c85f84f06c 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -616,15 +616,17 @@ char * __init pcibios_setup(char *str) * but we want to try to avoid allocating at 0x2900-0x2bff * which might be mirrored at 0x0100-0x03ff.. */ -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { resource_size_t start = res->start; if (res->flags & IORESOURCE_IO && start & 0x300) start = (start + 0x3ff) & ~0x3ff; - res->start = (start + align - 1) & ~(align - 1); + start = (start + align - 1) & ~(align - 1); + + return start; } /** diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c index 77ee319193c3..5811e2f8945c 100644 --- a/arch/cris/arch-v32/drivers/pci/bios.c +++ b/arch/cris/arch-v32/drivers/pci/bios.c @@ -41,18 +41,16 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, return 0; } -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { - if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; + resource_size_t start = res->start; - if (start & 0x300) { - start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } - } + if ((res->flags & IORESOURCE_IO) && (start & 0x300)) + start = (start + 0x3ff) & ~0x3ff; + + return start } int pcibios_enable_resources(struct pci_dev *dev, int mask) diff --git a/arch/frv/mb93090-mb00/pci-frv.c b/arch/frv/mb93090-mb00/pci-frv.c index 566bdeb499d1..c947aa43f2a9 100644 --- a/arch/frv/mb93090-mb00/pci-frv.c +++ b/arch/frv/mb93090-mb00/pci-frv.c @@ -32,18 +32,16 @@ * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { - if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; + resource_size_t start = res->start; - if (start & 0x300) { - start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } - } + if ((res->flags & IORESOURCE_IO) && (start & 0x300)) + start = (start + 0x3ff) & ~0x3ff; + + return start } diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index df639db779f9..ef574cd311d1 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -547,10 +547,11 @@ pcibios_disable_device (struct pci_dev *dev) acpi_pci_irq_disable(dev); } -void +resource_size_t pcibios_align_resource (void *data, struct resource *res, resource_size_t size, resource_size_t align) { + return res->start; } /* diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 9a11c2226891..9085988f7cf3 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -49,7 +49,7 @@ static int pci_initialized; * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { @@ -73,7 +73,7 @@ pcibios_align_resource(void *data, struct resource *res, start = PCIBIOS_MIN_MEM + hose->mem_resource->start; } - res->start = start; + return start; } static void __devinit pcibios_scanbus(struct pci_controller *hose) diff --git a/arch/mips/pmc-sierra/yosemite/ht.c b/arch/mips/pmc-sierra/yosemite/ht.c index 678388fd34b1..5e410087b628 100644 --- a/arch/mips/pmc-sierra/yosemite/ht.c +++ b/arch/mips/pmc-sierra/yosemite/ht.c @@ -345,14 +345,13 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pcibios_enable_resources(dev); } -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; + resource_size_t start = res->start; if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; - /* We need to avoid collisions with `mirrored' VGA ports and other strange ISA hardware, so we always want the addresses kilobyte aligned. */ @@ -363,8 +362,9 @@ void pcibios_align_resource(void *data, struct resource *res, } start = (start + 1024 - 1) & ~(1024 - 1); - res->start = start; } + + return start; } struct pci_ops titan_pci_ops = { diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c index 78cd134ddf7d..b0b3758fba08 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.c +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -31,9 +31,11 @@ * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { + resource_size_t start = res->start; + #if 0 struct pci_dev *dev = data; @@ -47,14 +49,10 @@ void pcibios_align_resource(void *data, struct resource *res, ); #endif - if (res->flags & IORESOURCE_IO) { - unsigned long start = res->start; + if ((res->flags & IORESOURCE_IO) && (start & 0x300)) + start = (start + 0x3ff) & ~0x3ff; - if (start & 0x300) { - start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } - } + return start; } diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index f7064abc3bb6..4463a31973ba 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -254,10 +254,10 @@ EXPORT_SYMBOL(pcibios_bus_to_resource); * Since we are just checking candidates, don't use any fields other * than res->start. */ -void pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t alignment) { - resource_size_t mask, align; + resource_size_t mask, align, start = res->start; DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n", pci_name(((struct pci_dev *) data)), @@ -269,10 +269,10 @@ void pcibios_align_resource(void *data, struct resource *res, /* Align to largest of MIN or input size */ mask = max(alignment, align) - 1; - res->start += mask; - res->start &= ~mask; + start += mask; + start &= ~mask; - /* The caller updates the end field, we don't. */ + return start; } diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index cadbed679fbb..d7eebbaf01f1 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1181,21 +1181,20 @@ static int skip_isa_ioresource_align(struct pci_dev *dev) * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; + resource_size_t start = res->start; if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; - if (skip_isa_ioresource_align(dev)) - return; - if (start & 0x300) { + return start; + if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } } + + return start; } EXPORT_SYMBOL(pcibios_align_resource); diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index c481df639022..b36ca825b3d3 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -148,8 +148,8 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) * addresses to be allocated in the 0x000-0x0ff region * modulo 0x400. */ -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; struct pci_channel *chan = dev->sysdata; @@ -171,7 +171,7 @@ void pcibios_align_resource(void *data, struct resource *res, start = PCIBIOS_MIN_MEM + chan->mem_resource->start; } - res->start = start; + return start; } void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 592b03d85167..d56c1f34689e 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -722,9 +722,10 @@ void pcibios_update_irq(struct pci_dev *pdev, int irq) { } -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { + return res->start; } int pcibios_enable_device(struct pci_dev *dev, int mask) diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 4e2724ec2bb6..46fda64ff163 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -768,9 +768,10 @@ char * __devinit pcibios_setup(char *str) return str; } -void pcibios_align_resource(void *data, struct resource *res, - resource_size_t size, resource_size_t align) +resource_size_t pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) { + return res->start; } int pcibios_enable_device(struct pci_dev *pdev, int mask) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 5dc9e8c63fcd..924e40c916d3 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -60,22 +60,20 @@ skip_isa_ioresource_align(struct pci_dev *dev) { * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; + resource_size_t start = res->start; if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; - if (skip_isa_ioresource_align(dev)) - return; - if (start & 0x300) { + return start; + if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } } + return start; } EXPORT_SYMBOL(pcibios_align_resource); diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index b7c073484e01..d7efab03164e 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -69,26 +69,25 @@ static int pci_bus_count; * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void +resource_size_t pcibios_align_resource(void *data, struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; + resource_size_t start = res->start; if (res->flags & IORESOURCE_IO) { - resource_size_t start = res->start; - if (size > 0x100) { printk(KERN_ERR "PCI: I/O Region %s/%d too large" " (%ld bytes)\n", pci_name(dev), dev->resource - res, size); } - if (start & 0x300) { + if (start & 0x300) start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } } + + return start; } int diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index cef28a79103f..d29d69a37940 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -36,8 +36,10 @@ 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, - void (*alignf)(void *, struct resource *, resource_size_t, - resource_size_t), + resource_size_t (*alignf)(void *, + struct resource *, + resource_size_t, + resource_size_t), void *alignf_data) { int i, ret = -ENOMEM; diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 52db17263d8b..f92a2dac13a2 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -114,22 +114,20 @@ struct pcmcia_align_data { unsigned long offset; }; -static void pcmcia_align(void *align_data, struct resource *res, - unsigned long size, unsigned long align) +static resource_size_t pcmcia_align(void *align_data, struct resource *res, + resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; - unsigned long start; + resource_size_t start; start = (res->start & ~data->mask) + data->offset; if (start < res->start) start += data->mask + 1; - res->start = start; #ifdef CONFIG_X86 if (res->flags & IORESOURCE_IO) { if (start & 0x300) { start = (start + 0x3ff) & ~0x3ff; - res->start = start; } } #endif @@ -137,9 +135,11 @@ static void pcmcia_align(void *align_data, struct resource *res, #ifdef CONFIG_M68K if (res->flags & IORESOURCE_IO) { if ((res->start + size - 1) >= 1024) - res->start = res->end; + start = res->end; } #endif + + return start; } diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 9b0dc433a8c3..b65902870e44 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -533,7 +533,7 @@ struct pcmcia_align_data { struct resource_map *map; }; -static void +static resource_size_t pcmcia_common_align(void *align_data, struct resource *res, resource_size_t size, resource_size_t align) { @@ -545,17 +545,18 @@ pcmcia_common_align(void *align_data, struct resource *res, start = (res->start & ~data->mask) + data->offset; if (start < res->start) start += data->mask + 1; - res->start = start; + return start; } -static void +static resource_size_t pcmcia_align(void *align_data, struct resource *res, resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; struct resource_map *m; + resource_size_t start; - pcmcia_common_align(data, res, size, align); + start = pcmcia_common_align(data, res, size, align); for (m = data->map->next; m != data->map; m = m->next) { unsigned long start = m->base; @@ -567,8 +568,7 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size, * fit here. */ if (res->start < start) { - res->start = start; - pcmcia_common_align(data, res, size, align); + start = pcmcia_common_align(data, res, size, align); } /* @@ -586,7 +586,9 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size, * If we failed to find something suitable, ensure we fail. */ if (m == data->map) - res->start = res->end; + start = res->end; + + return start; } /* diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 7129504e053d..f4195de4eb9d 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -120,8 +120,10 @@ extern void insert_resource_expand_to_fit(struct resource *root, struct resource extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, - void (*alignf)(void *, struct resource *, - resource_size_t, resource_size_t), + resource_size_t (*alignf)(void *, + struct resource *, + resource_size_t, + resource_size_t), void *alignf_data); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); diff --git a/include/linux/pci.h b/include/linux/pci.h index 16f48e7506ed..1bd9f52b24b8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -592,7 +592,8 @@ int __must_check pcibios_enable_device(struct pci_dev *, int mask); char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -void pcibios_align_resource(void *, struct resource *, resource_size_t, +resource_size_t pcibios_align_resource(void *, struct resource *, + resource_size_t, resource_size_t); void pcibios_update_irq(struct pci_dev *, int irq); @@ -827,8 +828,9 @@ int __must_check 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, - void (*alignf)(void *, struct resource *, - resource_size_t, resource_size_t), + resource_size_t (*alignf)(void *, struct resource *, + resource_size_t, + resource_size_t), void *alignf_data); void pci_enable_bridges(struct pci_bus *bus); diff --git a/kernel/resource.c b/kernel/resource.c index af96c1e4b54b..e697f20e2288 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -303,8 +303,10 @@ int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, static int find_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, - void (*alignf)(void *, struct resource *, - resource_size_t, resource_size_t), + resource_size_t (*alignf)(void *, + struct resource *, + resource_size_t, + resource_size_t), void *alignf_data) { struct resource *this = root->child; @@ -330,7 +332,7 @@ static int find_resource(struct resource *root, struct resource *new, tmp.end = max; tmp.start = ALIGN(tmp.start, align); if (alignf) - alignf(alignf_data, &tmp, size, align); + tmp.start = alignf(alignf_data, &tmp, size, align); if (tmp.start < tmp.end && tmp.end - tmp.start >= size - 1) { new->start = tmp.start; new->end = tmp.start + size - 1; @@ -358,8 +360,10 @@ static int find_resource(struct resource *root, struct resource *new, int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, - void (*alignf)(void *, struct resource *, - resource_size_t, resource_size_t), + resource_size_t (*alignf)(void *, + struct resource *, + resource_size_t, + resource_size_t), void *alignf_data) { int err; From 3b7a17fcdae532d29dffab9d564a28be08960988 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 1 Jan 2010 17:40:50 +0100 Subject: [PATCH 11/48] resource/PCI: mark struct resource as const Now that we return the new resource start position, there is no need to update "struct resource" inside the align function. Therefore, mark the struct resource as const. Cc: Bjorn Helgaas Cc: Yinghai Lu Signed-off-by: Dominik Brodowski Signed-off-by: Jesse Barnes --- arch/alpha/kernel/pci.c | 2 +- arch/arm/kernel/bios32.c | 2 +- arch/cris/arch-v32/drivers/pci/bios.c | 2 +- arch/frv/mb93090-mb00/pci-frv.c | 2 +- arch/ia64/pci/pci.c | 2 +- arch/mips/pci/pci.c | 2 +- arch/mips/pmc-sierra/yosemite/ht.c | 2 +- arch/mn10300/unit-asb2305/pci-asb2305.c | 2 +- arch/parisc/kernel/pci.c | 2 +- arch/powerpc/kernel/pci-common.c | 2 +- arch/sh/drivers/pci/pci.c | 2 +- arch/sparc/kernel/pci.c | 2 +- arch/sparc/kernel/pcic.c | 2 +- arch/x86/pci/i386.c | 2 +- arch/xtensa/kernel/pci.c | 4 ++-- drivers/pci/bus.c | 2 +- drivers/pcmcia/rsrc_mgr.c | 3 ++- drivers/pcmcia/rsrc_nonstatic.c | 6 +++--- include/linux/ioport.h | 2 +- include/linux/pci.h | 5 +++-- kernel/resource.c | 4 ++-- 21 files changed, 28 insertions(+), 26 deletions(-) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 5cf111ed851c..c9ab94ee1ca8 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -127,7 +127,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final); #define GB (1024*MB) resource_size_t -pcibios_align_resource(void *data, struct resource *res, +pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index a7c85f84f06c..bd397e0b663e 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -616,7 +616,7 @@ char * __init pcibios_setup(char *str) * but we want to try to avoid allocating at 0x2900-0x2bff * which might be mirrored at 0x0100-0x03ff.. */ -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { resource_size_t start = res->start; diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c index 5811e2f8945c..d4b9c36ddc0f 100644 --- a/arch/cris/arch-v32/drivers/pci/bios.c +++ b/arch/cris/arch-v32/drivers/pci/bios.c @@ -42,7 +42,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, } resource_size_t -pcibios_align_resource(void *data, struct resource *res, +pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { resource_size_t start = res->start; diff --git a/arch/frv/mb93090-mb00/pci-frv.c b/arch/frv/mb93090-mb00/pci-frv.c index c947aa43f2a9..1ed15d7fea20 100644 --- a/arch/frv/mb93090-mb00/pci-frv.c +++ b/arch/frv/mb93090-mb00/pci-frv.c @@ -33,7 +33,7 @@ * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t -pcibios_align_resource(void *data, struct resource *res, +pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { resource_size_t start = res->start; diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index ef574cd311d1..783c83bb2b49 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -548,7 +548,7 @@ pcibios_disable_device (struct pci_dev *dev) } resource_size_t -pcibios_align_resource (void *data, struct resource *res, +pcibios_align_resource (void *data, const struct resource *res, resource_size_t size, resource_size_t align) { return res->start; diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 9085988f7cf3..f87f5e188fa6 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -50,7 +50,7 @@ static int pci_initialized; * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t -pcibios_align_resource(void *data, struct resource *res, +pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/mips/pmc-sierra/yosemite/ht.c b/arch/mips/pmc-sierra/yosemite/ht.c index 5e410087b628..fd22597edb64 100644 --- a/arch/mips/pmc-sierra/yosemite/ht.c +++ b/arch/mips/pmc-sierra/yosemite/ht.c @@ -345,7 +345,7 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pcibios_enable_resources(dev); } -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c index b0b3758fba08..d6119b879a98 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.c +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -31,7 +31,7 @@ * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { resource_size_t start = res->start; diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 4463a31973ba..5179e5e6f8fb 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -254,7 +254,7 @@ EXPORT_SYMBOL(pcibios_bus_to_resource); * Since we are just checking candidates, don't use any fields other * than res->start. */ -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t alignment) { resource_size_t mask, align, start = res->start; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index d7eebbaf01f1..e640810e813f 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1181,7 +1181,7 @@ static int skip_isa_ioresource_align(struct pci_dev *dev) * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index b36ca825b3d3..96213fd172ce 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -148,7 +148,7 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) * addresses to be allocated in the 0x000-0x0ff region * modulo 0x400. */ -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index d56c1f34689e..37b66c60abe3 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -722,7 +722,7 @@ void pcibios_update_irq(struct pci_dev *pdev, int irq) { } -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { return res->start; diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 46fda64ff163..75e88c00bca3 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -768,7 +768,7 @@ char * __devinit pcibios_setup(char *str) return str; } -resource_size_t pcibios_align_resource(void *data, struct resource *res, +resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { return res->start; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 924e40c916d3..5a8fbf8d4cac 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -61,7 +61,7 @@ skip_isa_ioresource_align(struct pci_dev *dev) { * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t -pcibios_align_resource(void *data, struct resource *res, +pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index d7efab03164e..cd1026931203 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -70,8 +70,8 @@ static int pci_bus_count; * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t -pcibios_align_resource(void *data, struct resource *res, resource_size_t size, - resource_size_t align) +pcibios_align_resource(void *data, const struct resource *res, + resource_size_t size, resource_size_t align) { struct pci_dev *dev = data; resource_size_t start = res->start; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index d29d69a37940..a26135bb0ffd 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -37,7 +37,7 @@ 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 (*alignf)(void *, - struct resource *, + const struct resource *, resource_size_t, resource_size_t), void *alignf_data) diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index f92a2dac13a2..f8401a0ef89b 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -114,7 +114,8 @@ struct pcmcia_align_data { unsigned long offset; }; -static resource_size_t pcmcia_align(void *align_data, struct resource *res, +static resource_size_t pcmcia_align(void *align_data, + const struct resource *res, resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index b65902870e44..45d75dc452f0 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -534,7 +534,7 @@ struct pcmcia_align_data { }; static resource_size_t -pcmcia_common_align(void *align_data, struct resource *res, +pcmcia_common_align(void *align_data, const struct resource *res, resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; @@ -549,8 +549,8 @@ pcmcia_common_align(void *align_data, struct resource *res, } static resource_size_t -pcmcia_align(void *align_data, struct resource *res, resource_size_t size, - resource_size_t align) +pcmcia_align(void *align_data, const struct resource *res, + resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; struct resource_map *m; diff --git a/include/linux/ioport.h b/include/linux/ioport.h index f4195de4eb9d..4a811891dbe3 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -121,7 +121,7 @@ extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t (*alignf)(void *, - struct resource *, + const struct resource *, resource_size_t, resource_size_t), void *alignf_data); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1bd9f52b24b8..449d51560fc2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -592,7 +592,7 @@ int __must_check pcibios_enable_device(struct pci_dev *, int mask); char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -resource_size_t pcibios_align_resource(void *, struct resource *, +resource_size_t pcibios_align_resource(void *, const struct resource *, resource_size_t, resource_size_t); void pcibios_update_irq(struct pci_dev *, int irq); @@ -828,7 +828,8 @@ int __must_check 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 (*alignf)(void *, struct resource *, + resource_size_t (*alignf)(void *, + const struct resource *, resource_size_t, resource_size_t), void *alignf_data); diff --git a/kernel/resource.c b/kernel/resource.c index e697f20e2288..7fd123ad00aa 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -304,7 +304,7 @@ static int find_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t (*alignf)(void *, - struct resource *, + const struct resource *, resource_size_t, resource_size_t), void *alignf_data) @@ -361,7 +361,7 @@ int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t (*alignf)(void *, - struct resource *, + const struct resource *, resource_size_t, resource_size_t), void *alignf_data) From 6fcaf17ac7a512227112ac81c0e1a5862bab57a6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 6 Jan 2010 17:47:56 +0100 Subject: [PATCH 12/48] PCI hotplug: fix memory leaks Stanse found a cut&pasted memory leak in pciehp_queue_pushbutton_work and shpchp_queue_pushbutton_work. info is not freed/assigned on all paths. Fix that. Reviewed-by: Kenji Kaneshige Signed-off-by: Jiri Slaby Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_ctrl.c | 1 + drivers/pci/hotplug/shpchp_ctrl.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index d6ac1b261dd9..9a7f247e8ac1 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -341,6 +341,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) p_slot->state = POWERON_STATE; break; default: + kfree(info); goto out; } queue_work(pciehp_wq, &info->work); diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index 179b1c1cb99b..3bba0c0888ff 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -453,6 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work) p_slot->state = POWERON_STATE; break; default: + kfree(info); goto out; } queue_work(shpchp_wq, &info->work); From b0fc889c4311835ae7d02f433154bc20cad9ee11 Mon Sep 17 00:00:00 2001 From: Chandru Date: Mon, 11 Jan 2010 11:49:21 +0530 Subject: [PATCH 13/48] PCI hotplug: ibmphp: read the length of ebda and map entire ebda region ibmphp driver currently maps only 1KB of ebda memory area into kernel address space during driver initialization. This causes kernel oops when the driver is modprobe'd and it accesses memory beyond 1KB within ebda segment. The first byte of ebda segment actually stores the length of the ebda region in Kilobytes. Hence make use of the length parameter and map the entire ebda region. Signed-off-by: Chandru Siddalingappa Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/ibmphp_ebda.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index c1abac8ab5c3..7d3bf31d224d 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -245,7 +245,7 @@ static void __init print_ebda_hpc (void) int __init ibmphp_access_ebda (void) { - u8 format, num_ctlrs, rio_complete, hs_complete; + u8 format, num_ctlrs, rio_complete, hs_complete, ebda_sz; u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, re, rc_id, re_id, base; int rc = 0; @@ -260,7 +260,14 @@ int __init ibmphp_access_ebda (void) iounmap (io_mem); debug ("returned ebda segment: %x\n", ebda_seg); - io_mem = ioremap(ebda_seg<<4, 1024); + io_mem = ioremap(ebda_seg<<4, 1); + ebda_sz = readb(io_mem); + iounmap(io_mem); + debug("ebda size: %d(KiB)\n", ebda_sz); + if (ebda_sz == 0) + return -ENOMEM; + + io_mem = ioremap(ebda_seg<<4, (ebda_sz * 1024)); if (!io_mem ) return -ENOMEM; next_offset = 0x180; From 7cc5997d1dada3bdeed95a59c2f4f6c66cbb0767 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 22 Dec 2009 15:02:21 -0800 Subject: [PATCH 14/48] PCI: separate pci_setup_bridge to small functions This is a good cleanup in itself, and makes it easier to modify specific resource types in later code. Signed-off-by: Yinghai Lu Acked-by: Linus Torvalds Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 66 +++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index c48cd377b3f5..1bd41ac2abd9 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -134,18 +134,12 @@ EXPORT_SYMBOL(pci_setup_cardbus); config space writes, so it's quite possible that an I/O window of the bridge will have some undesirable address (e.g. 0) after the first write. Ditto 64-bit prefetchable MMIO. */ -static void pci_setup_bridge(struct pci_bus *bus) +static void pci_setup_bridge_io(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; - u32 l, bu, lu, io_upper16; - - if (pci_is_enabled(bridge)) - return; - - dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", - bus->secondary, bus->subordinate); + u32 l, io_upper16; /* Set up the top and bottom of the PCI I/O segment for this bus. */ res = bus->resource[0]; @@ -158,8 +152,7 @@ static void pci_setup_bridge(struct pci_bus *bus) /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); - } - else { + } else { /* Clear upper 16 bits of I/O base/limit. */ io_upper16 = 0; l = 0x00f0; @@ -171,21 +164,35 @@ static void pci_setup_bridge(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); +} - /* Set up the top and bottom of the PCI Memory segment - for this bus. */ +static void pci_setup_bridge_mmio(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + struct resource *res; + struct pci_bus_region region; + u32 l; + + /* Set up the top and bottom of the PCI Memory segment for this bus. */ res = bus->resource[1]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; dev_info(&bridge->dev, " bridge window %pR\n", res); - } - else { + } else { l = 0x0000fff0; dev_info(&bridge->dev, " bridge window [mem disabled]\n"); } pci_write_config_dword(bridge, PCI_MEMORY_BASE, l); +} + +static void pci_setup_bridge_mmio_pref(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + struct resource *res; + struct pci_bus_region region; + u32 l, bu, lu; /* Clear out the upper 32 bits of PREF limit. If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily @@ -204,8 +211,7 @@ static void pci_setup_bridge(struct pci_bus *bus) lu = upper_32_bits(region.end); } dev_info(&bridge->dev, " bridge window %pR\n", res); - } - else { + } else { l = 0x0000fff0; dev_info(&bridge->dev, " bridge window [mem pref disabled]\n"); } @@ -214,10 +220,38 @@ static void pci_setup_bridge(struct pci_bus *bus) /* Set the upper 32 bits of PREF base & limit. */ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); +} + +static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) +{ + struct pci_dev *bridge = bus->self; + + if (pci_is_enabled(bridge)) + return; + + dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", + bus->secondary, bus->subordinate); + + if (type & IORESOURCE_IO) + pci_setup_bridge_io(bus); + + if (type & IORESOURCE_MEM) + pci_setup_bridge_mmio(bus); + + if (type & IORESOURCE_PREFETCH) + pci_setup_bridge_mmio_pref(bus); pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } +static void pci_setup_bridge(struct pci_bus *bus) +{ + unsigned long type = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + + __pci_setup_bridge(bus, type); +} + /* Check whether the bridge supports optional I/O and prefetchable memory ranges. If not, the respective base/limit registers must be read-only and read as 0. */ From 5eeec0ec931a01e85b3701ce121b7d8a1800ec60 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 22 Dec 2009 15:02:22 -0800 Subject: [PATCH 15/48] resource: add release_child_resources Useful for freeing a portion of the resource tree, e.g. when trying to reallocate resources more efficiently. Signed-off-by: Yinghai Lu Acked-by: Linus Torvalds Signed-off-by: Jesse Barnes --- include/linux/ioport.h | 1 + kernel/resource.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 4a811891dbe3..dda98410d588 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -112,6 +112,7 @@ extern struct resource iomem_resource; extern int request_resource(struct resource *root, struct resource *new); extern int release_resource(struct resource *new); +void release_child_resources(struct resource *new); extern void reserve_region_with_split(struct resource *root, resource_size_t start, resource_size_t end, const char *name); diff --git a/kernel/resource.c b/kernel/resource.c index 7fd123ad00aa..24e9e60c1459 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -188,6 +188,36 @@ static int __release_resource(struct resource *old) return -EINVAL; } +static void __release_child_resources(struct resource *r) +{ + struct resource *tmp, *p; + resource_size_t size; + + p = r->child; + r->child = NULL; + while (p) { + tmp = p; + p = p->sibling; + + tmp->parent = NULL; + tmp->sibling = NULL; + __release_child_resources(tmp); + + printk(KERN_DEBUG "release child resource %pR\n", tmp); + /* need to restore size, and keep flags */ + size = resource_size(tmp); + tmp->start = 0; + tmp->end = size - 1; + } +} + +void release_child_resources(struct resource *r) +{ + write_lock(&resource_lock); + __release_child_resources(r); + write_unlock(&resource_lock); +} + /** * request_resource - request and reserve an I/O or memory resource * @root: root resource descriptor From 7c9342b8dd1a32386fc32bffb9eedebbfe264763 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 22 Dec 2009 15:02:24 -0800 Subject: [PATCH 16/48] PCI: don't dump resource when bus resource flags indicates unused Don't print out resources without flags to avoid cluttering up the debug output. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 1bd41ac2abd9..52fbd426a1c7 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -610,7 +610,8 @@ static void pci_bus_dump_res(struct pci_bus *bus) for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { struct resource *res = bus->resource[i]; - if (!res || !res->end) + + if (!res || !res->end || !res->flags) continue; dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res); From 41a68a748bbc61f5bcea999e33ba72926dfbe6f7 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Mon, 18 Jan 2010 17:24:10 +0100 Subject: [PATCH 17/48] PCI: push deprecated pci_find_device() function to last user The ISDN4Linux HiSax driver family contains the last remaining users of the deprecated pci_find_device() function. This patch creates a private copy of that function in HiSax, and removes the now unused global function together with its controlling configuration option, CONFIG_PCI_LEGACY. Signed-off-by: Tilman Schmidt Signed-off-by: Jesse Barnes --- drivers/isdn/hisax/Kconfig | 18 ++++++++-------- drivers/isdn/hisax/avm_pci.c | 6 +++--- drivers/isdn/hisax/bkm_a4t.c | 2 +- drivers/isdn/hisax/bkm_a8.c | 2 +- drivers/isdn/hisax/diva.c | 14 ++++++------- drivers/isdn/hisax/elsa.c | 8 ++++---- drivers/isdn/hisax/enternow_pci.c | 2 +- drivers/isdn/hisax/gazel.c | 8 ++++---- drivers/isdn/hisax/hfc_pci.c | 2 +- drivers/isdn/hisax/hisax.h | 23 +++++++++++++++++++++ drivers/isdn/hisax/niccy.c | 6 +++--- drivers/isdn/hisax/nj_s.c | 2 +- drivers/isdn/hisax/nj_u.c | 2 +- drivers/isdn/hisax/sedlbauer.c | 6 +++--- drivers/isdn/hisax/telespci.c | 2 +- drivers/isdn/hisax/w6692.c | 2 +- drivers/pci/Kconfig | 11 ---------- drivers/pci/Makefile | 3 --- drivers/pci/legacy.c | 34 ------------------------------- include/linux/pci.h | 13 ------------ 20 files changed, 64 insertions(+), 102 deletions(-) delete mode 100644 drivers/pci/legacy.c diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig index 3464ebc4cdbc..452fde9edf86 100644 --- a/drivers/isdn/hisax/Kconfig +++ b/drivers/isdn/hisax/Kconfig @@ -109,7 +109,7 @@ config HISAX_16_3 config HISAX_TELESPCI bool "Teles PCI" - depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) + depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) help This enables HiSax support for the Teles PCI. See on how to configure it. @@ -237,7 +237,7 @@ config HISAX_MIC config HISAX_NETJET bool "NETjet card" - depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) + depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) help This enables HiSax support for the NetJet from Traverse Technologies. @@ -248,7 +248,7 @@ config HISAX_NETJET config HISAX_NETJET_U bool "NETspider U card" - depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) + depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) help This enables HiSax support for the Netspider U interface ISDN card from Traverse Technologies. @@ -287,7 +287,7 @@ config HISAX_HSTSAPHIR config HISAX_BKM_A4T bool "Telekom A4T card" - depends on PCI && PCI_LEGACY + depends on PCI help This enables HiSax support for the Telekom A4T card. @@ -297,7 +297,7 @@ config HISAX_BKM_A4T config HISAX_SCT_QUADRO bool "Scitel Quadro card" - depends on PCI && PCI_LEGACY + depends on PCI help This enables HiSax support for the Scitel Quadro card. @@ -316,7 +316,7 @@ config HISAX_GAZEL config HISAX_HFC_PCI bool "HFC PCI-Bus cards" - depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) + depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) help This enables HiSax support for the HFC-S PCI 2BDS0 based cards. @@ -325,7 +325,7 @@ config HISAX_HFC_PCI config HISAX_W6692 bool "Winbond W6692 based cards" - depends on PCI && PCI_LEGACY + depends on PCI help This enables HiSax support for Winbond W6692 based PCI ISDN cards. @@ -341,7 +341,7 @@ config HISAX_HFC_SX config HISAX_ENTERNOW_PCI bool "Formula-n enter:now PCI card" - depends on HISAX_NETJET && PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) + depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV)) help This enables HiSax support for the Formula-n enter:now PCI ISDN card. @@ -412,7 +412,7 @@ config HISAX_HFC4S8S config HISAX_FRITZ_PCIPNP tristate "AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)" - depends on PCI && PCI_LEGACY && EXPERIMENTAL + depends on PCI && EXPERIMENTAL help This enables the driver for the AVM Fritz!Card PCI, Fritz!Card PCI v2 and Fritz!Card PnP. diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 7cabc5a19492..14295a155e71 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -822,7 +822,7 @@ static int __devinit avm_pnp_setup(struct IsdnCardState *cs) #endif /* __ISAPNP__ */ -#ifndef CONFIG_PCI_LEGACY +#ifndef CONFIG_PCI static int __devinit avm_pci_setup(struct IsdnCardState *cs) { @@ -835,7 +835,7 @@ static struct pci_dev *dev_avm __devinitdata = NULL; static int __devinit avm_pci_setup(struct IsdnCardState *cs) { - if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, + if ((dev_avm = hisax_find_pci_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, dev_avm))) { if (pci_enable_device(dev_avm)) @@ -864,7 +864,7 @@ static int __devinit avm_pci_setup(struct IsdnCardState *cs) return (1); } -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ int __devinit setup_avm_pcipnp(struct IsdnCard *card) diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index 9ca2ee54cc94..9f2009c0b69c 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -340,7 +340,7 @@ setup_bkm_a4t(struct IsdnCard *card) } else return (0); - while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN, + while ((dev_a4t = hisax_find_pci_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) { ret = a4t_pci_probe(dev_a4t, cs, &found, &pci_memaddr); if (!ret) diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index e1ff4717a8a6..e775706c60e3 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -301,7 +301,7 @@ setup_sct_quadro(struct IsdnCard *card) (sub_vendor_id != PCI_VENDOR_ID_BERKOM))) return (0); if (cs->subtyp == SCT_1) { - while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX, + while ((dev_a8 = hisax_find_pci_device(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, dev_a8))) { sub_vendor_id = dev_a8->subsystem_vendor; diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 0b0c2e5d806b..780da9bda915 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1148,7 +1148,7 @@ static int __devinit setup_diva_isapnp(struct IsdnCard *card) #endif /* ISAPNP */ -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI static struct pci_dev *dev_diva __devinitdata = NULL; static struct pci_dev *dev_diva_u __devinitdata = NULL; static struct pci_dev *dev_diva201 __devinitdata = NULL; @@ -1159,21 +1159,21 @@ static int __devinit setup_diva_pci(struct IsdnCard *card) struct IsdnCardState *cs = card->cs; cs->subtyp = 0; - if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON, + if ((dev_diva = hisax_find_pci_device(PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { if (pci_enable_device(dev_diva)) return(0); cs->subtyp = DIVA_PCI; cs->irq = dev_diva->irq; cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2); - } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON, + } else if ((dev_diva_u = hisax_find_pci_device(PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) { if (pci_enable_device(dev_diva_u)) return(0); cs->subtyp = DIVA_PCI; cs->irq = dev_diva_u->irq; cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2); - } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON, + } else if ((dev_diva201 = hisax_find_pci_device(PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) { if (pci_enable_device(dev_diva201)) return(0); @@ -1183,7 +1183,7 @@ static int __devinit setup_diva_pci(struct IsdnCard *card) (ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096); cs->hw.diva.cfg_reg = (ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096); - } else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON, + } else if ((dev_diva202 = hisax_find_pci_device(PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) { if (pci_enable_device(dev_diva202)) return(0); @@ -1229,14 +1229,14 @@ static int __devinit setup_diva_pci(struct IsdnCard *card) return (1); /* card found */ } -#else /* if !CONFIG_PCI_LEGACY */ +#else /* if !CONFIG_PCI */ static int __devinit setup_diva_pci(struct IsdnCard *card) { return (-1); /* card not found; continue search */ } -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ int __devinit setup_diva(struct IsdnCard *card) diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index aa29d1cf16af..23c41fcd864e 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1025,7 +1025,7 @@ setup_elsa_pcmcia(struct IsdnCard *card) cs->irq); } -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI static struct pci_dev *dev_qs1000 __devinitdata = NULL; static struct pci_dev *dev_qs3000 __devinitdata = NULL; @@ -1035,7 +1035,7 @@ setup_elsa_pci(struct IsdnCard *card) struct IsdnCardState *cs = card->cs; cs->subtyp = 0; - if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, + if ((dev_qs1000 = hisax_find_pci_device(PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) { if (pci_enable_device(dev_qs1000)) return(0); @@ -1043,7 +1043,7 @@ setup_elsa_pci(struct IsdnCard *card) cs->irq = dev_qs1000->irq; cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1); cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3); - } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA, + } else if ((dev_qs3000 = hisax_find_pci_device(PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) { if (pci_enable_device(dev_qs3000)) return(0); @@ -1093,7 +1093,7 @@ setup_elsa_pci(struct IsdnCard *card) { return (1); } -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ static int __devinit setup_elsa_common(struct IsdnCard *card) diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c index 39f421ed8de8..26264abf1f58 100644 --- a/drivers/isdn/hisax/enternow_pci.c +++ b/drivers/isdn/hisax/enternow_pci.c @@ -406,7 +406,7 @@ setup_enternow_pci(struct IsdnCard *card) for ( ;; ) { - if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET, + if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { ret = en_pci_probe(dev_netjet, cs); if (!ret) diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index 0ea3b4607680..353982fc1436 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -531,7 +531,7 @@ setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) return (0); } -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI static struct pci_dev *dev_tel __devinitdata = NULL; static int __devinit @@ -546,7 +546,7 @@ setup_gazelpci(struct IsdnCardState *cs) found = 0; seekcard = PCI_DEVICE_ID_PLX_R685; for (nbseek = 0; nbseek < 4; nbseek++) { - if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX, + if ((dev_tel = hisax_find_pci_device(PCI_VENDOR_ID_PLX, seekcard, dev_tel))) { if (pci_enable_device(dev_tel)) return 1; @@ -620,7 +620,7 @@ setup_gazelpci(struct IsdnCardState *cs) return (0); } -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ int __devinit setup_gazel(struct IsdnCard *card) @@ -640,7 +640,7 @@ setup_gazel(struct IsdnCard *card) return (0); } else { -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI if (setup_gazelpci(cs)) return (0); #else diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 10914731b304..917cc84065bd 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1658,7 +1658,7 @@ setup_hfcpci(struct IsdnCard *card) i = 0; while (id_list[i].vendor_id) { - tmp_hfcpci = pci_find_device(id_list[i].vendor_id, + tmp_hfcpci = hisax_find_pci_device(id_list[i].vendor_id, id_list[i].device_id, dev_hfcpci); i++; diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 0685c1946969..832a87855ffb 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1323,3 +1323,26 @@ void release_tei(struct IsdnCardState *cs); char *HiSax_getrev(const char *revision); int TeiNew(void); void TeiFree(void); + +#ifdef CONFIG_PCI + +#include + +/* adaptation wrapper for old usage + * WARNING! This is unfit for use in a PCI hotplug environment, + * as the returned PCI device can disappear at any moment in time. + * Callers should be converted to use pci_get_device() instead. + */ +static inline struct pci_dev *hisax_find_pci_device(unsigned int vendor, + unsigned int device, + struct pci_dev *from) +{ + struct pci_dev *pdev; + + pci_dev_get(from); + pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); + pci_dev_put(pdev); + return pdev; +} + +#endif diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index ef00633e1d2a..ccaa6e13310f 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -297,12 +297,12 @@ int __devinit setup_niccy(struct IsdnCard *card) return 0; } } else { -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI static struct pci_dev *niccy_dev __devinitdata; u_int pci_ioaddr; cs->subtyp = 0; - if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, + if ((niccy_dev = hisax_find_pci_device(PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) { if (pci_enable_device(niccy_dev)) @@ -354,7 +354,7 @@ int __devinit setup_niccy(struct IsdnCard *card) printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); return 0; -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ } printk(KERN_INFO "HiSax: NICCY %s config irq:%d data:0x%X ale:0x%X\n", (cs->subtyp == 1) ? "PnP" : "PCI", diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c index 8d36ccc87d81..2344e7b33448 100644 --- a/drivers/isdn/hisax/nj_s.c +++ b/drivers/isdn/hisax/nj_s.c @@ -276,7 +276,7 @@ setup_netjet_s(struct IsdnCard *card) for ( ;; ) { - if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET, + if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { ret = njs_pci_probe(dev_netjet, cs); if (!ret) diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c index d306c946ffba..095e974aed80 100644 --- a/drivers/isdn/hisax/nj_u.c +++ b/drivers/isdn/hisax/nj_u.c @@ -240,7 +240,7 @@ setup_netjet_u(struct IsdnCard *card) for ( ;; ) { - if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET, + if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) { ret = nju_pci_probe(dev_netjet, cs); if (!ret) diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index 5569a522e2a1..69dfc8d29017 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -598,7 +598,7 @@ setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt) } #endif /* __ISAPNP__ */ -#ifdef CONFIG_PCI_LEGACY +#ifdef CONFIG_PCI static struct pci_dev *dev_sedl __devinitdata = NULL; static int __devinit @@ -607,7 +607,7 @@ setup_sedlbauer_pci(struct IsdnCard *card) struct IsdnCardState *cs = card->cs; u16 sub_vendor_id, sub_id; - if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, + if ((dev_sedl = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { if (pci_enable_device(dev_sedl)) return(0); @@ -673,7 +673,7 @@ setup_sedlbauer_pci(struct IsdnCard *card) return (1); } -#endif /* CONFIG_PCI_LEGACY */ +#endif /* CONFIG_PCI */ int __devinit setup_sedlbauer(struct IsdnCard *card) diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 28b08de4673d..b85ceb3746ce 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -300,7 +300,7 @@ setup_telespci(struct IsdnCard *card) if (cs->typ != ISDN_CTYPE_TELESPCI) return (0); - if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { + if ((dev_tel = hisax_find_pci_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { if (pci_enable_device(dev_tel)) return(0); cs->irq = dev_tel->irq; diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index c4d862c11a60..9d6e864023fe 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -1007,7 +1007,7 @@ setup_w6692(struct IsdnCard *card) return (0); while (id_list[id_idx].vendor_id) { - dev_w6692 = pci_find_device(id_list[id_idx].vendor_id, + dev_w6692 = hisax_find_pci_device(id_list[id_idx].vendor_id, id_list[id_idx].device_id, dev_w6692); if (dev_w6692) { diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b1ecefa2a23d..7858a117e80b 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -21,17 +21,6 @@ config PCI_MSI If you don't know what to do here, say N. -config PCI_LEGACY - bool "Enable deprecated pci_find_* API" - depends on PCI - default y - help - Say Y here if you want to include support for the deprecated - pci_find_device() API. Most drivers have been converted over - to using the proper hotplug APIs, so this option serves to - include/exclude only a few drivers that are still using this - API. - config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index adb74253a996..8674c1ebe979 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -10,9 +10,6 @@ obj-$(CONFIG_SYSFS) += slot.o obj-$(CONFIG_PCI_QUIRKS) += quirks.o -obj-$(CONFIG_PCI_LEGACY) += legacy.o -CFLAGS_legacy.o += -Wno-deprecated-declarations - # Build PCI Express stuff if needed obj-$(CONFIG_PCIEPORTBUS) += pcie/ diff --git a/drivers/pci/legacy.c b/drivers/pci/legacy.c deleted file mode 100644 index 871f65c15936..000000000000 --- a/drivers/pci/legacy.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include "pci.h" - -/** - * pci_find_device - begin or continue searching for a PCI device by vendor/device id - * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids - * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids - * @from: Previous PCI device found in search, or %NULL for new search. - * - * Iterates through the list of known PCI devices. If a PCI device is found - * with a matching @vendor and @device, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL as the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device - * on the global list. - * - * NOTE: Do not use this function any more; use pci_get_device() instead, as - * the PCI device returned by this function can disappear at any moment in - * time. - */ -struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, - struct pci_dev *from) -{ - struct pci_dev *pdev; - - pci_dev_get(from); - pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); - pci_dev_put(pdev); - return pdev; -} -EXPORT_SYMBOL(pci_find_device); diff --git a/include/linux/pci.h b/include/linux/pci.h index 449d51560fc2..8b79403cc072 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -646,12 +646,6 @@ extern void pci_sort_breadthfirst(void); /* Generic PCI functions exported to card drivers */ -#ifdef CONFIG_PCI_LEGACY -struct pci_dev __deprecated *pci_find_device(unsigned int vendor, - unsigned int device, - struct pci_dev *from); -#endif /* CONFIG_PCI_LEGACY */ - enum pci_lost_interrupt_reason { PCI_LOST_IRQ_NO_INFORMATION = 0, PCI_LOST_IRQ_DISABLE_MSI, @@ -1010,13 +1004,6 @@ static inline int pci_proc_domain(struct pci_bus *bus) _PCI_NOP_ALL(read, *) _PCI_NOP_ALL(write,) -static inline struct pci_dev *pci_find_device(unsigned int vendor, - unsigned int device, - struct pci_dev *from) -{ - return NULL; -} - static inline struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) From 0bf01c3c86d4b9ea279d6215420484db887f5db5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 20 Jan 2010 14:15:54 +0100 Subject: [PATCH 18/48] PCI: hotplug/cpcihp, fix pci device refcounting Stanse found an ommitted pci_dev_put on one error path in cpcihp_generic_init. The path is taken on !dev, but also when dev->hdr_type != PCI_HEADER_TYPE_BRIDGE. However it omits to pci_dev_put on the latter. As it is fine to pass NULL to pci_dev_put, put it in there uncoditionally. Signed-off-by: Jiri Slaby Cc: Scott Murray Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/cpcihp_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c index 148fb463b81c..fb3f84661bdc 100644 --- a/drivers/pci/hotplug/cpcihp_generic.c +++ b/drivers/pci/hotplug/cpcihp_generic.c @@ -162,6 +162,7 @@ static int __init cpcihp_generic_init(void) dev = pci_get_slot(bus, PCI_DEVFN(bridge_slot, 0)); if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { err("Invalid bridge device %s", bridge); + pci_dev_put(dev); return -EINVAL; } bus = dev->subordinate; From 4fb88c1a28a8dc302bdc09858e7cdafc97bef794 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 17 Jan 2010 14:01:41 -0700 Subject: [PATCH 19/48] PCI: Make pci_scan_slot more robust Yinghai pointed out that the new pci_scan_slot() crashes when called on an ARI-capable slot that is empty. Fix this by exiting early from pci_scan_slot if there is no device in the slot. Also make next_ari_func() robust against devices not existing in case the ARI capability is corrupt. ARI also requires that the devices be listed in order, so if we find a function listed that is out of order, stop scanning to prevent loops. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9672760c7ade..233d1c275d96 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1222,11 +1222,19 @@ EXPORT_SYMBOL(pci_scan_single_device); static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn) { u16 cap; - unsigned pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); + unsigned pos, next_fn; + + if (!dev) + return 0; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); if (!pos) return 0; pci_read_config_word(dev, pos + 4, &cap); - return cap >> 8; + next_fn = cap >> 8; + if (next_fn <= fn) + return 0; + return next_fn; } static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn) @@ -1271,12 +1279,14 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) return 0; /* Already scanned the entire slot */ dev = pci_scan_single_device(bus, devfn); - if (dev && !dev->is_added) /* new device? */ + if (!dev) + return 0; + if (!dev->is_added) nr++; if (pci_ari_enabled(bus)) next_fn = next_ari_fn; - else if (dev && dev->multifunction) + else if (dev->multifunction) next_fn = next_trad_fn; for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) { From 939fdc67350e15aeeea70457d5b4bc1116bb4fd5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 4 Feb 2010 12:12:23 -0800 Subject: [PATCH 20/48] PCI hotplug: fix ibmphp build error Add header file to fix build error: drivers/pci/hotplug/ibmphp_hpc.c:135: error: implicit declaration of function 'init_MUTEX' drivers/pci/hotplug/ibmphp_hpc.c:136: error: implicit declaration of function 'init_MUTEX_LOCKED' drivers/pci/hotplug/ibmphp_hpc.c:797: error: implicit declaration of function 'down' drivers/pci/hotplug/ibmphp_hpc.c:807: error: implicit declaration of function 'up' Signed-off-by: Randy Dunlap Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/ibmphp_hpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c index c7084f0eca5a..1aaf3f32d3cd 100644 --- a/drivers/pci/hotplug/ibmphp_hpc.c +++ b/drivers/pci/hotplug/ibmphp_hpc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "ibmphp.h" From ba02b242bbf8e4e1bc63d62e8ccec33b4e5ea132 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Feb 2010 14:45:54 -0800 Subject: [PATCH 21/48] PCI hotplug: check ioremap() return value in ibmphp_ebda.c check ioremap() return value. Cc: Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/ibmphp_ebda.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index 7d3bf31d224d..5becbdee4027 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -261,6 +261,8 @@ int __init ibmphp_access_ebda (void) debug ("returned ebda segment: %x\n", ebda_seg); io_mem = ioremap(ebda_seg<<4, 1); + if (!io_mem) + return -ENOMEM; ebda_sz = readb(io_mem); iounmap(io_mem); debug("ebda size: %d(KiB)\n", ebda_sz); From 5009b46025acb2d3955d2c93574604fba667ef39 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:20 -0800 Subject: [PATCH 22/48] PCI: add pci_bridge_release_resources and pci_bus_release_bridge_resources We use this in later patches to free resrouce ranges for reassignment in an effort to support a wider variety of PCI topologies. Signed-off-by: Yinghai Lu Reviewed-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 52fbd426a1c7..7371a5479a99 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -604,6 +604,88 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_assign_resources); +static void pci_bridge_release_resources(struct pci_bus *bus, + unsigned long type) +{ + int idx; + bool changed = false; + struct pci_dev *dev; + struct resource *r; + unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + + dev = bus->self; + for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END; + idx++) { + r = &dev->resource[idx]; + if ((r->flags & type_mask) != type) + continue; + if (!r->parent) + continue; + /* + * if there are children under that, we should release them + * all + */ + release_child_resources(r); + if (!release_resource(r)) { + dev_printk(KERN_DEBUG, &dev->dev, + "resource %d %pR released\n", idx, r); + /* keep the old size */ + r->end = resource_size(r) - 1; + r->start = 0; + r->flags = 0; + changed = true; + } + } + + if (changed) { + /* avoiding touch the one without PREF */ + if (type & IORESOURCE_PREFETCH) + type = IORESOURCE_PREFETCH; + __pci_setup_bridge(bus, type); + } +} + +enum release_type { + leaf_only, + whole_subtree, +}; +/* + * try to release pci bridge resources that is from leaf bridge, + * so we can allocate big new one later + */ +static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus, + unsigned long type, + enum release_type rel_type) +{ + struct pci_dev *dev; + bool is_leaf_bridge = true; + + list_for_each_entry(dev, &bus->devices, bus_list) { + struct pci_bus *b = dev->subordinate; + if (!b) + continue; + + is_leaf_bridge = false; + + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + continue; + + if (rel_type == whole_subtree) + pci_bus_release_bridge_resources(b, type, + whole_subtree); + } + + if (pci_is_root_bus(bus)) + return; + + if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI) + return; + + if ((rel_type == whole_subtree) || is_leaf_bridge) + pci_bridge_release_resources(bus, type); +} + static void pci_bus_dump_res(struct pci_bus *bus) { int i; From 568ddef8735d4a51a521ba6af026ee0c32281566 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:21 -0800 Subject: [PATCH 23/48] PCI: add failed_list to pci_bus_assign_resources This allows us to track failed allocations for later re-trying with reallocation. Signed-off-by: Yinghai Lu Reviewed-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 61 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7371a5479a99..7e87ea8f3200 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -27,7 +27,52 @@ #include #include "pci.h" -static void pbus_assign_resources_sorted(const struct pci_bus *bus) +struct resource_list_x { + struct resource_list_x *next; + struct resource *res; + struct pci_dev *dev; + resource_size_t start; + resource_size_t end; + unsigned long flags; +}; + +static void add_to_failed_list(struct resource_list_x *head, + struct pci_dev *dev, struct resource *res) +{ + struct resource_list_x *list = head; + struct resource_list_x *ln = list->next; + struct resource_list_x *tmp; + + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + pr_warning("add_to_failed_list: kmalloc() failed!\n"); + return; + } + + tmp->next = ln; + tmp->res = res; + tmp->dev = dev; + tmp->start = res->start; + tmp->end = res->end; + tmp->flags = res->flags; + list->next = tmp; +} + +static void free_failed_list(struct resource_list_x *head) +{ + struct resource_list_x *list, *tmp; + + for (list = head->next; list;) { + tmp = list; + list = list->next; + kfree(tmp); + } + + head->next = NULL; +} + +static void pbus_assign_resources_sorted(const struct pci_bus *bus, + struct resource_list_x *fail_head) { struct pci_dev *dev; struct resource *res; @@ -58,6 +103,8 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus) res = list->res; idx = res - &list->dev->resource[0]; if (pci_assign_resource(list->dev, idx)) { + if (fail_head && !pci_is_root_bus(list->dev->bus)) + add_to_failed_list(fail_head, list->dev, res); res->start = 0; res->end = 0; res->flags = 0; @@ -572,19 +619,20 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_size_bridges); -void __ref pci_bus_assign_resources(const struct pci_bus *bus) +static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, + struct resource_list_x *fail_head) { struct pci_bus *b; struct pci_dev *dev; - pbus_assign_resources_sorted(bus); + pbus_assign_resources_sorted(bus, fail_head); list_for_each_entry(dev, &bus->devices, bus_list) { b = dev->subordinate; if (!b) continue; - pci_bus_assign_resources(b); + __pci_bus_assign_resources(b, fail_head); switch (dev->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -602,6 +650,11 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus) } } } + +void __ref pci_bus_assign_resources(const struct pci_bus *bus) +{ + __pci_bus_assign_resources(bus, NULL); +} EXPORT_SYMBOL(pci_bus_assign_resources); static void pci_bridge_release_resources(struct pci_bus *bus, From cd81e1ea1a4cda94aa5f3e942301cf0da497c262 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:22 -0800 Subject: [PATCH 24/48] PCI: reject mmio ranges starting at 0 on pci_bridge read We already track unassigned resources in struct resource, and this prevents us from overwriting resource flags and info in the unassigned case. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 233d1c275d96..d3009430eab6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -316,13 +316,17 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) limit |= (io_limit_hi << 16); } - if (base <= limit) { + if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; if (!res->start) res->start = base; if (!res->end) res->end = limit + 0xfff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [io %04lx - %04lx] reg reading\n", + base, limit); } res = child->resource[1]; @@ -330,11 +334,15 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; - if (base <= limit) { + if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; res->start = base; res->end = limit + 0xfffff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [mem 0x%08lx - 0x%08lx] reg reading\n", + base, limit + 0xfffff); } res = child->resource[2]; @@ -366,7 +374,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) #endif } } - if (base <= limit) { + if (base && base <= limit) { res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; if (res->flags & PCI_PREF_RANGE_TYPE_64) @@ -374,6 +382,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) res->start = base; res->end = limit + 0xfffff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [mem 0x%08lx - %08lx pref] reg reading\n", + base, limit + 0xfffff); } } From d65245c3297ac63abc51a976d92f45f2195d2854 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:23 -0800 Subject: [PATCH 25/48] PCI: don't shrink bridge resources When clearing leaf bridge resources, trying to get a big enough one, we could shrink the bridge if there is no resource under it. Confirm against the old resource side to make sure we're increasing the allocation. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7e87ea8f3200..f560814c557c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -382,7 +382,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); - unsigned long size = 0, size1 = 0; + unsigned long size = 0, size1 = 0, old_size; if (!b_res) return; @@ -407,12 +407,17 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) } if (size < min_size) size = min_size; + old_size = resource_size(b_res); + if (old_size == 1) + old_size = 0; /* To be fixed in 2.5: we should have sort of HAVE_ISA flag in the struct pci_bus. */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA) size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif size = ALIGN(size + size1, 4096); + if (size < old_size) + size = old_size; if (!size) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " @@ -433,7 +438,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type, resource_size_t min_size) { struct pci_dev *dev; - resource_size_t min_align, align, size; + resource_size_t min_align, align, size, old_size; resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ int order, max_order; struct resource *b_res = find_free_bus_resource(bus, type); @@ -483,6 +488,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } if (size < min_size) size = min_size; + old_size = resource_size(b_res); + if (old_size == 1) + old_size = 0; + if (size < old_size) + size = old_size; align = 0; min_align = 0; From 977d17bb1749517b353874ccdc9b85abc7a58c2a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:24 -0800 Subject: [PATCH 26/48] PCI: update bridge resources to get more big ranges in PCI assign unssigned BIOS separates IO ranges between several IOHs, and on some slots, BIOS assigns resources to a bridge, but stops assigning resources to the device under that bridge, because the device needs a big resource. So: 1. allocate resources and record the failed device resources 2. clear the BIOS assigned resources of the parent bridge of failing device 3. go back and call pci assign unassigned 4. if it still fails, go up the tree, clear more bridges. and try again Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 114 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f560814c557c..f76f6e90f3b9 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -780,11 +780,65 @@ static void pci_bus_dump_resources(struct pci_bus *bus) } } +static int __init pci_bus_get_depth(struct pci_bus *bus) +{ + int depth = 0; + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + int ret; + struct pci_bus *b = dev->subordinate; + if (!b) + continue; + + ret = pci_bus_get_depth(b); + if (ret + 1 > depth) + depth = ret + 1; + } + + return depth; +} +static int __init pci_get_max_depth(void) +{ + int depth = 0; + struct pci_bus *bus; + + list_for_each_entry(bus, &pci_root_buses, node) { + int ret; + + ret = pci_bus_get_depth(bus); + if (ret > depth) + depth = ret; + } + + return depth; +} + +/* + * first try will not touch pci bridge res + * second and later try will clear small leaf bridge res + * will stop till to the max deepth if can not find good one + */ void __init pci_assign_unassigned_resources(void) { struct pci_bus *bus; + int tried_times = 0; + enum release_type rel_type = leaf_only; + struct resource_list_x head, *list; + unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + unsigned long failed_type; + int max_depth = pci_get_max_depth(); + int pci_try_num; + head.next = NULL; + + pci_try_num = max_depth + 1; + printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", + max_depth, pci_try_num); + +again: /* Depth first, calculate sizes and alignments of all subordinate buses. */ list_for_each_entry(bus, &pci_root_buses, node) { @@ -792,9 +846,65 @@ pci_assign_unassigned_resources(void) } /* Depth last, allocate resources and update the hardware. */ list_for_each_entry(bus, &pci_root_buses, node) { - pci_bus_assign_resources(bus); - pci_enable_bridges(bus); + __pci_bus_assign_resources(bus, &head); } + tried_times++; + + /* any device complain? */ + if (!head.next) + goto enable_and_dump; + failed_type = 0; + for (list = head.next; list;) { + failed_type |= list->flags; + list = list->next; + } + /* + * io port are tight, don't try extra + * or if reach the limit, don't want to try more + */ + failed_type &= type_mask; + if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) { + free_failed_list(&head); + goto enable_and_dump; + } + + printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", + tried_times + 1); + + /* third times and later will not check if it is leaf */ + if ((tried_times + 1) > 2) + rel_type = whole_subtree; + + /* + * Try to release leaf bridge's resources that doesn't fit resource of + * child device under that bridge + */ + for (list = head.next; list;) { + bus = list->dev->bus; + pci_bus_release_bridge_resources(bus, list->flags & type_mask, + rel_type); + list = list->next; + } + /* restore size and flags */ + for (list = head.next; list;) { + struct resource *res = list->res; + + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + if (list->dev->subordinate) + res->flags = 0; + + list = list->next; + } + free_failed_list(&head); + + goto again; + +enable_and_dump: + /* Depth last, update the hardware. */ + list_for_each_entry(bus, &pci_root_buses, node) + pci_enable_bridges(bus); /* dump the resource on buses */ list_for_each_entry(bus, &pci_root_buses, node) { From 6841ec681a88b66651e4563040b9c7a7ad25d7b5 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:25 -0800 Subject: [PATCH 27/48] PCI: introduce pci_assign_unassigned_bridge_resources For use by pciehp. pci_setup_bridge() will not check enabled for the slot bridge, otherwise update res is not updated to bridge BAR. That is, bridge is already enabled for port service. Signed-off-by: Yinghai Lu Reviewed-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 118 ++++++++++++++++++++++++++++++---------- include/linux/pci.h | 1 + 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f76f6e90f3b9..b19a56b8b17a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -71,35 +71,34 @@ static void free_failed_list(struct resource_list_x *head) head->next = NULL; } -static void pbus_assign_resources_sorted(const struct pci_bus *bus, - struct resource_list_x *fail_head) +static void __dev_sort_resources(struct pci_dev *dev, + struct resource_list *head) { - struct pci_dev *dev; - struct resource *res; - struct resource_list head, *list, *tmp; - int idx; + u16 class = dev->class >> 8; - head.next = NULL; - list_for_each_entry(dev, &bus->devices, bus_list) { - u16 class = dev->class >> 8; + /* Don't touch classless devices or host bridges or ioapics. */ + if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) + return; - /* Don't touch classless devices or host bridges or ioapics. */ - if (class == PCI_CLASS_NOT_DEFINED || - class == PCI_CLASS_BRIDGE_HOST) - continue; - - /* Don't touch ioapic devices already enabled by firmware */ - if (class == PCI_CLASS_SYSTEM_PIC) { - u16 command; - pci_read_config_word(dev, PCI_COMMAND, &command); - if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) - continue; - } - - pdev_sort_resources(dev, &head); + /* Don't touch ioapic devices already enabled by firmware */ + if (class == PCI_CLASS_SYSTEM_PIC) { + u16 command; + pci_read_config_word(dev, PCI_COMMAND, &command); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + return; } - for (list = head.next; list;) { + pdev_sort_resources(dev, head); +} + +static void __assign_resources_sorted(struct resource_list *head, + struct resource_list_x *fail_head) +{ + struct resource *res; + struct resource_list *list, *tmp; + int idx; + + for (list = head->next; list;) { res = list->res; idx = res - &list->dev->resource[0]; if (pci_assign_resource(list->dev, idx)) { @@ -115,6 +114,30 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, } } +static void pdev_assign_resources_sorted(struct pci_dev *dev, + struct resource_list_x *fail_head) +{ + struct resource_list head; + + head.next = NULL; + __dev_sort_resources(dev, &head); + __assign_resources_sorted(&head, fail_head); + +} + +static void pbus_assign_resources_sorted(const struct pci_bus *bus, + struct resource_list_x *fail_head) +{ + struct pci_dev *dev; + struct resource_list head; + + head.next = NULL; + list_for_each_entry(dev, &bus->devices, bus_list) + __dev_sort_resources(dev, &head); + + __assign_resources_sorted(&head, fail_head); +} + void pci_setup_cardbus(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; @@ -273,9 +296,6 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) { struct pci_dev *bridge = bus->self; - if (pci_is_enabled(bridge)) - return; - dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", bus->secondary, bus->subordinate); @@ -646,7 +666,8 @@ static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, switch (dev->class >> 8) { case PCI_CLASS_BRIDGE_PCI: - pci_setup_bridge(b); + if (!pci_is_enabled(dev)) + pci_setup_bridge(b); break; case PCI_CLASS_BRIDGE_CARDBUS: @@ -667,6 +688,34 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_assign_resources); +static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, + struct resource_list_x *fail_head) +{ + struct pci_bus *b; + + pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head); + + b = bridge->subordinate; + if (!b) + return; + + __pci_bus_assign_resources(b, fail_head); + + switch (bridge->class >> 8) { + case PCI_CLASS_BRIDGE_PCI: + pci_setup_bridge(b); + break; + + case PCI_CLASS_BRIDGE_CARDBUS: + pci_setup_cardbus(b); + break; + + default: + dev_info(&bridge->dev, "not setting up bridge for bus " + "%04x:%02x\n", pci_domain_nr(b), b->number); + break; + } +} static void pci_bridge_release_resources(struct pci_bus *bus, unsigned long type) { @@ -911,3 +960,16 @@ pci_assign_unassigned_resources(void) pci_bus_dump_resources(bus); } } + +void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) +{ + struct pci_bus *parent = bridge->subordinate; + int retval; + + pci_bus_size_bridges(parent); + __pci_bridge_assign_resources(bridge, NULL); + retval = pci_reenable_device(bridge); + pci_set_master(bridge); + pci_enable_bridges(parent); +} +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); diff --git a/include/linux/pci.h b/include/linux/pci.h index 8b79403cc072..f0c056780a1f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -801,6 +801,7 @@ void pci_bus_assign_resources(const struct pci_bus *bus); void pci_bus_size_bridges(struct pci_bus *bus); int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(void); +void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pdev_enable_device(struct pci_dev *); void pdev_sort_resources(struct pci_dev *, struct resource_list *); int pci_enable_resources(struct pci_dev *, int mask); From 9789ac979b6b6ae6cc09f7b29c88e95ecb14ec39 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:26 -0800 Subject: [PATCH 28/48] PCI: pciehp: cleanup flow in pciehp_configure_device Move bus_size_bridges and assign resources out of pciehp_add_bridge() and do them all together, one time, including slot bridge, to avoid to calling assign resources several times when there are several bridges under the slot bridge. Using pci_assign_unassigned_bridge_resources. Signed-off-by: Yinghai Lu Reviewed-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_pci.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 21733108adde..0a16444c14c9 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -53,17 +53,15 @@ static int __ref pciehp_add_bridge(struct pci_dev *dev) busnr = pci_scan_bridge(parent, dev, busnr, pass); if (!dev->subordinate) return -1; - pci_bus_size_bridges(dev->subordinate); - pci_bus_assign_resources(parent); - pci_enable_bridges(parent); - pci_bus_add_devices(parent); + return 0; } int pciehp_configure_device(struct slot *p_slot) { struct pci_dev *dev; - struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; + struct pci_dev *bridge = p_slot->ctrl->pcie->port; + struct pci_bus *parent = bridge->subordinate; int num, fn; struct controller *ctrl = p_slot->ctrl; @@ -96,12 +94,25 @@ int pciehp_configure_device(struct slot *p_slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { pciehp_add_bridge(dev); } + pci_dev_put(dev); + } + + pci_assign_unassigned_bridge_resources(bridge); + + for (fn = 0; fn < 8; fn++) { + dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); + if (!dev) + continue; + if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { + pci_dev_put(dev); + continue; + } pci_configure_slot(dev); pci_dev_put(dev); } - pci_bus_assign_resources(parent); pci_bus_add_devices(parent); + return 0; } From 32180e402f9ff1f3389c99edf3f393425e706080 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:27 -0800 Subject: [PATCH 29/48] PCI: pciehp: second try to get big range for pcie devices Handle the case where the slot bridge that doesn't get a pre-allocated resource big enough to handle its child resources.. For example pcie devices need 256M, but the bridge only gets 2M preallocated. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 51 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b19a56b8b17a..ed545f669459 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -964,12 +964,61 @@ pci_assign_unassigned_resources(void) void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) { struct pci_bus *parent = bridge->subordinate; + int tried_times = 0; + struct resource_list_x head, *list; int retval; + unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + head.next = NULL; + +again: pci_bus_size_bridges(parent); - __pci_bridge_assign_resources(bridge, NULL); + __pci_bridge_assign_resources(bridge, &head); retval = pci_reenable_device(bridge); pci_set_master(bridge); pci_enable_bridges(parent); + + tried_times++; + + if (!head.next) + return; + + if (tried_times >= 2) { + /* still fail, don't need to try more */ + free_failed_list(&head); + return; + } + + printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", + tried_times + 1); + + /* + * Try to release leaf bridge's resources that doesn't fit resource of + * child device under that bridge + */ + for (list = head.next; list;) { + struct pci_bus *bus = list->dev->bus; + unsigned long flags = list->flags; + + pci_bus_release_bridge_resources(bus, flags & type_mask, + whole_subtree); + list = list->next; + } + /* restore size and flags */ + for (list = head.next; list;) { + struct resource *res = list->res; + + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + if (list->dev->subordinate) + res->flags = 0; + + list = list->next; + } + free_failed_list(&head); + + goto again; } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); From 9958610552c0bd7558b41cb8addbd865587f142a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:28 -0800 Subject: [PATCH 30/48] PCI: set PCI_PREF_RANGE_TYPE_64 in pci_bridge_check_ranges Make pci_bridge_check_ranges() store the PCI_PREF_RANGE_TYPE_64 in addition to IORESOURCE_MEM_64. Just like pci_read_bridge_bases(). Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ed545f669459..743ed8c48b9c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -354,8 +354,11 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) } if (pmem) { b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == + PCI_PREF_RANGE_TYPE_64) { b_res[2].flags |= IORESOURCE_MEM_64; + b_res[2].flags |= PCI_PREF_RANGE_TYPE_64; + } } /* double check if bridge does support 64 bit pref */ From 6d3be84aab461815978d970aa45f5bc9e52dd772 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 9 Feb 2010 12:21:27 +0900 Subject: [PATCH 31/48] PCI: mark is_pcie obsolete The "is_pcie" field in struct pci_dev is no longer needed because struct pci_dev has PCIe capability offset in "pcie_cap" field and (pcie_cap != 0) means the device is PCIe capable. This patch marks "is_pcie" fields obsolete. Current users of "is_pcie" field are: - drivers/ssb/scan.c - drivers/net/wireless/ath/ath9k/pci.c - drivers/net/wireless/ath/ath5k/attach.c - drivers/net/wireless/ath/ath5k/reset.c - drivers/acpi/hest.c - drivers/pci/pcie/pme/pcie_pme.c Will post patches for each to use pci_is_pcie() as a follow-up. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- include/linux/pci.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index f0c056780a1f..578a0770961a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -302,7 +302,8 @@ struct pci_dev { unsigned int msix_enabled:1; unsigned int ari_enabled:1; /* ARI forwarding */ unsigned int is_managed:1; - unsigned int is_pcie:1; + unsigned int is_pcie:1; /* Obsolete. Will be removed. + Use pci_is_pcie() instead */ unsigned int needs_freset:1; /* Dev requires fundamental reset */ unsigned int state_saved:1; unsigned int is_physfn:1; From 58ff463396ad00828e922d50998787e97fd32512 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:36:58 +0100 Subject: [PATCH 32/48] PCI PM: Add function for checking PME status of devices Add function pci_check_pme_status() that will check the PME status bit of given device and clear it along with the PME enable bit. It will be necessary for PCI run-time power management. Based on a patch from Shaohua Li Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 35 +++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + 2 files changed, 36 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1f9be53c39ba..5723446544fd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1194,6 +1194,41 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) return pcibios_set_pcie_reset_state(dev, state); } +/** + * pci_check_pme_status - Check if given device has generated PME. + * @dev: Device to check. + * + * Check the PME status of the device and if set, clear it and clear PME enable + * (if set). Return 'true' if PME status and PME enable were both set or + * 'false' otherwise. + */ +bool pci_check_pme_status(struct pci_dev *dev) +{ + int pmcsr_pos; + u16 pmcsr; + bool ret = false; + + if (!dev->pm_cap) + return false; + + pmcsr_pos = dev->pm_cap + PCI_PM_CTRL; + pci_read_config_word(dev, pmcsr_pos, &pmcsr); + if (!(pmcsr & PCI_PM_CTRL_PME_STATUS)) + return false; + + /* Clear PME status. */ + pmcsr |= PCI_PM_CTRL_PME_STATUS; + if (pmcsr & PCI_PM_CTRL_PME_ENABLE) { + /* Disable PME to avoid interrupt flood. */ + pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; + ret = true; + } + + pci_write_config_word(dev, pmcsr_pos, pmcsr); + + return ret; +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5d169bc3ccd1..b95b0a077d31 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -49,6 +49,7 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); +extern bool pci_check_pme_status(struct pci_dev *dev); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); From c7f486567c1d0acd2e4166c47069835b9f75e77b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:39:08 +0100 Subject: [PATCH 33/48] PCI PM: PCIe PME root port service driver PCIe native PME detection mechanism is based on interrupts generated by root ports or event collectors every time a PCIe device sends a PME message upstream. Once a PME message has been sent by an endpoint device and received by its root port (or event collector in the case of root complex integrated endpoints), the Requester ID from the message header is registered in the root port's Root Status register. At the same time, the PME Status bit of the Root Status register is set to indicate that there's a PME to handle. If PCIe PME interrupt is enabled for the root port, it generates an interrupt once the PME Status has been set. After receiving the interrupt, the kernel can identify the PCIe device that generated the PME using the Requester ID from the root port's Root Status register. [For details, see PCI Express Base Specification, Rev. 2.0.] Implement a driver for the PCIe PME root port service working in accordance with the above description. Based on a patch from Shaohua Li . Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 6 + drivers/pci/pcie/Kconfig | 4 + drivers/pci/pcie/Makefile | 2 + drivers/pci/pcie/pme/Makefile | 8 + drivers/pci/pcie/pme/pcie_pme.c | 493 +++++++++++++++++++++++++++ drivers/pci/pcie/pme/pcie_pme.h | 28 ++ drivers/pci/pcie/pme/pcie_pme_acpi.c | 54 +++ include/linux/pci.h | 1 + 8 files changed, 596 insertions(+) create mode 100644 drivers/pci/pcie/pme/Makefile create mode 100644 drivers/pci/pcie/pme/pcie_pme.c create mode 100644 drivers/pci/pcie/pme/pcie_pme.h create mode 100644 drivers/pci/pcie/pme/pcie_pme_acpi.c diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e7848a0d99eb..747a2c8f62f3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1998,6 +1998,12 @@ and is between 256 and 4096 characters. It is defined in the file force Enable ASPM even on devices that claim not to support it. WARNING: Forcing ASPM on may cause system lockups. + pcie_pme= [PCIE,PM] Native PCIe PME signaling options: + off Do not use native PCIe PME signaling. + force Use native PCIe PME signaling even if the BIOS refuses + to allow the kernel to control the relevant PCIe config + registers. + pcmv= [HW,PCMCIA] BadgePAD 4 pd. [PARIDE] diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 5a0c6ad53f8e..b8b494b3e0d0 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -46,3 +46,7 @@ config PCIEASPM_DEBUG help This enables PCI Express ASPM debug support. It will add per-device interface to control ASPM. + +config PCIE_PME + def_bool y + depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 11f6bb1eae24..ea654545e7c4 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o # Build PCI Express AER if needed obj-$(CONFIG_PCIEAER) += aer/ + +obj-$(CONFIG_PCIE_PME) += pme/ diff --git a/drivers/pci/pcie/pme/Makefile b/drivers/pci/pcie/pme/Makefile new file mode 100644 index 000000000000..8b9238053080 --- /dev/null +++ b/drivers/pci/pcie/pme/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for PCI-Express Root Port PME signaling driver +# + +obj-$(CONFIG_PCIE_PME) += pmedriver.o + +pmedriver-objs := pcie_pme.o +pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c new file mode 100644 index 000000000000..b5f96fb3cd83 --- /dev/null +++ b/drivers/pci/pcie/pme/pcie_pme.c @@ -0,0 +1,493 @@ +/* + * PCIe Native PME support + * + * Copyright (C) 2007 - 2009 Intel Corp + * Copyright (C) 2007 - 2009 Shaohua Li + * Copyright (C) 2009 Rafael J. Wysocki , Novell Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License V2. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie_pme.h" + +#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ + +/* + * If set, this switch will prevent the PCIe root port PME service driver from + * being registered. Consequently, the interrupt-based PCIe PME signaling will + * not be used by any PCIe root ports in that case. + */ +static bool pcie_pme_disabled; + +/* + * The PCI Express Base Specification 2.0, Section 6.1.8, states the following: + * "In order to maintain compatibility with non-PCI Express-aware system + * software, system power management logic must be configured by firmware to use + * the legacy mechanism of signaling PME by default. PCI Express-aware system + * software must notify the firmware prior to enabling native, interrupt-based + * PME signaling." However, if the platform doesn't provide us with a suitable + * notification mechanism or the notification fails, it is not clear whether or + * not we are supposed to use the interrupt-based PCIe PME signaling. The + * switch below can be used to indicate the desired behaviour. When set, it + * will make the kernel use the interrupt-based PCIe PME signaling regardless of + * the platform notification status, although the kernel will attempt to notify + * the platform anyway. When unset, it will prevent the kernel from using the + * the interrupt-based PCIe PME signaling if the platform notification fails, + * which is the default. + */ +static bool pcie_pme_force_enable; + +static int __init pcie_pme_setup(char *str) +{ + if (!strcmp(str, "off")) + pcie_pme_disabled = true; + else if (!strcmp(str, "force")) + pcie_pme_force_enable = true; + return 1; +} +__setup("pcie_pme=", pcie_pme_setup); + +/** + * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME. + * @srv: PCIe PME root port service to use for carrying out the check. + * + * Notify the platform that the native PCIe PME is going to be used and return + * 'true' if the control of the PCIe PME registers has been acquired from the + * platform. + */ +static bool pcie_pme_platform_setup(struct pcie_device *srv) +{ + return !pcie_pme_platform_notify(srv) || pcie_pme_force_enable; +} + +struct pcie_pme_service_data { + spinlock_t lock; + struct pcie_device *srv; + struct work_struct work; + bool noirq; /* Don't enable the PME interrupt used by this service. */ +}; + +/** + * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation. + * @dev: PCIe root port or event collector. + * @enable: Enable or disable the interrupt. + */ +static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable) +{ + int rtctl_pos; + u16 rtctl; + + rtctl_pos = pci_find_capability(dev, PCI_CAP_ID_EXP) + PCI_EXP_RTCTL; + + pci_read_config_word(dev, rtctl_pos, &rtctl); + if (enable) + rtctl |= PCI_EXP_RTCTL_PMEIE; + else + rtctl &= ~PCI_EXP_RTCTL_PMEIE; + pci_write_config_word(dev, rtctl_pos, rtctl); +} + +/** + * pcie_pme_clear_status - Clear root port PME interrupt status. + * @dev: PCIe root port or event collector. + */ +static void pcie_pme_clear_status(struct pci_dev *dev) +{ + int rtsta_pos; + u32 rtsta; + + rtsta_pos = pci_find_capability(dev, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + + pci_read_config_dword(dev, rtsta_pos, &rtsta); + rtsta |= PCI_EXP_RTSTA_PME; + pci_write_config_dword(dev, rtsta_pos, rtsta); +} + +/** + * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#. + * @bus: PCI bus to scan. + * + * Scan given PCI bus and all buses under it for devices asserting PME#. + */ +static bool pcie_pme_walk_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; + bool ret = false; + + list_for_each_entry(dev, &bus->devices, bus_list) { + /* Skip PCIe devices in case we started from a root port. */ + if (!dev->is_pcie && pci_check_pme_status(dev)) { + pm_request_resume(&dev->dev); + ret = true; + } + + if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate)) + ret = true; + } + + return ret; +} + +/** + * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME. + * @bus: Secondary bus of the bridge. + * @devfn: Device/function number to check. + * + * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band + * PCIe PME message. In such that case the bridge should use the Requester ID + * of device/function number 0 on its secondary bus. + */ +static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn) +{ + struct pci_dev *dev; + bool found = false; + + if (devfn) + return false; + + dev = pci_dev_get(bus->self); + if (!dev) + return false; + + if (dev->is_pcie && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { + down_read(&pci_bus_sem); + if (pcie_pme_walk_bus(bus)) + found = true; + up_read(&pci_bus_sem); + } + + pci_dev_put(dev); + return found; +} + +/** + * pcie_pme_handle_request - Find device that generated PME and handle it. + * @port: Root port or event collector that generated the PME interrupt. + * @req_id: PCIe Requester ID of the device that generated the PME. + */ +static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id) +{ + u8 busnr = req_id >> 8, devfn = req_id & 0xff; + struct pci_bus *bus; + struct pci_dev *dev; + bool found = false; + + /* First, check if the PME is from the root port itself. */ + if (port->devfn == devfn && port->bus->number == busnr) { + if (pci_check_pme_status(port)) { + pm_request_resume(&port->dev); + found = true; + } else { + /* + * Apparently, the root port generated the PME on behalf + * of a non-PCIe device downstream. If this is done by + * a root port, the Requester ID field in its status + * register may contain either the root port's, or the + * source device's information (PCI Express Base + * Specification, Rev. 2.0, Section 6.1.9). + */ + down_read(&pci_bus_sem); + found = pcie_pme_walk_bus(port->subordinate); + up_read(&pci_bus_sem); + } + goto out; + } + + /* Second, find the bus the source device is on. */ + bus = pci_find_bus(pci_domain_nr(port->bus), busnr); + if (!bus) + goto out; + + /* Next, check if the PME is from a PCIe-PCI bridge. */ + found = pcie_pme_from_pci_bridge(bus, devfn); + if (found) + goto out; + + /* Finally, try to find the PME source on the bus. */ + down_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_get(dev); + if (dev->devfn == devfn) { + found = true; + break; + } + pci_dev_put(dev); + } + up_read(&pci_bus_sem); + + if (found) { + /* The device is there, but we have to check its PME status. */ + found = pci_check_pme_status(dev); + if (found) + pm_request_resume(&dev->dev); + pci_dev_put(dev); + } else if (devfn) { + /* + * The device is not there, but we can still try to recover by + * assuming that the PME was reported by a PCIe-PCI bridge that + * used devfn different from zero. + */ + dev_dbg(&port->dev, "PME interrupt generated for " + "non-existent device %02x:%02x.%d\n", + busnr, PCI_SLOT(devfn), PCI_FUNC(devfn)); + found = pcie_pme_from_pci_bridge(bus, 0); + } + + out: + if (!found) + dev_dbg(&port->dev, "Spurious native PME interrupt!\n"); +} + +/** + * pcie_pme_work_fn - Work handler for PCIe PME interrupt. + * @work: Work structure giving access to service data. + */ +static void pcie_pme_work_fn(struct work_struct *work) +{ + struct pcie_pme_service_data *data = + container_of(work, struct pcie_pme_service_data, work); + struct pci_dev *port = data->srv->port; + int rtsta_pos; + u32 rtsta; + + rtsta_pos = pci_find_capability(port, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + + spin_lock_irq(&data->lock); + + for (;;) { + if (data->noirq) + break; + + pci_read_config_dword(port, rtsta_pos, &rtsta); + if (rtsta & PCI_EXP_RTSTA_PME) { + /* + * Clear PME status of the port. If there are other + * pending PMEs, the status will be set again. + */ + pcie_pme_clear_status(port); + + spin_unlock_irq(&data->lock); + pcie_pme_handle_request(port, rtsta & 0xffff); + spin_lock_irq(&data->lock); + + continue; + } + + /* No need to loop if there are no more PMEs pending. */ + if (!(rtsta & PCI_EXP_RTSTA_PENDING)) + break; + + spin_unlock_irq(&data->lock); + cpu_relax(); + spin_lock_irq(&data->lock); + } + + if (!data->noirq) + pcie_pme_interrupt_enable(port, true); + + spin_unlock_irq(&data->lock); +} + +/** + * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt. + * @irq: Interrupt vector. + * @context: Interrupt context pointer. + */ +static irqreturn_t pcie_pme_irq(int irq, void *context) +{ + struct pci_dev *port; + struct pcie_pme_service_data *data; + int rtsta_pos; + u32 rtsta; + unsigned long flags; + + port = ((struct pcie_device *)context)->port; + data = get_service_data((struct pcie_device *)context); + + rtsta_pos = pci_find_capability(port, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + + spin_lock_irqsave(&data->lock, flags); + pci_read_config_dword(port, rtsta_pos, &rtsta); + + if (!(rtsta & PCI_EXP_RTSTA_PME)) { + spin_unlock_irqrestore(&data->lock, flags); + return IRQ_NONE; + } + + pcie_pme_interrupt_enable(port, false); + spin_unlock_irqrestore(&data->lock, flags); + + /* We don't use pm_wq, because it's freezable. */ + schedule_work(&data->work); + + return IRQ_HANDLED; +} + +/** + * pcie_pme_set_native - Set the PME interrupt flag for given device. + * @dev: PCI device to handle. + * @ign: Ignored. + */ +static int pcie_pme_set_native(struct pci_dev *dev, void *ign) +{ + dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n"); + + device_set_run_wake(&dev->dev, true); + dev->pme_interrupt = true; + return 0; +} + +/** + * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port. + * @port: PCIe root port or event collector to handle. + * + * For each device below given root port, including the port itself (or for each + * root complex integrated endpoint if @port is a root complex event collector) + * set the flag indicating that it can signal run-time wake-up events via PCIe + * PME interrupts. + */ +static void pcie_pme_mark_devices(struct pci_dev *port) +{ + pcie_pme_set_native(port, NULL); + if (port->subordinate) { + pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL); + } else { + struct pci_bus *bus = port->bus; + struct pci_dev *dev; + + /* Check if this is a root port event collector. */ + if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus) + return; + + down_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->is_pcie + && dev->pcie_type == PCI_EXP_TYPE_RC_END) + pcie_pme_set_native(dev, NULL); + up_read(&pci_bus_sem); + } +} + +/** + * pcie_pme_probe - Initialize PCIe PME service for given root port. + * @srv: PCIe service to initialize. + */ +static int pcie_pme_probe(struct pcie_device *srv) +{ + struct pci_dev *port; + struct pcie_pme_service_data *data; + int ret; + + if (!pcie_pme_platform_setup(srv)) + return -EACCES; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->lock); + INIT_WORK(&data->work, pcie_pme_work_fn); + data->srv = srv; + set_service_data(srv, data); + + port = srv->port; + pcie_pme_interrupt_enable(port, false); + pcie_pme_clear_status(port); + + ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv); + if (ret) { + kfree(data); + } else { + pcie_pme_mark_devices(port); + pcie_pme_interrupt_enable(port, true); + } + + return ret; +} + +/** + * pcie_pme_suspend - Suspend PCIe PME service device. + * @srv: PCIe service device to suspend. + */ +static int pcie_pme_suspend(struct pcie_device *srv) +{ + struct pcie_pme_service_data *data = get_service_data(srv); + struct pci_dev *port = srv->port; + + spin_lock_irq(&data->lock); + pcie_pme_interrupt_enable(port, false); + pcie_pme_clear_status(port); + data->noirq = true; + spin_unlock_irq(&data->lock); + + synchronize_irq(srv->irq); + + return 0; +} + +/** + * pcie_pme_resume - Resume PCIe PME service device. + * @srv - PCIe service device to resume. + */ +static int pcie_pme_resume(struct pcie_device *srv) +{ + struct pcie_pme_service_data *data = get_service_data(srv); + struct pci_dev *port = srv->port; + + spin_lock_irq(&data->lock); + data->noirq = false; + pcie_pme_clear_status(port); + pcie_pme_interrupt_enable(port, true); + spin_unlock_irq(&data->lock); + + return 0; +} + +/** + * pcie_pme_remove - Prepare PCIe PME service device for removal. + * @srv - PCIe service device to resume. + */ +static void pcie_pme_remove(struct pcie_device *srv) +{ + pcie_pme_suspend(srv); + free_irq(srv->irq, srv); + kfree(get_service_data(srv)); +} + +static struct pcie_port_service_driver pcie_pme_driver = { + .name = "pcie_pme", + .port_type = PCI_EXP_TYPE_ROOT_PORT, + .service = PCIE_PORT_SERVICE_PME, + + .probe = pcie_pme_probe, + .suspend = pcie_pme_suspend, + .resume = pcie_pme_resume, + .remove = pcie_pme_remove, +}; + +/** + * pcie_pme_service_init - Register the PCIe PME service driver. + */ +static int __init pcie_pme_service_init(void) +{ + return pcie_pme_disabled ? + -ENODEV : pcie_port_service_register(&pcie_pme_driver); +} + +module_init(pcie_pme_service_init); diff --git a/drivers/pci/pcie/pme/pcie_pme.h b/drivers/pci/pcie/pme/pcie_pme.h new file mode 100644 index 000000000000..b30d2b7c9775 --- /dev/null +++ b/drivers/pci/pcie/pme/pcie_pme.h @@ -0,0 +1,28 @@ +/* + * drivers/pci/pcie/pme/pcie_pme.h + * + * PCI Express Root Port PME signaling support + * + * Copyright (C) 2009 Rafael J. Wysocki , Novell Inc. + */ + +#ifndef _PCIE_PME_H_ +#define _PCIE_PME_H_ + +struct pcie_device; + +#ifdef CONFIG_ACPI +extern int pcie_pme_acpi_setup(struct pcie_device *srv); + +static inline int pcie_pme_platform_notify(struct pcie_device *srv) +{ + return pcie_pme_acpi_setup(srv); +} +#else /* !CONFIG_ACPI */ +static inline int pcie_pme_platform_notify(struct pcie_device *srv) +{ + return 0; +} +#endif /* !CONFIG_ACPI */ + +#endif diff --git a/drivers/pci/pcie/pme/pcie_pme_acpi.c b/drivers/pci/pcie/pme/pcie_pme_acpi.c new file mode 100644 index 000000000000..83ab2287ae3f --- /dev/null +++ b/drivers/pci/pcie/pme/pcie_pme_acpi.c @@ -0,0 +1,54 @@ +/* + * PCIe Native PME support, ACPI-related part + * + * Copyright (C) 2009 Rafael J. Wysocki , Novell Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License V2. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include + +/** + * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME. + * @srv - PCIe PME service for a root port or event collector. + * + * Invoked when the PCIe bus type loads PCIe PME service driver. To avoid + * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME + * control to the kernel. + */ +int pcie_pme_acpi_setup(struct pcie_device *srv) +{ + acpi_status status = AE_NOT_FOUND; + struct pci_dev *port = srv->port; + acpi_handle handle; + int error = 0; + + if (acpi_pci_disabled) + return -ENOSYS; + + dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n"); + + handle = acpi_find_root_bridge_handle(port); + if (!handle) + return -EINVAL; + + status = acpi_pci_osc_control_set(handle, + OSC_PCI_EXPRESS_PME_CONTROL | + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + if (ACPI_FAILURE(status)) { + dev_info(&port->dev, + "Failed to receive control of PCIe PME service: %s\n", + (status == AE_SUPPORT || status == AE_NOT_FOUND) ? + "no _OSC support" : "ACPI _OSC failed"); + error = -ENODEV; + } + + return error; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 578a0770961a..9fe4b2089b78 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -266,6 +266,7 @@ struct pci_dev { configuration space */ unsigned int pme_support:5; /* Bitmask of states from which PME# can be generated */ + unsigned int pme_interrupt:1; unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ unsigned int no_d1d2:1; /* Only allow D0 and D3 */ From c39fae1416d59fd565606793f090cebe3720d50d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:40:07 +0100 Subject: [PATCH 34/48] PCI PM: Make it possible to force using INTx for PCIe PME signaling Apparently, some machines may have problems with PCI run-time power management if MSIs are used for the native PCIe PME signaling. In particular, on the MSI Wind U-100 PCIe PME interrupts are not generated by a PCIe root port after a resume from suspend to RAM, if the system wake-up was triggered by a PME from the device attached to this port. [It doesn't help to free the interrupt on suspend and request it back on resume, even if that is done along with disabling the MSI and re-enabling it, respectively.] However, if INTx interrupts are used for this purpose on the same machine, everything works just fine. For this reason, add a kernel command line switch allowing one to request that MSIs be not used for the native PCIe PME signaling, introduce a DMI table allowing us to blacklist machines that need this switch to be set by default and put the MSI Wind U-100 into this table. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 2 ++ drivers/pci/pcie/pme/pcie_pme.c | 14 +++++++++++++- drivers/pci/pcie/portdrv.h | 17 +++++++++++++++++ drivers/pci/pcie/portdrv_core.c | 12 ++++++++++-- drivers/pci/pcie/portdrv_pci.c | 27 +++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 747a2c8f62f3..516225a864f9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2003,6 +2003,8 @@ and is between 256 and 4096 characters. It is defined in the file force Use native PCIe PME signaling even if the BIOS refuses to allow the kernel to control the relevant PCIe config registers. + nomsi Do not use MSI for native PCIe PME signaling (this makes + all PCIe root ports use INTx for everything). pcmv= [HW,PCMCIA] BadgePAD 4 diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c index b5f96fb3cd83..51a69061b120 100644 --- a/drivers/pci/pcie/pme/pcie_pme.c +++ b/drivers/pci/pcie/pme/pcie_pme.c @@ -53,12 +53,22 @@ static bool pcie_pme_disabled; */ static bool pcie_pme_force_enable; +/* + * If this switch is set, MSI will not be used for PCIe PME signaling. This + * causes the PCIe port driver to use INTx interrupts only, but it turns out + * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based + * wake-up from system sleep states. + */ +bool pcie_pme_msi_disabled; + static int __init pcie_pme_setup(char *str) { if (!strcmp(str, "off")) pcie_pme_disabled = true; else if (!strcmp(str, "force")) pcie_pme_force_enable = true; + else if (!strcmp(str, "nomsi")) + pcie_pme_msi_disabled = true; return 1; } __setup("pcie_pme=", pcie_pme_setup); @@ -73,7 +83,9 @@ __setup("pcie_pme=", pcie_pme_setup); */ static bool pcie_pme_platform_setup(struct pcie_device *srv) { - return !pcie_pme_platform_notify(srv) || pcie_pme_force_enable; + if (!pcie_pme_platform_notify(srv)) + return true; + return pcie_pme_force_enable; } struct pcie_pme_service_data { diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index aaeb9d21cba5..813a5c3427b6 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -30,4 +30,21 @@ extern void pcie_port_device_remove(struct pci_dev *dev); extern int __must_check pcie_port_bus_register(void); extern void pcie_port_bus_unregister(void); +#ifdef CONFIG_PCIE_PME +extern bool pcie_pme_msi_disabled; + +static inline void pcie_pme_disable_msi(void) +{ + pcie_pme_msi_disabled = true; +} + +static inline bool pcie_pme_no_msi(void) +{ + return pcie_pme_msi_disabled; +} +#else /* !CONFIG_PCIE_PME */ +static inline void pcie_pme_disable_msi(void) {} +static inline bool pcie_pme_no_msi(void) { return false; } +#endif /* !CONFIG_PCIE_PME */ + #endif /* _PORTDRV_H_ */ diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index b174188ac121..0d34ff415399 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -186,16 +186,24 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) */ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) { - int i, irq; + int i, irq = -1; + + /* We have to use INTx if MSI cannot be used for PCIe PME. */ + if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) { + if (dev->pin) + irq = dev->irq; + goto no_msi; + } /* Try to use MSI-X if supported */ if (!pcie_port_enable_msix(dev, irqs, mask)) return 0; + /* We're not going to use MSI-X, so try MSI and fall back to INTx */ - irq = -1; if (!pci_enable_msi(dev) || dev->pin) irq = dev->irq; + no_msi: for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) irqs[i] = irq; irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 13c8972886e6..127e8f169d9c 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "portdrv.h" #include "aer/aerdrv.h" @@ -273,10 +274,36 @@ static struct pci_driver pcie_portdriver = { .driver.pm = PCIE_PORTDRV_PM_OPS, }; +static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d) +{ + pr_notice("%s detected: will not use MSI for PCIe PME signaling\n", + d->ident); + pcie_pme_disable_msi(); + return 0; +} + +static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = { + /* + * Boxes that should not use MSI for PCIe PME signaling. + */ + { + .callback = dmi_pcie_pme_disable_msi, + .ident = "MSI Wind U-100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "U-100"), + }, + }, + {} +}; + static int __init pcie_portdrv_init(void) { int retval; + dmi_check_system(pcie_portdrv_dmi_table); + retval = pcie_port_bus_register(); if (retval) { printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); From 9630bdd9b15d2f489c646d8bc04b60e53eb5ec78 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:41:07 +0100 Subject: [PATCH 35/48] ACPI: Use GPE reference counting to support shared GPEs ACPI GPEs may map to multiple devices. The current GPE interface only provides a mechanism for enabling and disabling GPEs, making it difficult to change the state of GPEs at runtime without extensive cooperation between devices. Add an API to allow devices to indicate whether or not they want their device's GPE to be enabled for both runtime and wakeup events. Remove the old GPE type handling entirely, which gets rid of various quirks, like the implicit disabling with GPE type setting. This requires a small amount of rework in order to ensure that non-wake GPEs are enabled by default to preserve existing behaviour. Based on patches from Matthew Garrett . Signed-off-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/acpica/acevents.h | 6 +- drivers/acpi/acpica/aclocal.h | 2 + drivers/acpi/acpica/evgpe.c | 153 ++++----------------------------- drivers/acpi/acpica/evgpeblk.c | 87 +++++++------------ drivers/acpi/acpica/evxface.c | 14 --- drivers/acpi/acpica/evxfevnt.c | 92 ++++++++++++++------ drivers/acpi/button.c | 13 ++- drivers/acpi/ec.c | 14 +-- drivers/acpi/sleep.c | 15 +++- drivers/acpi/system.c | 4 +- drivers/acpi/wakeup.c | 81 ++++++----------- include/acpi/acpixf.h | 6 +- include/acpi/actypes.h | 28 +++--- 13 files changed, 189 insertions(+), 326 deletions(-) diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 0bba148a2c61..197aa4f39640 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -76,8 +76,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node, * evgpe - GPE handling and dispatch */ acpi_status -acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, - u8 type); +acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info); acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, @@ -121,9 +120,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list); -acpi_status -acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type); - acpi_status acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 81e64f478679..13cb80caacde 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -426,6 +426,8 @@ struct acpi_gpe_event_info { struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ u8 flags; /* Misc info about this GPE */ u8 gpe_number; /* This GPE */ + u8 runtime_count; + u8 wakeup_count; }; /* Information about a GPE register pair, one per each status/enable pair in an array */ diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index afacf4416c73..30ca3a30ef00 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -52,56 +52,11 @@ ACPI_MODULE_NAME("evgpe") /* Local prototypes */ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context); -/******************************************************************************* - * - * FUNCTION: acpi_ev_set_gpe_type - * - * PARAMETERS: gpe_event_info - GPE to set - * Type - New type - * - * RETURN: Status - * - * DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run) - * - ******************************************************************************/ - -acpi_status -acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type) -{ - acpi_status status; - - ACPI_FUNCTION_TRACE(ev_set_gpe_type); - - /* Validate type and update register enable masks */ - - switch (type) { - case ACPI_GPE_TYPE_WAKE: - case ACPI_GPE_TYPE_RUNTIME: - case ACPI_GPE_TYPE_WAKE_RUN: - break; - - default: - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - - /* Disable the GPE if currently enabled */ - - status = acpi_ev_disable_gpe(gpe_event_info); - - /* Clear the type bits and insert the new Type */ - - gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK; - gpe_event_info->flags |= type; - return_ACPI_STATUS(status); -} - /******************************************************************************* * * FUNCTION: acpi_ev_update_gpe_enable_masks * * PARAMETERS: gpe_event_info - GPE to update - * Type - What to do: ACPI_GPE_DISABLE or - * ACPI_GPE_ENABLE * * RETURN: Status * @@ -110,8 +65,7 @@ acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type) ******************************************************************************/ acpi_status -acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, - u8 type) +acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) { struct acpi_gpe_register_info *gpe_register_info; u8 register_bit; @@ -127,37 +81,14 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, (1 << (gpe_event_info->gpe_number - gpe_register_info->base_gpe_number)); - /* 1) Disable case. Simply clear all enable bits */ + ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit); + ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); - if (type == ACPI_GPE_DISABLE) { - ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, - register_bit); - ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); - return_ACPI_STATUS(AE_OK); - } - - /* 2) Enable case. Set/Clear the appropriate enable bits */ - - switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { - case ACPI_GPE_TYPE_WAKE: - ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit); - ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); - break; - - case ACPI_GPE_TYPE_RUNTIME: - ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, - register_bit); + if (gpe_event_info->runtime_count) ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit); - break; - case ACPI_GPE_TYPE_WAKE_RUN: + if (gpe_event_info->wakeup_count) ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit); - ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit); - break; - - default: - return_ACPI_STATUS(AE_BAD_PARAMETER); - } return_ACPI_STATUS(AE_OK); } @@ -186,47 +117,20 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, /* Make sure HW enable masks are updated */ - status = - acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_ENABLE); - if (ACPI_FAILURE(status)) { + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); + if (ACPI_FAILURE(status)) return_ACPI_STATUS(status); - } /* Mark wake-enabled or HW enable, or both */ - switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { - case ACPI_GPE_TYPE_WAKE: + if (gpe_event_info->runtime_count && write_to_hardware) { + /* Clear the GPE (of stale events), then enable it */ + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(status); - ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); - break; - - case ACPI_GPE_TYPE_WAKE_RUN: - - ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); - - /*lint -fallthrough */ - - case ACPI_GPE_TYPE_RUNTIME: - - ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); - - if (write_to_hardware) { - - /* Clear the GPE (of stale events), then enable it */ - - status = acpi_hw_clear_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* Enable the requested runtime GPE */ - - status = acpi_hw_write_gpe_enable_reg(gpe_event_info); - } - break; - - default: - return_ACPI_STATUS(AE_BAD_PARAMETER); + /* Enable the requested runtime GPE */ + status = acpi_hw_write_gpe_enable_reg(gpe_event_info); } return_ACPI_STATUS(AE_OK); @@ -252,34 +156,9 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Make sure HW enable masks are updated */ - status = - acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_DISABLE); - if (ACPI_FAILURE(status)) { + status = acpi_ev_update_gpe_enable_masks(gpe_event_info); + if (ACPI_FAILURE(status)) return_ACPI_STATUS(status); - } - - /* Clear the appropriate enabled flags for this GPE */ - - switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { - case ACPI_GPE_TYPE_WAKE: - ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); - break; - - case ACPI_GPE_TYPE_WAKE_RUN: - ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); - - /* fallthrough */ - - case ACPI_GPE_TYPE_RUNTIME: - - /* Disable the requested runtime GPE */ - - ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); - break; - - default: - break; - } /* * Even if we don't know the GPE type, make sure that we always diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 247920900187..3d4c4aca11cd 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -258,7 +258,6 @@ acpi_ev_save_method_info(acpi_handle obj_handle, u32 gpe_number; char name[ACPI_NAME_SIZE + 1]; u8 type; - acpi_status status; ACPI_FUNCTION_TRACE(ev_save_method_info); @@ -325,26 +324,20 @@ acpi_ev_save_method_info(acpi_handle obj_handle, /* * Now we can add this information to the gpe_event_info block for use - * during dispatch of this GPE. Default type is RUNTIME, although this may - * change when the _PRW methods are executed later. + * during dispatch of this GPE. */ gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; - gpe_event_info->flags = (u8) - (type | ACPI_GPE_DISPATCH_METHOD | ACPI_GPE_TYPE_RUNTIME); + gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD); gpe_event_info->dispatch.method_node = (struct acpi_namespace_node *)obj_handle; - /* Update enable mask, but don't enable the HW GPE as of yet */ - - status = acpi_ev_enable_gpe(gpe_event_info, FALSE); - ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Registered GPE method %s as GPE number 0x%.2X\n", name, gpe_number)); - return_ACPI_STATUS(status); + return_ACPI_STATUS(AE_OK); } /******************************************************************************* @@ -454,20 +447,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, gpe_block-> block_base_number]; - /* Mark GPE for WAKE-ONLY but WAKE_DISABLED */ - - gpe_event_info->flags &= - ~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED); - - status = - acpi_ev_set_gpe_type(gpe_event_info, ACPI_GPE_TYPE_WAKE); - if (ACPI_FAILURE(status)) { - goto cleanup; - } - - status = - acpi_ev_update_gpe_enable_masks(gpe_event_info, - ACPI_GPE_DISABLE); + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; } cleanup: @@ -989,7 +969,6 @@ acpi_status acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, struct acpi_gpe_block_info *gpe_block) { - acpi_status status; struct acpi_gpe_event_info *gpe_event_info; struct acpi_gpe_walk_info gpe_info; u32 wake_gpe_count; @@ -1019,42 +998,50 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, gpe_info.gpe_block = gpe_block; gpe_info.gpe_device = gpe_device; - status = - acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_match_prw_and_gpe, NULL, &gpe_info, NULL); } /* - * Enable all GPEs in this block that have these attributes: - * 1) are "runtime" or "run/wake" GPEs, and - * 2) have a corresponding _Lxx or _Exx method - * - * Any other GPEs within this block must be enabled via the - * acpi_enable_gpe() external interface. + * Enable all GPEs that have a corresponding method and aren't + * capable of generating wakeups. Any other GPEs within this block + * must be enabled via the acpi_enable_gpe() interface. */ wake_gpe_count = 0; gpe_enabled_count = 0; + if (gpe_device == acpi_gbl_fadt_gpe_device) + gpe_device = NULL; for (i = 0; i < gpe_block->register_count; i++) { - for (j = 0; j < 8; j++) { + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + acpi_status status; + acpi_size gpe_index; + int gpe_number; /* Get the info block for this particular GPE */ + gpe_index = (acpi_size)i * ACPI_GPE_REGISTER_WIDTH + j; + gpe_event_info = &gpe_block->event_info[gpe_index]; - gpe_event_info = &gpe_block->event_info[((acpi_size) i * - ACPI_GPE_REGISTER_WIDTH) - + j]; - - if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_METHOD) && - (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) { - gpe_enabled_count++; - } - - if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) { + if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) { wake_gpe_count++; + if (acpi_gbl_leave_wake_gpes_disabled) + continue; } + + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)) + continue; + + gpe_number = gpe_index + gpe_block->block_base_number; + status = acpi_enable_gpe(gpe_device, gpe_number, + ACPI_GPE_TYPE_RUNTIME); + if (ACPI_FAILURE(status)) + ACPI_ERROR((AE_INFO, + "Failed to enable GPE %02X\n", + gpe_number)); + else + gpe_enabled_count++; } } @@ -1062,15 +1049,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, "Found %u Wake, Enabled %u Runtime GPEs in this block\n", wake_gpe_count, gpe_enabled_count)); - /* Enable all valid runtime GPEs found above */ - - status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block, NULL); - if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "Could not enable GPEs in GpeBlock %p", - gpe_block)); - } - - return_ACPI_STATUS(status); + return_ACPI_STATUS(AE_OK); } /******************************************************************************* diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 2fe0809d4eb2..166cbfe1c706 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -617,13 +617,6 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler->context = context; handler->method_node = gpe_event_info->dispatch.method_node; - /* Disable the GPE before installing the handler */ - - status = acpi_ev_disable_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - /* Install the handler */ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); @@ -707,13 +700,6 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, goto unlock_and_exit; } - /* Disable the GPE before removing the handler */ - - status = acpi_ev_disable_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - /* Make sure all deferred tasks are completed */ (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index eed7a38d25f2..1aea1a734159 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -201,23 +201,27 @@ ACPI_EXPORT_SYMBOL(acpi_enable_event) /******************************************************************************* * - * FUNCTION: acpi_set_gpe_type + * FUNCTION: acpi_set_gpe * * PARAMETERS: gpe_device - Parent GPE Device * gpe_number - GPE level within the GPE block - * Type - New GPE type + * action - Enable or disable + * Called from ISR or not * * RETURN: Status * - * DESCRIPTION: Set the type of an individual GPE + * DESCRIPTION: Enable or disable an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type) +acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) { acpi_status status = AE_OK; + acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; - ACPI_FUNCTION_TRACE(acpi_set_gpe_type); + ACPI_FUNCTION_TRACE(acpi_set_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -227,19 +231,29 @@ acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type) goto unlock_and_exit; } - if ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == type) { - return_ACPI_STATUS(AE_OK); + /* Perform the action */ + + switch (action) { + case ACPI_GPE_ENABLE: + status = acpi_ev_enable_gpe(gpe_event_info, TRUE); + break; + + case ACPI_GPE_DISABLE: + status = acpi_ev_disable_gpe(gpe_event_info); + break; + + default: + ACPI_ERROR((AE_INFO, "Invalid action\n")); + status = AE_BAD_PARAMETER; + break; } - /* Set the new type (will disable GPE if currently enabled) */ - - status = acpi_ev_set_gpe_type(gpe_event_info, type); - unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } -ACPI_EXPORT_SYMBOL(acpi_set_gpe_type) +ACPI_EXPORT_SYMBOL(acpi_set_gpe) /******************************************************************************* * @@ -247,15 +261,14 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe_type) * * PARAMETERS: gpe_device - Parent GPE Device * gpe_number - GPE level within the GPE block - * Flags - Just enable, or also wake enable? - * Called from ISR or not + * type - Purpose the GPE will be used for * * RETURN: Status * - * DESCRIPTION: Enable an ACPI event (general purpose) + * DESCRIPTION: Take a reference to a GPE and enable it if necessary * ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) { acpi_status status = AE_OK; acpi_cpu_flags flags; @@ -273,15 +286,32 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) goto unlock_and_exit; } - /* Perform the enable */ + if (type & ACPI_GPE_TYPE_RUNTIME) { + if (++gpe_event_info->runtime_count == 1) + status = acpi_ev_enable_gpe(gpe_event_info, TRUE); - status = acpi_ev_enable_gpe(gpe_event_info, TRUE); + if (ACPI_FAILURE(status)) + gpe_event_info->runtime_count--; + } - unlock_and_exit: + if (type & ACPI_GPE_TYPE_WAKE) { + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * Wake-up GPEs are only enabled right prior to putting the + * system into a sleep state. + */ + if (++gpe_event_info->wakeup_count == 1) + acpi_ev_update_gpe_enable_masks(gpe_event_info); + } + +unlock_and_exit: acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } - ACPI_EXPORT_SYMBOL(acpi_enable_gpe) /******************************************************************************* @@ -290,15 +320,14 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe) * * PARAMETERS: gpe_device - Parent GPE Device * gpe_number - GPE level within the GPE block - * Flags - Just disable, or also wake disable? - * Called from ISR or not + * type - Purpose the GPE won't be used for any more * * RETURN: Status * - * DESCRIPTION: Disable an ACPI event (general purpose) + * DESCRIPTION: Release a reference to a GPE and disable it if necessary * ******************************************************************************/ -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) { acpi_status status = AE_OK; acpi_cpu_flags flags; @@ -315,13 +344,24 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) goto unlock_and_exit; } - status = acpi_ev_disable_gpe(gpe_event_info); + if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->runtime_count) { + if (--gpe_event_info->runtime_count == 0) + acpi_ev_disable_gpe(gpe_event_info); + } + + if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->wakeup_count) { + /* + * Wake-up GPEs are not enabled after leaving system sleep + * states, so we don't need to disable them here. + */ + if (--gpe_event_info->wakeup_count == 0) + acpi_ev_update_gpe_enable_masks(gpe_event_info); + } unlock_and_exit: acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } - ACPI_EXPORT_SYMBOL(acpi_disable_gpe) /******************************************************************************* diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 8a95e8329df7..09ca3ce7a051 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -422,11 +422,9 @@ static int acpi_button_add(struct acpi_device *device) if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ - acpi_set_gpe_type(device->wakeup.gpe_device, - device->wakeup.gpe_number, - ACPI_GPE_TYPE_WAKE_RUN); acpi_enable_gpe(device->wakeup.gpe_device, - device->wakeup.gpe_number); + device->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE_RUN); device->wakeup.state.enabled = 1; } @@ -446,6 +444,13 @@ static int acpi_button_remove(struct acpi_device *device, int type) { struct acpi_button *button = acpi_driver_data(device); + if (device->wakeup.flags.valid) { + acpi_disable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE_RUN); + device->wakeup.state.enabled = 0; + } + acpi_button_remove_fs(device); input_unregister_device(button->input); kfree(button); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d6471bb6852f..0fa65a8210da 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -307,7 +307,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) pr_debug(PREFIX "transaction start\n"); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { - acpi_disable_gpe(NULL, ec->gpe); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); } status = acpi_ec_transaction_unlocked(ec, t); @@ -317,7 +317,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { msleep(1); /* it is safe to enable GPE outside of transaction */ - acpi_enable_gpe(NULL, ec->gpe); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); @@ -788,8 +788,8 @@ static int ec_install_handlers(struct acpi_ec *ec) &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) return -ENODEV; - acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe); + + acpi_enable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, @@ -806,6 +806,7 @@ static int ec_install_handlers(struct acpi_ec *ec) } else { acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); + acpi_disable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); return -ENODEV; } } @@ -816,6 +817,7 @@ static int ec_install_handlers(struct acpi_ec *ec) static void ec_remove_handlers(struct acpi_ec *ec) { + acpi_disable_gpe(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) pr_err(PREFIX "failed to remove space handler\n"); @@ -1058,7 +1060,7 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) { struct acpi_ec *ec = acpi_driver_data(device); /* Stop using GPE */ - acpi_disable_gpe(NULL, ec->gpe); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); return 0; } @@ -1066,7 +1068,7 @@ static int acpi_ec_resume(struct acpi_device *device) { struct acpi_ec *ec = acpi_driver_data(device); /* Enable use of GPE back */ - acpi_enable_gpe(NULL, ec->gpe); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); return 0; } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 79d33d908b5a..3bde594a9979 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -745,9 +745,18 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) return -ENODEV; } - error = enable ? - acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) : - acpi_disable_wakeup_device_power(adev); + if (enable) { + error = acpi_enable_wakeup_device_power(adev, + acpi_target_sleep_state); + if (!error) + acpi_enable_gpe(adev->wakeup.gpe_device, + adev->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE); + } else { + acpi_disable_gpe(adev->wakeup.gpe_device, adev->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE); + error = acpi_disable_wakeup_device_power(adev); + } if (!error) dev_info(dev, "wake-up capability %s by ACPI\n", enable ? "enabled" : "disabled"); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index d11282975f35..a206a12da78a 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -387,10 +387,10 @@ static ssize_t counter_set(struct kobject *kobj, if (index < num_gpes) { if (!strcmp(buf, "disable\n") && (status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_disable_gpe(handle, index); + result = acpi_set_gpe(handle, index, ACPI_GPE_DISABLE); else if (!strcmp(buf, "enable\n") && !(status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_enable_gpe(handle, index); + result = acpi_set_gpe(handle, index, ACPI_GPE_ENABLE); else if (!strcmp(buf, "clear\n") && (status & ACPI_EVENT_FLAG_SET)) result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index e0ee0c036f5a..6783986c7469 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -21,12 +21,12 @@ ACPI_MODULE_NAME("wakeup_devices") /** - * acpi_enable_wakeup_device_prep - prepare wakeup devices - * @sleep_state: ACPI state - * Enable all wakup devices power if the devices' wakeup level - * is higher than requested sleep level + * acpi_enable_wakeup_device_prep - Prepare wake-up devices. + * @sleep_state: ACPI system sleep state. + * + * Enable all wake-up devices' power, unless the requested system sleep state is + * too deep. */ - void acpi_enable_wakeup_device_prep(u8 sleep_state) { struct list_head *node, *next; @@ -36,9 +36,8 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state) struct acpi_device, wakeup_list); - if (!dev->wakeup.flags.valid || - !dev->wakeup.state.enabled || - (sleep_state > (u32) dev->wakeup.sleep_state)) + if (!dev->wakeup.flags.valid || !dev->wakeup.state.enabled + || (sleep_state > (u32) dev->wakeup.sleep_state)) continue; acpi_enable_wakeup_device_power(dev, sleep_state); @@ -46,9 +45,12 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state) } /** - * acpi_enable_wakeup_device - enable wakeup devices - * @sleep_state: ACPI state - * Enable all wakup devices's GPE + * acpi_enable_wakeup_device - Enable wake-up device GPEs. + * @sleep_state: ACPI system sleep state. + * + * Enable all wake-up devices' GPEs, with the assumption that + * acpi_disable_all_gpes() was executed before, so we don't need to disable any + * GPEs here. */ void acpi_enable_wakeup_device(u8 sleep_state) { @@ -65,29 +67,22 @@ void acpi_enable_wakeup_device(u8 sleep_state) if (!dev->wakeup.flags.valid) continue; - /* If users want to disable run-wake GPE, - * we only disable it for wake and leave it for runtime - */ if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) - || sleep_state > (u32) dev->wakeup.sleep_state) { - if (dev->wakeup.flags.run_wake) { - /* set_gpe_type will disable GPE, leave it like that */ - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_RUNTIME); - } + || sleep_state > (u32) dev->wakeup.sleep_state) continue; - } - if (!dev->wakeup.flags.run_wake) - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); + + /* The wake-up power should have been enabled already. */ + acpi_set_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + ACPI_GPE_ENABLE); } } /** - * acpi_disable_wakeup_device - disable devices' wakeup capability - * @sleep_state: ACPI state - * Disable all wakup devices's GPE and wakeup capability + * acpi_disable_wakeup_device - Disable devices' wakeup capability. + * @sleep_state: ACPI system sleep state. + * + * This function only affects devices with wakeup.state.enabled set, which means + * that it reverses the changes made by acpi_enable_wakeup_device_prep(). */ void acpi_disable_wakeup_device(u8 sleep_state) { @@ -97,30 +92,11 @@ void acpi_disable_wakeup_device(u8 sleep_state) struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); - if (!dev->wakeup.flags.valid) + if (!dev->wakeup.flags.valid || !dev->wakeup.state.enabled + || (sleep_state > (u32) dev->wakeup.sleep_state)) continue; - if ((!dev->wakeup.state.enabled && !dev->wakeup.prepare_count) - || sleep_state > (u32) dev->wakeup.sleep_state) { - if (dev->wakeup.flags.run_wake) { - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_WAKE_RUN); - /* Re-enable it, since set_gpe_type will disable it */ - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - } - continue; - } - acpi_disable_wakeup_device_power(dev); - /* Never disable run-wake GPE */ - if (!dev->wakeup.flags.run_wake) { - acpi_disable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - acpi_clear_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); - } } } @@ -136,11 +112,8 @@ int __init acpi_wakeup_device_init(void) /* In case user doesn't load button driver */ if (!dev->wakeup.flags.run_wake || dev->wakeup.state.enabled) continue; - acpi_set_gpe_type(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, - ACPI_GPE_TYPE_WAKE_RUN); - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); + acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + ACPI_GPE_TYPE_WAKE); dev->wakeup.state.enabled = 1; } mutex_unlock(&acpi_device_lock); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 86e9735a96bd..3988f93b9c66 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -281,11 +281,11 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status); /* * GPE Interfaces */ -acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type); +acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action); -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number); +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type); -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number); +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type); acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 153f12dc3373..73af40863371 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -668,15 +668,16 @@ typedef u32 acpi_event_status; /* * GPE info flags - Per GPE - * +-+-+-+---+---+-+ - * |7|6|5|4:3|2:1|0| - * +-+-+-+---+---+-+ - * | | | | | | - * | | | | | +--- Interrupt type: Edge or Level Triggered - * | | | | +--- Type: Wake-only, Runtime-only, or wake/runtime + * +-+-+-+---+-+-+-+ + * |7|6|5|4:3|2|1|0| + * +-+-+-+---+-+-+-+ + * | | | | | | | + * | | | | | | +--- Interrupt type: Edge or Level Triggered + * | | | | | +--- GPE can wake the system + * | | | | +--- Unused * | | | +--- Type of dispatch -- to method, handler, or none - * | | +--- Enabled for runtime? - * | +--- Enabled for wake? + * | | +--- Unused + * | +--- Unused * +--- Unused */ #define ACPI_GPE_XRUPT_TYPE_MASK (u8) 0x01 @@ -687,22 +688,13 @@ typedef u32 acpi_event_status; #define ACPI_GPE_TYPE_WAKE_RUN (u8) 0x06 #define ACPI_GPE_TYPE_WAKE (u8) 0x02 #define ACPI_GPE_TYPE_RUNTIME (u8) 0x04 /* Default */ +#define ACPI_GPE_CAN_WAKE (u8) 0x02 #define ACPI_GPE_DISPATCH_MASK (u8) 0x18 #define ACPI_GPE_DISPATCH_HANDLER (u8) 0x08 #define ACPI_GPE_DISPATCH_METHOD (u8) 0x10 #define ACPI_GPE_DISPATCH_NOT_USED (u8) 0x00 /* Default */ -#define ACPI_GPE_RUN_ENABLE_MASK (u8) 0x20 -#define ACPI_GPE_RUN_ENABLED (u8) 0x20 -#define ACPI_GPE_RUN_DISABLED (u8) 0x00 /* Default */ - -#define ACPI_GPE_WAKE_ENABLE_MASK (u8) 0x40 -#define ACPI_GPE_WAKE_ENABLED (u8) 0x40 -#define ACPI_GPE_WAKE_DISABLED (u8) 0x00 /* Default */ - -#define ACPI_GPE_ENABLE_MASK (u8) 0x60 /* Both run/wake */ - /* * Flags for GPE and Lock interfaces */ From f517709d65beed95f52f021b43e3035b52ef791a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:41:49 +0100 Subject: [PATCH 36/48] ACPI / PM: Add more run-time wake-up fields Use the run_wake flag to mark all devices for which run-time wake-up events may be generated by the platform. Introduce a new wake-up flag, always_enabled, for marking devices that should be permanently enabled to generate run-time events. Also, introduce a reference counter for run-wake devices and a function that will initialize all of the run-time wake-up fields for given device. Signed-off-by: Rafael J. Wysocki Acked-by: Len Brown Signed-off-by: Jesse Barnes --- drivers/acpi/button.c | 2 ++ drivers/acpi/scan.c | 37 +++++++++++++++++++++++++++---------- drivers/acpi/wakeup.c | 3 ++- include/acpi/acpi_bus.h | 2 ++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 09ca3ce7a051..f53fbe307c9d 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -425,6 +425,7 @@ static int acpi_button_add(struct acpi_device *device) acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + device->wakeup.run_wake_count++; device->wakeup.state.enabled = 1; } @@ -448,6 +449,7 @@ static int acpi_button_remove(struct acpi_device *device, int type) acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + device->wakeup.run_wake_count--; device->wakeup.state.enabled = 0; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3e009674f333..7491a52ad97a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -741,19 +741,39 @@ acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device, return AE_OK; } -static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) +static void acpi_bus_set_run_wake_flags(struct acpi_device *device) { - acpi_status status = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *package = NULL; - int psw_error; - struct acpi_device_id button_device_ids[] = { {"PNP0C0D", 0}, {"PNP0C0C", 0}, {"PNP0C0E", 0}, {"", 0}, }; + acpi_status status; + acpi_event_status event_status; + + device->wakeup.run_wake_count = 0; + + /* Power button, Lid switch always enable wakeup */ + if (!acpi_match_device_ids(device, button_device_ids)) { + device->wakeup.flags.run_wake = 1; + device->wakeup.flags.always_enabled = 1; + return; + } + + status = acpi_get_gpe_status(NULL, device->wakeup.gpe_number, + ACPI_NOT_ISR, &event_status); + if (status == AE_OK) + device->wakeup.flags.run_wake = + !!(event_status & ACPI_EVENT_FLAG_HANDLE); +} + +static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) +{ + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package = NULL; + int psw_error; /* _PRW */ status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer); @@ -773,6 +793,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) device->wakeup.flags.valid = 1; device->wakeup.prepare_count = 0; + acpi_bus_set_run_wake_flags(device); /* Call _PSW/_DSW object to disable its ability to wake the sleeping * system for the ACPI device with the _PRW object. * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW. @@ -784,10 +805,6 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in _DSW or _PSW evaluation\n")); - /* Power button, Lid switch always enable wakeup */ - if (!acpi_match_device_ids(device, button_device_ids)) - device->wakeup.flags.run_wake = 1; - end: if (ACPI_FAILURE(status)) device->flags.wake_capable = 0; diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index 6783986c7469..4b9d339a6e28 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -110,7 +110,8 @@ int __init acpi_wakeup_device_init(void) struct acpi_device, wakeup_list); /* In case user doesn't load button driver */ - if (!dev->wakeup.flags.run_wake || dev->wakeup.state.enabled) + if (!dev->wakeup.flags.always_enabled || + dev->wakeup.state.enabled) continue; acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 3cd9ccdcbd8f..60fcff419352 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -242,6 +242,7 @@ struct acpi_device_perf { struct acpi_device_wakeup_flags { u8 valid:1; /* Can successfully enable wakeup? */ u8 run_wake:1; /* Run-Wake GPE devices */ + u8 always_enabled:1; /* Run-wake devices that are always enabled */ }; struct acpi_device_wakeup_state { @@ -256,6 +257,7 @@ struct acpi_device_wakeup { struct acpi_device_wakeup_state state; struct acpi_device_wakeup_flags flags; int prepare_count; + int run_wake_count; }; /* Device */ From 3f0be67188c60ebf1b5d00354b44b4b24f5af313 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:42:59 +0100 Subject: [PATCH 37/48] ACPI / ACPICA: Multiple system notify handlers per device Currently it only is possible to install one system notify handler per namespace node, but this is not enough for PCI run-time power management, because we need to install power management notifiers for devices that already have hotplug notifiers installed. While in principle this could be handled at the PCI level, that would be suboptimal due to the way in which the ACPI-based PCI hotplug code is designed. For this reason, modify ACPICA so that it is possible to install more than one system notify handler per namespace node. Namely, make acpi_install_notify_handler(), acpi_remove_notify_handler() and acpi_ev_notify_dispatch() use a list of system notify handler objects associated with a namespace node. Make acpi_remove_notify_handler() call acpi_os_wait_events_complete() upfront to avoid a situation in which concurrent instance of acpi_remove_notify_handler() removes the handler from under us while we're waiting for the event queues to flush. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/acpica/acobject.h | 2 + drivers/acpi/acpica/evmisc.c | 12 ++- drivers/acpi/acpica/evxface.c | 175 ++++++++++++++++++++++++++------- 3 files changed, 150 insertions(+), 39 deletions(-) diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 64062b1be3ee..07f6e2ea2ee5 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -287,8 +287,10 @@ struct acpi_object_buffer_field { struct acpi_object_notify_handler { ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ + u32 handler_type; acpi_notify_handler handler; void *context; + struct acpi_object_notify_handler *next; }; struct acpi_object_addr_handler { diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index ce224e1eaa89..8f0fac6c4366 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -259,9 +259,15 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) handler_obj = notify_info->notify.handler_obj; if (handler_obj) { - handler_obj->notify.handler(notify_info->notify.node, - notify_info->notify.value, - handler_obj->notify.context); + struct acpi_object_notify_handler *notifier; + + notifier = &handler_obj->notify; + while (notifier) { + notifier->handler(notify_info->notify.node, + notify_info->notify.value, + notifier->context); + notifier = notifier->next; + } } /* All done with the info object */ diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 166cbfe1c706..474e2cab603d 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -216,6 +216,72 @@ acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler) ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler) +/******************************************************************************* + * + * FUNCTION: acpi_populate_handler_object + * + * PARAMETERS: handler_obj - Handler object to populate + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) + * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) + * ACPI_ALL_NOTIFY: both system and device + * handler - Address of the handler + * context - Value passed to the handler on each GPE + * next - Address of a handler object to link to + * + * RETURN: None + * + * DESCRIPTION: Populate a handler object. + * + ******************************************************************************/ +static void +acpi_populate_handler_object(struct acpi_object_notify_handler *handler_obj, + u32 handler_type, + acpi_notify_handler handler, void *context, + struct acpi_object_notify_handler *next) +{ + handler_obj->handler_type = handler_type; + handler_obj->handler = handler; + handler_obj->context = context; + handler_obj->next = next; +} + +/******************************************************************************* + * + * FUNCTION: acpi_add_handler_object + * + * PARAMETERS: parent_obj - Parent of the new object + * handler - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Create a new handler object and populate it. + * + ******************************************************************************/ +static acpi_status +acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj, + acpi_notify_handler handler, void *context) +{ + struct acpi_object_notify_handler *handler_obj; + + /* The parent must not be a defice notify handler object. */ + if (parent_obj->handler_type & ACPI_DEVICE_NOTIFY) + return AE_BAD_PARAMETER; + + handler_obj = ACPI_ALLOCATE_ZEROED(sizeof(*handler_obj)); + if (!handler_obj) + return AE_NO_MEMORY; + + acpi_populate_handler_object(handler_obj, + ACPI_SYSTEM_NOTIFY, + handler, context, + parent_obj->next); + parent_obj->next = handler_obj; + + return AE_OK; +} + /******************************************************************************* * * FUNCTION: acpi_install_notify_handler @@ -316,15 +382,32 @@ acpi_install_notify_handler(acpi_handle device, obj_desc = acpi_ns_get_attached_object(node); if (obj_desc) { - /* Object exists - make sure there's no handler */ + /* Object exists. */ - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - obj_desc->common_notify.system_notify) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - obj_desc->common_notify.device_notify)) { + /* For a device notify, make sure there's no handler. */ + if ((handler_type & ACPI_DEVICE_NOTIFY) && + obj_desc->common_notify.device_notify) { status = AE_ALREADY_EXISTS; goto unlock_and_exit; } + + /* System notifies may have more handlers installed. */ + notify_obj = obj_desc->common_notify.system_notify; + + if ((handler_type & ACPI_SYSTEM_NOTIFY) && notify_obj) { + struct acpi_object_notify_handler *parent_obj; + + if (handler_type & ACPI_DEVICE_NOTIFY) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + parent_obj = ¬ify_obj->notify; + status = acpi_add_handler_object(parent_obj, + handler, + context); + goto unlock_and_exit; + } } else { /* Create a new object */ @@ -356,9 +439,10 @@ acpi_install_notify_handler(acpi_handle device, goto unlock_and_exit; } - notify_obj->notify.node = node; - notify_obj->notify.handler = handler; - notify_obj->notify.context = context; + acpi_populate_handler_object(¬ify_obj->notify, + handler_type, + handler, context, + NULL); if (handler_type & ACPI_SYSTEM_NOTIFY) { obj_desc->common_notify.system_notify = notify_obj; @@ -418,6 +502,10 @@ acpi_remove_notify_handler(acpi_handle device, goto exit; } + + /* Make sure all deferred tasks are completed */ + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { goto exit; @@ -445,15 +533,6 @@ acpi_remove_notify_handler(acpi_handle device, goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; - } - if (handler_type & ACPI_SYSTEM_NOTIFY) { acpi_gbl_system_notify.node = NULL; acpi_gbl_system_notify.handler = NULL; @@ -488,28 +567,60 @@ acpi_remove_notify_handler(acpi_handle device, /* Object exists - make sure there's an existing handler */ if (handler_type & ACPI_SYSTEM_NOTIFY) { + struct acpi_object_notify_handler *handler_obj; + struct acpi_object_notify_handler *parent_obj; + notify_obj = obj_desc->common_notify.system_notify; if (!notify_obj) { status = AE_NOT_EXIST; goto unlock_and_exit; } - if (notify_obj->notify.handler != handler) { + handler_obj = ¬ify_obj->notify; + parent_obj = NULL; + while (handler_obj->handler != handler) { + if (handler_obj->next) { + parent_obj = handler_obj; + handler_obj = handler_obj->next; + } else { + break; + } + } + + if (handler_obj->handler != handler) { status = AE_BAD_PARAMETER; goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; + /* + * Remove the handler. There are three possible cases. + * First, we may need to remove a non-embedded object. + * Second, we may need to remove the embedded object's + * handler data, while non-embedded objects exist. + * Finally, we may need to remove the embedded object + * entirely along with its container. + */ + if (parent_obj) { + /* Non-embedded object is being removed. */ + parent_obj->next = handler_obj->next; + ACPI_FREE(handler_obj); + } else if (notify_obj->notify.next) { + /* + * The handler matches the embedded object, but + * there are more handler objects in the list. + * Replace the embedded object's data with the + * first next object's data and remove that + * object. + */ + parent_obj = ¬ify_obj->notify; + handler_obj = notify_obj->notify.next; + *parent_obj = *handler_obj; + ACPI_FREE(handler_obj); + } else { + /* No more handler objects in the list. */ + obj_desc->common_notify.system_notify = NULL; + acpi_ut_remove_reference(notify_obj); } - - /* Remove the handler */ - obj_desc->common_notify.system_notify = NULL; - acpi_ut_remove_reference(notify_obj); } if (handler_type & ACPI_DEVICE_NOTIFY) { @@ -523,14 +634,6 @@ acpi_remove_notify_handler(acpi_handle device, status = AE_BAD_PARAMETER; goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; - } /* Remove the handler */ obj_desc->common_notify.device_notify = NULL; From b67ea76172d4b1922c4b3c46c8ea8e9fec1ff38c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:44:09 +0100 Subject: [PATCH 38/48] PCI / ACPI / PM: Platform support for PCI PME wake-up Although the majority of PCI devices can generate PMEs that in principle may be used to wake up devices suspended at run time, platform support is generally necessary to convert PMEs into wake-up events that can be delivered to the kernel. If ACPI is used for this purpose, PME signals generated by a PCI device will trigger the ACPI GPE associated with the device to generate an ACPI wake-up event that we can set up a handler for, provided that everything is configured correctly. Unfortunately, the subset of PCI devices that have GPEs associated with them is quite limited. The devices without dedicated GPEs have to rely on the GPEs associated with other devices (in the majority of cases their upstream bridges and, possibly, the root bridge) to generate ACPI wake-up events in response to PME signals from them. Add ACPI platform support for PCI PME wake-up: o Add a framework making is possible to use ACPI system notify handlers for run-time PM. o Add new PCI platform callback ->run_wake() to struct pci_platform_pm_ops allowing us to enable/disable the platform to generate wake-up events for given device. Implemet this callback for the ACPI platform. o Define ACPI wake-up handlers for PCI devices and PCI root buses and make the PCI-ACPI binding code register wake-up notifiers for all PCI devices present in the ACPI tables. o Add function pci_dev_run_wake() which can be used by PCI drivers to check if given device is capable of generating wake-up events at run time. Developed in cooperation with Matthew Garrett . Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/internal.h | 2 - drivers/acpi/pci_bind.c | 14 ++- drivers/acpi/pci_root.c | 8 ++ drivers/acpi/scan.c | 1 + drivers/pci/pci-acpi.c | 211 +++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 67 +++++++++++++ drivers/pci/pci.h | 7 ++ include/acpi/acpi_bus.h | 4 + include/linux/pci-acpi.h | 7 ++ include/linux/pci.h | 1 + 10 files changed, 319 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index cb28e0502acc..9c4c962e46e3 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -36,8 +36,6 @@ static inline int acpi_debug_init(void) { return 0; } int acpi_power_init(void); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); -int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state); -int acpi_disable_wakeup_device_power(struct acpi_device *dev); int acpi_power_get_inferred_state(struct acpi_device *device); int acpi_power_transition(struct acpi_device *device, int state); extern int acpi_power_nocheck; diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index a5a77b78a723..2ef04098cc1d 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #include @@ -38,7 +40,13 @@ static int acpi_pci_unbind(struct acpi_device *device) struct pci_dev *dev; dev = acpi_get_pci_dev(device->handle); - if (!dev || !dev->subordinate) + if (!dev) + goto out; + + device_set_run_wake(&dev->dev, false); + pci_acpi_remove_pm_notifier(device); + + if (!dev->subordinate) goto out; acpi_pci_irq_del_prt(dev->subordinate); @@ -62,6 +70,10 @@ static int acpi_pci_bind(struct acpi_device *device) if (!dev) return 0; + pci_acpi_add_pm_notifier(device, dev); + if (device->wakeup.flags.run_wake) + device_set_run_wake(&dev->dev, true); + /* * Install the 'bind' function to facilitate callbacks for * children of the P2P bridge. diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 64f55b6db73c..9cd8bedb1e5a 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -528,6 +529,10 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) if (flags != base_flags) acpi_pci_osc_support(root, flags); + pci_acpi_add_bus_pm_notifier(device, root->bus); + if (device->wakeup.flags.run_wake) + device_set_run_wake(root->bus->bridge, true); + return 0; end: @@ -549,6 +554,9 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) { struct acpi_pci_root *root = acpi_driver_data(device); + device_set_run_wake(root->bus->bridge, false); + pci_acpi_remove_bus_pm_notifier(device); + kfree(root); return 0; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7491a52ad97a..fb7fc24fe727 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -753,6 +753,7 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) acpi_event_status event_status; device->wakeup.run_wake_count = 0; + device->wakeup.flags.notifier_present = 0; /* Power button, Lid switch always enable wakeup */ if (!acpi_match_device_ids(device, button_device_ids)) { diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7e2829538a4c..c0c73913833d 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -16,8 +16,144 @@ #include #include +#include #include "pci.h" +static DEFINE_MUTEX(pci_acpi_pm_notify_mtx); + +/** + * pci_acpi_wake_bus - Wake-up notification handler for root buses. + * @handle: ACPI handle of a device the notification is for. + * @event: Type of the signaled event. + * @context: PCI root bus to wake up devices on. + */ +static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context) +{ + struct pci_bus *pci_bus = context; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus) + pci_pme_wakeup_bus(pci_bus); +} + +/** + * pci_acpi_wake_dev - Wake-up notification handler for PCI devices. + * @handle: ACPI handle of a device the notification is for. + * @event: Type of the signaled event. + * @context: PCI device object to wake up. + */ +static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) +{ + struct pci_dev *pci_dev = context; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) { + pci_check_pme_status(pci_dev); + pm_runtime_resume(&pci_dev->dev); + if (pci_dev->subordinate) + pci_pme_wakeup_bus(pci_dev->subordinate); + } +} + +/** + * add_pm_notifier - Register PM notifier for given ACPI device. + * @dev: ACPI device to add the notifier for. + * @context: PCI device or bus to check for PME status if an event is signaled. + * + * NOTE: @dev need not be a run-wake or wake-up device to be a valid source of + * PM wake-up events. For example, wake-up events may be generated for bridges + * if one of the devices below the bridge is signaling PME, even if the bridge + * itself doesn't have a wake-up GPE associated with it. + */ +static acpi_status add_pm_notifier(struct acpi_device *dev, + acpi_notify_handler handler, + void *context) +{ + acpi_status status = AE_ALREADY_EXISTS; + + mutex_lock(&pci_acpi_pm_notify_mtx); + + if (dev->wakeup.flags.notifier_present) + goto out; + + status = acpi_install_notify_handler(dev->handle, + ACPI_SYSTEM_NOTIFY, + handler, context); + if (ACPI_FAILURE(status)) + goto out; + + dev->wakeup.flags.notifier_present = true; + + out: + mutex_unlock(&pci_acpi_pm_notify_mtx); + return status; +} + +/** + * remove_pm_notifier - Unregister PM notifier from given ACPI device. + * @dev: ACPI device to remove the notifier from. + */ +static acpi_status remove_pm_notifier(struct acpi_device *dev, + acpi_notify_handler handler) +{ + acpi_status status = AE_BAD_PARAMETER; + + mutex_lock(&pci_acpi_pm_notify_mtx); + + if (!dev->wakeup.flags.notifier_present) + goto out; + + status = acpi_remove_notify_handler(dev->handle, + ACPI_SYSTEM_NOTIFY, + handler); + if (ACPI_FAILURE(status)) + goto out; + + dev->wakeup.flags.notifier_present = false; + + out: + mutex_unlock(&pci_acpi_pm_notify_mtx); + return status; +} + +/** + * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus. + * @dev: ACPI device to add the notifier for. + * @pci_bus: PCI bus to walk checking for PME status if an event is signaled. + */ +acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, + struct pci_bus *pci_bus) +{ + return add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus); +} + +/** + * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier. + * @dev: ACPI device to remove the notifier from. + */ +acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) +{ + return remove_pm_notifier(dev, pci_acpi_wake_bus); +} + +/** + * pci_acpi_add_pm_notifier - Register PM notifier for given PCI device. + * @dev: ACPI device to add the notifier for. + * @pci_dev: PCI device to check for the PME status if an event is signaled. + */ +acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, + struct pci_dev *pci_dev) +{ + return add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev); +} + +/** + * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier. + * @dev: ACPI device to remove the notifier from. + */ +acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) +{ + return remove_pm_notifier(dev, pci_acpi_wake_dev); +} + /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". @@ -131,12 +267,87 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) return 0; } +/** + * acpi_dev_run_wake - Enable/disable wake-up for given device. + * @phys_dev: Device to enable/disable the platform to wake-up the system for. + * @enable: Whether enable or disable the wake-up functionality. + * + * Find the ACPI device object corresponding to @pci_dev and try to + * enable/disable the GPE associated with it. + */ +static int acpi_dev_run_wake(struct device *phys_dev, bool enable) +{ + struct acpi_device *dev; + acpi_handle handle; + int error = -ENODEV; + + if (!device_run_wake(phys_dev)) + return -EINVAL; + + handle = DEVICE_ACPI_HANDLE(phys_dev); + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) { + dev_dbg(phys_dev, "ACPI handle has no context in %s!\n", + __func__); + return -ENODEV; + } + + if (enable) { + if (!dev->wakeup.run_wake_count++) { + acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, + ACPI_GPE_TYPE_RUNTIME); + } + } else if (dev->wakeup.run_wake_count > 0) { + if (!--dev->wakeup.run_wake_count) { + acpi_disable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, + ACPI_GPE_TYPE_RUNTIME); + acpi_disable_wakeup_device_power(dev); + } + } else { + error = -EALREADY; + } + + return error; +} + +static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) +{ + while (bus->parent) { + struct pci_dev *bridge = bus->self; + + if (bridge->pme_interrupt) + return; + if (!acpi_dev_run_wake(&bridge->dev, enable)) + return; + bus = bus->parent; + } + + /* We have reached the root bus. */ + if (bus->bridge) + acpi_dev_run_wake(bus->bridge, enable); +} + +static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) +{ + if (dev->pme_interrupt) + return 0; + + if (!acpi_dev_run_wake(&dev->dev, enable)) + return 0; + + acpi_pci_propagate_run_wake(dev->bus, enable); + return 0; +} + static struct pci_platform_pm_ops acpi_pci_platform_pm = { .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .choose_state = acpi_pci_choose_state, .can_wakeup = acpi_pci_can_wakeup, .sleep_wake = acpi_pci_sleep_wake, + .run_wake = acpi_pci_run_wake, }; /* ACPI bus type */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5723446544fd..df55a2f351b3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "pci.h" @@ -462,6 +463,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) pci_platform_pm->sleep_wake(dev, enable) : -ENODEV; } +static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable) +{ + return pci_platform_pm ? + pci_platform_pm->run_wake(dev, enable) : -ENODEV; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -1229,6 +1236,31 @@ bool pci_check_pme_status(struct pci_dev *dev) return ret; } +/** + * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set. + * @dev: Device to handle. + * @ign: Ignored. + * + * Check if @dev has generated PME and queue a resume request for it in that + * case. + */ +static int pci_pme_wakeup(struct pci_dev *dev, void *ign) +{ + if (pci_check_pme_status(dev)) + pm_request_resume(&dev->dev); + return 0; +} + +/** + * pci_pme_wakeup_bus - Walk given bus and wake up devices on it, if necessary. + * @bus: Top bus of the subtree to walk. + */ +void pci_pme_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_pme_wakeup, NULL); +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. @@ -1433,6 +1465,41 @@ int pci_back_from_sleep(struct pci_dev *dev) return pci_set_power_state(dev, PCI_D0); } +/** + * pci_dev_run_wake - Check if device can generate run-time wake-up events. + * @dev: Device to check. + * + * Return true if the device itself is cabable of generating wake-up events + * (through the platform or using the native PCIe PME) or if the device supports + * PME and one of its upstream bridges can generate wake-up events. + */ +bool pci_dev_run_wake(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + + if (device_run_wake(&dev->dev)) + return true; + + if (!dev->pme_support) + return false; + + while (bus->parent) { + struct pci_dev *bridge = bus->self; + + if (device_run_wake(&bridge->dev)) + return true; + + bus = bus->parent; + } + + /* We have reached the root bus. */ + if (bus->bridge) + return device_run_wake(bus->bridge); + + return false; +} +EXPORT_SYMBOL_GPL(pci_dev_run_wake); + /** * pci_pm_init - Initialize PM functions of given PCI device * @dev: PCI device to handle. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b95b0a077d31..286c50821949 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -35,6 +35,10 @@ int pci_probe_reset_function(struct pci_dev *dev); * * @sleep_wake: enables/disables the system wake up capability of given device * + * @run_wake: enables/disables the platform to generate run-time wake-up events + * for given device (the device's wake-up capability has to be + * enabled by @sleep_wake for this feature to work) + * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. */ @@ -44,12 +48,15 @@ struct pci_platform_pm_ops { pci_power_t (*choose_state)(struct pci_dev *dev); bool (*can_wakeup)(struct pci_dev *dev); int (*sleep_wake)(struct pci_dev *dev, bool enable); + int (*run_wake)(struct pci_dev *dev, bool enable); }; extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern bool pci_check_pme_status(struct pci_dev *dev); +extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +extern void pci_pme_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 60fcff419352..54508ccea023 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -243,6 +243,7 @@ struct acpi_device_wakeup_flags { u8 valid:1; /* Can successfully enable wakeup? */ u8 run_wake:1; /* Run-Wake GPE devices */ u8 always_enabled:1; /* Run-wake devices that are always enabled */ + u8 notifier_present:1; /* Wake-up notify handler has been installed */ }; struct acpi_device_wakeup_state { @@ -388,6 +389,9 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle)) +int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); +int acpi_disable_wakeup_device_power(struct acpi_device *dev); + #ifdef CONFIG_PM_SLEEP int acpi_pm_device_sleep_state(struct device *, int *); int acpi_pm_device_sleep_wake(struct device *, bool); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 93a7c08f869d..c8b6473c5f42 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -11,6 +11,13 @@ #include #ifdef CONFIG_ACPI +extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, + struct pci_bus *pci_bus); +extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev); +extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, + struct pci_dev *pci_dev); +extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev); + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { struct pci_bus *pbus = pdev->bus; diff --git a/include/linux/pci.h b/include/linux/pci.h index 9fe4b2089b78..3f787ce78bd1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -782,6 +782,7 @@ int pci_wake_from_d3(struct pci_dev *dev, bool enable); pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); +bool pci_dev_run_wake(struct pci_dev *dev); /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); From 552be54cc4232dc5acc49ccb372129d6f1b6923f Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 22 Feb 2010 14:12:24 +0900 Subject: [PATCH 39/48] PCIe PME: use pci_is_pcie() Use pci_is_pcie() instead of looking at obsolete is_pcie field in struct pci_dev. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/pme/pcie_pme.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c index 51a69061b120..6b516309c094 100644 --- a/drivers/pci/pcie/pme/pcie_pme.c +++ b/drivers/pci/pcie/pme/pcie_pme.c @@ -144,7 +144,7 @@ static bool pcie_pme_walk_bus(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { /* Skip PCIe devices in case we started from a root port. */ - if (!dev->is_pcie && pci_check_pme_status(dev)) { + if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) { pm_request_resume(&dev->dev); ret = true; } @@ -177,7 +177,7 @@ static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn) if (!dev) return false; - if (dev->is_pcie && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { + if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { down_read(&pci_bus_sem); if (pcie_pme_walk_bus(bus)) found = true; @@ -389,7 +389,7 @@ static void pcie_pme_mark_devices(struct pci_dev *port) down_read(&pci_bus_sem); list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->is_pcie + if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_RC_END) pcie_pme_set_native(dev, NULL); up_read(&pci_bus_sem); From 6cbf82148ff286ec22a55be6836c3a5bffc489c1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 17 Feb 2010 23:44:58 +0100 Subject: [PATCH 40/48] PCI PM: Run-time callbacks for PCI bus type Introduce run-time PM callbacks for the PCI bus type. Make the new callbacks work in analogy with the existing system sleep PM callbacks, so that the drivers already converted to struct dev_pm_ops can use their suspend and resume routines for run-time PM without modifications. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 160 ++++++++++++++++++++++++++++++++------- drivers/pci/pci.c | 43 +++++++++-- drivers/pci/pci.h | 1 + include/linux/pci.h | 9 ++- kernel/power/Kconfig | 5 ++ 5 files changed, 184 insertions(+), 34 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e5d47be3c6d7..f9a0aec3abcf 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "pci.h" struct pci_dynid { @@ -404,6 +405,35 @@ static void pci_device_shutdown(struct device *dev) pci_msix_shutdown(pci_dev); } +#ifdef CONFIG_PM_OPS + +/* Auxiliary functions used for system resume and run-time resume. */ + +/** + * pci_restore_standard_config - restore standard config registers of PCI device + * @pci_dev: PCI device to handle + */ +static int pci_restore_standard_config(struct pci_dev *pci_dev) +{ + pci_update_current_state(pci_dev, PCI_UNKNOWN); + + if (pci_dev->current_state != PCI_D0) { + int error = pci_set_power_state(pci_dev, PCI_D0); + if (error) + return error; + } + + return pci_restore_state(pci_dev); +} + +static void pci_pm_default_resume_early(struct pci_dev *pci_dev) +{ + pci_restore_standard_config(pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); +} + +#endif + #ifdef CONFIG_PM_SLEEP /* @@ -520,29 +550,6 @@ static int pci_legacy_resume(struct device *dev) /* Auxiliary functions used by the new power management framework */ -/** - * pci_restore_standard_config - restore standard config registers of PCI device - * @pci_dev: PCI device to handle - */ -static int pci_restore_standard_config(struct pci_dev *pci_dev) -{ - pci_update_current_state(pci_dev, PCI_UNKNOWN); - - if (pci_dev->current_state != PCI_D0) { - int error = pci_set_power_state(pci_dev, PCI_D0); - if (error) - return error; - } - - return pci_restore_state(pci_dev); -} - -static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) -{ - pci_restore_standard_config(pci_dev); - pci_fixup_device(pci_fixup_resume_early, pci_dev); -} - static void pci_pm_default_resume(struct pci_dev *pci_dev) { pci_fixup_device(pci_fixup_resume, pci_dev); @@ -581,6 +588,17 @@ static int pci_pm_prepare(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* + * PCI devices suspended at run time need to be resumed at this + * point, because in general it is necessary to reconfigure them for + * system suspend. Namely, if the device is supposed to wake up the + * system from the sleep state, we may need to reconfigure it for this + * purpose. In turn, if the device is not supposed to wake up the + * system from the sleep state, we'll have to prevent it from signaling + * wake-up. + */ + pm_runtime_resume(dev); + if (drv && drv->pm && drv->pm->prepare) error = drv->pm->prepare(dev); @@ -595,6 +613,13 @@ static void pci_pm_complete(struct device *dev) drv->pm->complete(dev); } +#else /* !CONFIG_PM_SLEEP */ + +#define pci_pm_prepare NULL +#define pci_pm_complete NULL + +#endif /* !CONFIG_PM_SLEEP */ + #ifdef CONFIG_SUSPEND static int pci_pm_suspend(struct device *dev) @@ -681,7 +706,7 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_pm_default_resume_noirq(pci_dev); + pci_pm_default_resume_early(pci_dev); if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); @@ -879,7 +904,7 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_pm_default_resume_noirq(pci_dev); + pci_pm_default_resume_early(pci_dev); if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); @@ -931,6 +956,84 @@ static int pci_pm_restore(struct device *dev) #endif /* !CONFIG_HIBERNATION */ +#ifdef CONFIG_PM_RUNTIME + +static int pci_pm_runtime_suspend(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + pci_power_t prev = pci_dev->current_state; + int error; + + if (!pm || !pm->runtime_suspend) + return -ENOSYS; + + error = pm->runtime_suspend(dev); + suspend_report_result(pm->runtime_suspend, error); + if (error) + return error; + + pci_fixup_device(pci_fixup_suspend, pci_dev); + + if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 + && pci_dev->current_state != PCI_UNKNOWN) { + WARN_ONCE(pci_dev->current_state != prev, + "PCI PM: State of device not saved by %pF\n", + pm->runtime_suspend); + return 0; + } + + if (!pci_dev->state_saved) + pci_save_state(pci_dev); + + pci_finish_runtime_suspend(pci_dev); + + return 0; +} + +static int pci_pm_runtime_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm || !pm->runtime_resume) + return -ENOSYS; + + pci_pm_default_resume_early(pci_dev); + __pci_enable_wake(pci_dev, PCI_D0, true, false); + pci_fixup_device(pci_fixup_resume, pci_dev); + + return pm->runtime_resume(dev); +} + +static int pci_pm_runtime_idle(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (!pm) + return -ENOSYS; + + if (pm->runtime_idle) { + int ret = pm->runtime_idle(dev); + if (ret) + return ret; + } + + pm_runtime_suspend(dev); + + return 0; +} + +#else /* !CONFIG_PM_RUNTIME */ + +#define pci_pm_runtime_suspend NULL +#define pci_pm_runtime_resume NULL +#define pci_pm_runtime_idle NULL + +#endif /* !CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_OPS + const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, @@ -946,15 +1049,18 @@ const struct dev_pm_ops pci_dev_pm_ops = { .thaw_noirq = pci_pm_thaw_noirq, .poweroff_noirq = pci_pm_poweroff_noirq, .restore_noirq = pci_pm_restore_noirq, + .runtime_suspend = pci_pm_runtime_suspend, + .runtime_resume = pci_pm_runtime_resume, + .runtime_idle = pci_pm_runtime_idle, }; #define PCI_PM_OPS_PTR (&pci_dev_pm_ops) -#else /* !CONFIG_PM_SLEEP */ +#else /* !COMFIG_PM_OPS */ #define PCI_PM_OPS_PTR NULL -#endif /* !CONFIG_PM_SLEEP */ +#endif /* !COMFIG_PM_OPS */ /** * __pci_register_driver - register a new pci driver diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index df55a2f351b3..d62a5de81672 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1302,9 +1302,10 @@ void pci_pme_active(struct pci_dev *dev, bool enable) } /** - * pci_enable_wake - enable PCI device as wakeup event source + * __pci_enable_wake - enable PCI device as wakeup event source * @dev: PCI device affected * @state: PCI state from which device will issue wakeup events + * @runtime: True if the events are to be generated at run time * @enable: True to enable event generation; false to disable * * This enables the device as a wakeup event source, or disables it. @@ -1320,11 +1321,12 @@ void pci_pme_active(struct pci_dev *dev, bool enable) * Error code depending on the platform is returned if both the platform and * the native mechanism fail to enable the generation of wake-up events */ -int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) +int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, + bool runtime, bool enable) { int ret = 0; - if (enable && !device_may_wakeup(&dev->dev)) + if (enable && !runtime && !device_may_wakeup(&dev->dev)) return -EINVAL; /* Don't do the same thing twice in a row for one device. */ @@ -1344,19 +1346,24 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) pci_pme_active(dev, true); else ret = 1; - error = platform_pci_sleep_wake(dev, true); + error = runtime ? platform_pci_run_wake(dev, true) : + platform_pci_sleep_wake(dev, true); if (ret) ret = error; if (!ret) dev->wakeup_prepared = true; } else { - platform_pci_sleep_wake(dev, false); + if (runtime) + platform_pci_run_wake(dev, false); + else + platform_pci_sleep_wake(dev, false); pci_pme_active(dev, false); dev->wakeup_prepared = false; } return ret; } +EXPORT_SYMBOL(__pci_enable_wake); /** * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold @@ -1465,6 +1472,31 @@ int pci_back_from_sleep(struct pci_dev *dev) return pci_set_power_state(dev, PCI_D0); } +/** + * pci_finish_runtime_suspend - Carry out PCI-specific part of runtime suspend. + * @dev: PCI device being suspended. + * + * Prepare @dev to generate wake-up events at run time and put it into a low + * power state. + */ +int pci_finish_runtime_suspend(struct pci_dev *dev) +{ + pci_power_t target_state = pci_target_state(dev); + int error; + + if (target_state == PCI_POWER_ERROR) + return -EIO; + + __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); + + error = pci_set_power_state(dev, target_state); + + if (error) + __pci_enable_wake(dev, target_state, true, false); + + return error; +} + /** * pci_dev_run_wake - Check if device can generate run-time wake-up events. * @dev: Device to check. @@ -2978,7 +3010,6 @@ EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_pme_capable); EXPORT_SYMBOL(pci_pme_active); -EXPORT_SYMBOL(pci_enable_wake); EXPORT_SYMBOL(pci_wake_from_d3); EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 286c50821949..4eb10f48d270 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -55,6 +55,7 @@ extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern bool pci_check_pme_status(struct pci_dev *dev); +extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); extern void pci_pme_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 3f787ce78bd1..1a29f9f6b2dc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -777,13 +777,20 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); void pci_pme_active(struct pci_dev *dev, bool enable); -int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable); +int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, + bool runtime, bool enable); int pci_wake_from_d3(struct pci_dev *dev, bool enable); pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); bool pci_dev_run_wake(struct pci_dev *dev); +static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, + bool enable) +{ + return __pci_enable_wake(dev, state, false, enable); +} + /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 91e09d3b2eb2..4c9cffcf69c7 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -222,3 +222,8 @@ config PM_RUNTIME and the bus type drivers of the buses the devices are on are responsible for the actual handling of the autosuspend requests and wake-up events. + +config PM_OPS + bool + depends on PM_SLEEP || PM_RUNTIME + default y From b16694f70c40ea8d539cdc93a422039771e85870 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 22 Feb 2010 14:13:39 +0900 Subject: [PATCH 41/48] PCIe PME: use pci_pcie_cap() Use pci_pcie_cap() instead of pci_find_capability() to get PCIe capability offset. This reduces redundant search in PCI configuration space. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/pme/pcie_pme.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c index 6b516309c094..7b3cbff547ee 100644 --- a/drivers/pci/pcie/pme/pcie_pme.c +++ b/drivers/pci/pcie/pme/pcie_pme.c @@ -105,7 +105,7 @@ static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable) int rtctl_pos; u16 rtctl; - rtctl_pos = pci_find_capability(dev, PCI_CAP_ID_EXP) + PCI_EXP_RTCTL; + rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL; pci_read_config_word(dev, rtctl_pos, &rtctl); if (enable) @@ -124,7 +124,7 @@ static void pcie_pme_clear_status(struct pci_dev *dev) int rtsta_pos; u32 rtsta; - rtsta_pos = pci_find_capability(dev, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA; pci_read_config_dword(dev, rtsta_pos, &rtsta); rtsta |= PCI_EXP_RTSTA_PME; @@ -278,7 +278,7 @@ static void pcie_pme_work_fn(struct work_struct *work) int rtsta_pos; u32 rtsta; - rtsta_pos = pci_find_capability(port, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA; spin_lock_irq(&data->lock); @@ -332,7 +332,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) port = ((struct pcie_device *)context)->port; data = get_service_data((struct pcie_device *)context); - rtsta_pos = pci_find_capability(port, PCI_CAP_ID_EXP) + PCI_EXP_RTSTA; + rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA; spin_lock_irqsave(&data->lock, flags); pci_read_config_dword(port, rtsta_pos, &rtsta); From fa27b2d108fa49685129867a8c5b968344d6e197 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:21 -0700 Subject: [PATCH 42/48] PCI: split up pci_read_bridge_bases() No functional change; this breaks up pci_read_bridge_bases() into separate pieces for the I/O, memory, and prefetchable memory windows, similar to how Yinghai recently split up pci_setup_bridge() in 68e84ff3bdc. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 54 ++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index d3009430eab6..4b47b4bfb066 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -281,26 +281,12 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } -void __devinit pci_read_bridge_bases(struct pci_bus *child) +static void __devinit pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; - u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; struct resource *res; - int i; - - if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ - return; - - dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", - child->secondary, child->subordinate, - dev->transparent ? " (subtractive decode)": ""); - - if (dev->transparent) { - for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i - 3]; - } res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); @@ -328,6 +314,14 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) " bridge window [io %04lx - %04lx] reg reading\n", base, limit); } +} + +static void __devinit pci_read_bridge_mmio(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + struct resource *res; res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); @@ -344,6 +338,14 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) " bridge window [mem 0x%08lx - 0x%08lx] reg reading\n", base, limit + 0xfffff); } +} + +static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + struct resource *res; res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); @@ -389,6 +391,28 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) } } +void __devinit pci_read_bridge_bases(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + int i; + + if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ + return; + + dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", + child->secondary, child->subordinate, + dev->transparent ? " (subtractive decode)" : ""); + + if (dev->transparent) { + for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) + child->resource[i] = child->parent->resource[i - 3]; + } + + pci_read_bridge_io(child); + pci_read_bridge_mmio(child); + pci_read_bridge_mmio_pref(child); +} + static struct pci_bus * pci_alloc_bus(void) { struct pci_bus *b; From 2adf75160b10bf3f09ed7d3d04e937f923fc557e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:26 -0700 Subject: [PATCH 43/48] PCI: read bridge windows before filling in subtractive decode resources No functional change; this fills in the bus subtractive decode resources after reading the bridge window information rather than before. Also, print out the subtractive decode resources as we already do for the positive decode windows. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4b47b4bfb066..70c4ed2e67cc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -403,14 +403,19 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) child->secondary, child->subordinate, dev->transparent ? " (subtractive decode)" : ""); - if (dev->transparent) { - for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i - 3]; - } - pci_read_bridge_io(child); pci_read_bridge_mmio(child); pci_read_bridge_mmio_pref(child); + + if (dev->transparent) { + for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) { + child->resource[i] = child->parent->resource[i - 3]; + if (child->resource[i]) + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window %pR (subtractive decode)\n", + child->resource[i]); + } + } } static struct pci_bus * pci_alloc_bus(void) From 89a74ecccd1f78e51faf6287e5c0e93a92ac096e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:31 -0700 Subject: [PATCH 44/48] PCI: add pci_bus_for_each_resource(), remove direct bus->resource[] refs No functional change; this converts loops that iterate from 0 to PCI_BUS_NUM_RESOURCES through pci_bus resource[] table to use the pci_bus_for_each_resource() iterator instead. This doesn't change the way resources are stored; it merely removes dependencies on the fact that they're in a table. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/ia64/pci/pci.c | 5 ++--- arch/mn10300/unit-asb2305/pci.c | 6 ++---- arch/powerpc/kernel/pci-common.c | 11 ++++------- arch/powerpc/platforms/fsl_uli1575.c | 12 ++++++------ drivers/pci/bus.c | 4 ++-- drivers/pci/hotplug/shpchp_sysfs.c | 9 +++------ drivers/pci/pci.c | 5 ++--- drivers/pci/setup-bus.c | 10 ++++------ drivers/pcmcia/rsrc_nonstatic.c | 3 +-- drivers/pcmcia/yenta_socket.c | 5 +++-- include/linux/pci.h | 3 +++ 11 files changed, 32 insertions(+), 41 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 783c83bb2b49..89f957ca3eb2 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -452,13 +452,12 @@ EXPORT_SYMBOL(pcibios_bus_to_resource); static int __devinit is_valid_resource(struct pci_dev *dev, int idx) { unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; - struct resource *devr = &dev->resource[idx]; + struct resource *devr = &dev->resource[idx], *busr; if (!dev->bus) return 0; - for (i=0; ibus->resource[i]; + pci_bus_for_each_resource(dev->bus, busr, i) { if (!busr || ((busr->flags ^ devr->flags) & type_mask)) continue; if ((devr->start) && (devr->start >= busr->start) && diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c index 2cb7e75ba1c0..6d8720a0a599 100644 --- a/arch/mn10300/unit-asb2305/pci.c +++ b/arch/mn10300/unit-asb2305/pci.c @@ -331,12 +331,10 @@ static int __init pci_check_direct(void) static int __devinit is_valid_resource(struct pci_dev *dev, int idx) { unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; - struct resource *devr = &dev->resource[idx]; + struct resource *devr = &dev->resource[idx], *busr; if (dev->bus) { - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *busr = dev->bus->resource[i]; - + pci_bus_for_each_resource(dev->bus, busr, i) { if (!busr || (busr->flags ^ devr->flags) & type_mask) continue; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index e640810e813f..2597f9545d8a 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1047,10 +1047,8 @@ static void __devinit pcibios_fixup_bridge(struct pci_bus *bus) struct pci_dev *dev = bus->self; - for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) { - if ((res = bus->resource[i]) == NULL) - continue; - if (!res->flags) + pci_bus_for_each_resource(bus, res, i) { + if (!res || !res->flags) continue; if (i >= 3 && bus->self->transparent) continue; @@ -1277,9 +1275,8 @@ void pcibios_allocate_bus_resources(struct pci_bus *bus) pr_debug("PCI: Allocating bus resources for %04x:%02x...\n", pci_domain_nr(bus), bus->number); - for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) { - if ((res = bus->resource[i]) == NULL || !res->flags - || res->start > res->end || res->parent) + pci_bus_for_each_resource(bus, res, i) { + if (!res || !res->flags || res->start > res->end || res->parent) continue; if (bus->parent == NULL) pr = (res->flags & IORESOURCE_IO) ? diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c index fd23a1d4b39d..8b0c2082a783 100644 --- a/arch/powerpc/platforms/fsl_uli1575.c +++ b/arch/powerpc/platforms/fsl_uli1575.c @@ -222,6 +222,7 @@ static void __devinit quirk_final_uli5249(struct pci_dev *dev) int i; u8 *dummy; struct pci_bus *bus = dev->bus; + struct resource *res; resource_size_t end = 0; for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCES+3; i++) { @@ -230,13 +231,12 @@ static void __devinit quirk_final_uli5249(struct pci_dev *dev) end = pci_resource_end(dev, i); } - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - if ((bus->resource[i]) && - (bus->resource[i]->flags & IORESOURCE_MEM)) { - if (bus->resource[i]->end == end) - dummy = ioremap(bus->resource[i]->start, 0x4); + pci_bus_for_each_resource(bus, res, i) { + if (res && res->flags & IORESOURCE_MEM) { + if (res->end == end) + dummy = ioremap(res->start, 0x4); else - dummy = ioremap(bus->resource[i]->end - 3, 0x4); + dummy = ioremap(res->end - 3, 0x4); if (dummy) { in_8(dummy); iounmap(dummy); diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index a26135bb0ffd..e75d219fd107 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -43,6 +43,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, void *alignf_data) { int i, ret = -ENOMEM; + struct resource *r; resource_size_t max = -1; type_mask |= IORESOURCE_IO | IORESOURCE_MEM; @@ -51,8 +52,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, if (!(res->flags & IORESOURCE_MEM_64)) max = PCIBIOS_MAX_MEM_32; - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *r = bus->resource[i]; + pci_bus_for_each_resource(bus, r, i) { if (!r) continue; diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index 29fa9d26adae..071b7dc0094b 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c @@ -47,8 +47,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha bus = pdev->subordinate; out += sprintf(buf, "Free resources: memory\n"); - for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) { - res = bus->resource[index]; + pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_MEM) && !(res->flags & IORESOURCE_PREFETCH)) { out += sprintf(out, "start = %8.8llx, " @@ -58,8 +57,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha } } out += sprintf(out, "Free resources: prefetchable memory\n"); - for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) { - res = bus->resource[index]; + pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_MEM) && (res->flags & IORESOURCE_PREFETCH)) { out += sprintf(out, "start = %8.8llx, " @@ -69,8 +67,7 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha } } out += sprintf(out, "Free resources: IO\n"); - for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) { - res = bus->resource[index]; + pci_bus_for_each_resource(bus, res, index) { if (res && (res->flags & IORESOURCE_IO)) { out += sprintf(out, "start = %8.8llx, " "length = %8.8llx\n", diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d62a5de81672..f4a2738bf0bf 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -386,10 +386,9 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) { const struct pci_bus *bus = dev->bus; int i; - struct resource *best = NULL; + struct resource *best = NULL, *r; - for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *r = bus->resource[i]; + pci_bus_for_each_resource(bus, r, i) { if (!r) continue; if (res->start && !(res->start >= r->start && res->end <= r->end)) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 743ed8c48b9c..bf32f07c4efb 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -387,8 +387,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - r = bus->resource[i]; + pci_bus_for_each_resource(bus, r, i) { if (r == &ioport_resource || r == &iomem_resource) continue; if (r && (r->flags & type_mask) == type && !r->parent) @@ -803,11 +802,10 @@ static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus, static void pci_bus_dump_res(struct pci_bus *bus) { - int i; - - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *res = bus->resource[i]; + struct resource *res; + int i; + pci_bus_for_each_resource(bus, res, i) { if (!res || !res->end || !res->flags) continue; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 45d75dc452f0..c67638fe6914 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -803,8 +803,7 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) return -EINVAL; #endif - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - res = s->cb_dev->bus->resource[i]; + pci_bus_for_each_resource(s->cb_dev->bus, res, i) { if (!res) continue; diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index e4d12acdd525..1f2039d5e966 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -649,9 +649,10 @@ static int yenta_search_one_res(struct resource *root, struct resource *res, static int yenta_search_res(struct yenta_socket *socket, struct resource *res, u32 min) { + struct resource *root; int i; - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *root = socket->dev->bus->resource[i]; + + pci_bus_for_each_resource(socket->dev->bus, root, i) { if (!root) continue; diff --git a/include/linux/pci.h b/include/linux/pci.h index 1a29f9f6b2dc..2ff9d26a078f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -829,6 +829,9 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ +#define pci_bus_for_each_resource(bus, res, i) \ + for (i = 0; res = bus->resource[i], i < PCI_BUS_NUM_RESOURCES; i++) + int __must_check pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, resource_size_t min, From 2fe2abf896c1e7a0ee65faaf3ef0ce654848abbd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:36 -0700 Subject: [PATCH 45/48] PCI: augment bus resource table with a list Previously we used a table of size PCI_BUS_NUM_RESOURCES (16) for resources forwarded to a bus by its upstream bridge. We've increased this size several times when the table overflowed. But there's no good limit on the number of resources because host bridges and subtractive decode bridges can forward any number of ranges to their secondary buses. This patch reduces the table to only PCI_BRIDGE_RESOURCE_NUM (4) entries, which corresponds to the number of windows a PCI-to-PCI (3) or CardBus (4) bridge can positively decode. Any additional resources, e.g., PCI host bridge windows or subtractively-decoded regions, are kept in a list. I'd prefer a single list rather than this split table/list approach, but that requires simultaneous changes to every architecture. This approach only requires immediate changes where we set up (a) host bridges with more than four windows and (b) subtractive-decode P2P bridges, and we can incrementally change other architectures to use the list. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/ia64/pci/pci.c | 12 +++-------- arch/x86/pci/acpi.c | 33 ++++------------------------- arch/x86/pci/bus_numa.c | 3 ++- arch/x86/pci/bus_numa.h | 3 +-- drivers/pci/bus.c | 46 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/probe.c | 17 +++++++++++---- include/linux/pci.h | 35 +++++++++++++++++++++++++------ 7 files changed, 98 insertions(+), 51 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 89f957ca3eb2..64aff520b899 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -320,9 +320,9 @@ static __devinit acpi_status add_window(struct acpi_resource *res, void *data) static void __devinit pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl) { - int i, j; + int i; - j = 0; + pci_bus_remove_resources(bus); for (i = 0; i < ctrl->windows; i++) { struct resource *res = &ctrl->window[i].resource; /* HP's firmware has a hack to work around a Windows bug. @@ -330,13 +330,7 @@ pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl) if ((res->flags & IORESOURCE_MEM) && (res->end - res->start < 16)) continue; - if (j >= PCI_BUS_NUM_RESOURCES) { - dev_warn(&bus->dev, - "ignoring host bridge window %pR (no space)\n", - res); - continue; - } - bus->resource[j++] = res; + pci_bus_add_resource(bus, res, 0); } } diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 959e548a7039..a2f8cdb8c1d5 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -45,20 +45,6 @@ count_resource(struct acpi_resource *acpi_res, void *data) return AE_OK; } -static int -bus_has_transparent_bridge(struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) { - u16 class = dev->class >> 8; - - if (class == PCI_CLASS_BRIDGE_PCI && dev->transparent) - return true; - } - return false; -} - static void align_resource(struct acpi_device *bridge, struct resource *res) { @@ -92,12 +78,8 @@ setup_resource(struct acpi_resource *acpi_res, void *data) acpi_status status; unsigned long flags; struct resource *root; - int max_root_bus_resources = PCI_BUS_NUM_RESOURCES; u64 start, end; - if (bus_has_transparent_bridge(info->bus)) - max_root_bus_resources -= 3; - status = resource_to_addr(acpi_res, &addr); if (!ACPI_SUCCESS(status)) return AE_OK; @@ -115,15 +97,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) start = addr.minimum + addr.translation_offset; end = start + addr.address_length - 1; - if (info->res_num >= max_root_bus_resources) { - if (pci_probe & PCI_USE__CRS) - printk(KERN_WARNING "PCI: Failed to allocate " - "0x%lx-0x%lx from %s for %s due to _CRS " - "returning more than %d resource descriptors\n", - (unsigned long) start, (unsigned long) end, - root->name, info->name, max_root_bus_resources); - return AE_OK; - } res = &info->res[info->res_num]; res->name = info->name; @@ -143,7 +116,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data) dev_err(&info->bridge->dev, "can't allocate host bridge window %pR\n", res); } else { - info->bus->resource[info->res_num] = res; + pci_bus_add_resource(info->bus, res, 0); info->res_num++; if (addr.translation_offset) dev_info(&info->bridge->dev, "host bridge window %pR " @@ -164,7 +137,9 @@ get_current_resources(struct acpi_device *device, int busnum, struct pci_root_info info; size_t size; - if (!(pci_probe & PCI_USE__CRS)) + if (pci_probe & PCI_USE__CRS) + pci_bus_remove_resources(bus); + else dev_info(&device->dev, "ignoring host bridge windows from ACPI; " "boot with \"pci=use_crs\" to use them\n"); diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c index f939d603adfa..12d54ff3654d 100644 --- a/arch/x86/pci/bus_numa.c +++ b/arch/x86/pci/bus_numa.c @@ -36,13 +36,14 @@ void x86_pci_root_bus_res_quirks(struct pci_bus *b) printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n", b->number); + pci_bus_remove_resources(b); info = &pci_root_info[i]; for (j = 0; j < info->res_num; j++) { struct resource *res; struct resource *root; res = &info->res[j]; - b->resource[j] = res; + pci_bus_add_resource(b, res, 0); if (res->flags & IORESOURCE_IO) root = &ioport_resource; else diff --git a/arch/x86/pci/bus_numa.h b/arch/x86/pci/bus_numa.h index adbc23fe82ac..731b64ee8d84 100644 --- a/arch/x86/pci/bus_numa.h +++ b/arch/x86/pci/bus_numa.h @@ -2,8 +2,7 @@ /* * sub bus (transparent) will use entres from 3 to store extra from - * root, so need to make sure we have enough slot there, Should we - * increase PCI_BUS_NUM_RESOURCES? + * root, so need to make sure we have enough slot there. */ #define RES_NUM 16 struct pci_root_info { diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index e75d219fd107..712250f5874a 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -17,6 +17,52 @@ #include "pci.h" +void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, + unsigned int flags) +{ + struct pci_bus_resource *bus_res; + + bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); + if (!bus_res) { + dev_err(&bus->dev, "can't add %pR resource\n", res); + return; + } + + bus_res->res = res; + bus_res->flags = flags; + list_add_tail(&bus_res->list, &bus->resources); +} + +struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n) +{ + struct pci_bus_resource *bus_res; + + if (n < PCI_BRIDGE_RESOURCE_NUM) + return bus->resource[n]; + + n -= PCI_BRIDGE_RESOURCE_NUM; + list_for_each_entry(bus_res, &bus->resources, list) { + if (n-- == 0) + return bus_res->res; + } + return NULL; +} +EXPORT_SYMBOL_GPL(pci_bus_resource_n); + +void pci_bus_remove_resources(struct pci_bus *bus) +{ + struct pci_bus_resource *bus_res, *tmp; + int i; + + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) + bus->resource[i] = 0; + + list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) { + list_del(&bus_res->list); + kfree(bus_res); + } +} + /** * pci_bus_alloc_resource - allocate a resource from a parent bus * @bus: PCI bus diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70c4ed2e67cc..270d069819f7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -89,6 +89,7 @@ static void release_pcibus_dev(struct device *dev) if (pci_bus->bridge) put_device(pci_bus->bridge); + pci_bus_remove_resources(pci_bus); kfree(pci_bus); } @@ -394,6 +395,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) void __devinit pci_read_bridge_bases(struct pci_bus *child) { struct pci_dev *dev = child->self; + struct resource *res; int i; if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ @@ -403,17 +405,23 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) child->secondary, child->subordinate, dev->transparent ? " (subtractive decode)" : ""); + pci_bus_remove_resources(child); + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + pci_read_bridge_io(child); pci_read_bridge_mmio(child); pci_read_bridge_mmio_pref(child); if (dev->transparent) { - for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) { - child->resource[i] = child->parent->resource[i - 3]; - if (child->resource[i]) + pci_bus_for_each_resource(child->parent, res, i) { + if (res) { + pci_bus_add_resource(child, res, + PCI_SUBTRACTIVE_DECODE); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR (subtractive decode)\n", - child->resource[i]); + res); + } } } } @@ -428,6 +436,7 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); INIT_LIST_HEAD(&b->slots); + INIT_LIST_HEAD(&b->resources); b->max_bus_speed = PCI_SPEED_UNKNOWN; b->cur_bus_speed = PCI_SPEED_UNKNOWN; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 2ff9d26a078f..e19a69613d8f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -364,9 +364,26 @@ static inline void pci_add_saved_cap(struct pci_dev *pci_dev, hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); } -#ifndef PCI_BUS_NUM_RESOURCES -#define PCI_BUS_NUM_RESOURCES 16 -#endif +/* + * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond + * to P2P or CardBus bridge windows) go in a table. Additional ones (for + * buses below host bridges or subtractive decode bridges) go in the list. + * Use pci_bus_for_each_resource() to iterate through all the resources. + */ + +/* + * PCI_SUBTRACTIVE_DECODE means the bridge forwards the window implicitly + * and there's no way to program the bridge with the details of the window. + * This does not apply to ACPI _CRS windows, even with the _DEC subtractive- + * decode bit set, because they are explicit and can be programmed with _SRS. + */ +#define PCI_SUBTRACTIVE_DECODE 0x1 + +struct pci_bus_resource { + struct list_head list; + struct resource *res; + unsigned int flags; +}; #define PCI_REGION_FLAG_MASK 0x0fU /* These bits of resource flags tell us the PCI region flags */ @@ -377,8 +394,8 @@ struct pci_bus { struct list_head devices; /* list of devices on this bus */ struct pci_dev *self; /* bridge device as seen by parent */ struct list_head slots; /* list of slots on this bus */ - struct resource *resource[PCI_BUS_NUM_RESOURCES]; - /* address space routed to this bus */ + struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; + struct list_head resources; /* address space routed to this bus */ struct pci_ops *ops; /* configuration access functions */ void *sysdata; /* hook for sys-specific extension */ @@ -829,8 +846,14 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ +void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, unsigned int flags); +struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n); +void pci_bus_remove_resources(struct pci_bus *bus); + #define pci_bus_for_each_resource(bus, res, i) \ - for (i = 0; res = bus->resource[i], i < PCI_BUS_NUM_RESOURCES; i++) + for (i = 0; \ + (res = pci_bus_resource_n(bus, i)) || i < PCI_BRIDGE_RESOURCE_NUM; \ + i++) int __must_check pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, From 7bc5e3f2be32ae6fb0c74cd0f707f986b3a01a26 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:41 -0700 Subject: [PATCH 46/48] x86/PCI: use host bridge _CRS info by default on 2008 and newer machines The main benefit of using ACPI host bridge window information is that we can do better resource allocation in systems with multiple host bridges, e.g., http://bugzilla.kernel.org/show_bug.cgi?id=14183 Sometimes we need _CRS information even if we only have one host bridge, e.g., https://bugs.launchpad.net/ubuntu/+source/linux/+bug/341681 Most of these systems are relatively new, so this patch turns on "pci=use_crs" only on machines with a BIOS date of 2008 or newer. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 8 +++-- arch/ia64/include/asm/acpi.h | 1 + arch/x86/include/asm/pci_x86.h | 1 + arch/x86/pci/acpi.c | 53 +++++++++++++++++++++++++---- arch/x86/pci/common.c | 3 ++ drivers/acpi/pci_root.c | 1 + include/acpi/acpi_drivers.h | 1 + 7 files changed, 60 insertions(+), 8 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 516225a864f9..3e69c1c4f508 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1948,8 +1948,12 @@ and is between 256 and 4096 characters. It is defined in the file IRQ routing is enabled. noacpi [X86] Do not use ACPI for IRQ routing or for PCI scanning. - use_crs [X86] Use _CRS for PCI resource - allocation. + use_crs [X86] Use PCI host bridge window information + from ACPI. On BIOSes from 2008 or later, this + is enabled by default. If you need to use this, + please report a bug. + nocrs [X86] Ignore PCI host bridge windows from ACPI. + If you need to use this, please report a bug. routeirq Do IRQ routing for all PCI devices. This is normally done in pci_enable_device(), so this option is a temporary workaround diff --git a/arch/ia64/include/asm/acpi.h b/arch/ia64/include/asm/acpi.h index e97b255d97bc..93997bd5edc3 100644 --- a/arch/ia64/include/asm/acpi.h +++ b/arch/ia64/include/asm/acpi.h @@ -98,6 +98,7 @@ ia64_acpi_release_global_lock (unsigned int *lock) #endif #define acpi_processor_cstate_check(x) (x) /* no idle limits on IA64 :) */ static inline void disable_acpi(void) { } +static inline void pci_acpi_crs_quirks(void) { } const char *acpi_get_sysname (void); int acpi_request_vector (u32 int_type); diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index b4bf9a942ed0..05b58ccb2e82 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -29,6 +29,7 @@ #define PCI_CHECK_ENABLE_AMD_MMCONF 0x20000 #define PCI_HAS_IO_ECS 0x40000 #define PCI_NOASSIGN_ROMS 0x80000 +#define PCI_ROOT_NO_CRS 0x100000 extern unsigned int pci_probe; extern unsigned long pirq_table_addr; diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index a2f8cdb8c1d5..5f11ff6f5389 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -15,6 +15,51 @@ struct pci_root_info { int busnum; }; +static bool pci_use_crs = true; + +static int __init set_use_crs(const struct dmi_system_id *id) +{ + pci_use_crs = true; + return 0; +} + +static const struct dmi_system_id pci_use_crs_table[] __initconst = { + /* http://bugzilla.kernel.org/show_bug.cgi?id=14183 */ + { + .callback = set_use_crs, + .ident = "IBM System x3800", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "x3800"), + }, + }, + {} +}; + +void __init pci_acpi_crs_quirks(void) +{ + int year; + + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year < 2008) + pci_use_crs = false; + + dmi_check_system(pci_use_crs_table); + + /* + * If the user specifies "pci=use_crs" or "pci=nocrs" explicitly, that + * takes precedence over anything we figured out above. + */ + if (pci_probe & PCI_ROOT_NO_CRS) + pci_use_crs = false; + else if (pci_probe & PCI_USE__CRS) + pci_use_crs = true; + + printk(KERN_INFO "PCI: %s host bridge windows from ACPI; " + "if necessary, use \"pci=%s\" and report a bug\n", + pci_use_crs ? "Using" : "Ignoring", + pci_use_crs ? "nocrs" : "use_crs"); +} + static acpi_status resource_to_addr(struct acpi_resource *resource, struct acpi_resource_address64 *addr) @@ -106,7 +151,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data) res->child = NULL; align_resource(info->bridge, res); - if (!(pci_probe & PCI_USE__CRS)) { + if (!pci_use_crs) { dev_printk(KERN_DEBUG, &info->bridge->dev, "host bridge window %pR (ignored)\n", res); return AE_OK; @@ -137,12 +182,8 @@ get_current_resources(struct acpi_device *device, int busnum, struct pci_root_info info; size_t size; - if (pci_probe & PCI_USE__CRS) + if (pci_use_crs) pci_bus_remove_resources(bus); - else - dev_info(&device->dev, - "ignoring host bridge windows from ACPI; " - "boot with \"pci=use_crs\" to use them\n"); info.bridge = device; info.bus = bus; diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index d2552c68e94d..3736176acaab 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -520,6 +520,9 @@ char * __devinit pcibios_setup(char *str) } else if (!strcmp(str, "use_crs")) { pci_probe |= PCI_USE__CRS; return NULL; + } else if (!strcmp(str, "nocrs")) { + pci_probe |= PCI_ROOT_NO_CRS; + return NULL; } else if (!strcmp(str, "earlydump")) { pci_early_dump_regs = 1; return NULL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 9cd8bedb1e5a..d724736d56c8 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -566,6 +566,7 @@ static int __init acpi_pci_root_init(void) if (acpi_pci_disabled) return 0; + pci_acpi_crs_quirks(); if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0) return -ENODEV; diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index f4906f6568d4..3a4767c01c5f 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -104,6 +104,7 @@ int acpi_pci_bind_root(struct acpi_device *device); struct pci_bus *pci_acpi_scan_root(struct acpi_device *device, int domain, int bus); +void pci_acpi_crs_quirks(void); /* -------------------------------------------------------------------------- Processor From cbbc0de700e61d0cdc854d435dbc2ef148de0e00 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Feb 2010 00:52:08 +0100 Subject: [PATCH 47/48] ACPI: Use GPE reference counting to support shared GPEs To fix a bug and address the reviewers' comments regarding the ACPI GPE refcounting patch, do the following additional changes: o Remove the second argument of acpi_ev_enable_gpe(), 'write_to_hardware', because it is not necessary any more. o Add the "bad parameter" test against 'type' in acpi_enable_gpe() and acpi_disable_gpe(). o Make acpi_enable_gpe() only check 'status' for runtime GPEs if acpi_ev_enable_gpe() was actually called. o Make acpi_disable_gpe() return 'status' returned by acpi_ev_disable_gpe() and fix a bug where ACPI_GPE_TYPE_WAKE and ACPI_GPE_TYPE_RUNTIME were exchanged by mistake. o Add comments explaining why acpi_set_gpe() is used by the ACPI EC driver. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/acpica/acevents.h | 4 +--- drivers/acpi/acpica/evgpe.c | 10 +++------- drivers/acpi/acpica/evxfevnt.c | 24 +++++++++++++++--------- drivers/acpi/ec.c | 14 +++++++++++--- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 197aa4f39640..4ced54f7a5d9 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -78,9 +78,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node, acpi_status acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info); -acpi_status -acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, - u8 write_to_hardware); +acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 30ca3a30ef00..0b453467a5a0 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -98,8 +98,6 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) * FUNCTION: acpi_ev_enable_gpe * * PARAMETERS: gpe_event_info - GPE to enable - * write_to_hardware - Enable now, or just mark data structs - * (WAKE GPEs should be deferred) * * RETURN: Status * @@ -107,9 +105,7 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) * ******************************************************************************/ -acpi_status -acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, - u8 write_to_hardware) +acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) { acpi_status status; @@ -123,7 +119,7 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, /* Mark wake-enabled or HW enable, or both */ - if (gpe_event_info->runtime_count && write_to_hardware) { + if (gpe_event_info->runtime_count) { /* Clear the GPE (of stale events), then enable it */ status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) @@ -400,7 +396,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) /* Set the GPE flags for return to enabled state */ - (void)acpi_ev_enable_gpe(gpe_event_info, FALSE); + (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); /* * Take a snapshot of the GPE info for this level - we copy the info to diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 1aea1a734159..124c157215bf 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -235,7 +235,7 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) switch (action) { case ACPI_GPE_ENABLE: - status = acpi_ev_enable_gpe(gpe_event_info, TRUE); + status = acpi_ev_enable_gpe(gpe_event_info); break; case ACPI_GPE_DISABLE: @@ -276,6 +276,9 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) ACPI_FUNCTION_TRACE(acpi_enable_gpe); + if (type & ~ACPI_GPE_TYPE_WAKE_RUN) + return_ACPI_STATUS(AE_BAD_PARAMETER); + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -287,11 +290,11 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) } if (type & ACPI_GPE_TYPE_RUNTIME) { - if (++gpe_event_info->runtime_count == 1) - status = acpi_ev_enable_gpe(gpe_event_info, TRUE); - - if (ACPI_FAILURE(status)) - gpe_event_info->runtime_count--; + if (++gpe_event_info->runtime_count == 1) { + status = acpi_ev_enable_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) + gpe_event_info->runtime_count--; + } } if (type & ACPI_GPE_TYPE_WAKE) { @@ -335,6 +338,9 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) ACPI_FUNCTION_TRACE(acpi_disable_gpe); + if (type & ~ACPI_GPE_TYPE_WAKE_RUN) + return_ACPI_STATUS(AE_BAD_PARAMETER); + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -344,12 +350,12 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type) goto unlock_and_exit; } - if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->runtime_count) { + if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->runtime_count) { if (--gpe_event_info->runtime_count == 0) - acpi_ev_disable_gpe(gpe_event_info); + status = acpi_ev_disable_gpe(gpe_event_info); } - if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->wakeup_count) { + if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) { /* * Wake-up GPEs are not enabled after leaving system sleep * states, so we don't need to disable them here. diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 0fa65a8210da..27e0b92b2e39 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -307,6 +307,10 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) pr_debug(PREFIX "transaction start\n"); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { + /* + * It has to be disabled at the hardware level regardless of the + * GPE reference counting, so that it doesn't trigger. + */ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); } @@ -316,7 +320,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) ec_check_sci_sync(ec, acpi_ec_read_status(ec)); if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { msleep(1); - /* it is safe to enable GPE outside of transaction */ + /* + * It is safe to enable the GPE outside of the transaction. Use + * acpi_set_gpe() for that, since we used it to disable the GPE + * above. + */ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { pr_info(PREFIX "GPE storm detected, " @@ -1059,7 +1067,7 @@ int __init acpi_ec_ecdt_probe(void) static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) { struct acpi_ec *ec = acpi_driver_data(device); - /* Stop using GPE */ + /* Stop using the GPE, but keep it reference counted. */ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); return 0; } @@ -1067,7 +1075,7 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) static int acpi_ec_resume(struct acpi_device *device) { struct acpi_ec *ec = acpi_driver_data(device); - /* Enable use of GPE back */ + /* Enable the GPE again, but don't reference count it once more. */ acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); return 0; } From bb8d41330ce27edb91adb6922d3f8e1a8923f727 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Feb 2010 16:42:11 +0100 Subject: [PATCH 48/48] x86/PCI: Prevent mmconfig memory corruption commit ff097ddd4 (x86/PCI: MMCONFIG: manage pci_mmcfg_region as a list, not a table) introduced a nasty memory corruption when pci_mmcfg_list is empty. pci_mmcfg_check_end_bus_number() dereferences pci_mmcfg_list.prev even when the list is empty. The following write hits some variable near to pci_mmcfg_list. Further down a similar problem exists, where cfg->list.next is dereferenced unconditionally and a comparison with some variable near to pci_mmcfg_list happens. Add a check for the last element into the for_each_entry() loop and remove all the other crappy logic which is just a leftover of the old array based code which was replaced by the list conversion. Reported-by: Ingo Molnar Signed-off-by: Thomas Gleixner Cc: Bjorn Helgaas Cc: Yinghai Lu Cc: stable@kernel.org Signed-off-by: Jesse Barnes --- arch/x86/pci/mmconfig-shared.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index b19d1e54201e..8f3f9a50b1e0 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -303,22 +303,17 @@ static void __init pci_mmcfg_check_end_bus_number(void) { struct pci_mmcfg_region *cfg, *cfgx; - /* last one*/ - cfg = list_entry(pci_mmcfg_list.prev, typeof(*cfg), list); - if (cfg) - if (cfg->end_bus < cfg->start_bus) - cfg->end_bus = 255; - - if (list_is_singular(&pci_mmcfg_list)) - return; - - /* don't overlap please */ + /* Fixup overlaps */ list_for_each_entry(cfg, &pci_mmcfg_list, list) { if (cfg->end_bus < cfg->start_bus) cfg->end_bus = 255; + /* Don't access the list head ! */ + if (cfg->list.next == &pci_mmcfg_list) + break; + cfgx = list_entry(cfg->list.next, typeof(*cfg), list); - if (cfg != cfgx && cfg->end_bus >= cfgx->start_bus) + if (cfg->end_bus >= cfgx->start_bus) cfg->end_bus = cfgx->start_bus - 1; } }