@@ -97,6 +97,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_device *smmu);
+static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master);
static void parse_driver_options(struct arm_smmu_device *smmu)
{
@@ -1206,29 +1207,51 @@ static void arm_smmu_write_cd_l1_desc(__le64 *dst,
struct arm_smmu_cd *arm_smmu_get_cd_ptr(struct arm_smmu_master *master,
u32 ssid)
{
- __le64 *l1ptr;
- unsigned int idx;
struct arm_smmu_l1_ctx_desc *l1_desc;
- struct arm_smmu_device *smmu = master->smmu;
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
+ if (!cd_table->cdtab)
+ return NULL;
+
if (cd_table->s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
return (struct arm_smmu_cd *)(cd_table->cdtab +
ssid * CTXDESC_CD_DWORDS);
- idx = ssid >> CTXDESC_SPLIT;
- l1_desc = &cd_table->l1_desc[idx];
- if (!l1_desc->l2ptr) {
- if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
- return NULL;
+ l1_desc = &cd_table->l1_desc[ssid / CTXDESC_L2_ENTRIES];
+ if (!l1_desc->l2ptr)
+ return NULL;
+ return &l1_desc->l2ptr[ssid % CTXDESC_L2_ENTRIES];
+}
- l1ptr = cd_table->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
- arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
- /* An invalid L1CD can be cached */
- arm_smmu_sync_cd(master, ssid, false);
+static struct arm_smmu_cd *arm_smmu_alloc_cd_ptr(struct arm_smmu_master *master,
+ u32 ssid)
+{
+ struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
+ struct arm_smmu_device *smmu = master->smmu;
+
+ if (!cd_table->cdtab) {
+ if (arm_smmu_alloc_cd_tables(master))
+ return NULL;
}
- idx = ssid & (CTXDESC_L2_ENTRIES - 1);
- return &l1_desc->l2ptr[idx];
+
+ if (cd_table->s1fmt == STRTAB_STE_0_S1FMT_64K_L2) {
+ unsigned int idx = ssid / CTXDESC_L2_ENTRIES;
+ struct arm_smmu_l1_ctx_desc *l1_desc;
+
+ l1_desc = &cd_table->l1_desc[idx];
+ if (!l1_desc->l2ptr) {
+ __le64 *l1ptr;
+
+ if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
+ return NULL;
+
+ l1ptr = cd_table->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
+ arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
+ /* An invalid L1CD can be cached */
+ arm_smmu_sync_cd(master, ssid, false);
+ }
+ }
+ return arm_smmu_get_cd_ptr(master, ssid);
}
struct arm_smmu_cd_writer {
@@ -1354,7 +1377,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid,
if (WARN_ON(ssid >= (1 << cd_table->s1cdmax)))
return -E2BIG;
- cd_table_entry = arm_smmu_get_cd_ptr(master, ssid);
+ cd_table_entry = arm_smmu_alloc_cd_ptr(master, ssid);
if (!cd_table_entry)
return -ENOMEM;
@@ -2673,13 +2696,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
struct arm_smmu_cd target_cd;
struct arm_smmu_cd *cdptr;
- if (!master->cd_table.cdtab) {
- ret = arm_smmu_alloc_cd_tables(master);
- if (ret)
- goto out_list_del;
- }
-
- cdptr = arm_smmu_get_cd_ptr(master, IOMMU_NO_PASID);
+ cdptr = arm_smmu_alloc_cd_ptr(master, IOMMU_NO_PASID);
if (!cdptr) {
ret = -ENOMEM;
goto out_list_del;
@@ -275,8 +275,7 @@ struct arm_smmu_ste {
* 2lvl: at most 1024 L1 entries,
* 1024 lazy entries per table.
*/
-#define CTXDESC_SPLIT 10
-#define CTXDESC_L2_ENTRIES (1 << CTXDESC_SPLIT)
+#define CTXDESC_L2_ENTRIES 1024
#define CTXDESC_L1_DESC_DWORDS 1
#define CTXDESC_L1_DESC_V (1UL << 0)