From b95d58eaf20eb33c245a2172ec4ecf46bd832309 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 30 Jan 2008 18:20:04 +0900 Subject: pci: allow multiple calls to pcim_enable_device() There's no reason not to allow multiple calls to pcim_enable_device(). Calls after the first one can simply be noop. All PCI resources will be released when the initial pcim_enable_device() resource is released. This allows more flexibility to managed PCI users. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jeff Garzik --- drivers/pci/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 71d561fda0a..7d4ce906d20 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -823,7 +823,8 @@ int pcim_enable_device(struct pci_dev *pdev) dr = get_pci_dr(pdev); if (unlikely(!dr)) return -ENOMEM; - WARN_ON(!!dr->enabled); + if (dr->enabled) + return 0; rc = pci_enable_device(pdev); if (!rc) { -- cgit v1.2.3 From ad668599f263988eaac74354349d64e3c0990a77 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 27 Oct 2007 03:06:22 +0200 Subject: PCI: make pci_restore_bars() static This patch makes the needlessly global pci_restore_bars() static. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 71d561fda0a..089184bc327 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -353,7 +353,7 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) * Restore the BAR values for a given device, so as to make it * accessible by its driver. */ -void +static void pci_restore_bars(struct pci_dev *dev) { int i, numres; @@ -1618,7 +1618,6 @@ early_param("pci", pci_setup); device_initcall(pci_init); -EXPORT_SYMBOL_GPL(pci_restore_bars); EXPORT_SYMBOL(pci_reenable_device); EXPORT_SYMBOL(pci_enable_device_bars); EXPORT_SYMBOL(pci_enable_device); -- cgit v1.2.3 From 4348a2dc49f9baecd34a9b0904245488c6189398 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 24 Oct 2007 10:45:08 +0800 Subject: pcie: utilize pcie transaction pending bit PCIE has a mechanism to wait for Non-Posted request to complete. I think pci_disable_device is a good place to do this. Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 089184bc327..d30e802d9a1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -314,6 +314,24 @@ int pci_find_ht_capability(struct pci_dev *dev, int ht_cap) } EXPORT_SYMBOL_GPL(pci_find_ht_capability); +void pcie_wait_pending_transaction(struct pci_dev *dev) +{ + int pos; + u16 reg16; + + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return; + while (1) { + pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, ®16); + if (!(reg16 & PCI_EXP_DEVSTA_TRPND)) + break; + cpu_relax(); + } + +} +EXPORT_SYMBOL_GPL(pcie_wait_pending_transaction); + /** * pci_find_parent_resource - return resource region of parent bus of given region * @dev: PCI device structure contains resources to be searched @@ -884,6 +902,9 @@ pci_disable_device(struct pci_dev *dev) if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; + /* Wait for all transactions are finished before disabling the device */ + pcie_wait_pending_transaction(dev); + pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; -- cgit v1.2.3 From f34303de9e0263b389a215483adddc7d918cf8c8 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 18 Dec 2007 09:56:47 +0800 Subject: PCI: fix typo in pci_save_pcix_state pci_save/store_state has multiple bugs, which will cause cap can't be saved/restored correctly. Below 3 patches fix them. fix the typo in pci_save_pcix_state Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d30e802d9a1..b01ed9a5ab4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -620,7 +620,7 @@ static int pci_save_pcix_state(struct pci_dev *dev) if (pos <= 0) return 0; - save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); if (!save_state) save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); if (!save_state) { -- cgit v1.2.3 From ec0a3a27fbb5792980b8c3ce4a93bc2ee93d0b35 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 18 Dec 2007 09:56:56 +0800 Subject: PCI: correctly initialize a structure for pcie_save_pcix_state() save_state->cap_nr should be correctly set, otherwise we can't find the saved cap at resume. Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b01ed9a5ab4..342857c555d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -587,6 +587,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); + save_state->cap_nr = PCI_CAP_ID_EXP; pci_add_saved_cap(dev, save_state); return 0; } @@ -630,6 +631,7 @@ static int pci_save_pcix_state(struct pci_dev *dev) cap = (u16 *)&save_state->data[0]; pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]); + save_state->cap_nr = PCI_CAP_ID_PCIX; pci_add_saved_cap(dev, save_state); return 0; } -- cgit v1.2.3 From 017fc480cc8cc0594dc250951d78e814667ae4c2 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 18 Dec 2007 09:57:09 +0800 Subject: PCI: avoid save the same type of cap multiple times Avoid adding the same type of cap multiple times, otherwise we will see dead loop. Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 342857c555d..7248e9fb12b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -569,6 +569,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) int pos, i = 0; struct pci_cap_saved_state *save_state; u16 *cap; + int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (pos <= 0) @@ -577,6 +578,8 @@ static int pci_save_pcie_state(struct pci_dev *dev) save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); if (!save_state) save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); + else + found = 1; if (!save_state) { dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n"); return -ENOMEM; @@ -588,7 +591,8 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); save_state->cap_nr = PCI_CAP_ID_EXP; - pci_add_saved_cap(dev, save_state); + if (!found) + pci_add_saved_cap(dev, save_state); return 0; } @@ -616,6 +620,7 @@ static int pci_save_pcix_state(struct pci_dev *dev) int pos, i = 0; struct pci_cap_saved_state *save_state; u16 *cap; + int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (pos <= 0) @@ -624,6 +629,8 @@ static int pci_save_pcix_state(struct pci_dev *dev) save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); if (!save_state) save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); + else + found = 1; if (!save_state) { dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n"); return -ENOMEM; @@ -632,7 +639,8 @@ static int pci_save_pcix_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]); save_state->cap_nr = PCI_CAP_ID_PCIX; - pci_add_saved_cap(dev, save_state); + if (!found) + pci_add_saved_cap(dev, save_state); return 0; } -- cgit v1.2.3 From b718989da7cf1f77ed5665dba0d2c73bd9dfe2d7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 20 Dec 2007 15:28:08 +1100 Subject: PCI: Add pci_enable_device_{io,mem} intefaces The pci_enable_device_bars() interface isn't well suited to PCI because you can't actually enable/disable BARs individually on a device. So for example, if a device has 2 memory BARs 0 and 1, and one of them (let's say 1) has not been successfully allocated by the firmware or the kernel, then enabling memory decoding shouldn't be permitted for the entire device since it will decode whatever random address is still in that BAR 1. So a device must be either fully enabled for IO, for Memory, or for both. Not on a per-BAR basis. This provides two new functions, pci_enable_device_io() and pci_enable_device_mem() to replace pci_enable_device_bars(). The implementation internally builds a BAR mask in order to be able to use existing arch infrastructure. Signed-off-by: Benjamin Herrenschmidt Acked-by: Ivan Kokshaysky Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7248e9fb12b..5027e4d08b4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -764,6 +764,51 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) return err; } +static int __pci_enable_device_flags(struct pci_dev *dev, + resource_size_t flags) +{ + int err; + int i, bars = 0; + + if (atomic_add_return(1, &dev->enable_cnt) > 1) + return 0; /* already enabled */ + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + if (dev->resource[i].flags & flags) + bars |= (1 << i); + + err = do_pci_enable_device(dev, bars); + if (err < 0) + atomic_dec(&dev->enable_cnt); + return err; +} + +/** + * pci_enable_device_io - Initialize a device for use with IO space + * @dev: PCI device to be initialized + * + * Initialize device before it's used by a driver. Ask low-level code + * to enable I/O resources. Wake up the device if it was suspended. + * Beware, this function can fail. + */ +int pci_enable_device_io(struct pci_dev *dev) +{ + return __pci_enable_device_flags(dev, IORESOURCE_IO); +} + +/** + * pci_enable_device_mem - Initialize a device for use with Memory space + * @dev: PCI device to be initialized + * + * Initialize device before it's used by a driver. Ask low-level code + * to enable Memory resources. Wake up the device if it was suspended. + * Beware, this function can fail. + */ +int pci_enable_device_mem(struct pci_dev *dev) +{ + return __pci_enable_device_flags(dev, IORESOURCE_MEM); +} + /** * pci_enable_device - Initialize device before it's used by a driver. * @dev: PCI device to be initialized @@ -777,7 +822,7 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) */ int pci_enable_device(struct pci_dev *dev) { - return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); + return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO); } /* @@ -1651,6 +1696,8 @@ device_initcall(pci_init); EXPORT_SYMBOL(pci_reenable_device); EXPORT_SYMBOL(pci_enable_device_bars); +EXPORT_SYMBOL(pci_enable_device_io); +EXPORT_SYMBOL(pci_enable_device_mem); EXPORT_SYMBOL(pci_enable_device); EXPORT_SYMBOL(pcim_enable_device); EXPORT_SYMBOL(pcim_pin_device); -- cgit v1.2.3 From 7cbe5b6005f80de33a205d3052cdc89aacaac07c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 20 Dec 2007 15:28:10 +1100 Subject: PCI: Remove pci_enable_device_bars() Now that all in-tree users are gone, this removes pci_enable_device_bars() completely. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5027e4d08b4..35f78f1628f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -741,29 +741,6 @@ int pci_reenable_device(struct pci_dev *dev) return 0; } -/** - * pci_enable_device_bars - Initialize some of a device for use - * @dev: PCI device to be initialized - * @bars: bitmask of BAR's that must be configured - * - * Initialize device before it's used by a driver. Ask low-level code - * to enable selected I/O and memory resources. Wake up the device if it - * was suspended. Beware, this function can fail. - */ -int -pci_enable_device_bars(struct pci_dev *dev, int bars) -{ - int err; - - if (atomic_add_return(1, &dev->enable_cnt) > 1) - return 0; /* already enabled */ - - err = do_pci_enable_device(dev, bars); - if (err < 0) - atomic_dec(&dev->enable_cnt); - return err; -} - static int __pci_enable_device_flags(struct pci_dev *dev, resource_size_t flags) { @@ -1695,7 +1672,6 @@ early_param("pci", pci_setup); device_initcall(pci_init); EXPORT_SYMBOL(pci_reenable_device); -EXPORT_SYMBOL(pci_enable_device_bars); EXPORT_SYMBOL(pci_enable_device_io); EXPORT_SYMBOL(pci_enable_device_mem); EXPORT_SYMBOL(pci_enable_device); -- cgit v1.2.3 From 6c723d5bd89f03fc3ef627d50f89ade054d2ee3b Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 24 Jan 2008 10:21:57 +0800 Subject: PCI: PCIE ASPM support PCI Express ASPM defines a protocol for PCI Express components in the D0 state to reduce Link power by placing their Links into a low power state and instructing the other end of the Link to do likewise. This capability allows hardware-autonomous, dynamic Link power reduction beyond what is achievable by software-only controlled power management. However, The device should be configured by software appropriately. Enabling ASPM will save power, but will introduce device latency. This patch adds ASPM support in Linux. It introduces a global policy for ASPM, a sysfs file /sys/module/pcie_aspm/parameters/policy can control it. The interface can be used as a boot option too. Currently we have below setting: -default, BIOS default setting -powersave, highest power saving mode, enable all available ASPM state and clock power management -performance, highest performance, disable ASPM and clock power management By default, the 'default' policy is used currently. In my test, power difference between powersave mode and performance mode is about 1.3w in a system with 3 PCIE links. Signed-off-by: Shaohua Li Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 35f78f1628f..1f169316195 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /* isa_dma_bridge_buggy */ #include "pci.h" @@ -519,6 +520,9 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (need_restore) pci_restore_bars(dev); + if (dev->bus->self) + pcie_aspm_pm_state_change(dev->bus->self); + return 0; } -- cgit v1.2.3