提交 e76e5b2c 编写于 作者: L Linus Torvalds

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: (88 commits)
  PCI: fix HT MSI mapping fix
  PCI: don't enable too much HT MSI mapping
  x86/PCI: make pci=lastbus=255 work when acpi is on
  PCI: save and restore PCIe 2.0 registers
  PCI: update fakephp for bus_id removal
  PCI: fix kernel oops on bridge removal
  PCI: fix conflict between SR-IOV and config space sizing
  powerpc/PCI: include pci.h in powerpc MSI implementation
  PCI Hotplug: schedule fakephp for feature removal
  PCI Hotplug: rename legacy_fakephp to fakephp
  PCI Hotplug: restore fakephp interface with complete reimplementation
  PCI: Introduce /sys/bus/pci/devices/.../rescan
  PCI: Introduce /sys/bus/pci/devices/.../remove
  PCI: Introduce /sys/bus/pci/rescan
  PCI: Introduce pci_rescan_bus()
  PCI: do not enable bridges more than once
  PCI: do not initialize bridges more than once
  PCI: always scan child buses
  PCI: pci_scan_slot() returns newly found devices
  PCI: don't scan existing devices
  ...

Fix trivial append-only conflict in Documentation/feature-removal-schedule.txt
......@@ -41,6 +41,49 @@ Description:
for the device and attempt to bind to it. For example:
# echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id
What: /sys/bus/pci/drivers/.../remove_id
Date: February 2009
Contact: Chris Wright <chrisw@sous-sol.org>
Description:
Writing a device ID to this file will remove an ID
that was dynamically added via the new_id sysfs entry.
The format for the device ID is:
VVVV DDDD SVVV SDDD CCCC MMMM. That is Vendor ID, Device
ID, Subsystem Vendor ID, Subsystem Device ID, Class,
and Class Mask. The Vendor ID and Device ID fields are
required, the rest are optional. After successfully
removing an ID, the driver will no longer support the
device. This is useful to ensure auto probing won't
match the driver to the device. For example:
# echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id
What: /sys/bus/pci/rescan
Date: January 2009
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
Description:
Writing a non-zero value to this attribute will
force a rescan of all PCI buses in the system, and
re-discover previously removed devices.
Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../remove
Date: January 2009
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
Description:
Writing a non-zero value to this attribute will
hot-remove the PCI device and any of its children.
Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../rescan
Date: January 2009
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
Description:
Writing a non-zero value to this attribute will
force a rescan of the device's parent bus and all
child buses, and re-discover devices removed earlier
from this part of the device tree.
Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../vpd
Date: February 2008
Contact: Ben Hutchings <bhutchings@solarflare.com>
......@@ -52,3 +95,30 @@ Description:
that some devices may have malformatted data. If the
underlying VPD has a writable section then the
corresponding section of this file will be writable.
What: /sys/bus/pci/devices/.../virtfnN
Date: March 2009
Contact: Yu Zhao <yu.zhao@intel.com>
Description:
This symbolic link appears when hardware supports the SR-IOV
capability and the Physical Function driver has enabled it.
The symbolic link points to the PCI device sysfs entry of the
Virtual Function whose index is N (0...MaxVFs-1).
What: /sys/bus/pci/devices/.../dep_link
Date: March 2009
Contact: Yu Zhao <yu.zhao@intel.com>
Description:
This symbolic link appears when hardware supports the SR-IOV
capability and the Physical Function driver has enabled it,
and this device has vendor specific dependencies with others.
The symbolic link points to the PCI device sysfs entry of
Physical Function this device depends on.
What: /sys/bus/pci/devices/.../physfn
Date: March 2009
Contact: Yu Zhao <yu.zhao@intel.com>
Description:
This symbolic link appears when a device is a Virtual Function.
The symbolic link points to the PCI device sysfs entry of the
Physical Function this device associates with.
......@@ -199,6 +199,7 @@ X!Edrivers/pci/hotplug.c
-->
!Edrivers/pci/probe.c
!Edrivers/pci/rom.c
!Edrivers/pci/iov.c
</sect1>
<sect1><title>PCI Hotplug Support Library</title>
!Edrivers/pci/hotplug/pci_hotplug_core.c
......
此差异已折叠。
PCI Express I/O Virtualization Howto
Copyright (C) 2009 Intel Corporation
Yu Zhao <yu.zhao@intel.com>
1. Overview
1.1 What is SR-IOV
Single Root I/O Virtualization (SR-IOV) is a PCI Express Extended
capability which makes one physical device appear as multiple virtual
devices. The physical device is referred to as Physical Function (PF)
while the virtual devices are referred to as Virtual Functions (VF).
Allocation of the VF can be dynamically controlled by the PF via
registers encapsulated in the capability. By default, this feature is
not enabled and the PF behaves as traditional PCIe device. Once it's
turned on, each VF's PCI configuration space can be accessed by its own
Bus, Device and Function Number (Routing ID). And each VF also has PCI
Memory Space, which is used to map its register set. VF device driver
operates on the register set so it can be functional and appear as a
real existing PCI device.
2. User Guide
2.1 How can I enable SR-IOV capability
The device driver (PF driver) will control the enabling and disabling
of the capability via API provided by SR-IOV core. If the hardware
has SR-IOV capability, loading its PF driver would enable it and all
VFs associated with the PF.
2.2 How can I use the Virtual Functions
The VF is treated as hot-plugged PCI devices in the kernel, so they
should be able to work in the same way as real PCI devices. The VF
requires device driver that is same as a normal PCI device's.
3. Developer Guide
3.1 SR-IOV API
To enable SR-IOV capability:
int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn);
'nr_virtfn' is number of VFs to be enabled.
To disable SR-IOV capability:
void pci_disable_sriov(struct pci_dev *dev);
To notify SR-IOV core of Virtual Function Migration:
irqreturn_t pci_sriov_migration(struct pci_dev *dev);
3.2 Usage example
Following piece of code illustrates the usage of the SR-IOV API.
static int __devinit dev_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
pci_enable_sriov(dev, NR_VIRTFN);
...
return 0;
}
static void __devexit dev_remove(struct pci_dev *dev)
{
pci_disable_sriov(dev);
...
}
static int dev_suspend(struct pci_dev *dev, pm_message_t state)
{
...
return 0;
}
static int dev_resume(struct pci_dev *dev)
{
...
return 0;
}
static void dev_shutdown(struct pci_dev *dev)
{
...
}
static struct pci_driver dev_driver = {
.name = "SR-IOV Physical Function driver",
.id_table = dev_id_table,
.probe = dev_probe,
.remove = __devexit_p(dev_remove),
.suspend = dev_suspend,
.resume = dev_resume,
.shutdown = dev_shutdown,
};
......@@ -392,3 +392,35 @@ Why: The defines and typedefs (hw_interrupt_type, no_irq_type, irq_desc_t)
have been kept around for migration reasons. After more than two years
it's time to remove them finally
Who: Thomas Gleixner <tglx@linutronix.de>
---------------------------
What: fakephp and associated sysfs files in /sys/bus/pci/slots/
When: 2011
Why: In 2.6.27, the semantics of /sys/bus/pci/slots was redefined to
represent a machine's physical PCI slots. The change in semantics
had userspace implications, as the hotplug core no longer allowed
drivers to create multiple sysfs files per physical slot (required
for multi-function devices, e.g.). fakephp was seen as a developer's
tool only, and its interface changed. Too late, we learned that
there were some users of the fakephp interface.
In 2.6.30, the original fakephp interface was restored. At the same
time, the PCI core gained the ability that fakephp provided, namely
function-level hot-remove and hot-add.
Since the PCI core now provides the same functionality, exposed in:
/sys/bus/pci/rescan
/sys/bus/pci/devices/.../remove
/sys/bus/pci/devices/.../rescan
there is no functional reason to maintain fakephp as well.
We will keep the existing module so that 'modprobe fakephp' will
present the old /sys/bus/pci/slots/... interface for compatibility,
but users are urged to migrate their applications to the API above.
After a reasonable transition period, we will remove the legacy
fakephp interface.
Who: Alex Chiang <achiang@hp.com>
......@@ -12,6 +12,7 @@ that support it. For example, a given bus might look like this:
| |-- enable
| |-- irq
| |-- local_cpus
| |-- remove
| |-- resource
| |-- resource0
| |-- resource1
......@@ -36,6 +37,7 @@ files, each with their own function.
enable Whether the device is enabled (ascii, rw)
irq IRQ number (ascii, ro)
local_cpus nearby CPU mask (cpumask, ro)
remove remove device from kernel's list (ascii, wo)
resource PCI resource host addresses (ascii, ro)
resource0..N PCI resource N, if present (binary, mmap)
resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap)
......@@ -46,6 +48,7 @@ files, each with their own function.
ro - read only file
rw - file is readable and writable
wo - write only file
mmap - file is mmapable
ascii - file contains ascii text
binary - file contains binary data
......@@ -73,6 +76,13 @@ that the device must be enabled for a rom read to return data succesfully.
In the event a driver is not bound to the device, it can be enabled using the
'enable' file, documented above.
The 'remove' file is used to remove the PCI device, by writing a non-zero
integer to the file. This does not involve any kind of hot-plug functionality,
e.g. powering off the device. The device is removed from the kernel's list of
PCI devices, the sysfs directory for it is removed, and the device will be
removed from any drivers attached to it. Removal of PCI root buses is
disallowed.
Accessing legacy resources through sysfs
----------------------------------------
......
......@@ -1695,6 +1695,8 @@ and is between 256 and 4096 characters. It is defined in the file
See also Documentation/blockdev/paride.txt.
pci=option[,option...] [PCI] various PCI subsystem options:
earlydump [X86] dump PCI config space before the kernel
changes anything
off [X86] don't probe for the PCI bus
bios [X86-32] force use of PCI BIOS, don't access
the hardware directly. Use this if your machine
......@@ -1794,6 +1796,15 @@ and is between 256 and 4096 characters. It is defined in the file
cbmemsize=nn[KMG] The fixed amount of bus space which is
reserved for the CardBus bridge's memory
window. The default value is 64 megabytes.
resource_alignment=
Format:
[<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...]
Specifies alignment and device to reassign
aligned memory resources.
If <order of align> is not specified,
PAGE_SIZE is used as alignment.
PCI-PCI bridge can be specified, if resource
windows need to be expanded.
pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power
Management.
......
......@@ -273,4 +273,18 @@ struct pci_dev *alpha_gendev_to_pci(struct device *dev);
extern struct pci_dev *isa_bridge;
extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
size_t count);
extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
size_t count);
extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_state);
extern void pci_adjust_legacy_attr(struct pci_bus *bus,
enum pci_mmap_state mmap_type);
#define HAVE_PCI_LEGACY 1
extern int pci_create_resource_files(struct pci_dev *dev);
extern void pci_remove_resource_files(struct pci_dev *dev);
#endif /* __ALPHA_PCI_H */
......@@ -12,7 +12,7 @@ obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \
obj-$(CONFIG_VGA_HOSE) += console.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PCI) += pci.o pci_iommu.o
obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
obj-$(CONFIG_SRM_ENV) += srm_env.o
obj-$(CONFIG_MODULES) += module.o
......
/*
* arch/alpha/kernel/pci-sysfs.c
*
* Copyright (C) 2009 Ivan Kokshaysky
*
* Alpha PCI resource files.
*
* Loosely based on generic HAVE_PCI_MMAP implementation in
* drivers/pci/pci-sysfs.c
*/
#include <linux/sched.h>
#include <linux/pci.h>
static int hose_mmap_page_range(struct pci_controller *hose,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_type, int sparse)
{
unsigned long base;
if (mmap_type == pci_mmap_mem)
base = sparse ? hose->sparse_mem_base : hose->dense_mem_base;
else
base = sparse ? hose->sparse_io_base : hose->dense_io_base;
vma->vm_pgoff += base >> PAGE_SHIFT;
vma->vm_flags |= (VM_IO | VM_RESERVED);
return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
static int __pci_mmap_fits(struct pci_dev *pdev, int num,
struct vm_area_struct *vma, int sparse)
{
unsigned long nr, start, size;
int shift = sparse ? 5 : 0;
nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
start = vma->vm_pgoff;
size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
if (start < size && size - start >= nr)
return 1;
WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d "
"(size 0x%08lx)\n",
current->comm, sparse ? " sparse" : "", start, start + nr,
pci_name(pdev), num, size);
return 0;
}
/**
* pci_mmap_resource - map a PCI resource into user memory space
* @kobj: kobject for mapping
* @attr: struct bin_attribute for the file being mapped
* @vma: struct vm_area_struct passed into the mmap
* @sparse: address space type
*
* Use the bus mapping routines to map a PCI resource into userspace.
*/
static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
struct vm_area_struct *vma, int sparse)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
struct resource *res = (struct resource *)attr->private;
enum pci_mmap_state mmap_type;
struct pci_bus_region bar;
int i;
for (i = 0; i < PCI_ROM_RESOURCE; i++)
if (res == &pdev->resource[i])
break;
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
if (!__pci_mmap_fits(pdev, i, vma, sparse))
return -EINVAL;
if (iomem_is_exclusive(res->start))
return -EINVAL;
pcibios_resource_to_bus(pdev, &bar, res);
vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
}
static int pci_mmap_resource_sparse(struct kobject *kobj,
struct bin_attribute *attr,
struct vm_area_struct *vma)
{
return pci_mmap_resource(kobj, attr, vma, 1);
}
static int pci_mmap_resource_dense(struct kobject *kobj,
struct bin_attribute *attr,
struct vm_area_struct *vma)
{
return pci_mmap_resource(kobj, attr, vma, 0);
}
/**
* pci_remove_resource_files - cleanup resource files
* @dev: dev to cleanup
*
* If we created resource files for @dev, remove them from sysfs and
* free their resources.
*/
void pci_remove_resource_files(struct pci_dev *pdev)
{
int i;
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
struct bin_attribute *res_attr;
res_attr = pdev->res_attr[i];
if (res_attr) {
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 sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
{
struct pci_bus_region bar;
struct pci_controller *hose = pdev->sysdata;
long dense_offset;
unsigned long sparse_size;
pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]);
/* All core logic chips have 4G sparse address space, except
CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
definitions in asm/core_xxx.h files). This corresponds
to 128M or 512M of the bus space. */
dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base);
sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000;
return bar.end < sparse_size;
}
static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
char *suffix, struct bin_attribute *res_attr,
unsigned long sparse)
{
size_t size = pci_resource_len(pdev, num);
sprintf(name, "resource%d%s", num, suffix);
res_attr->mmap = sparse ? pci_mmap_resource_sparse :
pci_mmap_resource_dense;
res_attr->attr.name = name;
res_attr->attr.mode = S_IRUSR | S_IWUSR;
res_attr->size = sparse ? size << 5 : size;
res_attr->private = &pdev->resource[num];
return sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
}
static int pci_create_attr(struct pci_dev *pdev, int num)
{
/* allocate attribute structure, piggyback attribute name */
int retval, nlen1, nlen2 = 0, res_count = 1;
unsigned long sparse_base, dense_base;
struct bin_attribute *attr;
struct pci_controller *hose = pdev->sysdata;
char *suffix, *attr_name;
suffix = ""; /* Assume bwx machine, normal resourceN files. */
nlen1 = 10;
if (pdev->resource[num].flags & IORESOURCE_MEM) {
sparse_base = hose->sparse_mem_base;
dense_base = hose->dense_mem_base;
if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) {
sparse_base = 0;
suffix = "_dense";
nlen1 = 16; /* resourceN_dense */
}
} else {
sparse_base = hose->sparse_io_base;
dense_base = hose->dense_io_base;
}
if (sparse_base) {
suffix = "_sparse";
nlen1 = 17;
if (dense_base) {
nlen2 = 16; /* resourceN_dense */
res_count = 2;
}
}
attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC);
if (!attr)
return -ENOMEM;
/* Create bwx, sparse or single dense file */
attr_name = (char *)(attr + res_count);
pdev->res_attr[num] = attr;
retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr,
sparse_base);
if (retval || res_count == 1)
return retval;
/* Create dense file */
attr_name += nlen1;
attr++;
pdev->res_attr_wc[num] = attr;
return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0);
}
/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
*
* Walk the resources in @dev creating files for each resource available.
*/
int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
int retval;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
retval = pci_create_attr(pdev, i);
if (retval) {
pci_remove_resource_files(pdev);
return retval;
}
}
return 0;
}
/* Legacy I/O bus mapping stuff. */
static int __legacy_mmap_fits(struct pci_controller *hose,
struct vm_area_struct *vma,
unsigned long res_size, int sparse)
{
unsigned long nr, start, size;
nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
start = vma->vm_pgoff;
size = ((res_size - 1) >> PAGE_SHIFT) + 1;
if (start < size && size - start >= nr)
return 1;
WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d "
"(size 0x%08lx)\n",
current->comm, sparse ? " sparse" : "", start, start + nr,
hose->index, size);
return 0;
}
static inline int has_sparse(struct pci_controller *hose,
enum pci_mmap_state mmap_type)
{
unsigned long base;
base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base :
hose->sparse_io_base;
return base != 0;
}
int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
enum pci_mmap_state mmap_type)
{
struct pci_controller *hose = bus->sysdata;
int sparse = has_sparse(hose, mmap_type);
unsigned long res_size;
res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size :
bus->legacy_io->size;
if (!__legacy_mmap_fits(hose, vma, res_size, sparse))
return -EINVAL;
return hose_mmap_page_range(hose, vma, mmap_type, sparse);
}
/**
* pci_adjust_legacy_attr - adjustment of legacy file attributes
* @b: bus to create files under
* @mmap_type: I/O port or memory
*
* Adjust file name and size for sparse mappings.
*/
void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type)
{
struct pci_controller *hose = bus->sysdata;
if (!has_sparse(hose, mmap_type))
return;
if (mmap_type == pci_mmap_mem) {
bus->legacy_mem->attr.name = "legacy_mem_sparse";
bus->legacy_mem->size <<= 5;
} else {
bus->legacy_io->attr.name = "legacy_io_sparse";
bus->legacy_io->size <<= 5;
}
return;
}
/* Legacy I/O bus read/write functions */
int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
{
struct pci_controller *hose = bus->sysdata;
port += hose->io_space->start;
switch(size) {
case 1:
*((u8 *)val) = inb(port);
return 1;
case 2:
if (port & 1)
return -EINVAL;
*((u16 *)val) = inw(port);
return 2;
case 4:
if (port & 3)
return -EINVAL;
*((u32 *)val) = inl(port);
return 4;
}
return -EINVAL;
}
int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
{
struct pci_controller *hose = bus->sysdata;
port += hose->io_space->start;
switch(size) {
case 1:
outb(port, val);
return 1;
case 2:
if (port & 1)
return -EINVAL;
outw(port, val);
return 2;
case 4:
if (port & 3)
return -EINVAL;
outl(port, val);
return 4;
}
return -EINVAL;
}
......@@ -114,6 +114,10 @@ extern int pci_domain_nr(struct pci_bus *bus);
/* Decide whether to display the domain number in /proc */
extern int pci_proc_domain(struct pci_bus *bus);
/* MSI arch hooks */
#define arch_setup_msi_irqs arch_setup_msi_irqs
#define arch_teardown_msi_irqs arch_teardown_msi_irqs
#define arch_msi_check_device arch_msi_check_device
struct vm_area_struct;
/* Map a range of PCI memory or I/O space for a device into user space */
......
......@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <asm/machdep.h>
......@@ -19,6 +20,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
return -ENOSYS;
}
/* PowerPC doesn't support multiple MSI yet */
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
if (ppc_md.msi_check_device) {
pr_debug("msi: Using platform check routine.\n");
return ppc_md.msi_check_device(dev, nvec, type);
......
......@@ -86,6 +86,9 @@ static inline void early_quirks(void) { }
extern void pci_iommu_alloc(void);
/* MSI arch hook */
#define arch_setup_msi_irqs arch_setup_msi_irqs
#endif /* __KERNEL__ */
#ifdef CONFIG_X86_32
......
......@@ -3468,6 +3468,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
struct intel_iommu *iommu = NULL;
int index = 0;
/* x86 doesn't support multiple MSI yet */
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
irq_want = nr_irqs_gsi;
sub_handle = 0;
list_for_each_entry(msidesc, &dev->msi_list, list) {
......
......@@ -300,8 +300,7 @@ fs_initcall(pci_iommu_init);
static __devinit void via_no_dac(struct pci_dev *dev)
{
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) {
printk(KERN_INFO
"PCI: VIA PCI bridge detected. Disabling DAC.\n");
dev_info(&dev->dev, "disabling DAC on VIA PCI bridge\n");
forbid_dac = 1;
}
}
......
......@@ -69,11 +69,12 @@ void early_dump_pci_device(u8 bus, u8 slot, u8 func)
int j;
u32 val;
printk(KERN_INFO "PCI: %02x:%02x:%02x", bus, slot, func);
printk(KERN_INFO "pci 0000:%02x:%02x.%d config space:",
bus, slot, func);
for (i = 0; i < 256; i += 4) {
if (!(i & 0x0f))
printk("\n%04x:",i);
printk("\n %02x:",i);
val = read_pci_config(bus, slot, func, i);
for (j = 0; j < 4; j++) {
......@@ -96,20 +97,22 @@ void early_dump_pci_devices(void)
for (func = 0; func < 8; func++) {
u32 class;
u8 type;
class = read_pci_config(bus, slot, func,
PCI_CLASS_REVISION);
if (class == 0xffffffff)
break;
continue;
early_dump_pci_device(bus, slot, func);
/* No multi-function device? */
type = read_pci_config_byte(bus, slot, func,
if (func == 0) {
type = read_pci_config_byte(bus, slot,
func,
PCI_HEADER_TYPE);
if (!(type & 0x80))
break;
}
}
}
}
}
......@@ -494,26 +494,6 @@ static void __devinit pci_siemens_interrupt_controller(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SIEMENS, 0x0015,
pci_siemens_interrupt_controller);
/*
* Regular PCI devices have 256 bytes, but AMD Family 10h/11h CPUs have
* 4096 bytes configuration space for each function of their processor
* configuration space.
*/
static void amd_cpu_pci_cfg_space_size(struct pci_dev *dev)
{
dev->cfg_size = pci_cfg_space_size_ext(dev);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1200, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1300, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1301, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1302, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1303, amd_cpu_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1304, amd_cpu_pci_cfg_space_size);
/*
* SB600: Disable BAR1 on device 14.0 to avoid HPET resources from
* confusing the PCI engine:
......
......@@ -50,8 +50,6 @@ static int __init pci_legacy_init(void)
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
pcibios_fixup_peer_bridges();
return 0;
}
......@@ -67,6 +65,7 @@ int __init pci_subsys_init(void)
pci_visws_init();
#endif
pci_legacy_init();
pcibios_fixup_peer_bridges();
pcibios_irq_init();
pcibios_init();
......
......@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/sort.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
......@@ -24,24 +25,49 @@
/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted;
static __init int extend_mmcfg(int num)
{
struct acpi_mcfg_allocation *new;
int new_num = pci_mmcfg_config_num + num;
new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
if (!new)
return -1;
if (pci_mmcfg_config) {
memcpy(new, pci_mmcfg_config,
sizeof(pci_mmcfg_config[0]) * new_num);
kfree(pci_mmcfg_config);
}
pci_mmcfg_config = new;
return 0;
}
static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end)
{
int i = pci_mmcfg_config_num;
pci_mmcfg_config_num++;
pci_mmcfg_config[i].address = addr;
pci_mmcfg_config[i].pci_segment = segment;
pci_mmcfg_config[i].start_bus_number = start;
pci_mmcfg_config[i].end_bus_number = end;
}
static const char __init *pci_mmcfg_e7520(void)
{
u32 win;
raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
win = win & 0xf000;
if(win == 0x0000 || win == 0xf000)
pci_mmcfg_config_num = 0;
else {
pci_mmcfg_config_num = 1;
pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
if (!pci_mmcfg_config)
if (win == 0x0000 || win == 0xf000)
return NULL;
pci_mmcfg_config[0].address = win << 16;
pci_mmcfg_config[0].pci_segment = 0;
pci_mmcfg_config[0].start_bus_number = 0;
pci_mmcfg_config[0].end_bus_number = 255;
}
if (extend_mmcfg(1) == -1)
return NULL;
fill_one_mmcfg(win << 16, 0, 0, 255);
return "Intel Corporation E7520 Memory Controller Hub";
}
......@@ -50,13 +76,11 @@ static const char __init *pci_mmcfg_intel_945(void)
{
u32 pciexbar, mask = 0, len = 0;
pci_mmcfg_config_num = 1;
raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar);
/* Enable bit */
if (!(pciexbar & 1))
pci_mmcfg_config_num = 0;
return NULL;
/* Size bits */
switch ((pciexbar >> 1) & 3) {
......@@ -73,28 +97,23 @@ static const char __init *pci_mmcfg_intel_945(void)
len = 0x04000000U;
break;
default:
pci_mmcfg_config_num = 0;
return NULL;
}
/* Errata #2, things break when not aligned on a 256Mb boundary */
/* Can only happen in 64M/128M mode */
if ((pciexbar & mask) & 0x0fffffffU)
pci_mmcfg_config_num = 0;
return NULL;
/* Don't hit the APIC registers and their friends */
if ((pciexbar & mask) >= 0xf0000000U)
pci_mmcfg_config_num = 0;
return NULL;
if (pci_mmcfg_config_num) {
pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
if (!pci_mmcfg_config)
if (extend_mmcfg(1) == -1)
return NULL;
pci_mmcfg_config[0].address = pciexbar & mask;
pci_mmcfg_config[0].pci_segment = 0;
pci_mmcfg_config[0].start_bus_number = 0;
pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1;
}
fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1);
return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
}
......@@ -138,22 +157,77 @@ static const char __init *pci_mmcfg_amd_fam10h(void)
busnbits = 8;
}
pci_mmcfg_config_num = (1 << segnbits);
pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]) *
pci_mmcfg_config_num, GFP_KERNEL);
if (!pci_mmcfg_config)
if (extend_mmcfg(1 << segnbits) == -1)
return NULL;
for (i = 0; i < (1 << segnbits); i++) {
pci_mmcfg_config[i].address = base + (1<<28) * i;
pci_mmcfg_config[i].pci_segment = i;
pci_mmcfg_config[i].start_bus_number = 0;
pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1;
}
for (i = 0; i < (1 << segnbits); i++)
fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1);
return "AMD Family 10h NB";
}
static bool __initdata mcp55_checked;
static const char __init *pci_mmcfg_nvidia_mcp55(void)
{
int bus;
int mcp55_mmconf_found = 0;
static const u32 extcfg_regnum = 0x90;
static const u32 extcfg_regsize = 4;
static const u32 extcfg_enable_mask = 1<<31;
static const u32 extcfg_start_mask = 0xff<<16;
static const int extcfg_start_shift = 16;
static const u32 extcfg_size_mask = 0x3<<28;
static const int extcfg_size_shift = 28;
static const int extcfg_sizebus[] = {0x100, 0x80, 0x40, 0x20};
static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
static const int extcfg_base_lshift = 25;
/*
* do check if amd fam10h already took over
*/
if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
return NULL;
mcp55_checked = true;
for (bus = 0; bus < 256; bus++) {
u64 base;
u32 l, extcfg;
u16 vendor, device;
int start, size_index, end;
raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l);
vendor = l & 0xffff;
device = (l >> 16) & 0xffff;
if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
continue;
raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum,
extcfg_regsize, &extcfg);
if (!(extcfg & extcfg_enable_mask))
continue;
if (extend_mmcfg(1) == -1)
continue;
size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
base = extcfg & extcfg_base_mask[size_index];
/* base could > 4G */
base <<= extcfg_base_lshift;
start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
end = start + extcfg_sizebus[size_index] - 1;
fill_one_mmcfg(base, 0, start, end);
mcp55_mmconf_found++;
}
if (!mcp55_mmconf_found)
return NULL;
return "nVidia MCP55";
}
struct pci_mmcfg_hostbridge_probe {
u32 bus;
u32 devfn;
......@@ -171,8 +245,52 @@ static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
0x1200, pci_mmcfg_amd_fam10h },
{ 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
0x1200, pci_mmcfg_amd_fam10h },
{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
0x0369, pci_mmcfg_nvidia_mcp55 },
};
static int __init cmp_mmcfg(const void *x1, const void *x2)
{
const typeof(pci_mmcfg_config[0]) *m1 = x1;
const typeof(pci_mmcfg_config[0]) *m2 = x2;
int start1, start2;
start1 = m1->start_bus_number;
start2 = m2->start_bus_number;
return start1 - start2;
}
static void __init pci_mmcfg_check_end_bus_number(void)
{
int i;
typeof(pci_mmcfg_config[0]) *cfg, *cfgx;
/* sort them at first */
sort(pci_mmcfg_config, pci_mmcfg_config_num,
sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
/* last one*/
if (pci_mmcfg_config_num > 0) {
i = pci_mmcfg_config_num - 1;
cfg = &pci_mmcfg_config[i];
if (cfg->end_bus_number < cfg->start_bus_number)
cfg->end_bus_number = 255;
}
/* don't overlap please */
for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
cfg = &pci_mmcfg_config[i];
cfgx = &pci_mmcfg_config[i+1];
if (cfg->end_bus_number < cfg->start_bus_number)
cfg->end_bus_number = 255;
if (cfg->end_bus_number >= cfgx->start_bus_number)
cfg->end_bus_number = cfgx->start_bus_number - 1;
}
}
static int __init pci_mmcfg_check_hostbridge(void)
{
u32 l;
......@@ -186,31 +304,33 @@ static int __init pci_mmcfg_check_hostbridge(void)
pci_mmcfg_config_num = 0;
pci_mmcfg_config = NULL;
name = NULL;
for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
bus = pci_mmcfg_probes[i].bus;
devfn = pci_mmcfg_probes[i].devfn;
raw_pci_ops->read(0, bus, devfn, 0, 4, &l);
vendor = l & 0xffff;
device = (l >> 16) & 0xffff;
name = NULL;
if (pci_mmcfg_probes[i].vendor == vendor &&
pci_mmcfg_probes[i].device == device)
name = pci_mmcfg_probes[i].probe();
}
if (name) {
printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n",
name, pci_mmcfg_config_num ? "with" : "without");
if (name)
printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n",
name);
}
return name != NULL;
/* some end_bus_number is crazy, fix it */
pci_mmcfg_check_end_bus_number();
return pci_mmcfg_config_num != 0;
}
static void __init pci_mmcfg_insert_resources(void)
{
#define PCI_MMCFG_RESOURCE_NAME_LEN 19
#define PCI_MMCFG_RESOURCE_NAME_LEN 24
int i;
struct resource *res;
char *names;
......@@ -228,9 +348,10 @@ static void __init pci_mmcfg_insert_resources(void)
struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i];
num_buses = cfg->end_bus_number - cfg->start_bus_number + 1;
res->name = names;
snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u",
cfg->pci_segment);
res->start = cfg->address;
snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN,
"PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment,
cfg->start_bus_number, cfg->end_bus_number);
res->start = cfg->address + (cfg->start_bus_number << 20);
res->end = res->start + (num_buses << 20) - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
insert_resource(&iomem_resource, res);
......@@ -354,8 +475,6 @@ static void __init pci_mmcfg_reject_broken(int early)
(pci_mmcfg_config[0].address == 0))
return;
cfg = &pci_mmcfg_config[0];
for (i = 0; i < pci_mmcfg_config_num; i++) {
int valid = 0;
u64 addr, size;
......@@ -423,10 +542,10 @@ static void __init __pci_mmcfg_init(int early)
known_bridge = 1;
}
if (!known_bridge) {
if (!known_bridge)
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
pci_mmcfg_reject_broken(early);
}
if ((pci_mmcfg_config_num == 0) ||
(pci_mmcfg_config == NULL) ||
......
......@@ -112,13 +112,18 @@ static struct pci_raw_ops pci_mmcfg = {
static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
{
void __iomem *addr;
u32 size;
size = (cfg->end_bus_number + 1) << 20;
addr = ioremap_nocache(cfg->address, size);
u64 start, size;
start = cfg->start_bus_number;
start <<= 20;
start += cfg->address;
size = cfg->end_bus_number + 1 - cfg->start_bus_number;
size <<= 20;
addr = ioremap_nocache(start, size);
if (addr) {
printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
cfg->address, cfg->address + size - 1);
start, start + size - 1);
addr -= cfg->start_bus_number << 20;
}
return addr;
}
......@@ -157,7 +162,7 @@ void __init pci_mmcfg_arch_free(void)
for (i = 0; i < pci_mmcfg_config_num; ++i) {
if (pci_mmcfg_virt[i].virt) {
iounmap(pci_mmcfg_virt[i].virt);
iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20));
pci_mmcfg_virt[i].virt = NULL;
pci_mmcfg_virt[i].cfg = NULL;
}
......
......@@ -66,11 +66,18 @@ struct acpi_pci_root {
struct acpi_device * device;
struct acpi_pci_id id;
struct pci_bus *bus;
u32 osc_support_set; /* _OSC state of support bits */
u32 osc_control_set; /* _OSC state of control bits */
u32 osc_control_qry; /* the latest _OSC query result */
u32 osc_queried:1; /* has _OSC control been queried? */
};
static LIST_HEAD(acpi_pci_roots);
static struct acpi_pci_driver *sub_driver;
static DEFINE_MUTEX(osc_lock);
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
{
......@@ -185,6 +192,175 @@ static void acpi_pci_bridge_scan(struct acpi_device *device)
}
}
static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
static acpi_status acpi_pci_run_osc(acpi_handle handle,
const u32 *capbuf, u32 *retval)
{
acpi_status status;
struct acpi_object_list input;
union acpi_object in_params[4];
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *out_obj;
u32 errors;
/* Setting up input parameters */
input.count = 4;
input.pointer = in_params;
in_params[0].type = ACPI_TYPE_BUFFER;
in_params[0].buffer.length = 16;
in_params[0].buffer.pointer = OSC_UUID;
in_params[1].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = 1;
in_params[2].type = ACPI_TYPE_INTEGER;
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 *)capbuf;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE(status))
return status;
if (!output.length)
return AE_NULL_OBJECT;
out_obj = output.pointer;
if (out_obj->type != ACPI_TYPE_BUFFER) {
printk(KERN_DEBUG "_OSC evaluation returned wrong type\n");
status = AE_TYPE;
goto out_kfree;
}
/* Need to ignore the bit0 in result code */
errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
if (errors) {
if (errors & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request failed\n");
if (errors & OSC_INVALID_UUID_ERROR)
printk(KERN_DEBUG "_OSC invalid UUID\n");
if (errors & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (errors & OSC_CAPABILITIES_MASK_ERROR) {
if (capbuf[OSC_QUERY_TYPE] & OSC_QUERY_ENABLE)
goto out_success;
printk(KERN_DEBUG
"Firmware did not grant requested _OSC control\n");
status = AE_SUPPORT;
goto out_kfree;
}
status = AE_ERROR;
goto out_kfree;
}
out_success:
*retval = *((u32 *)(out_obj->buffer.pointer + 8));
status = AE_OK;
out_kfree:
kfree(output.pointer);
return status;
}
static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
{
acpi_status status;
u32 support_set, result, capbuf[3];
/* do _OSC query for all possible controls */
support_set = root->osc_support_set | (flags & OSC_SUPPORT_MASKS);
capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
capbuf[OSC_SUPPORT_TYPE] = support_set;
capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
if (ACPI_SUCCESS(status)) {
root->osc_support_set = support_set;
root->osc_control_qry = result;
root->osc_queried = 1;
}
return status;
}
static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
{
acpi_status status;
acpi_handle tmp;
status = acpi_get_handle(root->device->handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
return status;
mutex_lock(&osc_lock);
status = acpi_pci_query_osc(root, flags);
mutex_unlock(&osc_lock);
return status;
}
static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
{
struct acpi_pci_root *root;
list_for_each_entry(root, &acpi_pci_roots, node) {
if (root->device->handle == handle)
return root;
}
return NULL;
}
/**
* acpi_pci_osc_control_set - commit requested control to Firmware
* @handle: acpi_handle for the target ACPI object
* @flags: driver's requested control bits
*
* Attempt to take control from Firmware on requested control bits.
**/
acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
{
acpi_status status;
u32 control_req, result, capbuf[3];
acpi_handle tmp;
struct acpi_pci_root *root;
status = acpi_get_handle(handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
return status;
control_req = (flags & OSC_CONTROL_MASKS);
if (!control_req)
return AE_TYPE;
root = acpi_pci_find_root(handle);
if (!root)
return AE_NOT_EXIST;
mutex_lock(&osc_lock);
/* No need to evaluate _OSC if the control was already granted. */
if ((root->osc_control_set & control_req) == control_req)
goto out;
/* Need to query controls first before requesting them */
if (!root->osc_queried) {
status = acpi_pci_query_osc(root, root->osc_support_set);
if (ACPI_FAILURE(status))
goto out;
}
if ((root->osc_control_qry & control_req) != control_req) {
printk(KERN_DEBUG
"Firmware did not grant requested _OSC control\n");
status = AE_SUPPORT;
goto out;
}
capbuf[OSC_QUERY_TYPE] = 0;
capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
status = acpi_pci_run_osc(handle, capbuf, &result);
if (ACPI_SUCCESS(status))
root->osc_control_set = result;
out:
mutex_unlock(&osc_lock);
return status;
}
EXPORT_SYMBOL(acpi_pci_osc_control_set);
static int __devinit acpi_pci_root_add(struct acpi_device *device)
{
int result = 0;
......@@ -217,7 +393,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
* PCI domains, so we indicate this in _OSC support capabilities.
*/
flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
pci_acpi_osc_support(device->handle, flags);
acpi_pci_osc_support(root, flags);
/*
* Segment
......@@ -353,7 +529,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
if (pci_msi_enabled())
flags |= OSC_MSI_SUPPORT;
if (flags != base_flags)
pci_acpi_osc_support(device->handle, flags);
acpi_pci_osc_support(root, flags);
end:
if (result) {
......
......@@ -59,3 +59,13 @@ config HT_IRQ
This allows native hypertransport devices to use interrupts.
If unsure say Y.
config PCI_IOV
bool "PCI IOV support"
depends on PCI
help
I/O Virtualization is a PCI feature supported by some devices
which allows them to create virtual devices which share their
physical resources.
If unsure, say N.
......@@ -29,6 +29,8 @@ obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
obj-$(CONFIG_PCI_IOV) += iov.o
#
# Some architectures use the generic PCI setup functions
#
......
......@@ -133,7 +133,7 @@ int pci_bus_add_child(struct pci_bus *bus)
*
* Call hotplug for each new devices.
*/
void pci_bus_add_devices(struct pci_bus *bus)
void pci_bus_add_devices(const struct pci_bus *bus)
{
struct pci_dev *dev;
struct pci_bus *child;
......@@ -184,8 +184,10 @@ void pci_enable_bridges(struct pci_bus *bus)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->subordinate) {
if (atomic_read(&dev->enable_cnt) == 0) {
retval = pci_enable_device(dev);
pci_set_master(dev);
}
pci_enable_bridges(dev->subordinate);
}
}
......
......@@ -30,9 +30,8 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#define MY_NAME "acpi_pcihp"
......@@ -333,19 +332,14 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
{
acpi_status status = AE_NOT_FOUND;
acpi_handle handle, phandle;
struct pci_bus *pbus = bus;
struct pci_dev *pdev;
do {
pdev = pbus->self;
if (!pdev) {
handle = acpi_get_pci_rootbridge_handle(
pci_domain_nr(pbus), pbus->number);
struct pci_bus *pbus;
handle = NULL;
for (pbus = bus; pbus; pbus = pbus->parent) {
handle = acpi_pci_get_bridge_handle(pbus);
if (handle)
break;
}
handle = DEVICE_ACPI_HANDLE(&(pdev->dev));
pbus = pbus->parent;
} while (!handle);
/*
* _HPP settings apply to all child buses, until another _HPP is
......@@ -378,12 +372,10 @@ EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
*
* Attempt to take hotplug control from firmware.
*/
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
{
acpi_status status;
acpi_handle chandle, handle;
struct pci_dev *pdev = dev;
struct pci_bus *parent;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
......@@ -408,33 +400,25 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
dbg("Trying to get hotplug control for %s\n",
(char *)string.pointer);
status = pci_osc_control_set(handle, flags);
status = acpi_pci_osc_control_set(handle, flags);
if (ACPI_SUCCESS(status))
goto got_one;
kfree(string.pointer);
string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
}
pdev = dev;
handle = DEVICE_ACPI_HANDLE(&dev->dev);
while (!handle) {
handle = DEVICE_ACPI_HANDLE(&pdev->dev);
if (!handle) {
/*
* This hotplug controller was not listed in the ACPI name
* space at all. Try to get acpi handle of parent pci bus.
*/
if (!pdev || !pdev->bus->parent)
struct pci_bus *pbus;
for (pbus = pdev->bus; pbus; pbus = pbus->parent) {
handle = acpi_pci_get_bridge_handle(pbus);
if (handle)
break;
parent = pdev->bus->parent;
dbg("Could not find %s in acpi namespace, trying parent\n",
pci_name(pdev));
if (!parent->self)
/* Parent must be a host bridge */
handle = acpi_get_pci_rootbridge_handle(
pci_domain_nr(parent),
parent->number);
else
handle = DEVICE_ACPI_HANDLE(&(parent->self->dev));
pdev = parent->self;
}
}
while (handle) {
......@@ -453,13 +437,13 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
}
dbg("Cannot get control of hotplug hardware for pci %s\n",
pci_name(dev));
pci_name(pdev));
kfree(string.pointer);
return -ENODEV;
got_one:
dbg("Gained control for hotplug HW for pci %s (%s)\n", pci_name(dev),
(char *)string.pointer);
dbg("Gained control for hotplug HW for pci %s (%s)\n",
pci_name(pdev), (char *)string.pointer);
kfree(string.pointer);
return 0;
}
......
/*
* Fake PCI Hot Plug Controller Driver
/* Works like the fakephp driver used to, except a little better.
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003 IBM Corp.
* Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
* - It's possible to remove devices with subordinate busses.
* - New PCI devices that appear via any method, not just a fakephp triggered
* rescan, will be noticed.
* - Devices that are removed via any method, not just a fakephp triggered
* removal, will also be noticed.
*
* Based on ideas and code from:
* Vladimir Kondratiev <vladimir.kondratiev@intel.com>
* Rolf Eike Beer <eike-kernel@sf-tec.de>
* Uses nothing from the pci-hotplug subsystem.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Send feedback to <greg@kroah.com>
*/
/*
*
* This driver will "emulate" removing PCI devices from the system. If
* the "power" file is written to with "0" then the specified PCI device
* will be completely removed from the kernel.
*
* WARNING, this does NOT turn off the power to the PCI device. This is
* a "logical" removal, not a physical or electrical removal.
*
* Use this module at your own risk, you have been warned!
*
* Enabling PCI devices is left as an exercise for the reader...
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/device.h>
#include "../pci.h"
#if !defined(MODULE)
#define MY_NAME "fakephp"
#else
#define MY_NAME THIS_MODULE->name
#endif
#define dbg(format, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: " format, \
MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
struct dummy_slot {
struct list_head node;
struct hotplug_slot *slot;
struct legacy_slot {
struct kobject kobj;
struct pci_dev *dev;
struct work_struct remove_work;
unsigned long removed;
struct list_head list;
};
static int debug;
static int dup_slots;
static LIST_HEAD(slot_list);
static struct workqueue_struct *dummyphp_wq;
static void pci_rescan_worker(struct work_struct *work);
static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
};
static LIST_HEAD(legacy_list);
static void dummy_release(struct hotplug_slot *slot)
static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct dummy_slot *dslot = slot->private;
list_del(&dslot->node);
kfree(dslot->slot->info);
kfree(dslot->slot);
pci_dev_put(dslot->dev);
kfree(dslot);
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
strcpy(buf, "1\n");
return 2;
}
#define SLOT_NAME_SIZE 8
static int add_slot(struct pci_dev *dev)
static void remove_callback(void *data)
{
struct dummy_slot *dslot;
struct hotplug_slot *slot;
char name[SLOT_NAME_SIZE];
int retval = -ENOMEM;
static int count = 1;
slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot)
goto error;
slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
if (!slot->info)
goto error_slot;
slot->info->power_status = 1;
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;
if (dup_slots)
snprintf(name, SLOT_NAME_SIZE, "fake");
else
snprintf(name, SLOT_NAME_SIZE, "fake%d", count++);
dbg("slot->name = %s\n", name);
slot->ops = &dummy_hotplug_slot_ops;
slot->release = &dummy_release;
slot->private = dslot;
retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn), name);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
}
dbg("slot->name = %s\n", hotplug_slot_name(slot));
dslot->slot = slot;
dslot->dev = pci_dev_get(dev);
list_add (&dslot->node, &slot_list);
return retval;
error_dslot:
kfree(dslot);
error_info:
kfree(slot->info);
error_slot:
kfree(slot);
error:
return retval;
pci_remove_bus_device((struct pci_dev *)data);
}
static int __init pci_scan_buses(void)
static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t len)
{
struct pci_dev *dev = NULL;
int lastslot = 0;
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
unsigned long val;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
if (PCI_FUNC(dev->devfn) > 0 &&
lastslot == PCI_SLOT(dev->devfn))
continue;
lastslot = PCI_SLOT(dev->devfn);
add_slot(dev);
}
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
return 0;
}
static void remove_slot(struct dummy_slot *dslot)
{
int retval;
dbg("removing slot %s\n", hotplug_slot_name(dslot->slot));
retval = pci_hp_deregister(dslot->slot);
if (retval)
err("Problem unregistering a slot %s\n",
hotplug_slot_name(dslot->slot));
if (val)
pci_rescan_bus(slot->dev->bus);
else
sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
slot->dev, THIS_MODULE);
return len;
}
/* called from the single-threaded workqueue handler to remove a slot */
static void remove_slot_worker(struct work_struct *work)
{
struct dummy_slot *dslot =
container_of(work, struct dummy_slot, remove_work);
remove_slot(dslot);
}
static struct attribute *legacy_attrs[] = {
&(struct attribute){ .name = "power", .mode = 0644 },
NULL,
};
/**
* pci_rescan_slot - Rescan slot
* @temp: Device template. Should be set: bus and devfn.
*
* Tries hard not to re-enable already existing devices;
* also handles scanning of subfunctions.
*/
static int pci_rescan_slot(struct pci_dev *temp)
static void legacy_release(struct kobject *kobj)
{
struct pci_bus *bus = temp->bus;
struct pci_dev *dev;
int func;
u8 hdr_type;
int count = 0;
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
temp->hdr_type = hdr_type & 0x7f;
if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
pci_dev_put(dev);
else {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
count++;
}
}
/* multifunction device? */
if (!(hdr_type & 0x80))
return count;
/* continue scanning for other functions */
for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
continue;
temp->hdr_type = hdr_type & 0x7f;
if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
pci_dev_put(dev);
else {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
count++;
}
}
}
}
return count;
pci_dev_put(slot->dev);
kfree(slot);
}
static struct kobj_type legacy_ktype = {
.sysfs_ops = &(struct sysfs_ops){
.store = legacy_store, .show = legacy_show
},
.release = &legacy_release,
.default_attrs = legacy_attrs,
};
/**
* pci_rescan_bus - Rescan PCI bus
* @bus: the PCI bus to rescan
*
* Call pci_rescan_slot for each possible function of the bus.
*/
static void pci_rescan_bus(const struct pci_bus *bus)
static int legacy_add_slot(struct pci_dev *pdev)
{
unsigned int devfn;
struct pci_dev *dev;
int retval;
int found = 0;
dev = alloc_pci_dev();
if (!dev)
return;
dev->bus = (struct pci_bus*)bus;
dev->sysdata = bus->sysdata;
for (devfn = 0; devfn < 0x100; devfn += 8) {
dev->devfn = devfn;
found += pci_rescan_slot(dev);
}
struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (found) {
pci_bus_assign_resources(bus);
list_for_each_entry(dev, &bus->devices, bus_list) {
/* Skip already-added devices */
if (dev->is_added)
continue;
retval = pci_bus_add_device(dev);
if (retval)
dev_err(&dev->dev,
"Error adding device, continuing\n");
else
add_slot(dev);
}
pci_bus_add_devices(bus);
}
kfree(dev);
}
if (!slot)
return -ENOMEM;
/* recursively scan all buses */
static void pci_rescan_buses(const struct list_head *list)
{
const struct list_head *l;
list_for_each(l,list) {
const struct pci_bus *b = pci_bus_b(l);
pci_rescan_bus(b);
pci_rescan_buses(&b->children);
if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
&pci_slots_kset->kobj, "%s",
dev_name(&pdev->dev))) {
dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
return -EINVAL;
}
}
slot->dev = pci_dev_get(pdev);
/* initiate rescan of all pci buses */
static inline void pci_rescan(void) {
pci_rescan_buses(&pci_root_buses);
}
/* called from the single-threaded workqueue handler to rescan all pci buses */
static void pci_rescan_worker(struct work_struct *work)
{
pci_rescan();
}
list_add(&slot->list, &legacy_list);
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
/* mis-use enable_slot for rescanning of the pci bus */
cancel_work_sync(&pci_rescan_work);
queue_work(dummyphp_wq, &pci_rescan_work);
return 0;
}
static int disable_slot(struct hotplug_slot *slot)
static int legacy_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct dummy_slot *dslot;
struct pci_dev *dev;
int func;
struct pci_dev *pdev = to_pci_dev(data);
if (!slot)
return -ENODEV;
dslot = slot->private;
dbg("%s - physical_slot = %s\n", __func__, hotplug_slot_name(slot));
if (action == BUS_NOTIFY_ADD_DEVICE) {
legacy_add_slot(pdev);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
struct legacy_slot *slot;
for (func = 7; func >= 0; func--) {
dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func);
if (!dev)
continue;
list_for_each_entry(slot, &legacy_list, list)
if (slot->dev == pdev)
goto found;
if (test_and_set_bit(0, &dslot->removed)) {
dbg("Slot already scheduled for removal\n");
pci_dev_put(dev);
dev_warn(&pdev->dev, "Missing legacy fake slot?");
return -ENODEV;
found:
kobject_del(&slot->kobj);
list_del(&slot->list);
kobject_put(&slot->kobj);
}
/* remove the device from the pci core */
pci_remove_bus_device(dev);
/* queue work item to blow away this sysfs entry and other
* parts.
*/
INIT_WORK(&dslot->remove_work, remove_slot_worker);
queue_work(dummyphp_wq, &dslot->remove_work);
pci_dev_put(dev);
}
return 0;
}
static void cleanup_slots (void)
static struct notifier_block legacy_notifier = {
.notifier_call = legacy_notify
};
static int __init init_legacy(void)
{
struct list_head *tmp;
struct list_head *next;
struct dummy_slot *dslot;
struct pci_dev *pdev = NULL;
destroy_workqueue(dummyphp_wq);
list_for_each_safe (tmp, next, &slot_list) {
dslot = list_entry (tmp, struct dummy_slot, node);
remove_slot(dslot);
}
/* Add existing devices */
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)))
legacy_add_slot(pdev);
/* Be alerted of any new ones */
bus_register_notifier(&pci_bus_type, &legacy_notifier);
return 0;
}
module_init(init_legacy);
static int __init dummyphp_init(void)
static void __exit remove_legacy(void)
{
info(DRIVER_DESC "\n");
struct legacy_slot *slot, *tmp;
dummyphp_wq = create_singlethread_workqueue(MY_NAME);
if (!dummyphp_wq)
return -ENOMEM;
bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
return pci_scan_buses();
}
static void __exit dummyphp_exit(void)
{
cleanup_slots();
list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
list_del(&slot->list);
kobject_del(&slot->kobj);
kobject_put(&slot->kobj);
}
}
module_exit(remove_legacy);
module_init(dummyphp_init);
module_exit(dummyphp_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
MODULE_DESCRIPTION("Legacy version of the fakephp interface");
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
module_param(dup_slots, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(dup_slots, "Force duplicate slot names for debugging");
......@@ -46,10 +46,10 @@ extern int pciehp_force;
extern struct workqueue_struct *pciehp_wq;
#define dbg(format, arg...) \
do { \
do { \
if (pciehp_debug) \
printk("%s: " format, MY_NAME , ## arg); \
} while (0)
printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) \
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) \
......@@ -60,7 +60,7 @@ extern struct workqueue_struct *pciehp_wq;
#define ctrl_dbg(ctrl, format, arg...) \
do { \
if (pciehp_debug) \
dev_printk(, &ctrl->pcie->device, \
dev_printk(KERN_DEBUG, &ctrl->pcie->device, \
format, ## arg); \
} while (0)
#define ctrl_err(ctrl, format, arg...) \
......@@ -108,10 +108,11 @@ struct controller {
u32 slot_cap;
u8 cap_base;
struct timer_list poll_timer;
int cmd_busy;
unsigned int cmd_busy:1;
unsigned int no_cmd_complete:1;
unsigned int link_active_reporting:1;
unsigned int notification_enabled:1;
unsigned int power_fault_detected;
};
#define INT_BUTTON_IGNORE 0
......
......@@ -67,37 +67,27 @@ static int __init parse_detect_mode(void)
return PCIEHP_DETECT_DEFAULT;
}
static struct pcie_port_service_id __initdata port_pci_ids[] = {
{
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.port_type = PCIE_ANY_PORT,
.service_type = PCIE_PORT_SERVICE_HP,
.driver_data = 0,
}, { /* end: all zeroes */ }
};
static int __initdata dup_slot_id;
static int __initdata acpi_slot_detected;
static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
/* Dummy driver for dumplicate name detection */
static int __init dummy_probe(struct pcie_device *dev,
const struct pcie_port_service_id *id)
static int __init dummy_probe(struct pcie_device *dev)
{
int pos;
u32 slot_cap;
struct slot *slot, *tmp;
struct pci_dev *pdev = dev->port;
struct pci_bus *pbus = pdev->subordinate;
if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL)))
return -ENOMEM;
/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
if (pciehp_get_hp_hw_control_from_firmware(pdev))
return -ENODEV;
if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP)))
return -ENODEV;
pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap);
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
slot->number = slot_cap >> 19;
list_for_each_entry(tmp, &dummy_slots, slot_list) {
if (tmp->number == slot->number)
......@@ -111,7 +101,8 @@ static int __init dummy_probe(struct pcie_device *dev,
static struct pcie_port_service_driver __initdata dummy_driver = {
.name = "pciehp_dummy",
.id_table = port_pci_ids,
.port_type = PCIE_ANY_PORT,
.service = PCIE_PORT_SERVICE_HP,
.probe = dummy_probe,
};
......
......@@ -401,7 +401,7 @@ static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe
return 0;
}
static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
static int pciehp_probe(struct pcie_device *dev)
{
int rc;
struct controller *ctrl;
......@@ -475,7 +475,7 @@ static void pciehp_remove (struct pcie_device *dev)
}
#ifdef CONFIG_PM
static int pciehp_suspend (struct pcie_device *dev, pm_message_t state)
static int pciehp_suspend (struct pcie_device *dev)
{
dev_info(&dev->device, "%s ENTRY\n", __func__);
return 0;
......@@ -503,20 +503,12 @@ static int pciehp_resume (struct pcie_device *dev)
}
return 0;
}
#endif
static struct pcie_port_service_id port_pci_ids[] = { {
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.port_type = PCIE_ANY_PORT,
.service_type = PCIE_PORT_SERVICE_HP,
.driver_data = 0,
}, { /* end: all zeroes */ }
};
#endif /* PM */
static struct pcie_port_service_driver hpdriver_portdrv = {
.name = PCIE_MODULE_NAME,
.id_table = &port_pci_ids[0],
.port_type = PCIE_ANY_PORT,
.service = PCIE_PORT_SERVICE_HP,
.probe = pciehp_probe,
.remove = pciehp_remove,
......
......@@ -548,23 +548,21 @@ static int hpc_power_on_slot(struct slot * slot)
slot_cmd = POWER_ON;
cmd_mask = PCI_EXP_SLTCTL_PCC;
/* Enable detection that we turned off at slot power-off time */
if (!pciehp_poll_mode) {
slot_cmd |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE |
PCI_EXP_SLTCTL_PDCE);
cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE |
PCI_EXP_SLTCTL_PDCE);
/* Enable power fault detection turned off at power off time */
slot_cmd |= PCI_EXP_SLTCTL_PFDE;
cmd_mask |= PCI_EXP_SLTCTL_PFDE;
}
retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
if (retval) {
ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd);
return -1;
return retval;
}
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
__func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
ctrl->power_fault_detected = 0;
return retval;
}
......@@ -621,18 +619,10 @@ static int hpc_power_off_slot(struct slot * slot)
slot_cmd = POWER_OFF;
cmd_mask = PCI_EXP_SLTCTL_PCC;
/*
* If we get MRL or presence detect interrupts now, the isr
* will notice the sticky power-fault bit too and issue power
* indicator change commands. This will lead to an endless loop
* of command completions, since the power-fault bit remains on
* till the slot is powered on again.
*/
if (!pciehp_poll_mode) {
slot_cmd &= ~(PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE |
PCI_EXP_SLTCTL_PDCE);
cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE |
PCI_EXP_SLTCTL_PDCE);
/* Disable power fault detection */
slot_cmd &= ~PCI_EXP_SLTCTL_PFDE;
cmd_mask |= PCI_EXP_SLTCTL_PFDE;
}
retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
......@@ -672,10 +662,11 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
PCI_EXP_SLTSTA_CC);
detected &= ~intr_loc;
intr_loc |= detected;
if (!intr_loc)
return IRQ_NONE;
if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, detected)) {
if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, intr_loc)) {
ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n",
__func__);
return IRQ_NONE;
......@@ -709,9 +700,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
pciehp_handle_presence_change(p_slot);
/* Check Power Fault Detected */
if (intr_loc & PCI_EXP_SLTSTA_PFD)
if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
ctrl->power_fault_detected = 1;
pciehp_handle_power_fault(p_slot);
}
return IRQ_HANDLED;
}
......
......@@ -48,10 +48,10 @@ extern int shpchp_debug;
extern struct workqueue_struct *shpchp_wq;
#define dbg(format, arg...) \
do { \
do { \
if (shpchp_debug) \
printk("%s: " format, MY_NAME , ## arg); \
} while (0)
printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) \
printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) \
......@@ -62,7 +62,7 @@ extern struct workqueue_struct *shpchp_wq;
#define ctrl_dbg(ctrl, format, arg...) \
do { \
if (shpchp_debug) \
dev_printk(, &ctrl->pci_dev->dev, \
dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev, \
format, ## arg); \
} while (0)
#define ctrl_err(ctrl, format, arg...) \
......
......@@ -137,7 +137,7 @@ int __ref shpchp_configure_device(struct slot *p_slot)
busnr))
break;
}
if (busnr >= end) {
if (busnr > end) {
ctrl_err(ctrl,
"No free bus for hot-added bridge\n");
pci_dev_put(dev);
......
......@@ -1782,7 +1782,7 @@ static inline void iommu_prepare_isa(void)
ret = iommu_prepare_identity_map(pdev, 0, 16*1024*1024);
if (ret)
printk("IOMMU: Failed to create 0-64M identity map, "
printk(KERN_ERR "IOMMU: Failed to create 0-64M identity map, "
"floppy might not work\n");
}
......
/*
* drivers/pci/iov.c
*
* Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
*
* PCI Express I/O Virtualization (IOV) support.
* Single Root IOV 1.0
*/
#include <linux/pci.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/delay.h>
#include "pci.h"
#define VIRTFN_ID_LEN 16
static inline u8 virtfn_bus(struct pci_dev *dev, int id)
{
return dev->bus->number + ((dev->devfn + dev->sriov->offset +
dev->sriov->stride * id) >> 8);
}
static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
{
return (dev->devfn + dev->sriov->offset +
dev->sriov->stride * id) & 0xff;
}
static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
{
int rc;
struct pci_bus *child;
if (bus->number == busnr)
return bus;
child = pci_find_bus(pci_domain_nr(bus), busnr);
if (child)
return child;
child = pci_add_new_bus(bus, NULL, busnr);
if (!child)
return NULL;
child->subordinate = busnr;
child->dev.parent = bus->bridge;
rc = pci_bus_add_child(child);
if (rc) {
pci_remove_bus(child);
return NULL;
}
return child;
}
static void virtfn_remove_bus(struct pci_bus *bus, int busnr)
{
struct pci_bus *child;
if (bus->number == busnr)
return;
child = pci_find_bus(pci_domain_nr(bus), busnr);
BUG_ON(!child);
if (list_empty(&child->devices))
pci_remove_bus(child);
}
static int virtfn_add(struct pci_dev *dev, int id, int reset)
{
int i;
int rc;
u64 size;
char buf[VIRTFN_ID_LEN];
struct pci_dev *virtfn;
struct resource *res;
struct pci_sriov *iov = dev->sriov;
virtfn = alloc_pci_dev();
if (!virtfn)
return -ENOMEM;
mutex_lock(&iov->dev->sriov->lock);
virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
if (!virtfn->bus) {
kfree(virtfn);
mutex_unlock(&iov->dev->sriov->lock);
return -ENOMEM;
}
virtfn->devfn = virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
pci_setup_device(virtfn);
virtfn->dev.parent = dev->dev.parent;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
if (!res->parent)
continue;
virtfn->resource[i].name = pci_name(virtfn);
virtfn->resource[i].flags = res->flags;
size = resource_size(res);
do_div(size, iov->total);
virtfn->resource[i].start = res->start + size * id;
virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
rc = request_resource(res, &virtfn->resource[i]);
BUG_ON(rc);
}
if (reset)
pci_execute_reset_function(virtfn);
pci_device_add(virtfn, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
virtfn->physfn = pci_dev_get(dev);
virtfn->is_virtfn = 1;
rc = pci_bus_add_device(virtfn);
if (rc)
goto failed1;
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
goto failed1;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
goto failed2;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
return 0;
failed2:
sysfs_remove_link(&dev->dev.kobj, buf);
failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
pci_remove_bus_device(virtfn);
virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
mutex_unlock(&iov->dev->sriov->lock);
return rc;
}
static void virtfn_remove(struct pci_dev *dev, int id, int reset)
{
char buf[VIRTFN_ID_LEN];
struct pci_bus *bus;
struct pci_dev *virtfn;
struct pci_sriov *iov = dev->sriov;
bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id));
if (!bus)
return;
virtfn = pci_get_slot(bus, virtfn_devfn(dev, id));
if (!virtfn)
return;
pci_dev_put(virtfn);
if (reset) {
device_release_driver(&virtfn->dev);
pci_execute_reset_function(virtfn);
}
sprintf(buf, "virtfn%u", id);
sysfs_remove_link(&dev->dev.kobj, buf);
sysfs_remove_link(&virtfn->dev.kobj, "physfn");
mutex_lock(&iov->dev->sriov->lock);
pci_remove_bus_device(virtfn);
virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
mutex_unlock(&iov->dev->sriov->lock);
pci_dev_put(dev);
}
static int sriov_migration(struct pci_dev *dev)
{
u16 status;
struct pci_sriov *iov = dev->sriov;
if (!iov->nr_virtfn)
return 0;
if (!(iov->cap & PCI_SRIOV_CAP_VFM))
return 0;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status);
if (!(status & PCI_SRIOV_STATUS_VFM))
return 0;
schedule_work(&iov->mtask);
return 1;
}
static void sriov_migration_task(struct work_struct *work)
{
int i;
u8 state;
u16 status;
struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask);
for (i = iov->initial; i < iov->nr_virtfn; i++) {
state = readb(iov->mstate + i);
if (state == PCI_SRIOV_VFM_MI) {
writeb(PCI_SRIOV_VFM_AV, iov->mstate + i);
state = readb(iov->mstate + i);
if (state == PCI_SRIOV_VFM_AV)
virtfn_add(iov->self, i, 1);
} else if (state == PCI_SRIOV_VFM_MO) {
virtfn_remove(iov->self, i, 1);
writeb(PCI_SRIOV_VFM_UA, iov->mstate + i);
state = readb(iov->mstate + i);
if (state == PCI_SRIOV_VFM_AV)
virtfn_add(iov->self, i, 0);
}
}
pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status);
status &= ~PCI_SRIOV_STATUS_VFM;
pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status);
}
static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn)
{
int bir;
u32 table;
resource_size_t pa;
struct pci_sriov *iov = dev->sriov;
if (nr_virtfn <= iov->initial)
return 0;
pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table);
bir = PCI_SRIOV_VFM_BIR(table);
if (bir > PCI_STD_RESOURCE_END)
return -EIO;
table = PCI_SRIOV_VFM_OFFSET(table);
if (table + nr_virtfn > pci_resource_len(dev, bir))
return -EIO;
pa = pci_resource_start(dev, bir) + table;
iov->mstate = ioremap(pa, nr_virtfn);
if (!iov->mstate)
return -ENOMEM;
INIT_WORK(&iov->mtask, sriov_migration_task);
iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR;
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
return 0;
}
static void sriov_disable_migration(struct pci_dev *dev)
{
struct pci_sriov *iov = dev->sriov;
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
cancel_work_sync(&iov->mtask);
iounmap(iov->mstate);
}
static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
{
int rc;
int i, j;
int nres;
u16 offset, stride, initial;
struct resource *res;
struct pci_dev *pdev;
struct pci_sriov *iov = dev->sriov;
if (!nr_virtfn)
return 0;
if (iov->nr_virtfn)
return -EINVAL;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
if (initial > iov->total ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
return -EIO;
if (nr_virtfn < 0 || nr_virtfn > iov->total ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
if (!offset || (nr_virtfn > 1 && !stride))
return -EIO;
nres = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
if (res->parent)
nres++;
}
if (nres != iov->nres) {
dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n");
return -ENOMEM;
}
iov->offset = offset;
iov->stride = stride;
if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) {
dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
return -ENOMEM;
}
if (iov->link != dev->devfn) {
pdev = pci_get_slot(dev->bus, iov->link);
if (!pdev)
return -ENODEV;
pci_dev_put(pdev);
if (!pdev->is_physfn)
return -ENODEV;
rc = sysfs_create_link(&dev->dev.kobj,
&pdev->dev.kobj, "dep_link");
if (rc)
return rc;
}
iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
pci_block_user_cfg_access(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
msleep(100);
pci_unblock_user_cfg_access(dev);
iov->initial = initial;
if (nr_virtfn < initial)
initial = nr_virtfn;
for (i = 0; i < initial; i++) {
rc = virtfn_add(dev, i, 0);
if (rc)
goto failed;
}
if (iov->cap & PCI_SRIOV_CAP_VFM) {
rc = sriov_enable_migration(dev, nr_virtfn);
if (rc)
goto failed;
}
kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
iov->nr_virtfn = nr_virtfn;
return 0;
failed:
for (j = 0; j < i; j++)
virtfn_remove(dev, j, 0);
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
pci_block_user_cfg_access(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
ssleep(1);
pci_unblock_user_cfg_access(dev);
if (iov->link != dev->devfn)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
return rc;
}
static void sriov_disable(struct pci_dev *dev)
{
int i;
struct pci_sriov *iov = dev->sriov;
if (!iov->nr_virtfn)
return;
if (iov->cap & PCI_SRIOV_CAP_VFM)
sriov_disable_migration(dev);
for (i = 0; i < iov->nr_virtfn; i++)
virtfn_remove(dev, i, 0);
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
pci_block_user_cfg_access(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
ssleep(1);
pci_unblock_user_cfg_access(dev);
if (iov->link != dev->devfn)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
iov->nr_virtfn = 0;
}
static int sriov_init(struct pci_dev *dev, int pos)
{
int i;
int rc;
int nres;
u32 pgsz;
u16 ctrl, total, offset, stride;
struct pci_sriov *iov;
struct resource *res;
struct pci_dev *pdev;
if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
return -ENODEV;
pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
if (ctrl & PCI_SRIOV_CTRL_VFE) {
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0);
ssleep(1);
}
pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total);
if (!total)
return 0;
ctrl = 0;
list_for_each_entry(pdev, &dev->bus->devices, bus_list)
if (pdev->is_physfn)
goto found;
pdev = NULL;
if (pci_ari_enabled(dev->bus))
ctrl |= PCI_SRIOV_CTRL_ARI;
found:
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
if (!offset || (total > 1 && !stride))
return -EIO;
pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0;
pgsz &= ~((1 << i) - 1);
if (!pgsz)
return -EIO;
pgsz &= ~(pgsz - 1);
pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
nres = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
i += __pci_read_base(dev, pci_bar_unknown, res,
pos + PCI_SRIOV_BAR + i * 4);
if (!res->flags)
continue;
if (resource_size(res) & (PAGE_SIZE - 1)) {
rc = -EIO;
goto failed;
}
res->end = res->start + resource_size(res) * total - 1;
nres++;
}
iov = kzalloc(sizeof(*iov), GFP_KERNEL);
if (!iov) {
rc = -ENOMEM;
goto failed;
}
iov->pos = pos;
iov->nres = nres;
iov->ctrl = ctrl;
iov->total = total;
iov->offset = offset;
iov->stride = stride;
iov->pgsz = pgsz;
iov->self = dev;
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
if (pdev)
iov->dev = pci_dev_get(pdev);
else {
iov->dev = dev;
mutex_init(&iov->lock);
}
dev->sriov = iov;
dev->is_physfn = 1;
return 0;
failed:
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
res->flags = 0;
}
return rc;
}
static void sriov_release(struct pci_dev *dev)
{
BUG_ON(dev->sriov->nr_virtfn);
if (dev == dev->sriov->dev)
mutex_destroy(&dev->sriov->lock);
else
pci_dev_put(dev->sriov->dev);
kfree(dev->sriov);
dev->sriov = NULL;
}
static void sriov_restore_state(struct pci_dev *dev)
{
int i;
u16 ctrl;
struct pci_sriov *iov = dev->sriov;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl);
if (ctrl & PCI_SRIOV_CTRL_VFE)
return;
for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++)
pci_update_resource(dev, i);
pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
msleep(100);
}
/**
* pci_iov_init - initialize the IOV capability
* @dev: the PCI device
*
* Returns 0 on success, or negative on failure.
*/
int pci_iov_init(struct pci_dev *dev)
{
int pos;
if (!dev->is_pcie)
return -ENODEV;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
if (pos)
return sriov_init(dev, pos);
return -ENODEV;
}
/**
* pci_iov_release - release resources used by the IOV capability
* @dev: the PCI device
*/
void pci_iov_release(struct pci_dev *dev)
{
if (dev->is_physfn)
sriov_release(dev);
}
/**
* pci_iov_resource_bar - get position of the SR-IOV BAR
* @dev: the PCI device
* @resno: the resource number
* @type: the BAR type to be filled in
*
* Returns position of the BAR encapsulated in the SR-IOV capability.
*/
int pci_iov_resource_bar(struct pci_dev *dev, int resno,
enum pci_bar_type *type)
{
if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
return 0;
BUG_ON(!dev->is_physfn);
*type = pci_bar_unknown;
return dev->sriov->pos + PCI_SRIOV_BAR +
4 * (resno - PCI_IOV_RESOURCES);
}
/**
* pci_restore_iov_state - restore the state of the IOV capability
* @dev: the PCI device
*/
void pci_restore_iov_state(struct pci_dev *dev)
{
if (dev->is_physfn)
sriov_restore_state(dev);
}
/**
* pci_iov_bus_range - find bus range used by Virtual Function
* @bus: the PCI bus
*
* Returns max number of buses (exclude current one) used by Virtual
* Functions.
*/
int pci_iov_bus_range(struct pci_bus *bus)
{
int max = 0;
u8 busnr;
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
if (!dev->is_physfn)
continue;
busnr = virtfn_bus(dev, dev->sriov->total - 1);
if (busnr > max)
max = busnr;
}
return max ? max - bus->number : 0;
}
/**
* pci_enable_sriov - enable the SR-IOV capability
* @dev: the PCI device
*
* Returns 0 on success, or negative on failure.
*/
int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
{
might_sleep();
if (!dev->is_physfn)
return -ENODEV;
return sriov_enable(dev, nr_virtfn);
}
EXPORT_SYMBOL_GPL(pci_enable_sriov);
/**
* pci_disable_sriov - disable the SR-IOV capability
* @dev: the PCI device
*/
void pci_disable_sriov(struct pci_dev *dev)
{
might_sleep();
if (!dev->is_physfn)
return;
sriov_disable(dev);
}
EXPORT_SYMBOL_GPL(pci_disable_sriov);
/**
* pci_sriov_migration - notify SR-IOV core of Virtual Function Migration
* @dev: the PCI device
*
* Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not.
*
* Physical Function driver is responsible to register IRQ handler using
* VF Migration Interrupt Message Number, and call this function when the
* interrupt is generated by the hardware.
*/
irqreturn_t pci_sriov_migration(struct pci_dev *dev)
{
if (!dev->is_physfn)
return IRQ_NONE;
return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE;
}
EXPORT_SYMBOL_GPL(pci_sriov_migration);
......@@ -27,48 +27,53 @@ static int pci_msi_enable = 1;
/* Arch hooks */
int __attribute__ ((weak))
arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
#ifndef arch_msi_check_device
int arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
{
return 0;
}
#endif
int __attribute__ ((weak))
arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry)
{
return 0;
}
int __attribute__ ((weak))
arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
#ifndef arch_setup_msi_irqs
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct msi_desc *entry;
int ret;
/*
* If an architecture wants to support multiple MSI, it needs to
* override arch_setup_msi_irqs()
*/
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
list_for_each_entry(entry, &dev->msi_list, list) {
ret = arch_setup_msi_irq(dev, entry);
if (ret)
if (ret < 0)
return ret;
if (ret > 0)
return -ENOSPC;
}
return 0;
}
#endif
void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq)
{
return;
}
void __attribute__ ((weak))
arch_teardown_msi_irqs(struct pci_dev *dev)
#ifndef arch_teardown_msi_irqs
void arch_teardown_msi_irqs(struct pci_dev *dev)
{
struct msi_desc *entry;
list_for_each_entry(entry, &dev->msi_list, list) {
if (entry->irq != 0)
arch_teardown_msi_irq(entry->irq);
int i, nvec;
if (entry->irq == 0)
continue;
nvec = 1 << entry->msi_attrib.multiple;
for (i = 0; i < nvec; i++)
arch_teardown_msi_irq(entry->irq + i);
}
}
#endif
static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
{
......@@ -111,27 +116,14 @@ static inline __attribute_const__ u32 msi_mask(unsigned x)
return (1 << (1 << x)) - 1;
}
static void msix_flush_writes(struct irq_desc *desc)
static inline __attribute_const__ u32 msi_capable_mask(u16 control)
{
struct msi_desc *entry;
return msi_mask((control >> 1) & 7);
}
entry = get_irq_desc_msi(desc);
BUG_ON(!entry || !entry->dev);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
/* nothing to do */
break;
case PCI_CAP_ID_MSIX:
{
int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
readl(entry->mask_base + offset);
break;
}
default:
BUG();
break;
}
static inline __attribute_const__ u32 msi_enabled_mask(u16 control)
{
return msi_mask((control >> 4) & 7);
}
/*
......@@ -143,49 +135,71 @@ static void msix_flush_writes(struct irq_desc *desc)
* Returns 1 if it succeeded in masking the interrupt and 0 if the device
* doesn't support MSI masking.
*/
static int msi_set_mask_bits(struct irq_desc *desc, u32 mask, u32 flag)
static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
{
struct msi_desc *entry;
u32 mask_bits = desc->masked;
entry = get_irq_desc_msi(desc);
BUG_ON(!entry || !entry->dev);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
if (entry->msi_attrib.maskbit) {
int pos;
u32 mask_bits;
if (!desc->msi_attrib.maskbit)
return;
pos = (long)entry->mask_base;
pci_read_config_dword(entry->dev, pos, &mask_bits);
mask_bits &= ~(mask);
mask_bits |= flag & mask;
pci_write_config_dword(entry->dev, pos, mask_bits);
} else {
return 0;
}
break;
case PCI_CAP_ID_MSIX:
{
int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
mask_bits &= ~mask;
mask_bits |= flag;
pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits);
desc->masked = mask_bits;
}
/*
* This internal function does not flush PCI writes to the device.
* All users must ensure that they read from the device before either
* assuming that the device state is up to date, or returning out of this
* file. This saves a few milliseconds when initialising devices with lots
* of MSI-X interrupts.
*/
static void msix_mask_irq(struct msi_desc *desc, u32 flag)
{
u32 mask_bits = desc->masked;
unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
writel(flag, entry->mask_base + offset);
readl(entry->mask_base + offset);
break;
}
default:
BUG();
break;
mask_bits &= ~1;
mask_bits |= flag;
writel(mask_bits, desc->mask_base + offset);
desc->masked = mask_bits;
}
static void msi_set_mask_bit(unsigned irq, u32 flag)
{
struct msi_desc *desc = get_irq_msi(irq);
if (desc->msi_attrib.is_msix) {
msix_mask_irq(desc, flag);
readl(desc->mask_base); /* Flush write to device */
} else {
unsigned offset = irq - desc->dev->irq;
msi_mask_irq(desc, 1 << offset, flag << offset);
}
entry->msi_attrib.masked = !!flag;
return 1;
}
void mask_msi_irq(unsigned int irq)
{
msi_set_mask_bit(irq, 1);
}
void unmask_msi_irq(unsigned int irq)
{
msi_set_mask_bit(irq, 0);
}
void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
{
struct msi_desc *entry = get_irq_desc_msi(desc);
switch(entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
{
if (entry->msi_attrib.is_msix) {
void __iomem *base = entry->mask_base +
entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET);
} else {
struct pci_dev *dev = entry->dev;
int pos = entry->msi_attrib.pos;
u16 data;
......@@ -201,21 +215,6 @@ void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
pci_read_config_word(dev, msi_data_reg(pos, 0), &data);
}
msg->data = data;
break;
}
case PCI_CAP_ID_MSIX:
{
void __iomem *base;
base = entry->mask_base +
entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET);
break;
}
default:
BUG();
}
}
......@@ -229,11 +228,25 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg)
void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
{
struct msi_desc *entry = get_irq_desc_msi(desc);
switch (entry->msi_attrib.type) {
case PCI_CAP_ID_MSI:
{
if (entry->msi_attrib.is_msix) {
void __iomem *base;
base = entry->mask_base +
entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
writel(msg->address_lo,
base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
writel(msg->address_hi,
base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
} else {
struct pci_dev *dev = entry->dev;
int pos = entry->msi_attrib.pos;
u16 msgctl;
pci_read_config_word(dev, msi_control_reg(pos), &msgctl);
msgctl &= ~PCI_MSI_FLAGS_QSIZE;
msgctl |= entry->msi_attrib.multiple << 4;
pci_write_config_word(dev, msi_control_reg(pos), msgctl);
pci_write_config_dword(dev, msi_lower_address_reg(pos),
msg->address_lo);
......@@ -246,23 +259,6 @@ void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg)
pci_write_config_word(dev, msi_data_reg(pos, 0),
msg->data);
}
break;
}
case PCI_CAP_ID_MSIX:
{
void __iomem *base;
base = entry->mask_base +
entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
writel(msg->address_lo,
base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
writel(msg->address_hi,
base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET);
break;
}
default:
BUG();
}
entry->msg = *msg;
}
......@@ -274,37 +270,18 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
write_msi_msg_desc(desc, msg);
}
void mask_msi_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
msi_set_mask_bits(desc, 1, 1);
msix_flush_writes(desc);
}
void unmask_msi_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
msi_set_mask_bits(desc, 1, 0);
msix_flush_writes(desc);
}
static int msi_free_irqs(struct pci_dev* dev);
static struct msi_desc* alloc_msi_entry(void)
static struct msi_desc *alloc_msi_entry(struct pci_dev *dev)
{
struct msi_desc *entry;
entry = kzalloc(sizeof(struct msi_desc), GFP_KERNEL);
if (!entry)
struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return NULL;
INIT_LIST_HEAD(&entry->list);
entry->irq = 0;
entry->dev = NULL;
INIT_LIST_HEAD(&desc->list);
desc->dev = dev;
return entry;
return desc;
}
static void pci_intx_for_msi(struct pci_dev *dev, int enable)
......@@ -328,15 +305,11 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
pci_intx_for_msi(dev, 0);
msi_set_enable(dev, 0);
write_msi_msg(dev->irq, &entry->msg);
if (entry->msi_attrib.maskbit) {
struct irq_desc *desc = irq_to_desc(dev->irq);
msi_set_mask_bits(desc, entry->msi_attrib.maskbits_mask,
entry->msi_attrib.masked);
}
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
msi_mask_irq(entry, msi_capable_mask(control), entry->masked);
control &= ~PCI_MSI_FLAGS_QSIZE;
control |= PCI_MSI_FLAGS_ENABLE;
control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE;
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
}
......@@ -354,9 +327,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
msix_set_enable(dev, 0);
list_for_each_entry(entry, &dev->msi_list, list) {
struct irq_desc *desc = irq_to_desc(entry->irq);
write_msi_msg(entry->irq, &entry->msg);
msi_set_mask_bits(desc, 1, entry->msi_attrib.masked);
msix_mask_irq(entry, entry->masked);
}
BUG_ON(list_empty(&dev->msi_list));
......@@ -378,52 +350,48 @@ EXPORT_SYMBOL_GPL(pci_restore_msi_state);
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
* @nvec: number of interrupts to allocate
*
* Setup the MSI capability structure of device function with a single
* MSI irq, regardless of device function is capable of handling
* multiple messages. A return of zero indicates the successful setup
* of an entry zero with the new MSI irq or non-zero for otherwise.
**/
static int msi_capability_init(struct pci_dev *dev)
* Setup the MSI capability structure of the device with the requested
* number of interrupts. A return value of zero indicates the successful
* setup of an entry with the new MSI irq. A negative return value indicates
* an error, and a positive return value indicates the number of interrupts
* which could have been allocated.
*/
static int msi_capability_init(struct pci_dev *dev, int nvec)
{
struct msi_desc *entry;
int pos, ret;
u16 control;
unsigned mask;
msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
pci_read_config_word(dev, msi_control_reg(pos), &control);
/* MSI Entry Initialization */
entry = alloc_msi_entry();
entry = alloc_msi_entry(dev);
if (!entry)
return -ENOMEM;
entry->msi_attrib.type = PCI_CAP_ID_MSI;
entry->msi_attrib.is_msix = 0;
entry->msi_attrib.is_64 = is_64bit_address(control);
entry->msi_attrib.entry_nr = 0;
entry->msi_attrib.maskbit = is_mask_bit_support(control);
entry->msi_attrib.masked = 1;
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry->msi_attrib.pos = pos;
entry->dev = dev;
if (entry->msi_attrib.maskbit) {
unsigned int base, maskbits, temp;
base = msi_mask_bits_reg(pos, entry->msi_attrib.is_64);
entry->mask_base = (void __iomem *)(long)base;
entry->mask_pos = msi_mask_bits_reg(pos, entry->msi_attrib.is_64);
/* All MSIs are unmasked by default, Mask them all */
pci_read_config_dword(dev, base, &maskbits);
temp = msi_mask((control & PCI_MSI_FLAGS_QMASK) >> 1);
maskbits |= temp;
pci_write_config_dword(dev, base, maskbits);
entry->msi_attrib.maskbits_mask = temp;
}
if (entry->msi_attrib.maskbit)
pci_read_config_dword(dev, entry->mask_pos, &entry->masked);
mask = msi_capable_mask(control);
msi_mask_irq(entry, mask, mask);
list_add_tail(&entry->list, &dev->msi_list);
/* Configure MSI capability structure */
ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
if (ret) {
msi_free_irqs(dev);
return ret;
......@@ -476,26 +444,28 @@ static int msix_capability_init(struct pci_dev *dev,
/* MSI-X Table Initialization */
for (i = 0; i < nvec; i++) {
entry = alloc_msi_entry();
entry = alloc_msi_entry(dev);
if (!entry)
break;
j = entries[i].entry;
entry->msi_attrib.type = PCI_CAP_ID_MSIX;
entry->msi_attrib.is_msix = 1;
entry->msi_attrib.is_64 = 1;
entry->msi_attrib.entry_nr = j;
entry->msi_attrib.maskbit = 1;
entry->msi_attrib.masked = 1;
entry->msi_attrib.default_irq = dev->irq;
entry->msi_attrib.pos = pos;
entry->dev = dev;
entry->mask_base = base;
entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
msix_mask_irq(entry, 1);
list_add_tail(&entry->list, &dev->msi_list);
}
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
if (ret) {
if (ret < 0) {
/* If we had some success report the number of irqs
* we succeeded in setting up. */
int avail = 0;
list_for_each_entry(entry, &dev->msi_list, list) {
if (entry->irq != 0) {
......@@ -503,14 +473,13 @@ static int msix_capability_init(struct pci_dev *dev,
}
}
msi_free_irqs(dev);
if (avail != 0)
ret = avail;
}
/* If we had some success report the number of irqs
* we succeeded in setting up.
*/
if (avail == 0)
avail = ret;
return avail;
if (ret) {
msi_free_irqs(dev);
return ret;
}
i = 0;
......@@ -575,39 +544,54 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type)
}
/**
* pci_enable_msi - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
* pci_enable_msi_block - configure device's MSI capability structure
* @dev: device to configure
* @nvec: number of interrupts to configure
*
* Setup the MSI capability structure of device function with
* a single MSI irq upon its software driver call to request for
* MSI mode enabled on its hardware device function. A return of zero
* indicates the successful setup of an entry zero with the new MSI
* irq or non-zero for otherwise.
**/
int pci_enable_msi(struct pci_dev* dev)
* Allocate IRQs for a device with the MSI capability.
* This function returns a negative errno if an error occurs. If it
* is unable to allocate the number of interrupts requested, it returns
* the number of interrupts it might be able to allocate. If it successfully
* allocates at least the number of interrupts requested, it returns 0 and
* updates the @dev's irq member to the lowest new interrupt number; the
* other interrupt numbers allocated to this device are consecutive.
*/
int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
{
int status;
int status, pos, maxvec;
u16 msgctl;
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (!pos)
return -EINVAL;
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
if (nvec > maxvec)
return maxvec;
status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
if (status)
return status;
WARN_ON(!!dev->msi_enabled);
/* Check whether driver already requested for MSI-X irqs */
/* Check whether driver already requested MSI-X irqs */
if (dev->msix_enabled) {
dev_info(&dev->dev, "can't enable MSI "
"(MSI-X already enabled)\n");
return -EINVAL;
}
status = msi_capability_init(dev);
status = msi_capability_init(dev, nvec);
return status;
}
EXPORT_SYMBOL(pci_enable_msi);
EXPORT_SYMBOL(pci_enable_msi_block);
void pci_msi_shutdown(struct pci_dev* dev)
void pci_msi_shutdown(struct pci_dev *dev)
{
struct msi_desc *entry;
struct msi_desc *desc;
u32 mask;
u16 ctrl;
if (!pci_msi_enable || !dev || !dev->msi_enabled)
return;
......@@ -617,19 +601,15 @@ void pci_msi_shutdown(struct pci_dev* dev)
dev->msi_enabled = 0;
BUG_ON(list_empty(&dev->msi_list));
entry = list_entry(dev->msi_list.next, struct msi_desc, list);
/* Return the the pci reset with msi irqs unmasked */
if (entry->msi_attrib.maskbit) {
u32 mask = entry->msi_attrib.maskbits_mask;
struct irq_desc *desc = irq_to_desc(dev->irq);
msi_set_mask_bits(desc, mask, ~mask);
}
if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
return;
desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &ctrl);
mask = msi_capable_mask(ctrl);
msi_mask_irq(desc, mask, ~mask);
/* Restore dev->irq to its default pin-assertion irq */
dev->irq = entry->msi_attrib.default_irq;
dev->irq = desc->msi_attrib.default_irq;
}
void pci_disable_msi(struct pci_dev* dev)
{
struct msi_desc *entry;
......@@ -640,7 +620,7 @@ void pci_disable_msi(struct pci_dev* dev)
pci_msi_shutdown(dev);
entry = list_entry(dev->msi_list.next, struct msi_desc, list);
if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
if (entry->msi_attrib.is_msix)
return;
msi_free_irqs(dev);
......@@ -652,14 +632,18 @@ static int msi_free_irqs(struct pci_dev* dev)
struct msi_desc *entry, *tmp;
list_for_each_entry(entry, &dev->msi_list, list) {
if (entry->irq)
BUG_ON(irq_has_action(entry->irq));
int i, nvec;
if (!entry->irq)
continue;
nvec = 1 << entry->msi_attrib.multiple;
for (i = 0; i < nvec; i++)
BUG_ON(irq_has_action(entry->irq + i));
}
arch_teardown_msi_irqs(dev);
list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) {
if (entry->msi_attrib.is_msix) {
writel(1, entry->mask_base + entry->msi_attrib.entry_nr
* PCI_MSIX_ENTRY_SIZE
+ PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
......@@ -674,6 +658,23 @@ static int msi_free_irqs(struct pci_dev* dev)
return 0;
}
/**
* pci_msix_table_size - return the number of device's MSI-X table entries
* @dev: pointer to the pci_dev data structure of MSI-X device function
*/
int pci_msix_table_size(struct pci_dev *dev)
{
int pos;
u16 control;
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
if (!pos)
return 0;
pci_read_config_word(dev, msi_control_reg(pos), &control);
return multi_msix_capable(control);
}
/**
* pci_enable_msix - configure device's MSI-X capability structure
* @dev: pointer to the pci_dev data structure of MSI-X device function
......@@ -691,9 +692,8 @@ static int msi_free_irqs(struct pci_dev* dev)
**/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
int status, pos, nr_entries;
int status, nr_entries;
int i, j;
u16 control;
if (!entries)
return -EINVAL;
......@@ -702,9 +702,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
if (status)
return status;
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
pci_read_config_word(dev, msi_control_reg(pos), &control);
nr_entries = multi_msix_capable(control);
nr_entries = pci_msix_table_size(dev);
if (nvec > nr_entries)
return -EINVAL;
......
......@@ -20,14 +20,8 @@
#define msi_mask_bits_reg(base, is64bit) \
( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE
#define multi_msi_capable(control) \
(1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
#define multi_msi_enable(control, num) \
control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE);
#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT))
#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT))
#define msi_enable(control, num) multi_msi_enable(control, num); \
control |= PCI_MSI_FLAGS_ENABLE
#define msix_table_offset_reg(base) (base + 0x04)
#define msix_pba_offset_reg(base) (base + 0x08)
......
......@@ -18,221 +18,6 @@
#include <linux/pci-acpi.h>
#include "pci.h"
struct acpi_osc_data {
acpi_handle handle;
u32 support_set;
u32 control_set;
u32 control_query;
int is_queried;
struct list_head sibiling;
};
static LIST_HEAD(acpi_osc_data_list);
struct acpi_osc_args {
u32 capbuf[3];
};
static DEFINE_MUTEX(pci_acpi_lock);
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 acpi_run_osc(acpi_handle handle,
struct acpi_osc_args *osc_args, u32 *retval)
{
acpi_status status;
struct acpi_object_list input;
union acpi_object in_params[4];
struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *out_obj;
u32 errors, flags = osc_args->capbuf[OSC_QUERY_TYPE];
/* Setting up input parameters */
input.count = 4;
input.pointer = in_params;
in_params[0].type = ACPI_TYPE_BUFFER;
in_params[0].buffer.length = 16;
in_params[0].buffer.pointer = OSC_UUID;
in_params[1].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = 1;
in_params[2].type = ACPI_TYPE_INTEGER;
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 *)osc_args->capbuf;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE(status))
return status;
if (!output.length)
return AE_NULL_OBJECT;
out_obj = output.pointer;
if (out_obj->type != ACPI_TYPE_BUFFER) {
printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n");
status = AE_TYPE;
goto out_kfree;
}
/* Need to ignore the bit0 in result code */
errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
if (errors) {
if (errors & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request fails\n");
if (errors & OSC_INVALID_UUID_ERROR)
printk(KERN_DEBUG "_OSC invalid UUID\n");
if (errors & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (errors & OSC_CAPABILITIES_MASK_ERROR) {
if (flags & OSC_QUERY_ENABLE)
goto out_success;
printk(KERN_DEBUG "_OSC FW not grant req. control\n");
status = AE_SUPPORT;
goto out_kfree;
}
status = AE_ERROR;
goto out_kfree;
}
out_success:
*retval = *((u32 *)(out_obj->buffer.pointer + 8));
status = AE_OK;
out_kfree:
kfree(output.pointer);
return status;
}
static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data)
{
acpi_status status;
u32 support_set, result;
struct acpi_osc_args osc_args;
/* do _OSC query for all possible controls */
support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS);
osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set;
osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
status = acpi_run_osc(osc_data->handle, &osc_args, &result);
if (ACPI_SUCCESS(status)) {
osc_data->support_set = support_set;
osc_data->control_query = result;
osc_data->is_queried = 1;
}
return status;
}
/*
* pci_acpi_osc_support: Invoke _OSC indicating support for the given feature
* @flags: Bitmask of flags to support
*
* See the ACPI spec for the definition of the flags
*/
int pci_acpi_osc_support(acpi_handle handle, u32 flags)
{
acpi_status status;
acpi_handle tmp;
struct acpi_osc_data *osc_data;
int rc = 0;
status = acpi_get_handle(handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
return -ENOTTY;
mutex_lock(&pci_acpi_lock);
osc_data = acpi_get_osc_data(handle);
if (!osc_data) {
printk(KERN_ERR "acpi osc data array is full\n");
rc = -ENOMEM;
goto out;
}
__acpi_query_osc(flags, osc_data);
out:
mutex_unlock(&pci_acpi_lock);
return rc;
}
/**
* pci_osc_control_set - commit requested control to Firmware
* @handle: acpi_handle for the target ACPI object
* @flags: driver's requested control bits
*
* Attempt to take control from Firmware on requested control bits.
**/
acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
{
acpi_status status;
u32 control_req, control_set, result;
acpi_handle tmp;
struct acpi_osc_data *osc_data;
struct acpi_osc_args osc_args;
status = acpi_get_handle(handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
return status;
mutex_lock(&pci_acpi_lock);
osc_data = acpi_get_osc_data(handle);
if (!osc_data) {
printk(KERN_ERR "acpi osc data array is full\n");
status = AE_ERROR;
goto out;
}
control_req = (flags & OSC_CONTROL_MASKS);
if (!control_req) {
status = AE_TYPE;
goto out;
}
/* No need to evaluate _OSC if the control was already granted. */
if ((osc_data->control_set & control_req) == control_req)
goto out;
if (!osc_data->is_queried) {
status = __acpi_query_osc(osc_data->support_set, osc_data);
if (ACPI_FAILURE(status))
goto out;
}
if ((osc_data->control_query & control_req) != control_req) {
status = AE_SUPPORT;
goto out;
}
control_set = osc_data->control_set | control_req;
osc_args.capbuf[OSC_QUERY_TYPE] = 0;
osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set;
osc_args.capbuf[OSC_CONTROL_TYPE] = control_set;
status = acpi_run_osc(handle, &osc_args, &result);
if (ACPI_SUCCESS(status))
osc_data->control_set = result;
out:
mutex_unlock(&pci_acpi_lock);
return status;
}
EXPORT_SYMBOL(pci_osc_control_set);
/*
* _SxD returns the D-state with the highest power
* (lowest D-state number) supported in the S-state "x".
......
......@@ -99,6 +99,52 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
/**
* store_remove_id - remove a PCI device ID from this driver
* @driver: target device driver
* @buf: buffer for scanning device ID data
* @count: input size
*
* Removes a dynamic pci device ID to this driver.
*/
static ssize_t
store_remove_id(struct device_driver *driver, const char *buf, size_t count)
{
struct pci_dynid *dynid, *n;
struct pci_driver *pdrv = to_pci_driver(driver);
__u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
int fields = 0;
int retval = -ENODEV;
fields = sscanf(buf, "%x %x %x %x %x %x",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask);
if (fields < 2)
return -EINVAL;
spin_lock(&pdrv->dynids.lock);
list_for_each_entry_safe(dynid, n, &pdrv->dynids.list, node) {
struct pci_device_id *id = &dynid->id;
if ((id->vendor == vendor) &&
(id->device == device) &&
(subvendor == PCI_ANY_ID || id->subvendor == subvendor) &&
(subdevice == PCI_ANY_ID || id->subdevice == subdevice) &&
!((id->class ^ class) & class_mask)) {
list_del(&dynid->node);
kfree(dynid);
retval = 0;
break;
}
}
spin_unlock(&pdrv->dynids.lock);
if (retval)
return retval;
return count;
}
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
static void
pci_free_dynids(struct pci_driver *drv)
{
......@@ -125,6 +171,20 @@ static void pci_remove_newid_file(struct pci_driver *drv)
{
driver_remove_file(&drv->driver, &driver_attr_new_id);
}
static int
pci_create_removeid_file(struct pci_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
error = driver_create_file(&drv->driver,&driver_attr_remove_id);
return error;
}
static void pci_remove_removeid_file(struct pci_driver *drv)
{
driver_remove_file(&drv->driver, &driver_attr_remove_id);
}
#else /* !CONFIG_HOTPLUG */
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv)
......@@ -132,6 +192,11 @@ static inline int pci_create_newid_file(struct pci_driver *drv)
return 0;
}
static inline void pci_remove_newid_file(struct pci_driver *drv) {}
static inline int pci_create_removeid_file(struct pci_driver *drv)
{
return 0;
}
static inline void pci_remove_removeid_file(struct pci_driver *drv) {}
#endif
/**
......@@ -899,13 +964,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
/* register with core */
error = driver_register(&drv->driver);
if (error)
return error;
goto out;
error = pci_create_newid_file(drv);
if (error)
driver_unregister(&drv->driver);
goto out_newid;
error = pci_create_removeid_file(drv);
if (error)
goto out_removeid;
out:
return error;
out_removeid:
pci_remove_newid_file(drv);
out_newid:
driver_unregister(&drv->driver);
goto out;
}
/**
......@@ -921,6 +996,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
void
pci_unregister_driver(struct pci_driver *drv)
{
pci_remove_removeid_file(drv);
pci_remove_newid_file(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
......@@ -1020,6 +1096,7 @@ struct bus_type pci_bus_type = {
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
.pm = PCI_PM_OPS_PTR,
};
......
......@@ -219,6 +219,83 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,
return count;
}
#ifdef CONFIG_HOTPLUG
static DEFINE_MUTEX(pci_remove_rescan_mutex);
static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
size_t count)
{
unsigned long val;
struct pci_bus *b = NULL;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
while ((b = pci_find_next_bus(b)) != NULL)
pci_rescan_bus(b);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
}
struct bus_attribute pci_bus_attrs[] = {
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
__ATTR_NULL
};
static ssize_t
dev_rescan_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
struct pci_dev *pdev = to_pci_dev(dev);
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
pci_rescan_bus(pdev->bus);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
}
static void remove_callback(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
mutex_lock(&pci_remove_rescan_mutex);
pci_remove_bus_device(pdev);
mutex_unlock(&pci_remove_rescan_mutex);
}
static ssize_t
remove_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
int ret = 0;
unsigned long val;
struct pci_dev *pdev = to_pci_dev(dev);
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (pci_is_root_bus(pdev->bus))
return -EBUSY;
/* An attribute cannot be unregistered by one of its own methods,
* so we have to use this roundabout approach.
*/
if (val)
ret = device_schedule_callback(dev, remove_callback);
if (ret)
count = ret;
return count;
}
#endif
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
......@@ -237,9 +314,24 @@ struct device_attribute pci_dev_attrs[] = {
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store),
__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
#ifdef CONFIG_HOTPLUG
__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store),
#endif
__ATTR_NULL,
};
static ssize_t
boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf(buf, "%u\n",
!!(pdev->resource[PCI_ROM_RESOURCE].flags &
IORESOURCE_ROM_SHADOW));
}
struct device_attribute vga_attr = __ATTR_RO(boot_vga);
static ssize_t
pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
......@@ -492,6 +584,19 @@ pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr,
return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
}
/**
* pci_adjust_legacy_attr - adjustment of legacy file attributes
* @b: bus to create files under
* @mmap_type: I/O port or memory
*
* Stub implementation. Can be overridden by arch if necessary.
*/
void __weak
pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type)
{
return;
}
/**
* pci_create_legacy_files - create legacy I/O port and memory files
* @b: bus to create files under
......@@ -518,6 +623,7 @@ void pci_create_legacy_files(struct pci_bus *b)
b->legacy_io->read = pci_read_legacy_io;
b->legacy_io->write = pci_write_legacy_io;
b->legacy_io->mmap = pci_mmap_legacy_io;
pci_adjust_legacy_attr(b, pci_mmap_io);
error = device_create_bin_file(&b->dev, b->legacy_io);
if (error)
goto legacy_io_err;
......@@ -528,6 +634,7 @@ void pci_create_legacy_files(struct pci_bus *b)
b->legacy_mem->size = 1024*1024;
b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
b->legacy_mem->mmap = pci_mmap_legacy_mem;
pci_adjust_legacy_attr(b, pci_mmap_mem);
error = device_create_bin_file(&b->dev, b->legacy_mem);
if (error)
goto legacy_mem_err;
......@@ -719,8 +826,8 @@ static int pci_create_resource_files(struct pci_dev *pdev)
return 0;
}
#else /* !HAVE_PCI_MMAP */
static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
/**
......@@ -884,18 +991,27 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
pdev->rom_attr = attr;
}
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
retval = device_create_file(&pdev->dev, &vga_attr);
if (retval)
goto err_rom_file;
}
/* add platform-specific attributes */
retval = pcibios_add_platform_entries(pdev);
if (retval)
goto err_rom_file;
goto err_vga_file;
/* add sysfs entries for various capabilities */
retval = pci_create_capabilities_sysfs(pdev);
if (retval)
goto err_rom_file;
goto err_vga_file;
return 0;
err_vga_file:
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
device_remove_file(&pdev->dev, &vga_attr);
err_rom_file:
if (rom_size) {
sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
......
此差异已折叠。
此差异已折叠。
......@@ -38,30 +38,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static int __devinit aer_probe (struct pcie_device *dev,
const struct pcie_port_service_id *id );
static int __devinit aer_probe (struct pcie_device *dev);
static void aer_remove(struct pcie_device *dev);
static int aer_suspend(struct pcie_device *dev, pm_message_t state)
{return 0;}
static int aer_resume(struct pcie_device *dev) {return 0;}
static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
enum pci_channel_state error);
static void aer_error_resume(struct pci_dev *dev);
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
/*
* PCI Express bus's AER Root service driver data structure
*/
static struct pcie_port_service_id aer_id[] = {
{
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.port_type = PCIE_RC_PORT,
.service_type = PCIE_PORT_SERVICE_AER,
},
{ /* end: all zeroes */ }
};
static struct pci_error_handlers aer_error_handlers = {
.error_detected = aer_error_detected,
.resume = aer_error_resume,
......@@ -69,14 +52,12 @@ static struct pci_error_handlers aer_error_handlers = {
static struct pcie_port_service_driver aerdriver = {
.name = "aer",
.id_table = &aer_id[0],
.port_type = PCIE_ANY_PORT,
.service = PCIE_PORT_SERVICE_AER,
.probe = aer_probe,
.remove = aer_remove,
.suspend = aer_suspend,
.resume = aer_resume,
.err_handler = &aer_error_handlers,
.reset_link = aer_root_reset,
......@@ -207,8 +188,7 @@ 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,
const struct pcie_port_service_id *id )
static int __devinit aer_probe (struct pcie_device *dev)
{
int status;
struct aer_rpc *rpc;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册