diff options
Diffstat (limited to 'drivers/pci/controller/vmd.c')
| -rw-r--r-- | drivers/pci/controller/vmd.c | 63 | 
1 files changed, 51 insertions, 12 deletions
| diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 5e80f28f0119..e3fcdfec58b3 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -28,6 +28,7 @@  #define BUS_RESTRICT_CAP(vmcap)	(vmcap & 0x1)  #define PCI_REG_VMCONFIG	0x44  #define BUS_RESTRICT_CFG(vmcfg)	((vmcfg >> 8) & 0x3) +#define VMCONFIG_MSI_REMAP	0x2  #define PCI_REG_VMLOCK		0x70  #define MB2_SHADOW_EN(vmlock)	(vmlock & 0x2) @@ -59,6 +60,13 @@ enum vmd_features {  	 * be used for MSI remapping  	 */  	VMD_FEAT_OFFSET_FIRST_VECTOR		= (1 << 3), + +	/* +	 * Device can bypass remapping MSI-X transactions into its MSI-X table, +	 * avoiding the requirement of a VMD MSI domain for child device +	 * interrupt handling. +	 */ +	VMD_FEAT_CAN_BYPASS_MSI_REMAP		= (1 << 4),  };  /* @@ -306,6 +314,16 @@ static struct msi_domain_info vmd_msi_domain_info = {  	.chip		= &vmd_msi_controller,  }; +static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable) +{ +	u16 reg; + +	pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, ®); +	reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) : +		       (reg | VMCONFIG_MSI_REMAP); +	pci_write_config_word(vmd->dev, PCI_REG_VMCONFIG, reg); +} +  static int vmd_create_irq_domain(struct vmd_dev *vmd)  {  	struct fwnode_handle *fn; @@ -325,6 +343,13 @@ static int vmd_create_irq_domain(struct vmd_dev *vmd)  static void vmd_remove_irq_domain(struct vmd_dev *vmd)  { +	/* +	 * Some production BIOS won't enable remapping between soft reboots. +	 * Ensure remapping is restored before unloading the driver. +	 */ +	if (!vmd->msix_count) +		vmd_set_msi_remapping(vmd, true); +  	if (vmd->irq_domain) {  		struct fwnode_handle *fn = vmd->irq_domain->fwnode; @@ -679,15 +704,32 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  	sd->node = pcibus_to_node(vmd->dev->bus); -	ret = vmd_create_irq_domain(vmd); -	if (ret) -		return ret; -  	/* -	 * Override the irq domain bus token so the domain can be distinguished -	 * from a regular PCI/MSI domain. +	 * Currently MSI remapping must be enabled in guest passthrough mode +	 * due to some missing interrupt remapping plumbing. This is probably +	 * acceptable because the guest is usually CPU-limited and MSI +	 * remapping doesn't become a performance bottleneck.  	 */ -	irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI); +	if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || +	    offset[0] || offset[1]) { +		ret = vmd_alloc_irqs(vmd); +		if (ret) +			return ret; + +		vmd_set_msi_remapping(vmd, true); + +		ret = vmd_create_irq_domain(vmd); +		if (ret) +			return ret; + +		/* +		 * Override the IRQ domain bus token so the domain can be +		 * distinguished from a regular PCI/MSI domain. +		 */ +		irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI); +	} else { +		vmd_set_msi_remapping(vmd, false); +	}  	pci_add_resource(&resources, &vmd->resources[0]);  	pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]); @@ -753,10 +795,6 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)  	if (features & VMD_FEAT_OFFSET_FIRST_VECTOR)  		vmd->first_vec = 1; -	err = vmd_alloc_irqs(vmd); -	if (err) -		return err; -  	spin_lock_init(&vmd->cfg_lock);  	pci_set_drvdata(dev, vmd);  	err = vmd_enable_domain(vmd, features); @@ -825,7 +863,8 @@ static const struct pci_device_id vmd_ids[] = {  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | -				VMD_FEAT_HAS_BUS_RESTRICTIONS,}, +				VMD_FEAT_HAS_BUS_RESTRICTIONS | +				VMD_FEAT_CAN_BYPASS_MSI_REMAP,},  	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |  				VMD_FEAT_HAS_BUS_RESTRICTIONS | | 
