mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-03 00:54:09 +08:00
iommu/arm-smmu-v3: Add support for PCI PASID
Enable PASID for PCI devices that support it. Initialize PASID early in add_device() because it must be enabled before ATS. Tested-by: Zhangfei Gao <zhangfei.gao@linaro.org> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
7682ce2b12
commit
058c59a047
@ -2628,6 +2628,53 @@ static void arm_smmu_disable_ats(struct arm_smmu_master *master)
|
||||
atomic_dec(&smmu_domain->nr_ats_masters);
|
||||
}
|
||||
|
||||
static int arm_smmu_enable_pasid(struct arm_smmu_master *master)
|
||||
{
|
||||
int ret;
|
||||
int features;
|
||||
int num_pasids;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (!dev_is_pci(master->dev))
|
||||
return -ENODEV;
|
||||
|
||||
pdev = to_pci_dev(master->dev);
|
||||
|
||||
features = pci_pasid_features(pdev);
|
||||
if (features < 0)
|
||||
return features;
|
||||
|
||||
num_pasids = pci_max_pasids(pdev);
|
||||
if (num_pasids <= 0)
|
||||
return num_pasids;
|
||||
|
||||
ret = pci_enable_pasid(pdev, features);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable PASID\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->ssid_bits = min_t(u8, ilog2(num_pasids),
|
||||
master->smmu->ssid_bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void arm_smmu_disable_pasid(struct arm_smmu_master *master)
|
||||
{
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (!dev_is_pci(master->dev))
|
||||
return;
|
||||
|
||||
pdev = to_pci_dev(master->dev);
|
||||
|
||||
if (!pdev->pasid_enabled)
|
||||
return;
|
||||
|
||||
master->ssid_bits = 0;
|
||||
pci_disable_pasid(pdev);
|
||||
}
|
||||
|
||||
static void arm_smmu_detach_dev(struct arm_smmu_master *master)
|
||||
{
|
||||
unsigned long flags;
|
||||
@ -2831,13 +2878,23 @@ static int arm_smmu_add_device(struct device *dev)
|
||||
|
||||
master->ssid_bits = min(smmu->ssid_bits, fwspec->num_pasid_bits);
|
||||
|
||||
/*
|
||||
* Note that PASID must be enabled before, and disabled after ATS:
|
||||
* PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register
|
||||
*
|
||||
* Behavior is undefined if this bit is Set and the value of the PASID
|
||||
* Enable, Execute Requested Enable, or Privileged Mode Requested bits
|
||||
* are changed.
|
||||
*/
|
||||
arm_smmu_enable_pasid(master);
|
||||
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
|
||||
master->ssid_bits = min_t(u8, master->ssid_bits,
|
||||
CTXDESC_LINEAR_CDMAX);
|
||||
|
||||
ret = iommu_device_link(&smmu->iommu, dev);
|
||||
if (ret)
|
||||
goto err_free_master;
|
||||
goto err_disable_pasid;
|
||||
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
if (IS_ERR(group)) {
|
||||
@ -2850,6 +2907,8 @@ static int arm_smmu_add_device(struct device *dev)
|
||||
|
||||
err_unlink:
|
||||
iommu_device_unlink(&smmu->iommu, dev);
|
||||
err_disable_pasid:
|
||||
arm_smmu_disable_pasid(master);
|
||||
err_free_master:
|
||||
kfree(master);
|
||||
fwspec->iommu_priv = NULL;
|
||||
@ -2870,6 +2929,7 @@ static void arm_smmu_remove_device(struct device *dev)
|
||||
arm_smmu_detach_dev(master);
|
||||
iommu_group_remove_device(dev);
|
||||
iommu_device_unlink(&smmu->iommu, dev);
|
||||
arm_smmu_disable_pasid(master);
|
||||
kfree(master);
|
||||
iommu_fwspec_free(dev);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user