diff options
Diffstat (limited to 'drivers/pci/controller/vmd.c')
| -rw-r--r-- | drivers/pci/controller/vmd.c | 61 | 
1 files changed, 60 insertions, 1 deletions
| diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index a45e8e59d3d4..cc166c683638 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -501,6 +501,40 @@ static inline void vmd_acpi_begin(void) { }  static inline void vmd_acpi_end(void) { }  #endif /* CONFIG_ACPI */ +static void vmd_domain_reset(struct vmd_dev *vmd) +{ +	u16 bus, max_buses = resource_size(&vmd->resources[0]); +	u8 dev, functions, fn, hdr_type; +	char __iomem *base; + +	for (bus = 0; bus < max_buses; bus++) { +		for (dev = 0; dev < 32; dev++) { +			base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, +						PCI_DEVFN(dev, 0), 0); + +			hdr_type = readb(base + PCI_HEADER_TYPE) & +					 PCI_HEADER_TYPE_MASK; + +			functions = (hdr_type & 0x80) ? 8 : 1; +			for (fn = 0; fn < functions; fn++) { +				base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, +						PCI_DEVFN(dev, fn), 0); + +				hdr_type = readb(base + PCI_HEADER_TYPE) & +						PCI_HEADER_TYPE_MASK; + +				if (hdr_type != PCI_HEADER_TYPE_BRIDGE || +				    (readw(base + PCI_CLASS_DEVICE) != +				     PCI_CLASS_BRIDGE_PCI)) +					continue; + +				memset_io(base + PCI_IO_BASE, 0, +					  PCI_ROM_ADDRESS1 - PCI_IO_BASE); +			} +		} +	} +} +  static void vmd_attach_resources(struct vmd_dev *vmd)  {  	vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1]; @@ -541,7 +575,7 @@ static int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,  		int ret;  		ret = pci_read_config_dword(dev, PCI_REG_VMLOCK, &vmlock); -		if (ret || vmlock == ~0) +		if (ret || PCI_POSSIBLE_ERROR(vmlock))  			return -ENODEV;  		if (MB2_SHADOW_EN(vmlock)) { @@ -661,6 +695,21 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)  	return 0;  } +/* + * Since VMD is an aperture to regular PCIe root ports, only allow it to + * control features that the OS is allowed to control on the physical PCI bus. + */ +static void vmd_copy_host_bridge_flags(struct pci_host_bridge *root_bridge, +				       struct pci_host_bridge *vmd_bridge) +{ +	vmd_bridge->native_pcie_hotplug = root_bridge->native_pcie_hotplug; +	vmd_bridge->native_shpc_hotplug = root_bridge->native_shpc_hotplug; +	vmd_bridge->native_aer = root_bridge->native_aer; +	vmd_bridge->native_pme = root_bridge->native_pme; +	vmd_bridge->native_ltr = root_bridge->native_ltr; +	vmd_bridge->native_dpc = root_bridge->native_dpc; +} +  static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  {  	struct pci_sysdata *sd = &vmd->sysdata; @@ -798,6 +847,9 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  		return -ENODEV;  	} +	vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus), +				   to_pci_host_bridge(vmd->bus->bridge)); +  	vmd_attach_resources(vmd);  	if (vmd->irq_domain)  		dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); @@ -805,6 +857,9 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  	vmd_acpi_begin();  	pci_scan_child_bus(vmd->bus); +	vmd_domain_reset(vmd); +	list_for_each_entry(child, &vmd->bus->children, node) +		pci_reset_bus(child->self);  	pci_assign_unassigned_bus_resources(vmd->bus);  	/* @@ -953,6 +1008,10 @@ static const struct pci_device_id vmd_ids[] = {  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |  				VMD_FEAT_HAS_BUS_RESTRICTIONS |  				VMD_FEAT_OFFSET_FIRST_VECTOR,}, +	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa77f), +		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | +				VMD_FEAT_HAS_BUS_RESTRICTIONS | +				VMD_FEAT_OFFSET_FIRST_VECTOR,},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |  				VMD_FEAT_HAS_BUS_RESTRICTIONS | |