master->cd_table.in_ste =
                FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(target->data[0])) ==
                STRTAB_STE_0_CFG_S1_TRANS;
+       master->ste_ats_enabled =
+               FIELD_GET(STRTAB_STE_1_EATS, le64_to_cpu(target->data[1])) ==
+               STRTAB_STE_1_EATS_TRANS;
 
        for (i = 0; i < master->num_streams; ++i) {
                u32 sid = master->streams[i].id;
        return 0;
 }
 
+static void arm_smmu_update_ste(struct arm_smmu_master *master,
+                               struct iommu_domain *sid_domain,
+                               bool ats_enabled)
+{
+       unsigned int s1dss = STRTAB_STE_1_S1DSS_TERMINATE;
+       struct arm_smmu_ste ste;
+
+       if (master->cd_table.in_ste && master->ste_ats_enabled == ats_enabled)
+               return;
+
+       if (sid_domain->type == IOMMU_DOMAIN_IDENTITY)
+               s1dss = STRTAB_STE_1_S1DSS_BYPASS;
+       else
+               WARN_ON(sid_domain->type != IOMMU_DOMAIN_BLOCKED);
+
+       /*
+        * Change the STE into a cdtable one with SID IDENTITY/BLOCKED behavior
+        * using s1dss if necessary. If the cd_table is already installed then
+        * the S1DSS is correct and this will just update the EATS. Otherwise it
+        * installs the entire thing. This will be hitless.
+        */
+       arm_smmu_make_cdtable_ste(&ste, master, ats_enabled, s1dss);
+       arm_smmu_install_ste_for_dev(master, &ste);
+}
+
 int arm_smmu_set_pasid(struct arm_smmu_master *master,
                       struct arm_smmu_domain *smmu_domain, ioasid_t pasid,
                       const struct arm_smmu_cd *cd)
 {
+       struct iommu_domain *sid_domain = iommu_get_domain_for_dev(master->dev);
        struct arm_smmu_attach_state state = {
                .master = master,
                /*
        if (smmu_domain->smmu != master->smmu)
                return -EINVAL;
 
-       if (!master->cd_table.in_ste)
-               return -ENODEV;
+       if (!master->cd_table.in_ste &&
+           sid_domain->type != IOMMU_DOMAIN_IDENTITY &&
+           sid_domain->type != IOMMU_DOMAIN_BLOCKED)
+               return -EINVAL;
 
        cdptr = arm_smmu_alloc_cd_ptr(master, pasid);
        if (!cdptr)
                goto out_unlock;
 
        arm_smmu_write_cd_entry(master, pasid, cdptr, cd);
+       arm_smmu_update_ste(master, sid_domain, state.ats_enabled);
 
        arm_smmu_attach_commit(&state);
 
                arm_smmu_atc_inv_master(master, pasid);
        arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
        mutex_unlock(&arm_smmu_asid_lock);
+
+       /*
+        * When the last user of the CD table goes away downgrade the STE back
+        * to a non-cd_table one.
+        */
+       if (!arm_smmu_ssids_in_use(&master->cd_table)) {
+               struct iommu_domain *sid_domain =
+                       iommu_get_domain_for_dev(master->dev);
+
+               if (sid_domain->type == IOMMU_DOMAIN_IDENTITY ||
+                   sid_domain->type == IOMMU_DOMAIN_BLOCKED)
+                       sid_domain->ops->attach_dev(sid_domain, dev);
+       }
 }
 
 static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,