diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2008-07-11 14:36:25 +0100 |
commit | a8931ef380c92d121ae74ecfb03b2d63f72eea6f (patch) | |
tree | 980fb6b019e11e6cb1ece55b7faff184721a8053 /drivers/pci | |
parent | 90574d0a4d4b73308ae54a2a57a4f3f1fa98e984 (diff) | |
parent | e5a5816f7875207cb0a0a7032e39a4686c5e10a4 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/access.c | 14 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 17 | ||||
-rw-r--r-- | drivers/pci/hotplug/pci_hotplug_core.c | 7 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 11 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 6 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 36 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 129 | ||||
-rw-r--r-- | drivers/pci/hotplug/rpadlpar_sysfs.c | 8 | ||||
-rw-r--r-- | drivers/pci/hotplug/shpchp_core.c | 4 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 6 | ||||
-rw-r--r-- | drivers/pci/pci-acpi.c | 109 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 89 | ||||
-rw-r--r-- | drivers/pci/pci.h | 2 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 20 | ||||
-rw-r--r-- | drivers/pci/probe.c | 33 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 47 |
17 files changed, 393 insertions, 147 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ec8f7002b09..39bb96b413e 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, int ret; int begin, end, i; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || - size > PCI_VPD_PCI22_SIZE - pos) + if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) return -EINVAL; if (size == 0) return 0; @@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, u32 val; int ret; - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 || - size > PCI_VPD_PCI22_SIZE - pos || size < 4) + if (pos < 0 || pos > vpd->base.len || pos & 3 || + size > vpd->base.len - pos || size < 4) return -EINVAL; val = (u8) *buf++; @@ -255,11 +254,6 @@ out: return 4; } -static int pci_vpd_pci22_get_size(struct pci_dev *dev) -{ - return PCI_VPD_PCI22_SIZE; -} - static void pci_vpd_pci22_release(struct pci_dev *dev) { kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); @@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) static struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, - .get_size = pci_vpd_pci22_get_size, .release = pci_vpd_pci22_release, }; @@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) if (!vpd) return -ENOMEM; + vpd->base.len = PCI_VPD_PCI22_SIZE; vpd->base.ops = &pci_vpd_pci22_ops; spin_lock_init(&vpd->lock); vpd->cap = cap; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d469f..91156f85a92 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); - if (!(bridge = acpiphp_handle_to_bridge(handle))) - return AE_OK; - cleanup_bridge(bridge); + bridge = acpiphp_handle_to_bridge(handle); + if (bridge) + cleanup_bridge(bridge); + return AE_OK; } @@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, cleanup_p2p_bridge, NULL, NULL); + /* + * On root bridges with hotplug slots directly underneath (ie, + * no p2p bridge inbetween), we call cleanup_bridge(). + * + * The else clause cleans up root bridges that either had no + * hotplug slots at all, or had a p2p bridge underneath. + */ bridge = acpiphp_handle_to_bridge(handle); if (bridge) cleanup_bridge(bridge); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_bridge); } static struct pci_dev * get_apic_pci_info(acpi_handle handle) diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 925ba16355c..a11021e8ce3 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -619,6 +619,7 @@ static struct hotplug_slot *get_slot_from_name (const char *name) int pci_hp_register (struct hotplug_slot *slot) { int result; + struct hotplug_slot *tmp; if (slot == NULL) return -ENODEV; @@ -630,7 +631,11 @@ int pci_hp_register (struct hotplug_slot *slot) return -EINVAL; } - /* this can fail if we have already registered a slot with the same name */ + /* Check if we have already registered a slot with the same name. */ + tmp = get_slot_from_name(slot->name); + if (tmp) + return -EEXIST; + slot->kobj.kset = pci_hotplug_slots_kset; result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL, "%s", slot->name); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 8264a768043..79c9ddaad3f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -97,6 +97,7 @@ struct controller { u8 cap_base; struct timer_list poll_timer; volatile int cmd_busy; + unsigned int no_cmd_complete:1; }; #define INT_BUTTON_IGNORE 0 @@ -135,6 +136,7 @@ struct controller { #define PWR_LED_PRSN 0x00000010 #define HP_SUPR_RM_SUP 0x00000020 #define EMI_PRSN 0x00020000 +#define NO_CMD_CMPL_SUP 0x00040000 #define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & ATTN_BUTTN_PRSN) #define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PWR_CTRL_PRSN) @@ -143,13 +145,14 @@ struct controller { #define PWR_LED(ctrl) ((ctrl)->slot_cap & PWR_LED_PRSN) #define HP_SUPR_RM(ctrl) ((ctrl)->slot_cap & HP_SUPR_RM_SUP) #define EMI(ctrl) ((ctrl)->slot_cap & EMI_PRSN) +#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & NO_CMD_CMPL_SUP) extern int pciehp_sysfs_enable_slot(struct slot *slot); extern int pciehp_sysfs_disable_slot(struct slot *slot); -extern u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl); -extern u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl); -extern u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl); -extern u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl); +extern u8 pciehp_handle_attention_button(struct slot *p_slot); + extern u8 pciehp_handle_switch_change(struct slot *p_slot); +extern u8 pciehp_handle_presence_change(struct slot *p_slot); +extern u8 pciehp_handle_power_fault(struct slot *p_slot); extern int pciehp_configure_device(struct slot *p_slot); extern int pciehp_unconfigure_device(struct slot *p_slot); extern void pciehp_queue_pushbutton_work(struct work_struct *work); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 43d8ddb2d67..48a2ed37891 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -254,7 +254,11 @@ static int init_slots(struct controller *ctrl) slot->hp_slot, slot->number, ctrl->slot_device_offset); retval = pci_hp_register(hotplug_slot); if (retval) { - err ("pci_hp_register failed with error %d\n", retval); + err("pci_hp_register failed with error %d\n", retval); + if (retval == -EEXIST) + err("Failed to register slot because of name " + "collision. Try \'pciehp_slot_with_bus\' " + "module option.\n"); goto error_info; } /* create additional sysfs entries */ diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 0a7aa628e95..96a5d55a498 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -55,16 +55,13 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) return 0; } -u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl) +u8 pciehp_handle_attention_button(struct slot *p_slot) { - struct slot *p_slot; u32 event_type; /* Attention Button Change */ dbg("pciehp: Attention button interrupt received.\n"); - p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - /* * Button pressed - See if need to TAKE ACTION!!! */ @@ -76,18 +73,15 @@ u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl) return 0; } -u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) +u8 pciehp_handle_switch_change(struct slot *p_slot) { - struct slot *p_slot; u8 getstatus; u32 event_type; /* Switch Change */ dbg("pciehp: Switch interrupt received.\n"); - p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); - if (getstatus) { /* * Switch opened @@ -107,17 +101,14 @@ u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl) return 1; } -u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) +u8 pciehp_handle_presence_change(struct slot *p_slot) { - struct slot *p_slot; u32 event_type; u8 presence_save; /* Presence Change */ dbg("pciehp: Presence/Notify input change.\n"); - p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - /* Switch is open, assume a presence change * Save the presence state */ @@ -141,16 +132,13 @@ u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl) return 1; } -u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl) +u8 pciehp_handle_power_fault(struct slot *p_slot) { - struct slot *p_slot; u32 event_type; /* power fault */ dbg("pciehp: Power fault interrupt received.\n"); - p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { /* * power fault Cleared @@ -163,7 +151,7 @@ u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl) */ info("Power fault on Slot(%s)\n", p_slot->name); event_type = INT_POWER_FAULT; - info("power fault bit %x set\n", hp_slot); + info("power fault bit %x set\n", 0); } queue_interrupt_event(p_slot, event_type); @@ -186,6 +174,13 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) } } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); + if (PWR_LED(ctrl)) pslot->hpc_ops->green_led_off(pslot); @@ -289,6 +284,13 @@ static int remove_board(struct slot *p_slot) } } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); + if (PWR_LED(ctrl)) /* turn off Green LED */ p_slot->hpc_ops->green_led_off(p_slot); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 891f81a0400..79f10496316 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -247,14 +247,38 @@ static inline void pciehp_free_irq(struct controller *ctrl) free_irq(ctrl->pci_dev->irq, ctrl); } -static inline int pcie_wait_cmd(struct controller *ctrl) +static inline int pcie_poll_cmd(struct controller *ctrl) +{ + u16 slot_status; + int timeout = 1000; + + if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) + if (slot_status & CMD_COMPLETED) + goto completed; + for (timeout = 1000; timeout > 0; timeout -= 100) { + msleep(100); + if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) + if (slot_status & CMD_COMPLETED) + goto completed; + } + return 0; /* timeout */ + +completed: + pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); + return timeout; +} + +static inline int pcie_wait_cmd(struct controller *ctrl, int poll) { int retval = 0; unsigned int msecs = pciehp_poll_mode ? 2500 : 1000; unsigned long timeout = msecs_to_jiffies(msecs); int rc; - rc = wait_event_interruptible_timeout(ctrl->queue, + if (poll) + rc = pcie_poll_cmd(ctrl); + else + rc = wait_event_interruptible_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); if (!rc) dbg("Command not completed in 1000 msec\n"); @@ -286,12 +310,28 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) goto out; } - if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { - /* After 1 sec and CMD_COMPLETED still not set, just - proceed forward to issue the next command according - to spec. Just print out the error message */ - dbg("%s: CMD_COMPLETED not clear after 1 sec.\n", - __func__); + if (slot_status & CMD_COMPLETED) { + if (!ctrl->no_cmd_complete) { + /* + * After 1 sec and CMD_COMPLETED still not set, just + * proceed forward to issue the next command according + * to spec. Just print out the error message. + */ + dbg("%s: CMD_COMPLETED not clear after 1 sec.\n", + __func__); + } else if (!NO_CMD_CMPL(ctrl)) { + /* + * This controller semms to notify of command completed + * event even though it supports none of power + * controller, attention led, power led and EMI. + */ + dbg("%s: Unexpected CMD_COMPLETED. Need to wait for " + "command completed event.\n", __func__); + ctrl->no_cmd_complete = 0; + } else { + dbg("%s: Unexpected CMD_COMPLETED. Maybe the " + "controller is broken.\n", __func__); + } } retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); @@ -315,8 +355,18 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) /* * Wait for command completion. */ - if (!retval) - retval = pcie_wait_cmd(ctrl); + if (!retval && !ctrl->no_cmd_complete) { + int poll = 0; + /* + * if hotplug interrupt is not enabled or command + * completed interrupt is not enabled, we need to poll + * command completed event. + */ + if (!(slot_ctrl & HP_INTR_ENABLE) || + !(slot_ctrl & CMD_CMPL_INTR_ENABLE)) + poll = 1; + retval = pcie_wait_cmd(ctrl, poll); + } out: mutex_unlock(&ctrl->ctrl_lock); return retval; @@ -704,13 +754,6 @@ static int hpc_power_off_slot(struct slot * slot) } dbg("%s: SLOTCTRL %x write cmd %x\n", __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); - - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); out: if (changed) pcie_unmask_bad_dllp(ctrl); @@ -722,6 +765,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; u16 detected, intr_loc; + struct slot *p_slot; /* * In order to guarantee that all interrupt events are @@ -756,21 +800,38 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) wake_up_interruptible(&ctrl->queue); } + if (!(intr_loc & ~CMD_COMPLETED)) + return IRQ_HANDLED; + + /* + * Return without handling events if this handler routine is + * called before controller initialization is done. This may + * happen if hotplug event or another interrupt that shares + * the IRQ with pciehp arrives before slot initialization is + * done after interrupt handler is registered. + * + * FIXME - Need more structural fixes. We need to be ready to + * handle the event before installing interrupt handler. + */ + p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); + if (!p_slot || !p_slot->hpc_ops) + return IRQ_HANDLED; + /* Check MRL Sensor Changed */ if (intr_loc & MRL_SENS_CHANGED) - pciehp_handle_switch_change(0, ctrl); + pciehp_handle_switch_change(p_slot); /* Check Attention Button Pressed */ if (intr_loc & ATTN_BUTTN_PRESSED) - pciehp_handle_attention_button(0, ctrl); + pciehp_handle_attention_button(p_slot); /* Check Presence Detect Changed */ if (intr_loc & PRSN_DETECT_CHANGED) - pciehp_handle_presence_change(0, ctrl); + pciehp_handle_presence_change(p_slot); /* Check Power Fault Detected */ if (intr_loc & PWR_FAULT_DETECTED) - pciehp_handle_power_fault(0, ctrl); + pciehp_handle_power_fault(p_slot); return IRQ_HANDLED; } @@ -1028,6 +1089,12 @@ static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) static int pcie_init_hardware_part1(struct controller *ctrl, struct pcie_device *dev) { + /* Clear all remaining event bits in Slot Status register */ + if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) { + err("%s: Cannot write to SLOTSTATUS register\n", __func__); + return -1; + } + /* Mask Hot-plug Interrupt Enable */ if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) { err("%s: Cannot mask hotplug interrupt enable\n", __func__); @@ -1040,16 +1107,6 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) { u16 cmd, mask; - /* - * We need to clear all events before enabling hotplug interrupt - * notification mechanism in order for hotplug controler to - * generate interrupts. - */ - if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); - return -1; - } - cmd = PRSN_DETECT_ENABLE; if (ATTN_BUTTN(ctrl)) cmd |= ATTN_BUTTN_ENABLE; @@ -1116,6 +1173,7 @@ static inline void dbg_ctrl(struct controller *ctrl) dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no"); dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no"); dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no"); + dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes"); pciehp_readw(ctrl, SLOTSTATUS, ®16); dbg("Slot Status : 0x%04x\n", reg16); pciehp_readw(ctrl, SLOTSTATUS, ®16); @@ -1147,6 +1205,15 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); dbg_ctrl(ctrl); + /* + * Controller doesn't notify of command completion if the "No + * Command Completed Support" bit is set in Slot Capability + * register or the controller supports none of power + * controller, attention led, power led and EMI. + */ + if (NO_CMD_CMPL(ctrl) || + !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) + ctrl->no_cmd_complete = 1; info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c index e32148a8fa1..779c5db71be 100644 --- a/drivers/pci/hotplug/rpadlpar_sysfs.c +++ b/drivers/pci/hotplug/rpadlpar_sysfs.c @@ -18,8 +18,12 @@ #include "rpadlpar.h" #define DLPAR_KOBJ_NAME "control" -#define ADD_SLOT_ATTR_NAME "add_slot" -#define REMOVE_SLOT_ATTR_NAME "remove_slot" + +/* Those two have no quotes because they are passed to __ATTR() which + * stringifies the argument (yuck !) + */ +#define ADD_SLOT_ATTR_NAME add_slot +#define REMOVE_SLOT_ATTR_NAME remove_slot #define MAX_DRC_NAME_LEN 64 diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 1648076600f..97848654652 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -162,6 +162,10 @@ static int init_slots(struct controller *ctrl) retval = pci_hp_register(slot->hotplug_slot); if (retval) { err("pci_hp_register failed with error %d\n", retval); + if (retval == -EEXIST) + err("Failed to register slot because of name " + "collision. Try \'shpchp_slot_with_bus\' " + "module option.\n"); goto error_info; } diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 1fd8bb76570..66c0fd21894 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -49,7 +49,7 @@ #define DEFAULT_DOMAIN_ADDRESS_WIDTH 48 -#define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */ +#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */ #define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) @@ -490,12 +490,12 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu) #define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \ {\ - unsigned long start_time = jiffies;\ + cycles_t start_time = get_cycles();\ while (1) {\ sts = op (iommu->reg + offset);\ if (cond)\ break;\ - if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT))\ + if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\ panic("DMAR hardware is malfunctioning\n");\ cpu_relax();\ }\ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 72f7476930c..9d6fc8e6285 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -19,8 +19,31 @@ #include <linux/pci-acpi.h> #include "pci.h" -static u32 ctrlset_buf[3] = {0, 0, 0}; -static u32 global_ctrlsets = 0; +struct acpi_osc_data { + acpi_handle handle; + u32 ctrlset_buf[3]; + u32 global_ctrlsets; + struct list_head sibiling; +}; +static LIST_HEAD(acpi_osc_data_list); + +static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle) +{ + struct acpi_osc_data *data; + + list_for_each_entry(data, &acpi_osc_data_list, sibiling) { + if (data->handle == handle) + return data; + } + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + INIT_LIST_HEAD(&data->sibiling); + data->handle = handle; + list_add_tail(&data->sibiling, &acpi_osc_data_list); + return data; +} + static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; static acpi_status @@ -37,8 +60,27 @@ acpi_query_osc ( union acpi_object *out_obj; u32 osc_dw0; acpi_status *ret_status = (acpi_status *)retval; + struct acpi_osc_data *osc_data; + u32 flags = (unsigned long)context, temp; + acpi_handle tmp; + + status = acpi_get_handle(handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return status; + + osc_data = acpi_get_osc_data(handle); + if (!osc_data) { + printk(KERN_ERR "acpi osc data array is full\n"); + return AE_ERROR; + } + + osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS); + + /* do _OSC query for all possible controls */ + temp = osc_data->ctrlset_buf[OSC_CONTROL_TYPE]; + osc_data->ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; + osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; - /* Setting up input parameters */ input.count = 4; input.pointer = in_params; @@ -51,13 +93,11 @@ acpi_query_osc ( in_params[2].integer.value = 3; in_params[3].type = ACPI_TYPE_BUFFER; in_params[3].buffer.length = 12; - in_params[3].buffer.pointer = (u8 *)context; + in_params[3].buffer.pointer = (u8 *)osc_data->ctrlset_buf; status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE (status)) { - *ret_status = status; - return status; - } + if (ACPI_FAILURE(status)) + goto out_nofree; out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_BUFFER) { @@ -76,7 +116,8 @@ acpi_query_osc ( printk(KERN_DEBUG "_OSC invalid revision\n"); if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { /* Update Global Control Set */ - global_ctrlsets = *((u32 *)(out_obj->buffer.pointer+8)); + osc_data->global_ctrlsets = + *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; goto query_osc_out; } @@ -85,12 +126,21 @@ acpi_query_osc ( } /* Update Global Control Set */ - global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8)); + osc_data->global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; query_osc_out: kfree(output.pointer); +out_nofree: *ret_status = status; + + osc_data->ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE; + osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = temp; + if (ACPI_FAILURE(status)) { + /* no osc support at all */ + osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] = 0; + } + return status; } @@ -165,28 +215,15 @@ run_osc_out: **/ acpi_status __pci_osc_support_set(u32 flags, const char *hid) { - u32 temp; - acpi_status retval; + acpi_status retval = AE_NOT_FOUND; if (!(flags & OSC_SUPPORT_MASKS)) { return AE_TYPE; } - ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS); - - /* do _OSC query for all possible controls */ - temp = ctrlset_buf[OSC_CONTROL_TYPE]; - ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; - ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; acpi_get_devices(hid, acpi_query_osc, - ctrlset_buf, + (void *)(unsigned long)flags, (void **) &retval ); - ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE; - ctrlset_buf[OSC_CONTROL_TYPE] = temp; - if (ACPI_FAILURE(retval)) { - /* no osc support at all */ - ctrlset_buf[OSC_SUPPORT_TYPE] = 0; - } return AE_OK; } @@ -201,19 +238,31 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) { acpi_status status; u32 ctrlset; + acpi_handle tmp; + struct acpi_osc_data *osc_data; + + status = acpi_get_handle(handle, "_OSC", &tmp); + if (ACPI_FAILURE(status)) + return status; + + osc_data = acpi_get_osc_data(handle); + if (!osc_data) { + printk(KERN_ERR "acpi osc data array is full\n"); + return AE_ERROR; + } ctrlset = (flags & OSC_CONTROL_MASKS); if (!ctrlset) { return AE_TYPE; } - if (ctrlset_buf[OSC_SUPPORT_TYPE] && - ((global_ctrlsets & ctrlset) != ctrlset)) { + if (osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] && + ((osc_data->global_ctrlsets & ctrlset) != ctrlset)) { return AE_SUPPORT; } - ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset; - status = acpi_run_osc(handle, ctrlset_buf); + osc_data->ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset; + status = acpi_run_osc(handle, osc_data->ctrlset_buf); if (ACPI_FAILURE (status)) { - ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset; + osc_data->ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset; } return status; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 72cf61ed8f9..e1637bd82b8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -181,7 +181,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, any need to change it. */ struct mempolicy *oldpol; cpumask_t oldmask = current->cpus_allowed; - int node = pcibus_to_node(dev->bus); + int node = dev_to_node(&dev->dev); if (node >= 0) { node_to_cpumask_ptr(nodecpumask, node); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 271d41cc05a..9c718583a23 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -489,13 +489,13 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, * @kobj: kobject for mapping * @attr: struct bin_attribute for the file being mapped * @vma: struct vm_area_struct passed into the mmap + * @write_combine: 1 for write_combine mapping * * Use the regular PCI mapping routines to map a PCI resource into userspace. - * FIXME: write combining? maybe automatic for prefetchable regions? */ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, - struct vm_area_struct *vma) + struct vm_area_struct *vma, int write_combine) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); @@ -518,7 +518,21 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; - return pci_mmap_page_range(pdev, vma, mmap_type, 0); + return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); +} + +static int +pci_mmap_resource_uc(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 0); +} + +static int +pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return pci_mmap_resource(kobj, attr, vma, 1); } /** @@ -541,9 +555,46 @@ pci_remove_resource_files(struct pci_dev *pdev) sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); kfree(res_attr); } + + res_attr = pdev->res_attr_wc[i]; + if (res_attr) { + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); + } } } +static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) +{ + /* allocate attribute structure, piggyback attribute name */ + int name_len = write_combine ? 13 : 10; + struct bin_attribute *res_attr; + int retval; + + res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); + if (res_attr) { + char *res_attr_name = (char *)(res_attr + 1); + + if (write_combine) { + pdev->res_attr_wc[num] = res_attr; + sprintf(res_attr_name, "resource%d_wc", num); + res_attr->mmap = pci_mmap_resource_wc; + } else { + pdev->res_attr[num] = res_attr; + sprintf(res_attr_name, "resource%d", num); + res_attr->mmap = pci_mmap_resource_uc; + } + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->size = pci_resource_len(pdev, num); + res_attr->private = &pdev->resource[num]; + retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + } else + retval = -ENOMEM; + + return retval; +} + /** * pci_create_resource_files - create resource files in sysfs for @dev * @dev: dev in question @@ -557,31 +608,19 @@ static int pci_create_resource_files(struct pci_dev *pdev) /* Expose the PCI resources from this device as files */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { - struct bin_attribute *res_attr; /* skip empty resources */ if (!pci_resource_len(pdev, i)) continue; - /* allocate attribute structure, piggyback attribute name */ - res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); - if (res_attr) { - char *res_attr_name = (char *)(res_attr + 1); - - pdev->res_attr[i] = res_attr; - sprintf(res_attr_name, "resource%d", i); - res_attr->attr.name = res_attr_name; - res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->size = pci_resource_len(pdev, i); - res_attr->mmap = pci_mmap_resource; - res_attr->private = &pdev->resource[i]; - retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } else { - return -ENOMEM; + retval = pci_create_attr(pdev, i, 0); + /* for prefetchable resources, create a WC mappable file */ + if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH) + retval = pci_create_attr(pdev, i, 1); + + if (retval) { + pci_remove_resource_files(pdev); + return retval; } } return 0; @@ -697,9 +736,9 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) attr = kzalloc(sizeof(*attr), GFP_ATOMIC); if (attr) { pdev->vpd->attr = attr; - attr->size = pdev->vpd->ops->get_size(pdev); + attr->size = pdev->vpd->len; attr->attr.name = "vpd"; - attr->attr.mode = S_IRUGO | S_IWUSR; + attr->attr.mode = S_IRUSR | S_IWUSR; attr->read = pci_read_vpd; attr->write = pci_write_vpd; retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0a497c1b422..00408c97e5f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { int (*read)(struct pci_dev *dev, int pos, int size, char *buf); int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); - int (*get_size)(struct pci_dev *dev); void (*release)(struct pci_dev *dev); }; struct pci_vpd { + unsigned int len; struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 61fedb2448b..f82495583e6 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -506,6 +506,23 @@ static void free_link_state(struct pci_dev *pdev) pdev->link_state = NULL; } +static int pcie_aspm_sanity_check(struct pci_dev *pdev) +{ + struct pci_dev *child_dev; + int child_pos; + + /* + * Some functions in a slot might not all be PCIE functions, very + * strange. Disable ASPM for the whole slot + */ + list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { + child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); + if (!child_pos) + return -EINVAL; + } + return 0; +} + /* * pcie_aspm_init_link_state: Initiate PCI express link state. * It is called after the pcie and its children devices are scaned. @@ -526,6 +543,9 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) if (list_empty(&pdev->subordinate->devices)) goto out; + if (pcie_aspm_sanity_check(pdev)) + goto out; + mutex_lock(&aspm_lock); link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4a55bf38095..3706ce7972d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -842,13 +842,25 @@ static void set_pcie_port_type(struct pci_dev *pdev) * reading the dword at 0x100 which must either be 0 or a valid extended * capability header. */ -int pci_cfg_space_size_ext(struct pci_dev *dev, unsigned check_exp_pcix) +int pci_cfg_space_size_ext(struct pci_dev *dev) { - int pos; u32 status; - if (!check_exp_pcix) - goto skip; + if (pci_read_config_dword(dev, 256, &status) != PCIBIOS_SUCCESSFUL) + goto fail; + if (status == 0xffffffff) + goto fail; + + return PCI_CFG_SPACE_EXP_SIZE; + + fail: + return PCI_CFG_SPACE_SIZE; +} + +int pci_cfg_space_size(struct pci_dev *dev) +{ + int pos; + u32 status; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (!pos) { @@ -861,23 +873,12 @@ int pci_cfg_space_size_ext(struct pci_dev *dev, unsigned check_exp_pcix) goto fail; } - skip: - if (pci_read_config_dword(dev, 256, &status) != PCIBIOS_SUCCESSFUL) - goto fail; - if (status == 0xffffffff) - goto fail; - - return PCI_CFG_SPACE_EXP_SIZE; + return pci_cfg_space_size_ext(dev); fail: return PCI_CFG_SPACE_SIZE; } -int pci_cfg_space_size(struct pci_dev *dev) -{ - return pci_cfg_space_size_ext(dev, 1); -} - static void pci_release_bus_bridge_dev(struct device *dev) { kfree(dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index afd914ebe21..338a3f94b4d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1503,8 +1503,7 @@ static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_f (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { #ifdef DEBUG dev_dbg(&dev->dev, "calling "); - print_fn_descriptor_symbol("%s()\n", - (unsigned long) f->hook); + print_fn_descriptor_symbol("%s\n", f->hook); #endif f->hook(dev); } @@ -1671,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); +/* + * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the + * VPD end tag will hang the device. This problem was initially + * observed when a vpd entry was created in sysfs + * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry + * will dump 32k of data. Reading a full 32k will cause an access + * beyond the VPD end tag causing the device to hang. Once the device + * is hung, the bnx2 driver will not be able to reset the device. + * We believe that it is legal to read beyond the end tag and + * therefore the solution is to limit the read/write length. + */ +static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +{ + /* Only disable the VPD capability for 5706, 5708, and 5709 rev. A */ + if ((dev->device == PCI_DEVICE_ID_NX2_5706) || + (dev->device == PCI_DEVICE_ID_NX2_5708) || + ((dev->device == PCI_DEVICE_ID_NX2_5709) && + (dev->revision & 0xf0) == 0x0)) { + if (dev->vpd) + dev->vpd->len = 0x80; + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); + #ifdef CONFIG_PCI_MSI /* Some chipsets do not support MSI. We cannot easily rely on setting * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually @@ -1686,6 +1727,7 @@ static void __init quirk_disable_all_msi(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi); /* Disable MSI on chipsets that are known to not support it */ @@ -1826,6 +1868,7 @@ static void __devinit nv_msi_ht_cap_quirk(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk); static void __devinit quirk_msi_intx_disable_bug(struct pci_dev *dev) { |