return (phys_fabric_id & df_cfg.node_id_mask) | log_fabric_id;
 }
 
+static u16 get_logical_coh_st_fabric_id_for_current_spa(struct addr_ctx *ctx,
+                                                       struct df4p5_denorm_ctx *denorm_ctx)
+{
+       bool hash_ctl_64k, hash_ctl_2M, hash_ctl_1G, hash_ctl_1T;
+       bool hash_pa8, hash_pa9, hash_pa12, hash_pa13;
+       u64 cs_id = 0;
+
+       hash_ctl_64k    = FIELD_GET(DF4_HASH_CTL_64K,  ctx->map.ctl);
+       hash_ctl_2M     = FIELD_GET(DF4_HASH_CTL_2M,   ctx->map.ctl);
+       hash_ctl_1G     = FIELD_GET(DF4_HASH_CTL_1G,   ctx->map.ctl);
+       hash_ctl_1T     = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);
+
+       hash_pa8  = FIELD_GET(BIT_ULL(8),  denorm_ctx->current_spa);
+       hash_pa8 ^= FIELD_GET(BIT_ULL(14), denorm_ctx->current_spa);
+       hash_pa8 ^= FIELD_GET(BIT_ULL(16), denorm_ctx->current_spa) & hash_ctl_64k;
+       hash_pa8 ^= FIELD_GET(BIT_ULL(21), denorm_ctx->current_spa) & hash_ctl_2M;
+       hash_pa8 ^= FIELD_GET(BIT_ULL(30), denorm_ctx->current_spa) & hash_ctl_1G;
+       hash_pa8 ^= FIELD_GET(BIT_ULL(40), denorm_ctx->current_spa) & hash_ctl_1T;
+
+       hash_pa9  = FIELD_GET(BIT_ULL(9),  denorm_ctx->current_spa);
+       hash_pa9 ^= FIELD_GET(BIT_ULL(17), denorm_ctx->current_spa) & hash_ctl_64k;
+       hash_pa9 ^= FIELD_GET(BIT_ULL(22), denorm_ctx->current_spa) & hash_ctl_2M;
+       hash_pa9 ^= FIELD_GET(BIT_ULL(31), denorm_ctx->current_spa) & hash_ctl_1G;
+       hash_pa9 ^= FIELD_GET(BIT_ULL(41), denorm_ctx->current_spa) & hash_ctl_1T;
+
+       hash_pa12  = FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa);
+       hash_pa12 ^= FIELD_GET(BIT_ULL(18), denorm_ctx->current_spa) & hash_ctl_64k;
+       hash_pa12 ^= FIELD_GET(BIT_ULL(23), denorm_ctx->current_spa) & hash_ctl_2M;
+       hash_pa12 ^= FIELD_GET(BIT_ULL(32), denorm_ctx->current_spa) & hash_ctl_1G;
+       hash_pa12 ^= FIELD_GET(BIT_ULL(42), denorm_ctx->current_spa) & hash_ctl_1T;
+
+       hash_pa13  = FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa);
+       hash_pa13 ^= FIELD_GET(BIT_ULL(19), denorm_ctx->current_spa) & hash_ctl_64k;
+       hash_pa13 ^= FIELD_GET(BIT_ULL(24), denorm_ctx->current_spa) & hash_ctl_2M;
+       hash_pa13 ^= FIELD_GET(BIT_ULL(33), denorm_ctx->current_spa) & hash_ctl_1G;
+       hash_pa13 ^= FIELD_GET(BIT_ULL(43), denorm_ctx->current_spa) & hash_ctl_1T;
+
+       switch (ctx->map.intlv_mode) {
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 13), denorm_ctx->current_spa) << 3;
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 2;
+               cs_id |= (hash_pa9 | (hash_pa12 << 1));
+               cs_id |= hash_pa8 << df_cfg.socket_id_shift;
+               break;
+
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 14), denorm_ctx->current_spa) << 4;
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 2;
+               cs_id |= (hash_pa12 | (hash_pa13 << 1));
+               cs_id |= hash_pa8 << df_cfg.socket_id_shift;
+               break;
+
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 2;
+               cs_id |= (hash_pa8 | (hash_pa9 << 1));
+               break;
+
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 13), denorm_ctx->current_spa) << 3;
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 2;
+               cs_id |= (hash_pa8 | (hash_pa12 << 1));
+               break;
+
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
+               cs_id |= (FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa) << 1);
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 1;
+               cs_id |= hash_pa8;
+               break;
+
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
+               cs_id %= denorm_ctx->mod_value;
+               cs_id <<= 1;
+               cs_id |= hash_pa8;
+               break;
+
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
+               cs_id |= FIELD_GET(GENMASK_ULL(9, 8), denorm_ctx->current_spa);
+               cs_id %= denorm_ctx->mod_value;
+               break;
+
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+               cs_id = FIELD_GET(GENMASK_ULL(63, 12), denorm_ctx->current_spa) << 2;
+               cs_id |= FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa) << 1;
+               cs_id %= denorm_ctx->mod_value;
+               break;
+
+       default:
+               atl_debug_on_bad_intlv_mode(ctx);
+               return 0;
+       }
+
+       if (cs_id > 0xffff) {
+               atl_debug(ctx, "Translation error: Resulting cs_id larger than u16\n");
+               return 0;
+       }
+
+       return cs_id;
+}
+
 static int denorm_addr_common(struct addr_ctx *ctx)
 {
        u64 denorm_addr;
        return 0;
 }
 
