mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 16:53:54 +08:00
Merge branches 'pci/dpc', 'pci/resource' and 'pci/thunderbolt' into next
* pci/dpc: PCI: Add Downstream Port Containment driver PCI: Add Downstream Port Containment portdrv service type PCI: Widen portdrv service type from 4 bits to 8 bits * pci/resource: alpha/PCI: Call iomem_is_exclusive() for IORESOURCE_MEM, but not IORESOURCE_IO PCI: Supply CPU physical address (not bus address) to iomem_is_exclusive() * pci/thunderbolt: thunderbolt: Fix double free of drom buffer
This commit is contained in:
commit
d9322d226f
@ -77,10 +77,10 @@ static int pci_mmap_resource(struct kobject *kobj,
|
|||||||
if (i >= PCI_ROM_RESOURCE)
|
if (i >= PCI_ROM_RESOURCE)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (iomem_is_exclusive(res->start))
|
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
||||||
|
@ -1008,6 +1008,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||||||
if (i >= PCI_ROM_RESOURCE)
|
if (i >= PCI_ROM_RESOURCE)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
||||||
WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
|
WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
|
||||||
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
|
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
|
||||||
@ -1024,10 +1027,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||||||
pci_resource_to_user(pdev, i, res, &start, &end);
|
pci_resource_to_user(pdev, i, res, &start, &end);
|
||||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||||
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
||||||
|
|
||||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,3 +81,17 @@ endchoice
|
|||||||
config PCIE_PME
|
config PCIE_PME
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on PCIEPORTBUS && PM
|
depends on PCIEPORTBUS && PM
|
||||||
|
|
||||||
|
config PCIE_DPC
|
||||||
|
tristate "PCIe Downstream Port Containment support"
|
||||||
|
depends on PCIEPORTBUS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables PCI Express Downstream Port Containment (DPC)
|
||||||
|
driver support. DPC events from Root and Downstream ports
|
||||||
|
will be handled by the DPC driver. If your system doesn't
|
||||||
|
have this capability or you do not want to use this feature,
|
||||||
|
it is safe to answer N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called pcie-dpc.
|
||||||
|
@ -14,3 +14,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
|
|||||||
obj-$(CONFIG_PCIEAER) += aer/
|
obj-$(CONFIG_PCIEAER) += aer/
|
||||||
|
|
||||||
obj-$(CONFIG_PCIE_PME) += pme.o
|
obj-$(CONFIG_PCIE_PME) += pme.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
|
||||||
|
163
drivers/pci/pcie/pcie-dpc.c
Normal file
163
drivers/pci/pcie/pcie-dpc.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* PCI Express Downstream Port Containment services driver
|
||||||
|
* Copyright (C) 2016 Intel Corp.
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/pcieport_if.h>
|
||||||
|
|
||||||
|
struct dpc_dev {
|
||||||
|
struct pcie_device *dev;
|
||||||
|
struct work_struct work;
|
||||||
|
int cap_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dpc_wait_link_inactive(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
unsigned long timeout = jiffies + HZ;
|
||||||
|
u16 lnk_status;
|
||||||
|
|
||||||
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||||
|
while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
|
||||||
|
!time_after(jiffies, timeout)) {
|
||||||
|
msleep(10);
|
||||||
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||||
|
}
|
||||||
|
if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
|
||||||
|
dev_warn(&pdev->dev, "Link state not disabled for DPC event");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interrupt_event_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
|
||||||
|
struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
|
||||||
|
struct pci_bus *parent = pdev->subordinate;
|
||||||
|
|
||||||
|
pci_lock_rescan_remove();
|
||||||
|
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
||||||
|
bus_list) {
|
||||||
|
pci_dev_get(dev);
|
||||||
|
pci_stop_and_remove_bus_device(dev);
|
||||||
|
pci_dev_put(dev);
|
||||||
|
}
|
||||||
|
pci_unlock_rescan_remove();
|
||||||
|
|
||||||
|
dpc_wait_link_inactive(pdev);
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
|
||||||
|
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t dpc_irq(int irq, void *context)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = (struct dpc_dev *)context;
|
||||||
|
struct pci_dev *pdev = dpc->dev->port;
|
||||||
|
u16 status, source;
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID,
|
||||||
|
&source);
|
||||||
|
if (!status)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
dev_info(&dpc->dev->device, "DPC containment event, status:%#06x source:%#06x\n",
|
||||||
|
status, source);
|
||||||
|
|
||||||
|
if (status & PCI_EXP_DPC_STATUS_TRIGGER) {
|
||||||
|
u16 reason = (status >> 1) & 0x3;
|
||||||
|
|
||||||
|
dev_warn(&dpc->dev->device, "DPC %s triggered, remove downstream devices\n",
|
||||||
|
(reason == 0) ? "unmasked uncorrectable error" :
|
||||||
|
(reason == 1) ? "ERR_NONFATAL" :
|
||||||
|
(reason == 2) ? "ERR_FATAL" : "extended error");
|
||||||
|
schedule_work(&dpc->work);
|
||||||
|
}
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
|
||||||
|
static int dpc_probe(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc;
|
||||||
|
struct pci_dev *pdev = dev->port;
|
||||||
|
int status;
|
||||||
|
u16 ctl, cap;
|
||||||
|
|
||||||
|
dpc = kzalloc(sizeof(*dpc), GFP_KERNEL);
|
||||||
|
if (!dpc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
|
||||||
|
dpc->dev = dev;
|
||||||
|
INIT_WORK(&dpc->work, interrupt_event_handler);
|
||||||
|
set_service_data(dev, dpc);
|
||||||
|
|
||||||
|
status = request_irq(dev->irq, dpc_irq, IRQF_SHARED, "pcie-dpc", dpc);
|
||||||
|
if (status) {
|
||||||
|
dev_warn(&dev->device, "request IRQ%d failed: %d\n", dev->irq,
|
||||||
|
status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||||
|
|
||||||
|
ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||||
|
|
||||||
|
dev_info(&dev->device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
|
||||||
|
cap & 0xf, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), (cap >> 8) & 0xf,
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
|
||||||
|
return status;
|
||||||
|
out:
|
||||||
|
kfree(dpc);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dpc_remove(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = get_service_data(dev);
|
||||||
|
struct pci_dev *pdev = dev->port;
|
||||||
|
u16 ctl;
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||||
|
ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||||
|
|
||||||
|
free_irq(dev->irq, dpc);
|
||||||
|
kfree(dpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pcie_port_service_driver dpcdriver = {
|
||||||
|
.name = "dpc",
|
||||||
|
.port_type = PCI_EXP_TYPE_ROOT_PORT | PCI_EXP_TYPE_DOWNSTREAM,
|
||||||
|
.service = PCIE_PORT_SERVICE_DPC,
|
||||||
|
.probe = dpc_probe,
|
||||||
|
.remove = dpc_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dpc_service_init(void)
|
||||||
|
{
|
||||||
|
return pcie_port_service_register(&dpcdriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dpc_service_exit(void)
|
||||||
|
{
|
||||||
|
pcie_port_service_unregister(&dpcdriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("PCI Express Downstream Port Containment driver");
|
||||||
|
MODULE_AUTHOR("Keith Busch <keith.busch@intel.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_VERSION("0.1");
|
||||||
|
|
||||||
|
module_init(dpc_service_init);
|
||||||
|
module_exit(dpc_service_exit);
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
|
|
||||||
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||||
/*
|
/*
|
||||||
* According to the PCI Express Base Specification 2.0, the indices of
|
* According to the PCI Express Base Specification 2.0, the indices of
|
||||||
* the MSI-X table entries used by port services must not exceed 31
|
* the MSI-X table entries used by port services must not exceed 31
|
||||||
*/
|
*/
|
||||||
#define PCIE_PORT_MAX_MSIX_ENTRIES 32
|
#define PCIE_PORT_MAX_MSIX_ENTRIES 32
|
||||||
|
|
||||||
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
|
#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
|
||||||
|
|
||||||
extern struct bus_type pcie_port_bus_type;
|
extern struct bus_type pcie_port_bus_type;
|
||||||
int pcie_port_device_register(struct pci_dev *dev);
|
int pcie_port_device_register(struct pci_dev *dev);
|
||||||
|
@ -51,7 +51,7 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
|
|||||||
|
|
||||||
flags = root->osc_control_set;
|
flags = root->osc_control_set;
|
||||||
|
|
||||||
*srv_mask = PCIE_PORT_SERVICE_VC;
|
*srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||||
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
||||||
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
||||||
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
||||||
|
@ -261,7 +261,7 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
||||||
| PCIE_PORT_SERVICE_VC;
|
| PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||||
if (pci_aer_available())
|
if (pci_aer_available())
|
||||||
cap_mask |= PCIE_PORT_SERVICE_AER;
|
cap_mask |= PCIE_PORT_SERVICE_AER;
|
||||||
|
|
||||||
@ -307,6 +307,8 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||||||
*/
|
*/
|
||||||
pcie_pme_interrupt_enable(dev, false);
|
pcie_pme_interrupt_enable(dev, false);
|
||||||
}
|
}
|
||||||
|
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
|
||||||
|
services |= PCIE_PORT_SERVICE_DPC;
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@ -334,7 +336,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
|
|||||||
device = &pcie->device;
|
device = &pcie->device;
|
||||||
device->bus = &pcie_port_bus_type;
|
device->bus = &pcie_port_bus_type;
|
||||||
device->release = release_pcie_device; /* callback to free pcie dev */
|
device->release = release_pcie_device; /* callback to free pcie dev */
|
||||||
dev_set_name(device, "%s:pcie%02x",
|
dev_set_name(device, "%s:pcie%03x",
|
||||||
pci_name(pdev),
|
pci_name(pdev),
|
||||||
get_descriptor_id(pci_pcie_type(pdev), service));
|
get_descriptor_id(pci_pcie_type(pdev), service));
|
||||||
device->parent = &pdev->dev;
|
device->parent = &pdev->dev;
|
||||||
|
@ -449,6 +449,7 @@ int tb_drom_read(struct tb_switch *sw)
|
|||||||
return tb_drom_parse_entries(sw);
|
return tb_drom_parse_entries(sw);
|
||||||
err:
|
err:
|
||||||
kfree(sw->drom);
|
kfree(sw->drom);
|
||||||
|
sw->drom = NULL;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
||||||
#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */
|
#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */
|
||||||
#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT)
|
#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT)
|
||||||
|
#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */
|
||||||
|
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
|
||||||
|
|
||||||
struct pcie_device {
|
struct pcie_device {
|
||||||
int irq; /* Service IRQ/MSI/MSI-X Vector */
|
int irq; /* Service IRQ/MSI/MSI-X Vector */
|
||||||
|
@ -670,7 +670,8 @@
|
|||||||
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
|
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
|
||||||
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
||||||
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
||||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID
|
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
|
||||||
|
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DPC
|
||||||
|
|
||||||
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
||||||
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
||||||
@ -946,4 +947,21 @@
|
|||||||
#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
|
#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
|
||||||
#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
|
#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
|
||||||
|
|
||||||
|
/* Downstream Port Containment */
|
||||||
|
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
|
||||||
|
#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
|
||||||
|
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
|
||||||
|
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
|
||||||
|
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_CTL 6 /* DPC control */
|
||||||
|
#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */
|
||||||
|
#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_STATUS 8 /* DPC Status */
|
||||||
|
#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */
|
||||||
|
#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
|
||||||
|
|
||||||
#endif /* LINUX_PCI_REGS_H */
|
#endif /* LINUX_PCI_REGS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user