]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/iommu/arm-smmu.c
Merge branches 'x86/vt-d', 'arm/omap', 'arm/smmu', 's390', 'core' and 'x86/amd' into...
[karo-tx-linux.git] / drivers / iommu / arm-smmu.c
index 48a39dfa977795deb8271dc5b34a7b3e0be002d0..47dc7a793f5cf070c50a2f2f98c15058582bb9ee 100644 (file)
                ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS)       \
                        ? 0x400 : 0))
 
+#ifdef CONFIG_64BIT
+#define smmu_writeq    writeq_relaxed
+#else
+#define smmu_writeq(reg64, addr)                               \
+       do {                                                    \
+               u64 __val = (reg64);                            \
+               void __iomem *__addr = (addr);                  \
+               writel_relaxed(__val >> 32, __addr + 4);        \
+               writel_relaxed(__val, __addr);                  \
+       } while (0)
+#endif
+
 /* Configuration registers */
 #define ARM_SMMU_GR0_sCR0              0x0
 #define sCR0_CLIENTPD                  (1 << 0)
 #define ARM_SMMU_CB_SCTLR              0x0
 #define ARM_SMMU_CB_RESUME             0x8
 #define ARM_SMMU_CB_TTBCR2             0x10
-#define ARM_SMMU_CB_TTBR0_LO           0x20
-#define ARM_SMMU_CB_TTBR0_HI           0x24
-#define ARM_SMMU_CB_TTBR1_LO           0x28
-#define ARM_SMMU_CB_TTBR1_HI           0x2c
+#define ARM_SMMU_CB_TTBR0              0x20
+#define ARM_SMMU_CB_TTBR1              0x28
 #define ARM_SMMU_CB_TTBCR              0x30
 #define ARM_SMMU_CB_S1_MAIR0           0x38
 #define ARM_SMMU_CB_S1_MAIR1           0x3c
 #define TTBCR2_SEP_SHIFT               15
 #define TTBCR2_SEP_UPSTREAM            (0x7 << TTBCR2_SEP_SHIFT)
 
-#define TTBRn_HI_ASID_SHIFT            16
+#define TTBRn_ASID_SHIFT               48
 
 #define FSR_MULTI                      (1 << 31)
 #define FSR_SS                         (1 << 30)
@@ -695,12 +705,12 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
                                       struct io_pgtable_cfg *pgtbl_cfg)
 {
        u32 reg;
+       u64 reg64;
        bool stage1;
        struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
        struct arm_smmu_device *smmu = smmu_domain->smmu;
-       void __iomem *cb_base, *gr0_base, *gr1_base;
+       void __iomem *cb_base, *gr1_base;
 
-       gr0_base = ARM_SMMU_GR0(smmu);
        gr1_base = ARM_SMMU_GR1(smmu);
        stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
        cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
@@ -738,22 +748,17 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
 
        /* TTBRs */
        if (stage1) {
-               reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
-               reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0] >> 32;
-               reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
-
-               reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_LO);
-               reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1] >> 32;
-               reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1_HI);
+               reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+
+               reg64 |= ((u64)ARM_SMMU_CB_ASID(cfg)) << TTBRn_ASID_SHIFT;
+               smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR0);
+
+               reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
+               reg64 |= ((u64)ARM_SMMU_CB_ASID(cfg)) << TTBRn_ASID_SHIFT;
+               smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR1);
        } else {
-               reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
-               reg = pgtbl_cfg->arm_lpae_s2_cfg.vttbr >> 32;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
+               reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+               smmu_writeq(reg64, cb_base + ARM_SMMU_CB_TTBR0);
        }
 
        /* TTBCR */
@@ -1212,17 +1217,15 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
 
        /* ATS1 registers can only be written atomically */
        va = iova & ~0xfffUL;
-#ifdef CONFIG_64BIT
        if (smmu->version == ARM_SMMU_V2)
-               writeq_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
+               smmu_writeq(va, cb_base + ARM_SMMU_CB_ATS1PR);
        else
-#endif
                writel_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
 
        if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp,
                                      !(tmp & ATSR_ACTIVE), 5, 50)) {
                dev_err(dev,
-                       "iova to phys timed out on 0x%pad. Falling back to software table walk.\n",
+                       "iova to phys timed out on %pad. Falling back to software table walk.\n",
                        &iova);
                return ops->iova_to_phys(ops, iova);
        }
