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:
Jean-Philippe Brucker 2020-02-24 17:58:42 +01:00 committed by Will Deacon
parent 7682ce2b12
commit 058c59a047

View File

@ -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);
} }