diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-16 07:49:54 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-16 07:49:54 -0700 |
commit | 4406c56d0a4da7a37b9180abeaece6cd00bcc874 (patch) | |
tree | 65a85fa73a25d24cbed6d163fdcf8df1b934a0be /drivers/pci/pcie | |
parent | 6b7b352f2102e21f9d8f38e932f01d9c5705c073 (diff) | |
parent | 5e3573db2bd5db6925159279d99576a4635bdb66 (diff) | |
download | linux-4406c56d0a4da7a37b9180abeaece6cd00bcc874.tar.bz2 |
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: (75 commits)
PCI hotplug: clean up acpi_run_hpp()
PCI hotplug: acpiphp: use generic pci_configure_slot()
PCI hotplug: shpchp: use generic pci_configure_slot()
PCI hotplug: pciehp: use generic pci_configure_slot()
PCI hotplug: add pci_configure_slot()
PCI hotplug: clean up acpi_get_hp_params_from_firmware() interface
PCI hotplug: acpiphp: don't cache hotplug_params in acpiphp_bridge
PCI hotplug: acpiphp: remove superfluous _HPP/_HPX evaluation
PCI: Clear saved_state after the state has been restored
PCI PM: Return error codes from pci_pm_resume()
PCI: use dev_printk in quirk messages
PCI / PCIe portdrv: Fix pcie_portdrv_slot_reset()
PCI Hotplug: convert acpi_pci_detect_ejectable() to take an acpi_handle
PCI Hotplug: acpiphp: find bridges the easy way
PCI: pcie portdrv: remove unused variable
PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support
ACPI PM: Replace wakeup.prepared with reference counter
PCI PM: Introduce device flag wakeup_prepared
PCI / ACPI PM: Rework some debug messages
PCI PM: Simplify PCI wake-up code
...
Fixed up conflict in arch/powerpc/kernel/pci_64.c due to OF device tree
scanning having been moved and merged for the 32- and 64-bit cases. The
'needs_freset' initialization added in 6e19314cc ("PCI/powerpc: support
PCIe fundamental reset") is now in arch/powerpc/kernel/pci_of_scan.c.
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/aer/aer_inject.c | 25 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.c | 22 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 34 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_core.c | 107 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_errprint.c | 190 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 495 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 6 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 1 |
8 files changed, 423 insertions, 457 deletions
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index d92ae21a59d8..62d15f652bb6 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -22,11 +22,10 @@ #include <linux/miscdevice.h> #include <linux/pci.h> #include <linux/fs.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "aerdrv.h" -struct aer_error_inj -{ +struct aer_error_inj { u8 bus; u8 dev; u8 fn; @@ -38,8 +37,7 @@ struct aer_error_inj u32 header_log3; }; -struct aer_error -{ +struct aer_error { struct list_head list; unsigned int bus; unsigned int devfn; @@ -55,8 +53,7 @@ struct aer_error u32 source_id; }; -struct pci_bus_ops -{ +struct pci_bus_ops { struct list_head list; struct pci_bus *bus; struct pci_ops *ops; @@ -150,7 +147,7 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where, target = &err->header_log1; break; case PCI_ERR_HEADER_LOG+8: - target = &err->header_log2; + target = &err->header_log2; break; case PCI_ERR_HEADER_LOG+12: target = &err->header_log3; @@ -258,8 +255,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus) bus_ops = NULL; out: spin_unlock_irqrestore(&inject_lock, flags); - if (bus_ops) - kfree(bus_ops); + kfree(bus_ops); return 0; } @@ -401,10 +397,8 @@ static int aer_inject(struct aer_error_inj *einj) else ret = -EINVAL; out_put: - if (err_alloc) - kfree(err_alloc); - if (rperr_alloc) - kfree(rperr_alloc); + kfree(err_alloc); + kfree(rperr_alloc); pci_dev_put(dev); return ret; } @@ -458,8 +452,7 @@ static void __exit aer_inject_exit(void) } spin_lock_irqsave(&inject_lock, flags); - list_for_each_entry_safe(err, err_next, - &pci_bus_ops_list, list) { + list_for_each_entry_safe(err, err_next, &pci_bus_ops_list, list) { list_del(&err->list); kfree(err); } diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 4770f13b3ca1..10c0e62bd5a8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -38,7 +38,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static int __devinit aer_probe (struct pcie_device *dev); +static int __devinit aer_probe(struct pcie_device *dev); static void aer_remove(struct pcie_device *dev); static pci_ers_result_t aer_error_detected(struct pci_dev *dev, enum pci_channel_state error); @@ -47,7 +47,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev); static struct pci_error_handlers aer_error_handlers = { .error_detected = aer_error_detected, - .resume = aer_error_resume, + .resume = aer_error_resume, }; static struct pcie_port_service_driver aerdriver = { @@ -134,12 +134,12 @@ EXPORT_SYMBOL_GPL(aer_irq); * * Invoked when Root Port's AER service is loaded. **/ -static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev) +static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) { struct aer_rpc *rpc; - if (!(rpc = kzalloc(sizeof(struct aer_rpc), - GFP_KERNEL))) + rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); + if (!rpc) return NULL; /* @@ -189,26 +189,28 @@ static void aer_remove(struct pcie_device *dev) * * Invoked when PCI Express bus loads AER service driver. **/ -static int __devinit aer_probe (struct pcie_device *dev) +static int __devinit aer_probe(struct pcie_device *dev) { int status; struct aer_rpc *rpc; struct device *device = &dev->device; /* Init */ - if ((status = aer_init(dev))) + status = aer_init(dev); + if (status) return status; /* Alloc rpc data structure */ - if (!(rpc = aer_alloc_rpc(dev))) { + rpc = aer_alloc_rpc(dev); + if (!rpc) { dev_printk(KERN_DEBUG, device, "alloc rpc failed\n"); aer_remove(dev); return -ENOMEM; } /* Request IRQ ISR */ - if ((status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", - dev))) { + status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev); + if (status) { dev_printk(KERN_DEBUG, device, "request IRQ failed\n"); aer_remove(dev); return status; diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index bbd7428ca2d0..bd833ea3ba49 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -16,12 +16,9 @@ #define AER_NONFATAL 0 #define AER_FATAL 1 #define AER_CORRECTABLE 2 -#define AER_UNCORRECTABLE 4 -#define AER_ERROR_MASK 0x001fffff -#define AER_ERROR(d) (d & AER_ERROR_MASK) /* Root Error Status Register Bits */ -#define ROOT_ERR_STATUS_MASKS 0x0f +#define ROOT_ERR_STATUS_MASKS 0x0f #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ @@ -32,8 +29,6 @@ #define ERR_COR_ID(d) (d & 0xffff) #define ERR_UNCOR_ID(d) (d >> 16) -#define AER_SUCCESS 0 -#define AER_UNSUCCESS 1 #define AER_ERROR_SOURCES_MAX 100 #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ @@ -43,13 +38,6 @@ PCI_ERR_UNC_UNX_COMP| \ PCI_ERR_UNC_MALF_TLP) -/* AER Error Info Flags */ -#define AER_TLP_HEADER_VALID_FLAG 0x00000001 -#define AER_MULTI_ERROR_VALID_FLAG 0x00000002 - -#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1 -#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010 - struct header_log_regs { unsigned int dw0; unsigned int dw1; @@ -61,11 +49,20 @@ struct header_log_regs { struct aer_err_info { struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; int error_dev_num; - u16 id; - int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ - int flags; + + unsigned int id:16; + + unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ + unsigned int __pad1:5; + unsigned int multi_error_valid:1; + + unsigned int first_error:5; + unsigned int __pad2:2; + unsigned int tlp_header_valid:1; + unsigned int status; /* COR/UNCOR Error Status */ - struct header_log_regs tlp; /* TLP Header */ + unsigned int mask; /* COR/UNCOR Error Mask */ + struct header_log_regs tlp; /* TLP Header */ }; struct aer_err_source { @@ -125,6 +122,7 @@ extern void aer_delete_rootport(struct aer_rpc *rpc); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); +extern void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); extern irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI @@ -136,4 +134,4 @@ static inline int aer_osc_setup(struct pcie_device *pciedev) } #endif -#endif //_AERDRV_H_ +#endif /* _AERDRV_H_ */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 3d8872704a58..9f5ccbeb4fa5 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -49,10 +49,11 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE; - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, - reg16); + pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + return 0; } +EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); int pci_disable_pcie_error_reporting(struct pci_dev *dev) { @@ -68,10 +69,11 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev) PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, - reg16); + pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + return 0; } +EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) { @@ -92,6 +94,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) return 0; } +EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); #if 0 int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) @@ -110,7 +113,6 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) } #endif /* 0 */ - static int set_device_error_reporting(struct pci_dev *dev, void *data) { bool enable = *((bool *)data); @@ -164,8 +166,9 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) e_info->dev[e_info->error_dev_num] = dev; e_info->error_dev_num++; return 1; - } else - return 0; + } + + return 0; } @@ -193,7 +196,7 @@ static int find_device_iter(struct pci_dev *dev, void *data) * If there is no multiple error, we stop * or continue based on the id comparing. */ - if (!(e_info->flags & AER_MULTI_ERROR_VALID_FLAG)) + if (!e_info->multi_error_valid) return result; /* @@ -233,24 +236,16 @@ static int find_device_iter(struct pci_dev *dev, void *data) status = 0; mask = 0; if (e_info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, - pos + PCI_ERR_COR_STATUS, - &status); - pci_read_config_dword(dev, - pos + PCI_ERR_COR_MASK, - &mask); - if (status & ERR_CORRECTABLE_ERROR_MASK & ~mask) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); + if (status & ~mask) { add_error_device(e_info, dev); goto added; } } else { - pci_read_config_dword(dev, - pos + PCI_ERR_UNCOR_STATUS, - &status); - pci_read_config_dword(dev, - pos + PCI_ERR_UNCOR_MASK, - &mask); - if (status & ERR_UNCORRECTABLE_ERROR_MASK & ~mask) { + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); + if (status & ~mask) { add_error_device(e_info, dev); goto added; } @@ -259,7 +254,7 @@ static int find_device_iter(struct pci_dev *dev, void *data) return 0; added: - if (e_info->flags & AER_MULTI_ERROR_VALID_FLAG) + if (e_info->multi_error_valid) return 0; else return 1; @@ -411,8 +406,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, pci_cleanup_aer_uncorrect_error_status(dev); dev->error_state = pci_channel_io_normal; } - } - else { + } else { /* * If the error is reported by an end point, we think this * error is related to the upstream link of the end point. @@ -473,7 +467,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) udev = dev; else - udev= dev->bus->self; + udev = dev->bus->self; data.is_downstream = 0; data.aer_driver = NULL; @@ -576,7 +570,7 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev, * * Invoked when an error being detected by Root Port. */ -static void handle_error_source(struct pcie_device * aerdev, +static void handle_error_source(struct pcie_device *aerdev, struct pci_dev *dev, struct aer_err_info *info) { @@ -682,7 +676,7 @@ static void disable_root_aer(struct aer_rpc *rpc) * * Invoked by DPC handler to consume an error. */ -static struct aer_err_source* get_e_source(struct aer_rpc *rpc) +static struct aer_err_source *get_e_source(struct aer_rpc *rpc) { struct aer_err_source *e_source; unsigned long flags; @@ -702,32 +696,50 @@ static struct aer_err_source* get_e_source(struct aer_rpc *rpc) return e_source; } +/** + * get_device_error_info - read error status from dev and store it to info + * @dev: pointer to the device expected to have a error record + * @info: pointer to structure to store the error record + * + * Return 1 on success, 0 on error. + */ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { - int pos; + int pos, temp; + + info->status = 0; + info->tlp_header_valid = 0; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); /* The device might not support AER */ if (!pos) - return AER_SUCCESS; + return 1; if (info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &info->status); - if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) - return AER_UNSUCCESS; + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || info->severity == AER_NONFATAL) { /* Link is still healthy for IO reads */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &info->status); - if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) - return AER_UNSUCCESS; + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) + return 0; + + /* Get First Error Pointer */ + pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); + info->first_error = PCI_ERR_CAP_FEP(temp); if (info->status & AER_LOG_TLP_MASKS) { - info->flags |= AER_TLP_HEADER_VALID_FLAG; + info->tlp_header_valid = 1; pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); pci_read_config_dword(dev, @@ -739,7 +751,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) } } - return AER_SUCCESS; + return 1; } static inline void aer_process_err_devices(struct pcie_device *p_device, @@ -753,14 +765,14 @@ static inline void aer_process_err_devices(struct pcie_device *p_device, e_info->id); } + /* Report all before handle them, not to lost records by reset etc. */ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info) == - AER_SUCCESS) { + if (get_device_error_info(e_info->dev[i], e_info)) aer_print_error(e_info->dev[i], e_info); - handle_error_source(p_device, - e_info->dev[i], - e_info); - } + } + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + handle_error_source(p_device, e_info->dev[i], e_info); } } @@ -806,7 +818,9 @@ static void aer_isr_one_error(struct pcie_device *p_device, if (e_src->status & (PCI_ERR_ROOT_MULTI_COR_RCV | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) - e_info->flags |= AER_MULTI_ERROR_VALID_FLAG; + e_info->multi_error_valid = 1; + + aer_print_port_info(p_device->port, e_info); find_source_device(p_device->port, e_info); aer_process_err_devices(p_device, e_info); @@ -863,10 +877,5 @@ int aer_init(struct pcie_device *dev) if (aer_osc_setup(dev) && !forceload) return -ENXIO; - return AER_SUCCESS; + return 0; } - -EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); -EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); -EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); - diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 0fc29ae80df8..44acde72294f 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -27,69 +27,70 @@ #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 -#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \ - PCI_ERR_UNC_UNSUP) - -#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT - -#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \ - ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0))) +#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) +#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : PCI_ERR_UNC_COMP_ABORT) +#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) #define AER_GET_AGENT(t, e) \ - ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \ - (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \ - (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \ + ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ + (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ + (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ AER_AGENT_RECEIVER) -#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR -#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \ - (PCI_ERR_UNC_DLP| \ - PCI_ERR_COR_BAD_TLP| \ - PCI_ERR_COR_BAD_DLLP| \ - PCI_ERR_COR_REP_ROLL| \ - ((t == AER_CORRECTABLE) ? \ - PCI_ERR_COR_REP_TIMER: 0)) - #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 -#define AER_GET_LAYER_ERROR(t, e) \ - ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \ - AER_PHYSICAL_LAYER_ERROR : \ - (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \ - AER_DATA_LINK_LAYER_ERROR : \ - AER_TRANSACTION_LAYER_ERROR) +#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_RCVR : 0) +#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_BAD_TLP| \ + PCI_ERR_COR_BAD_DLLP| \ + PCI_ERR_COR_REP_ROLL| \ + PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + +#define AER_GET_LAYER_ERROR(t, e) \ + ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ + (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + AER_TRANSACTION_LAYER_ERROR) + +#define AER_PR(info, pdev, fmt, args...) \ + printk("%s%s %s: " fmt, (info->severity == AER_CORRECTABLE) ? \ + KERN_WARNING : KERN_ERR, dev_driver_string(&pdev->dev), \ + dev_name(&pdev->dev), ## args) /* * AER error strings */ -static char* aer_error_severity_string[] = { +static char *aer_error_severity_string[] = { "Uncorrected (Non-Fatal)", "Uncorrected (Fatal)", "Corrected" }; -static char* aer_error_layer[] = { +static char *aer_error_layer[] = { "Physical Layer", "Data Link Layer", "Transaction Layer" }; -static char* aer_correctable_error_string[] = { - "Receiver Error ", /* Bit Position 0 */ +static char *aer_correctable_error_string[] = { + "Receiver Error ", /* Bit Position 0 */ NULL, NULL, NULL, NULL, NULL, - "Bad TLP ", /* Bit Position 6 */ - "Bad DLLP ", /* Bit Position 7 */ - "RELAY_NUM Rollover ", /* Bit Position 8 */ + "Bad TLP ", /* Bit Position 6 */ + "Bad DLLP ", /* Bit Position 7 */ + "RELAY_NUM Rollover ", /* Bit Position 8 */ NULL, NULL, NULL, - "Replay Timer Timeout ", /* Bit Position 12 */ - "Advisory Non-Fatal ", /* Bit Position 13 */ + "Replay Timer Timeout ", /* Bit Position 12 */ + "Advisory Non-Fatal ", /* Bit Position 13 */ NULL, NULL, NULL, @@ -110,7 +111,7 @@ static char* aer_correctable_error_string[] = { NULL, }; -static char* aer_uncorrectable_error_string[] = { +static char *aer_uncorrectable_error_string[] = { NULL, NULL, NULL, @@ -123,10 +124,10 @@ static char* aer_uncorrectable_error_string[] = { NULL, NULL, NULL, - "Poisoned TLP ", /* Bit Position 12 */ + "Poisoned TLP ", /* Bit Position 12 */ "Flow Control Protocol ", /* Bit Position 13 */ - "Completion Timeout ", /* Bit Position 14 */ - "Completer Abort ", /* Bit Position 15 */ + "Completion Timeout ", /* Bit Position 14 */ + "Completer Abort ", /* Bit Position 15 */ "Unexpected Completion ", /* Bit Position 16 */ "Receiver Overflow ", /* Bit Position 17 */ "Malformed TLP ", /* Bit Position 18 */ @@ -145,98 +146,69 @@ static char* aer_uncorrectable_error_string[] = { NULL, }; -static char* aer_agent_string[] = { +static char *aer_agent_string[] = { "Receiver ID", "Requester ID", "Completer ID", "Transmitter ID" }; -static char * aer_get_error_source_name(int severity, - unsigned int status, - char errmsg_buff[]) +static void __aer_print_error(struct aer_err_info *info, struct pci_dev *dev) { - int i; - char * errmsg = NULL; + int i, status; + char *errmsg = NULL; + + status = (info->status & ~info->mask); for (i = 0; i < 32; i++) { if (!(status & (1 << i))) continue; - if (severity == AER_CORRECTABLE) + if (info->severity == AER_CORRECTABLE) errmsg = aer_correctable_error_string[i]; else errmsg = aer_uncorrectable_error_string[i]; - if (!errmsg) { - sprintf(errmsg_buff, "Unknown Error Bit %2d ", i); - errmsg = errmsg_buff; - } - - break; + if (errmsg) + AER_PR(info, dev, " [%2d] %s%s\n", i, errmsg, + info->first_error == i ? " (First)" : ""); + else + AER_PR(info, dev, " [%2d] Unknown Error Bit%s\n", i, + info->first_error == i ? " (First)" : ""); } - - return errmsg; } -static DEFINE_SPINLOCK(logbuf_lock); -static char errmsg_buff[100]; void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { - char * errmsg; - int err_layer, agent; - char * loglevel; - - if (info->severity == AER_CORRECTABLE) - loglevel = KERN_WARNING; - else - loglevel = KERN_ERR; - - printk("%s+------ PCI-Express Device Error ------+\n", loglevel); - printk("%sError Severity\t\t: %s\n", loglevel, - aer_error_severity_string[info->severity]); - - if ( info->status == 0) { - printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel); - printk("%sUnaccessible Received\t: %s\n", loglevel, - info->flags & AER_MULTI_ERROR_VALID_FLAG ? - "Multiple" : "First"); - printk("%sUnregistered Agent ID\t: %04x\n", loglevel, - (dev->bus->number << 8) | dev->devfn); + int id = ((dev->bus->number << 8) | dev->devfn); + + if (info->status == 0) { + AER_PR(info, dev, + "PCIE Bus Error: severity=%s, type=Unaccessible, " + "id=%04x(Unregistered Agent ID)\n", + aer_error_severity_string[info->severity], id); } else { - err_layer = AER_GET_LAYER_ERROR(info->severity, info->status); - printk("%sPCIE Bus Error type\t: %s\n", loglevel, - aer_error_layer[err_layer]); - - spin_lock(&logbuf_lock); - errmsg = aer_get_error_source_name(info->severity, - info->status, - errmsg_buff); - printk("%s%s\t: %s\n", loglevel, errmsg, - info->flags & AER_MULTI_ERROR_VALID_FLAG ? - "Multiple" : "First"); - spin_unlock(&logbuf_lock); + int layer, agent; + layer = AER_GET_LAYER_ERROR(info->severity, info->status); agent = AER_GET_AGENT(info->severity, info->status); - printk("%s%s\t\t: %04x\n", loglevel, - aer_agent_string[agent], - (dev->bus->number << 8) | dev->devfn); - - printk("%sVendorID=%04xh, DeviceID=%04xh," - " Bus=%02xh, Device=%02xh, Function=%02xh\n", - loglevel, - dev->vendor, - dev->device, - dev->bus->number, - PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - - if (info->flags & AER_TLP_HEADER_VALID_FLAG) { + + AER_PR(info, dev, + "PCIE Bus Error: severity=%s, type=%s, id=%04x(%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], id, aer_agent_string[agent]); + + AER_PR(info, dev, + " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, info->status, info->mask); + + __aer_print_error(info, dev); + + if (info->tlp_header_valid) { unsigned char *tlp = (unsigned char *) &info->tlp; - printk("%sTLP Header:\n", loglevel); - printk("%s%02x%02x%02x%02x %02x%02x%02x%02x" + AER_PR(info, dev, " TLP Header:" + " %02x%02x%02x%02x %02x%02x%02x%02x" " %02x%02x%02x%02x %02x%02x%02x%02x\n", - loglevel, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), *(tlp + 11), *(tlp + 10), *(tlp + 9), @@ -244,5 +216,15 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) *(tlp + 13), *(tlp + 12)); } } + + if (info->id && info->error_dev_num > 1 && info->id == id) + AER_PR(info, dev, + " Error of this Agent(%04x) is reported first\n", id); } +void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) +{ + dev_info(&dev->dev, "AER: %s%s error received: id=%04x\n", + info->multi_error_valid ? "Multiple " : "", + aer_error_severity_string[info->severity], info->id); +} diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3d27c97e0486..f289ca9bf18d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -26,6 +26,13 @@ #endif #define MODULE_PARAM_PREFIX "pcie_aspm." +/* Note: those are not register definitions */ +#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ +#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ +#define ASPM_STATE_L1 (4) /* L1 state */ +#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) +#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1) + struct aspm_latency { u32 l0s; /* L0s latency (nsec) */ u32 l1; /* L1 latency (nsec) */ @@ -40,17 +47,20 @@ struct pcie_link_state { struct list_head link; /* node in parent's children list */ /* ASPM state */ - u32 aspm_support:2; /* Supported ASPM state */ - u32 aspm_enabled:2; /* Enabled ASPM state */ - u32 aspm_default:2; /* Default ASPM state by BIOS */ + u32 aspm_support:3; /* Supported ASPM state */ + u32 aspm_enabled:3; /* Enabled ASPM state */ + u32 aspm_capable:3; /* Capable ASPM state with latency */ + u32 aspm_default:3; /* Default ASPM state by BIOS */ + u32 aspm_disable:3; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ u32 clkpm_enabled:1; /* Current Clock PM state */ u32 clkpm_default:1; /* Default Clock PM state by BIOS */ - /* Latencies */ - struct aspm_latency latency; /* Exit latency */ + /* Exit latencies */ + struct aspm_latency latency_up; /* Upstream direction exit latency */ + struct aspm_latency latency_dw; /* Downstream direction exit latency */ /* * Endpoint acceptable latencies. A pcie downstream port only * has one slot under it, so at most there are 8 functions. @@ -82,7 +92,7 @@ static int policy_to_aspm_state(struct pcie_link_state *link) return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ - return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + return ASPM_STATE_ALL; case POLICY_DEFAULT: return link->aspm_default; } @@ -164,18 +174,6 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_capable = (blacklist) ? 0 : capable; } -static bool pcie_aspm_downstream_has_switch(struct pcie_link_state *link) -{ - struct pci_dev *child; - struct pci_bus *linkbus = link->pdev->subordinate; - - list_for_each_entry(child, &linkbus->devices, bus_list) { - if (child->pcie_type == PCI_EXP_TYPE_UPSTREAM) - return true; - } - return false; -} - /* * pcie_aspm_configure_common_clock: check if the 2 ends of a link * could use common clock. If they are, configure them to use the @@ -288,71 +286,133 @@ static u32 calc_l1_acceptable(u32 encoding) return (1000 << encoding); } -static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, - u32 *l0s, u32 *l1, u32 *enabled) +struct aspm_register_info { + u32 support:2; + u32 enabled:2; + u32 latency_encoding_l0s; + u32 latency_encoding_l1; +}; + +static void pcie_get_aspm_reg(struct pci_dev *pdev, + struct aspm_register_info *info) { int pos; u16 reg16; - u32 reg32, encoding; + u32 reg32; - *l0s = *l1 = *enabled = 0; pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32); - *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; - if (*state != PCIE_LINK_STATE_L0S && - *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S)) - *state = 0; - if (*state == 0) + info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; + /* 00b and 10b are defined as "Reserved". */ + if (info->support == PCIE_LINK_STATE_L1) + info->support = 0; + info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; + info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; + pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); + info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; +} + +static void pcie_aspm_check_latency(struct pci_dev *endpoint) +{ + u32 latency, l1_switch_latency = 0; + struct aspm_latency *acceptable; + struct pcie_link_state *link; + + /* Device not in D0 doesn't need latency check */ + if ((endpoint->current_state != PCI_D0) && + (endpoint->current_state != PCI_UNKNOWN)) return; - encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; - *l0s = calc_l0s_latency(encoding); - if (*state & PCIE_LINK_STATE_L1) { - encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; - *l1 = calc_l1_latency(encoding); + link = endpoint->bus->self->link_state; + acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + + while (link) { + /* Check upstream direction L0s latency */ + if ((link->aspm_capable & ASPM_STATE_L0S_UP) && + (link->latency_up.l0s > acceptable->l0s)) + link->aspm_capable &= ~ASPM_STATE_L0S_UP; + + /* Check downstream direction L0s latency */ + if ((link->aspm_capable & ASPM_STATE_L0S_DW) && + (link->latency_dw.l0s > acceptable->l0s)) + link->aspm_capable &= ~ASPM_STATE_L0S_DW; + /* + * Check L1 latency. + * Every switch on the path to root complex need 1 + * more microsecond for L1. Spec doesn't mention L0s. + */ + latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1); + if ((link->aspm_capable & ASPM_STATE_L1) && + (latency + l1_switch_latency > acceptable->l1)) + link->aspm_capable &= ~ASPM_STATE_L1; + l1_switch_latency += 1000; + + link = link->parent; } - pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); - *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); } static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { - u32 support, l0s, l1, enabled; struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; + struct aspm_register_info upreg, dwreg; if (blacklist) { - /* Set support state to 0, so we will disable ASPM later */ - link->aspm_support = 0; - link->aspm_default = 0; - link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + /* Set enabled/disable so that we will disable ASPM later */ + link->aspm_enabled = ASPM_STATE_ALL; + link->aspm_disable = ASPM_STATE_ALL; return; } /* Configure common clock before checking latencies */ pcie_aspm_configure_common_clock(link); - /* upstream component states */ - pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled); - link->aspm_support = support; - link->latency.l0s = l0s; - link->latency.l1 = l1; - link->aspm_enabled = enabled; - - /* downstream component states, all functions have the same setting */ + /* Get upstream/downstream components' register state */ + pcie_get_aspm_reg(parent, &upreg); child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); - pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled); - link->aspm_support &= support; - link->latency.l0s = max_t(u32, link->latency.l0s, l0s); - link->latency.l1 = max_t(u32, link->latency.l1, l1); + pcie_get_aspm_reg(child, &dwreg); - if (!link->aspm_support) - return; - - link->aspm_enabled &= link->aspm_support; + /* + * Setup L0s state + * + * Note that we must not enable L0s in either direction on a + * given link unless components on both sides of the link each + * support L0s. + */ + if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S) + link->aspm_support |= ASPM_STATE_L0S; + if (dwreg.enabled & PCIE_LINK_STATE_L0S) + link->aspm_enabled |= ASPM_STATE_L0S_UP; + if (upreg.enabled & PCIE_LINK_STATE_L0S) + link->aspm_enabled |= ASPM_STATE_L0S_DW; + link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s); + link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s); + + /* Setup L1 state */ + if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1) + link->aspm_support |= ASPM_STATE_L1; + if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1) + link->aspm_enabled |= ASPM_STATE_L1; + link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1); + link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1); + + /* Save default state */ link->aspm_default = link->aspm_enabled; - /* ENDPOINT states*/ + /* Setup initial capable state. Will be updated later */ + link->aspm_capable = link->aspm_support; + /* + * If the downstream component has pci bridge function, don't + * do ASPM for now. + */ + list_for_each_entry(child, &linkbus->devices, bus_list) { + if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { + link->aspm_disable = ASPM_STATE_ALL; + break; + } + } + + /* Get and check endpoint acceptable latencies */ list_for_each_entry(child, &linkbus->devices, bus_list) { int pos; u32 reg32, encoding; @@ -365,109 +425,46 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) pos = pci_find_capability(child, PCI_CAP_ID_EXP); pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); + /* Calculate endpoint L0s acceptable latency */ encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; acceptable->l0s = calc_l0s_acceptable(encoding); - if (link->aspm_support & PCIE_LINK_STATE_L1) { - encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; - acceptable->l1 = calc_l1_acceptable(encoding); - } - } -} - -/** - * __pcie_aspm_check_state_one - check latency for endpoint device. - * @endpoint: pointer to the struct pci_dev of endpoint device - * - * TBD: The latency from the endpoint to root complex vary per switch's - * upstream link state above the device. Here we just do a simple check - * which assumes all links above the device can be in L1 state, that - * is we just consider the worst case. If switch's upstream link can't - * be put into L0S/L1, then our check is too strictly. - */ -static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state) -{ - u32 l1_switch_latency = 0; - struct aspm_latency *acceptable; - struct pcie_link_state *link; - - link = endpoint->bus->self->link_state; - state &= link->aspm_support; - acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + /* Calculate endpoint L1 acceptable latency */ + encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; + acceptable->l1 = calc_l1_acceptable(encoding); - while (link && state) { - if ((state & PCIE_LINK_STATE_L0S) && - (link->latency.l0s > acceptable->l0s)) - state &= ~PCIE_LINK_STATE_L0S; - if ((state & PCIE_LINK_STATE_L1) && - (link->latency.l1 + l1_switch_latency > acceptable->l1)) - state &= ~PCIE_LINK_STATE_L1; - link = link->parent; - /* - * Every switch on the path to root complex need 1 - * more microsecond for L1. Spec doesn't mention L0s. - */ - l1_switch_latency += 1000; - } - return state; -} - -static u32 pcie_aspm_check_state(struct pcie_link_state *link, u32 state) -{ - pci_power_t power_state; - struct pci_dev *child; - struct pci_bus *linkbus = link->pdev->subordinate; - - /* If no child, ignore the link */ - if (list_empty(&linkbus->devices)) - return state; - - list_for_each_entry(child, &linkbus->devices, bus_list) { - /* - * If downstream component of a link is pci bridge, we - * disable ASPM for now for the link - */ - if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) - return 0; - - if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT && - child->pcie_type != PCI_EXP_TYPE_LEG_END)) - continue; - /* Device not in D0 doesn't need check latency */ - power_state = child->current_state; - if (power_state == PCI_D1 || power_state == PCI_D2 || - power_state == PCI_D3hot || power_state == PCI_D3cold) - continue; - state = __pcie_aspm_check_state_one(child, state); + pcie_aspm_check_latency(child); } - return state; } -static void __pcie_aspm_config_one_dev(struct pci_dev *pdev, unsigned int state) +static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { u16 reg16; int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); reg16 &= ~0x3; - reg16 |= state; + reg16 |= val; pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); } -static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) +static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) { + u32 upstream = 0, dwstream = 0; struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - /* If no child, disable the link */ - if (list_empty(&linkbus->devices)) - state = 0; - /* - * If the downstream component has pci bridge function, don't - * do ASPM now. - */ - list_for_each_entry(child, &linkbus->devices, bus_list) { - if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) - return; + /* Nothing to do if the link is already in the requested state */ + state &= (link->aspm_capable & ~link->aspm_disable); + if (link->aspm_enabled == state) + return; + /* Convert ASPM state to upstream/downstream ASPM register state */ + if (state & ASPM_STATE_L0S_UP) + dwstream |= PCIE_LINK_STATE_L0S; + if (state & ASPM_STATE_L0S_DW) + upstream |= PCIE_LINK_STATE_L0S; + if (state & ASPM_STATE_L1) { + upstream |= PCIE_LINK_STATE_L1; + dwstream |= PCIE_LINK_STATE_L1; } /* * Spec 2.0 suggests all functions should be configured the @@ -475,67 +472,24 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) * upstream component first and then downstream, and vice * versa for disabling ASPM L1. Spec doesn't mention L0S. */ - if (state & PCIE_LINK_STATE_L1) - __pcie_aspm_config_one_dev(parent, state); - + if (state & ASPM_STATE_L1) + pcie_config_aspm_dev(parent, upstream); list_for_each_entry(child, &linkbus->devices, bus_list) - __pcie_aspm_config_one_dev(child, state); - - if (!(state & PCIE_LINK_STATE_L1)) - __pcie_aspm_config_one_dev(parent, state); + pcie_config_aspm_dev(child, dwstream); + if (!(state & ASPM_STATE_L1)) + pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; } -/* Check the whole hierarchy, and configure each link in the hierarchy */ -static void __pcie_aspm_configure_link_state(struct pcie_link_state *link, - u32 state) +static void pcie_config_aspm_path(struct pcie_link_state *link) { - struct pcie_link_state *leaf, *root = link->root; - - state &= (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); - - /* Check all links who have specific root port link */ - list_for_each_entry(leaf, &link_list, sibling) { - if (!list_empty(&leaf->children) || (leaf->root != root)) - continue; - state = pcie_aspm_check_state(leaf, state); - } - /* Check root port link too in case it hasn't children */ - state = pcie_aspm_check_state(root, state); - if (link->aspm_enabled == state) - return; - /* - * We must change the hierarchy. See comments in - * __pcie_aspm_config_link for the order - **/ - if (state & PCIE_LINK_STATE_L1) { - list_for_each_entry(leaf, &link_list, sibling) { - if (leaf->root == root) - __pcie_aspm_config_link(leaf, state); - } - } else { - list_for_each_entry_reverse(leaf, &link_list, sibling) { - if (leaf->root == root) - __pcie_aspm_config_link(leaf, state); - } + while (link) { + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + link = link->parent; } } -/* - * pcie_aspm_configure_link_state: enable/disable PCI express link state - * @pdev: the root port or switch downstream port - */ -static void pcie_aspm_configure_link_state(struct pcie_link_state *link, - u32 state) -{ - down_read(&pci_bus_sem); - mutex_lock(&aspm_lock); - __pcie_aspm_configure_link_state(link, state); - mutex_unlock(&aspm_lock); - up_read(&pci_bus_sem); -} - static void free_link_state(struct pcie_link_state *link) { link->pdev->link_state = NULL; @@ -570,10 +524,9 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) return 0; } -static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) +static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) { struct pcie_link_state *link; - int blacklist = !!pcie_aspm_sanity_check(pdev); link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) @@ -599,15 +552,7 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) link->root = link->parent->root; list_add(&link->sibling, &link_list); - pdev->link_state = link; - - /* Check ASPM capability */ - pcie_aspm_cap_init(link, blacklist); - - /* Check Clock PM capability */ - pcie_clkpm_cap_init(link, blacklist); - return link; } @@ -618,8 +563,8 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) */ void pcie_aspm_init_link_state(struct pci_dev *pdev) { - u32 state; struct pcie_link_state *link; + int blacklist = !!pcie_aspm_sanity_check(pdev); if (aspm_disabled || !pdev->is_pcie || pdev->link_state) return; @@ -637,47 +582,64 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) goto out; mutex_lock(&aspm_lock); - link = pcie_aspm_setup_link_state(pdev); + link = alloc_pcie_link_state(pdev); if (!link) goto unlock; /* - * Setup initial ASPM state - * - * If link has switch, delay the link config. The leaf link - * initialization will config the whole hierarchy. But we must - * make sure BIOS doesn't set unsupported link state. + * Setup initial ASPM state. Note that we need to configure + * upstream links also because capable state of them can be + * update through pcie_aspm_cap_init(). */ - if (pcie_aspm_downstream_has_switch(link)) { - state = pcie_aspm_check_state(link, link->aspm_default); - __pcie_aspm_config_link(link, state); - } else { - state = policy_to_aspm_state(link); - __pcie_aspm_configure_link_state(link, state); - } + pcie_aspm_cap_init(link, blacklist); + pcie_config_aspm_path(link); /* Setup initial Clock PM state */ - state = (link->clkpm_capable) ? policy_to_clkpm_state(link) : 0; - pcie_set_clkpm(link, state); + pcie_clkpm_cap_init(link, blacklist); + pcie_set_clkpm(link, policy_to_clkpm_state(link)); unlock: mutex_unlock(&aspm_lock); out: up_read(&pci_bus_sem); } +/* Recheck latencies and update aspm_capable for links under the root */ +static void pcie_update_aspm_capable(struct pcie_link_state *root) +{ + struct pcie_link_state *link; + BUG_ON(root->parent); + list_for_each_entry(link, &link_list, sibling) { + if (link->root != root) + continue; + link->aspm_capable = link->aspm_support; + } + list_for_each_entry(link, &link_list, sibling) { + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; + if (link->root != root) + continue; + list_for_each_entry(child, &linkbus->devices, bus_list) { + if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT) && + (child->pcie_type != PCI_EXP_TYPE_LEG_END)) + continue; + pcie_aspm_check_latency(child); + } + } +} + /* @pdev: the endpoint device */ void pcie_aspm_exit_link_state(struct pci_dev *pdev) { struct pci_dev *parent = pdev->bus->self; - struct pcie_link_state *link_state = parent->link_state; + struct pcie_link_state *link, *root, *parent_link; - if (aspm_disabled || !pdev->is_pcie || !parent || !link_state) + if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state) return; - if (parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT && - parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) return; + down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - /* * All PCIe functions are in one slot, remove one function will remove * the whole slot, so just wait until we are the last function left. @@ -685,13 +647,20 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices)) goto out; + link = parent->link_state; + root = link->root; + parent_link = link->parent; + /* All functions are removed, so just disable ASPM for the link */ - __pcie_aspm_config_one_dev(parent, 0); - list_del(&link_state->sibling); - list_del(&link_state->link); + pcie_config_aspm_link(link, 0); + list_del(&link->sibling); + list_del(&link->link); /* Clock PM is for endpoint device */ + free_link_state(link); - free_link_state(link_state); + /* Recheck latencies and configure upstream links */ + pcie_update_aspm_capable(root); + pcie_config_aspm_path(parent_link); out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -700,18 +669,23 @@ out: /* @pdev: the root port or switch downstream port */ void pcie_aspm_pm_state_change(struct pci_dev *pdev) { - struct pcie_link_state *link_state = pdev->link_state; + struct pcie_link_state *link = pdev->link_state; - if (aspm_disabled || !pdev->is_pcie || !pdev->link_state) + if (aspm_disabled || !pdev->is_pcie || !link) return; - if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && - pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) return; /* - * devices changed PM state, we should recheck if latency meets all - * functions' requirement + * Devices changed PM state, we should recheck if latency + * meets all functions' requirement */ - pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); + down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + pcie_update_aspm_capable(link->root); + pcie_config_aspm_path(link); + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); } /* @@ -721,7 +695,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) void pci_disable_link_state(struct pci_dev *pdev, int state) { struct pci_dev *parent = pdev->bus->self; - struct pcie_link_state *link_state; + struct pcie_link_state *link; if (aspm_disabled || !pdev->is_pcie) return; @@ -733,12 +707,16 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - link_state = parent->link_state; - link_state->aspm_support &= ~state; - __pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); + link = parent->link_state; + if (state & PCIE_LINK_STATE_L0S) + link->aspm_disable |= ASPM_STATE_L0S; + if (state & PCIE_LINK_STATE_L1) + link->aspm_disable |= ASPM_STATE_L1; + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + if (state & PCIE_LINK_STATE_CLKPM) { - link_state->clkpm_capable = 0; - pcie_set_clkpm(link_state, 0); + link->clkpm_capable = 0; + pcie_set_clkpm(link, 0); } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -748,7 +726,7 @@ EXPORT_SYMBOL(pci_disable_link_state); static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; - struct pcie_link_state *link_state; + struct pcie_link_state *link; for (i = 0; i < ARRAY_SIZE(policy_str); i++) if (!strncmp(val, policy_str[i], strlen(policy_str[i]))) @@ -761,10 +739,9 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); aspm_policy = i; - list_for_each_entry(link_state, &link_list, sibling) { - __pcie_aspm_configure_link_state(link_state, - policy_to_aspm_state(link_state)); - pcie_set_clkpm(link_state, policy_to_clkpm_state(link_state)); + list_for_each_entry(link, &link_list, sibling) { + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + pcie_set_clkpm(link, policy_to_clkpm_state(link)); } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -802,18 +779,28 @@ static ssize_t link_state_store(struct device *dev, size_t n) { struct pci_dev *pdev = to_pci_dev(dev); - int state; + struct pcie_link_state *link, *root = pdev->link_state->root; + u32 val = buf[0] - '0', state = 0; - if (n < 1) + if (n < 1 || val > 3) return -EINVAL; - state = buf[0]-'0'; - if (state >= 0 && state <= 3) { - /* setup link aspm state */ - pcie_aspm_configure_link_state(pdev->link_state, state); - return n; - } - return -EINVAL; + /* Convert requested state to ASPM state */ + if (val & PCIE_LINK_STATE_L0S) + state |= ASPM_STATE_L0S; + if (val & PCIE_LINK_STATE_L1) + state |= ASPM_STATE_L1; + + down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + list_for_each_entry(link, &link_list, sibling) { + if (link->root != root) + continue; + pcie_config_aspm_link(link, state); + } + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); + return n; } static ssize_t clk_ctl_show(struct device *dev, diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 13ffdc35ea0e..52f84fca9f7d 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -187,14 +187,9 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) */ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { - struct pcie_port_data *port_data = pci_get_drvdata(dev); int irq, interrupt_mode = PCIE_PORT_NO_IRQ; int i; - /* Check MSI quirk */ - if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) - goto Fallback; - /* Try to use MSI-X if supported */ if (!pcie_port_enable_msix(dev, vectors, mask)) return PCIE_PORT_MSIX_MODE; @@ -203,7 +198,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (!pci_enable_msi(dev)) interrupt_mode = PCIE_PORT_MSI_MODE; - Fallback: if (interrupt_mode == PCIE_PORT_NO_IRQ && dev->pin) interrupt_mode = PCIE_PORT_INTx_MODE; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 091ce70051e0..6df5c984a791 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -205,6 +205,7 @@ static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) /* If fatal, restore cfg space for possible link reset at upstream */ if (dev->error_state == pci_channel_io_frozen) { + dev->state_saved = true; pci_restore_state(dev); pcie_portdrv_restore_config(dev); pci_enable_pcie_error_reporting(dev); |