mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-11 13:04:03 +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);
|
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)
|
static void arm_smmu_detach_dev(struct arm_smmu_master *master)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
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);
|
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))
|
if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
|
||||||
master->ssid_bits = min_t(u8, master->ssid_bits,
|
master->ssid_bits = min_t(u8, master->ssid_bits,
|
||||||
CTXDESC_LINEAR_CDMAX);
|
CTXDESC_LINEAR_CDMAX);
|
||||||
|
|
||||||
ret = iommu_device_link(&smmu->iommu, dev);
|
ret = iommu_device_link(&smmu->iommu, dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_master;
|
goto err_disable_pasid;
|
||||||
|
|
||||||
group = iommu_group_get_for_dev(dev);
|
group = iommu_group_get_for_dev(dev);
|
||||||
if (IS_ERR(group)) {
|
if (IS_ERR(group)) {
|
||||||
@ -2850,6 +2907,8 @@ static int arm_smmu_add_device(struct device *dev)
|
|||||||
|
|
||||||
err_unlink:
|
err_unlink:
|
||||||
iommu_device_unlink(&smmu->iommu, dev);
|
iommu_device_unlink(&smmu->iommu, dev);
|
||||||
|
err_disable_pasid:
|
||||||
|
arm_smmu_disable_pasid(master);
|
||||||
err_free_master:
|
err_free_master:
|
||||||
kfree(master);
|
kfree(master);
|
||||||
fwspec->iommu_priv = NULL;
|
fwspec->iommu_priv = NULL;
|
||||||
@ -2870,6 +2929,7 @@ static void arm_smmu_remove_device(struct device *dev)
|
|||||||
arm_smmu_detach_dev(master);
|
arm_smmu_detach_dev(master);
|
||||||
iommu_group_remove_device(dev);
|
iommu_group_remove_device(dev);
|
||||||
iommu_device_unlink(&smmu->iommu, dev);
|
iommu_device_unlink(&smmu->iommu, dev);
|
||||||
|
arm_smmu_disable_pasid(master);
|
||||||
kfree(master);
|
kfree(master);
|
||||||
iommu_fwspec_free(dev);
|
iommu_fwspec_free(dev);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user