@@ -1292,33 +1295,25 @@ static void __arm_smmu_release_pci_iommudata(void *data)
        kfree(data);
 }
 
-static int arm_smmu_add_pci_device(struct pci_dev *pdev)
+static int arm_smmu_init_pci_device(struct pci_dev *pdev,
+                                   struct iommu_group *group)
 {
-       int i, ret;
-       u16 sid;
-       struct iommu_group *group;
        struct arm_smmu_master_cfg *cfg;
-
-       group = iommu_group_get_for_dev(&pdev->dev);
-       if (IS_ERR(group))
-               return PTR_ERR(group);
+       u16 sid;
+       int i;
 
        cfg = iommu_group_get_iommudata(group);
        if (!cfg) {
                cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-               if (!cfg) {
-                       ret = -ENOMEM;
-                       goto out_put_group;
-               }
+               if (!cfg)
+                       return -ENOMEM;
 
                iommu_group_set_iommudata(group, cfg,
                                          __arm_smmu_release_pci_iommudata);
        }
 
-       if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) {
-               ret = -ENOSPC;
-               goto out_put_group;
-       }
+       if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
+               return -ENOSPC;
 
        /*
         * Assume Stream ID == Requester ID for now.
@@ -1334,16 +1329,13 @@ static int arm_smmu_add_pci_device(struct pci_dev *pdev)
                cfg->streamids[cfg->num_streamids++] = sid;
 
        return 0;
-out_put_group:
-       iommu_group_put(group);
-       return ret;
 }
 
-static int arm_smmu_add_platform_device(struct device *dev)
+static int arm_smmu_init_platform_device(struct device *dev,
+                                        struct iommu_group *group)
 {
-       struct iommu_group *group;
-       struct arm_smmu_master *master;
        struct arm_smmu_device *smmu = find_smmu_for_device(dev);
+       struct arm_smmu_master *master;
 
        if (!smmu)
                return -ENODEV;
@@ -1352,21 +1344,20 @@ static int arm_smmu_add_platform_device(struct device *dev)
        if (!master)
                return -ENODEV;
 
-       /* No automatic group creation for platform devices */
-       group = iommu_group_alloc();
-       if (IS_ERR(group))
-               return PTR_ERR(group);
-
        iommu_group_set_iommudata(group, &master->cfg, NULL);
-       return iommu_group_add_device(group, dev);
+
+       return 0;
 }
 
 static int arm_smmu_add_device(struct device *dev)
 {
-       if (dev_is_pci(dev))
-               return arm_smmu_add_pci_device(to_pci_dev(dev));
+       struct iommu_group *group;
+
+       group = iommu_group_get_for_dev(dev);
+       if (IS_ERR(group))
+               return PTR_ERR(group);
 
-       return arm_smmu_add_platform_device(dev);
+       return 0;
 }
 
 static void arm_smmu_remove_device(struct device *dev)
@@ -1374,6 +1365,32 @@ static void arm_smmu_remove_device(struct device *dev)
        iommu_group_remove_device(dev);
 }
 
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+       struct iommu_group *group;
+       int ret;
+
+       if (dev_is_pci(dev))
+               group = pci_device_group(dev);
+       else
+               group = generic_device_group(dev);
+
+       if (IS_ERR(group))
+               return group;
+
+       if (dev_is_pci(dev))
+               ret = arm_smmu_init_pci_device(to_pci_dev(dev), group);
+       else
+               ret = arm_smmu_init_platform_device(dev, group);
+
+       if (ret) {
+               iommu_group_put(group);
+               group = ERR_PTR(ret);
+       }
+
+       return group;
+}
+
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
                                    enum iommu_attr attr, void *data)
 {
@@ -1430,6 +1447,7 @@ static struct iommu_ops arm_smmu_ops = {
        .iova_to_phys           = arm_smmu_iova_to_phys,
        .add_device             = arm_smmu_add_device,
        .remove_device          = arm_smmu_remove_device,
+       .device_group           = arm_smmu_device_group,
        .domain_get_attr        = arm_smmu_domain_get_attr,
        .domain_set_attr        = arm_smmu_domain_set_attr,
        .pgsize_bitmap          = -1UL, /* Restricted during device attach */