+static u64 normalize_addr_df4p5_np2(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx,
+                                   u64 addr)
+{
+       u64 temp_addr_a = 0, temp_addr_b = 0;
+
+       switch (ctx->map.intlv_mode) {
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+               temp_addr_a = FIELD_GET(GENMASK_ULL(11, 10), addr) << 8;
+               break;
+
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+               temp_addr_a = FIELD_GET(GENMASK_ULL(11, 9), addr) << 8;
+               break;
+
+       default:
+               atl_debug_on_bad_intlv_mode(ctx);
+               return 0;
+       }
+
+       switch (ctx->map.intlv_mode) {
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 13), addr) / denorm_ctx->mod_value;
+               temp_addr_b <<= 10;
+               break;
+
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 14), addr) / denorm_ctx->mod_value;
+               temp_addr_b <<= 11;
+               break;
+
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) / denorm_ctx->mod_value;
+               temp_addr_b <<= 10;
+               break;
+
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 13), addr) / denorm_ctx->mod_value;
+               temp_addr_b <<= 11;
+               break;
+
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 1;
+               temp_addr_b |= FIELD_GET(BIT_ULL(9), addr);
+               temp_addr_b /= denorm_ctx->mod_value;
+               temp_addr_b <<= 10;
+               break;
+
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) / denorm_ctx->mod_value;
+               temp_addr_b <<= 11;
+               break;
+
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 2;
+               temp_addr_b |= FIELD_GET(GENMASK_ULL(9, 8), addr);
+               temp_addr_b /= denorm_ctx->mod_value;
+               temp_addr_b <<= 10;
+               break;
+
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+               temp_addr_b = FIELD_GET(GENMASK_ULL(63, 12), addr) << 1;
+               temp_addr_b |= FIELD_GET(BIT_ULL(8), addr);
+               temp_addr_b /= denorm_ctx->mod_value;
+               temp_addr_b <<= 11;
+               break;
+
+       default:
+               atl_debug_on_bad_intlv_mode(ctx);
+               return 0;
+       }
+
+       return denorm_ctx->base_denorm_addr | temp_addr_a | temp_addr_b;
+}
+
+static void recalculate_hashed_bits_df4p5_np2(struct addr_ctx *ctx,
+                                             struct df4p5_denorm_ctx *denorm_ctx)
+{
+       bool hash_ctl_64k, hash_ctl_2M, hash_ctl_1G, hash_ctl_1T, hashed_bit;
+
+       if (!denorm_ctx->rehash_vector)
+               return;
+
+       hash_ctl_64k    = FIELD_GET(DF4_HASH_CTL_64K,  ctx->map.ctl);
+       hash_ctl_2M     = FIELD_GET(DF4_HASH_CTL_2M,   ctx->map.ctl);
+       hash_ctl_1G     = FIELD_GET(DF4_HASH_CTL_1G,   ctx->map.ctl);
+       hash_ctl_1T     = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);
+
+       if (denorm_ctx->rehash_vector & BIT_ULL(8)) {
+               hashed_bit  = FIELD_GET(BIT_ULL(8),  denorm_ctx->current_spa);
+               hashed_bit ^= FIELD_GET(BIT_ULL(14), denorm_ctx->current_spa);
+               hashed_bit ^= FIELD_GET(BIT_ULL(16), denorm_ctx->current_spa) & hash_ctl_64k;
+               hashed_bit ^= FIELD_GET(BIT_ULL(21), denorm_ctx->current_spa) & hash_ctl_2M;
+               hashed_bit ^= FIELD_GET(BIT_ULL(30), denorm_ctx->current_spa) & hash_ctl_1G;
+               hashed_bit ^= FIELD_GET(BIT_ULL(40), denorm_ctx->current_spa) & hash_ctl_1T;
+
+               if (FIELD_GET(BIT_ULL(8), denorm_ctx->current_spa) != hashed_bit)
+                       denorm_ctx->current_spa ^= BIT_ULL(8);
+       }
+
+       if (denorm_ctx->rehash_vector & BIT_ULL(9)) {
+               hashed_bit  = FIELD_GET(BIT_ULL(9),  denorm_ctx->current_spa);
+               hashed_bit ^= FIELD_GET(BIT_ULL(17), denorm_ctx->current_spa) & hash_ctl_64k;
+               hashed_bit ^= FIELD_GET(BIT_ULL(22), denorm_ctx->current_spa) & hash_ctl_2M;
+               hashed_bit ^= FIELD_GET(BIT_ULL(31), denorm_ctx->current_spa) & hash_ctl_1G;
+               hashed_bit ^= FIELD_GET(BIT_ULL(41), denorm_ctx->current_spa) & hash_ctl_1T;
+
+               if (FIELD_GET(BIT_ULL(9), denorm_ctx->current_spa) != hashed_bit)
+                       denorm_ctx->current_spa ^= BIT_ULL(9);
+       }
+
+       if (denorm_ctx->rehash_vector & BIT_ULL(12)) {
+               hashed_bit  = FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa);
+               hashed_bit ^= FIELD_GET(BIT_ULL(18), denorm_ctx->current_spa) & hash_ctl_64k;
+               hashed_bit ^= FIELD_GET(BIT_ULL(23), denorm_ctx->current_spa) & hash_ctl_2M;
+               hashed_bit ^= FIELD_GET(BIT_ULL(32), denorm_ctx->current_spa) & hash_ctl_1G;
+               hashed_bit ^= FIELD_GET(BIT_ULL(42), denorm_ctx->current_spa) & hash_ctl_1T;
+
+               if (FIELD_GET(BIT_ULL(12), denorm_ctx->current_spa) != hashed_bit)
+                       denorm_ctx->current_spa ^= BIT_ULL(12);
+       }
+
+       if (denorm_ctx->rehash_vector & BIT_ULL(13)) {
+               hashed_bit  = FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa);
+               hashed_bit ^= FIELD_GET(BIT_ULL(19), denorm_ctx->current_spa) & hash_ctl_64k;
+               hashed_bit ^= FIELD_GET(BIT_ULL(24), denorm_ctx->current_spa) & hash_ctl_2M;
+               hashed_bit ^= FIELD_GET(BIT_ULL(33), denorm_ctx->current_spa) & hash_ctl_1G;
+               hashed_bit ^= FIELD_GET(BIT_ULL(43), denorm_ctx->current_spa) & hash_ctl_1T;
+
+               if (FIELD_GET(BIT_ULL(13), denorm_ctx->current_spa) != hashed_bit)
+                       denorm_ctx->current_spa ^= BIT_ULL(13);
+       }
+}
+
+static bool match_logical_coh_st_fabric_id(struct addr_ctx *ctx,
+                                          struct df4p5_denorm_ctx *denorm_ctx)
+{
+       /*
+        * The logical CS fabric ID of the permutation must be calculated from the
+        * current SPA with the base and with the MMIO hole.
+        */
+       u16 id = get_logical_coh_st_fabric_id_for_current_spa(ctx, denorm_ctx);
+
+       atl_debug(ctx, "Checking calculated logical coherent station fabric id:\n");
+       atl_debug(ctx, "  calculated fabric id         = 0x%x\n", id);
+       atl_debug(ctx, "  expected fabric id           = 0x%x\n", denorm_ctx->coh_st_fabric_id);
+
+       return denorm_ctx->coh_st_fabric_id == id;
+}
+
+static bool match_norm_addr(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
+{
+       u64 addr = remove_base_and_hole(ctx, denorm_ctx->current_spa);
+
+       /*
+        * The normalized address must be calculated with the current SPA without
+        * the base and without the MMIO hole.
+        */
+       addr = normalize_addr_df4p5_np2(ctx, denorm_ctx, addr);
+
+       atl_debug(ctx, "Checking calculated normalized address:\n");
+       atl_debug(ctx, "  calculated normalized addr = 0x%016llx\n", addr);
+       atl_debug(ctx, "  expected normalized addr   = 0x%016llx\n", ctx->ret_addr);
+
+       return addr == ctx->ret_addr;
+}
+
+static int check_permutations(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
+{
+       u64 test_perm, temp_addr, denorm_addr, num_perms;
+       unsigned int dropped_remainder;
+
+       denorm_ctx->div_addr *= denorm_ctx->mod_value;
+
+       /*
+        * The high order bits of num_permutations represent the permutations
+        * of the dropped remainder. This will be either 0-3 or 0-5 depending
+        * on the interleave mode. The low order bits represent the
+        * permutations of other "lost" bits which will be any combination of
+        * 1, 2, or 3 bits depending on the interleave mode.
+        */
+       num_perms = denorm_ctx->mod_value << denorm_ctx->perm_shift;
+
+       for (test_perm = 0; test_perm < num_perms; test_perm++) {
+               denorm_addr = denorm_ctx->base_denorm_addr;
+               dropped_remainder = test_perm >> denorm_ctx->perm_shift;
+               temp_addr = denorm_ctx->div_addr + dropped_remainder;
+
+               switch (ctx->map.intlv_mode) {
+               case DF4p5_NPS0_24CHAN_2K_HASH:
+                       denorm_addr |= temp_addr << 14;
+                       break;
+
+               case DF4p5_NPS0_24CHAN_1K_HASH:
+               case DF4p5_NPS1_12CHAN_2K_HASH:
+                       denorm_addr |= temp_addr << 13;
+                       break;
+
+               case DF4p5_NPS1_12CHAN_1K_HASH:
+               case DF4p5_NPS2_6CHAN_2K_HASH:
+               case DF4p5_NPS1_10CHAN_2K_HASH:
+                       denorm_addr |= temp_addr << 12;
+                       break;
+
+               case DF4p5_NPS2_6CHAN_1K_HASH:
+               case DF4p5_NPS1_10CHAN_1K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), temp_addr) << 9;
+                       denorm_addr |= FIELD_GET(GENMASK_ULL(63, 1), temp_addr) << 12;
+                       break;
+
+               case DF4p5_NPS4_3CHAN_1K_HASH:
+               case DF4p5_NPS2_5CHAN_1K_HASH:
+                       denorm_addr |= FIELD_GET(GENMASK_ULL(1, 0), temp_addr) << 8;
+                       denorm_addr |= FIELD_GET(GENMASK_ULL(63, 2), (temp_addr)) << 12;
+                       break;
+
+               case DF4p5_NPS4_3CHAN_2K_HASH:
+               case DF4p5_NPS2_5CHAN_2K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), temp_addr) << 8;
+                       denorm_addr |= FIELD_GET(GENMASK_ULL(63, 1), temp_addr) << 12;
+                       break;
+
+               default:
+                       atl_debug_on_bad_intlv_mode(ctx);
+                       return -EINVAL;
+               }
+
+               switch (ctx->map.intlv_mode) {
+               case DF4p5_NPS0_24CHAN_1K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
+                       denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 9;
+                       denorm_addr |= FIELD_GET(BIT_ULL(2), test_perm) << 12;
+                       break;
+
+               case DF4p5_NPS0_24CHAN_2K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
+                       denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 12;
+                       denorm_addr |= FIELD_GET(BIT_ULL(2), test_perm) << 13;
+                       break;
+
+               case DF4p5_NPS1_12CHAN_2K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
+                       denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 12;
+                       break;
+
+               case DF4p5_NPS1_12CHAN_1K_HASH:
+               case DF4p5_NPS4_3CHAN_1K_HASH:
+               case DF4p5_NPS2_5CHAN_1K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
+                       denorm_addr |= FIELD_GET(BIT_ULL(1), test_perm) << 9;
+                       break;
+
+               case DF4p5_NPS2_6CHAN_1K_HASH:
+               case DF4p5_NPS2_6CHAN_2K_HASH:
+               case DF4p5_NPS4_3CHAN_2K_HASH:
+               case DF4p5_NPS1_10CHAN_1K_HASH:
+               case DF4p5_NPS1_10CHAN_2K_HASH:
+               case DF4p5_NPS2_5CHAN_2K_HASH:
+                       denorm_addr |= FIELD_GET(BIT_ULL(0), test_perm) << 8;
+                       break;
+
+               default:
+                       atl_debug_on_bad_intlv_mode(ctx);
+                       return -EINVAL;
+               }
+
+               denorm_ctx->current_spa = add_base_and_hole(ctx, denorm_addr);
+               recalculate_hashed_bits_df4p5_np2(ctx, denorm_ctx);
+
+               atl_debug(ctx, "Checking potential system physical address 0x%016llx\n",
+                         denorm_ctx->current_spa);
+
+               if (!match_logical_coh_st_fabric_id(ctx, denorm_ctx))
+                       continue;
+
+               if (!match_norm_addr(ctx, denorm_ctx))
+                       continue;
+
+               if (denorm_ctx->resolved_spa == INVALID_SPA ||
+                   denorm_ctx->current_spa > denorm_ctx->resolved_spa)
+                       denorm_ctx->resolved_spa = denorm_ctx->current_spa;
+       }
+
+       if (denorm_ctx->resolved_spa == INVALID_SPA) {
+               atl_debug(ctx, "Failed to find valid SPA for normalized address 0x%016llx\n",
+                         ctx->ret_addr);
+               return -EINVAL;
+       }
+
+       /* Return the resolved SPA without the base, without the MMIO hole */
+       ctx->ret_addr = remove_base_and_hole(ctx, denorm_ctx->resolved_spa);
+
+       return 0;
+}
+
+static int init_df4p5_denorm_ctx(struct addr_ctx *ctx, struct df4p5_denorm_ctx *denorm_ctx)
+{
+       denorm_ctx->current_spa = INVALID_SPA;
+       denorm_ctx->resolved_spa = INVALID_SPA;
+
+       switch (ctx->map.intlv_mode) {
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+               denorm_ctx->perm_shift    = 3;
+               denorm_ctx->rehash_vector = BIT(8) | BIT(9) | BIT(12);
+               break;
+
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+               denorm_ctx->perm_shift    = 3;
+               denorm_ctx->rehash_vector = BIT(8) | BIT(12) | BIT(13);
+               break;
+
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+               denorm_ctx->perm_shift    = 2;
+               denorm_ctx->rehash_vector = BIT(8);
+               break;
+
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+               denorm_ctx->perm_shift    = 2;
+               denorm_ctx->rehash_vector = BIT(8) | BIT(12);
+               break;
+
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+               denorm_ctx->perm_shift    = 1;
+               denorm_ctx->rehash_vector = BIT(8);
+               break;
+
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+               denorm_ctx->perm_shift    = 2;
+               denorm_ctx->rehash_vector = 0;
+               break;
+
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+               denorm_ctx->perm_shift    = 1;
+               denorm_ctx->rehash_vector = 0;
+               break;
+
+       default:
+               atl_debug_on_bad_intlv_mode(ctx);
+               return -EINVAL;
+       }
+
+       denorm_ctx->base_denorm_addr = FIELD_GET(GENMASK_ULL(7, 0), ctx->ret_addr);
+
+       switch (ctx->map.intlv_mode) {
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+               denorm_ctx->base_denorm_addr |= FIELD_GET(GENMASK_ULL(9, 8), ctx->ret_addr) << 10;
+               denorm_ctx->div_addr          = FIELD_GET(GENMASK_ULL(63, 10), ctx->ret_addr);
+               break;
+
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+               denorm_ctx->base_denorm_addr |= FIELD_GET(GENMASK_ULL(10, 8), ctx->ret_addr) << 9;
+               denorm_ctx->div_addr          = FIELD_GET(GENMASK_ULL(63, 11), ctx->ret_addr);
+               break;
+
+       default:
+               atl_debug_on_bad_intlv_mode(ctx);
+               return -EINVAL;
+       }
+
+       if (ctx->map.num_intlv_chan % 3 == 0)
+               denorm_ctx->mod_value = 3;
+       else
+               denorm_ctx->mod_value = 5;
+
+       denorm_ctx->coh_st_fabric_id = get_logical_coh_st_fabric_id(ctx) - get_dst_fabric_id(ctx);
+
+       atl_debug(ctx, "Initialized df4p5_denorm_ctx:");
+       atl_debug(ctx, "  mod_value         = %d", denorm_ctx->mod_value);
+       atl_debug(ctx, "  perm_shift        = %d", denorm_ctx->perm_shift);
+       atl_debug(ctx, "  rehash_vector     = 0x%x", denorm_ctx->rehash_vector);
+       atl_debug(ctx, "  base_denorm_addr  = 0x%016llx", denorm_ctx->base_denorm_addr);
+       atl_debug(ctx, "  div_addr          = 0x%016llx", denorm_ctx->div_addr);
+       atl_debug(ctx, "  coh_st_fabric_id  = 0x%x", denorm_ctx->coh_st_fabric_id);
+
+       return 0;
+}
+
+/*
+ * For DF 4.5, parts of the physical address can be directly pulled from the
+ * normalized address. The exact bits will differ between interleave modes, but
+ * using NPS0_24CHAN_1K_HASH as an example, the normalized address consists of
+ * bits [63:13] (divided by 3), bits [11:10], and bits [7:0] of the system
+ * physical address.
+ *
+ * In this case, there is no way to reconstruct the missing bits (bits 8, 9,
+ * and 12) from the normalized address. Additionally, when bits [63:13] are
+ * divided by 3, the remainder is dropped. Determine the proper combination of
+ * "lost" bits and dropped remainder by iterating through each possible
+ * permutation of these bits and then normalizing the generated system physical
+ * addresses. If the normalized address matches the address we are trying to
+ * translate, then we have found the correct permutation of bits.
+ */
+static int denorm_addr_df4p5_np2(struct addr_ctx *ctx)
+{
+       struct df4p5_denorm_ctx denorm_ctx;
+       int ret = 0;
+
+       memset(&denorm_ctx, 0, sizeof(denorm_ctx));
+
+       atl_debug(ctx, "Denormalizing DF 4.5 normalized address 0x%016llx", ctx->ret_addr);
+
+       ret = init_df4p5_denorm_ctx(ctx, &denorm_ctx);
+       if (ret)
+               return ret;
+
+       return check_permutations(ctx, &denorm_ctx);
+}
+
 int denormalize_address(struct addr_ctx *ctx)
 {
        switch (ctx->map.intlv_mode) {
        case DF4_NPS2_5CHAN_HASH:
        case DF4_NPS1_10CHAN_HASH:
                return denorm_addr_df4_np2(ctx);
+       case DF4p5_NPS0_24CHAN_1K_HASH:
+       case DF4p5_NPS4_3CHAN_1K_HASH:
+       case DF4p5_NPS2_6CHAN_1K_HASH:
+       case DF4p5_NPS1_12CHAN_1K_HASH:
+       case DF4p5_NPS2_5CHAN_1K_HASH:
+       case DF4p5_NPS1_10CHAN_1K_HASH:
+       case DF4p5_NPS4_3CHAN_2K_HASH:
+       case DF4p5_NPS2_6CHAN_2K_HASH:
+       case DF4p5_NPS1_12CHAN_2K_HASH:
+       case DF4p5_NPS0_24CHAN_2K_HASH:
+       case DF4p5_NPS2_5CHAN_2K_HASH:
+       case DF4p5_NPS1_10CHAN_2K_HASH:
+               return denorm_addr_df4p5_np2(ctx);
        case DF3_6CHAN:
                return denorm_addr_df3_6chan(ctx);
        default: