bool __ro_after_init smccc_trng_available = false;
 u64 __ro_after_init smccc_has_sve_hint = false;
+s32 __ro_after_init smccc_soc_id_version = SMCCC_RET_NOT_SUPPORTED;
+s32 __ro_after_init smccc_soc_id_revision = SMCCC_RET_NOT_SUPPORTED;
 
 void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
 {
+       struct arm_smccc_res res;
+
        smccc_version = version;
        smccc_conduit = conduit;
 
        if (IS_ENABLED(CONFIG_ARM64_SVE) &&
            smccc_version >= ARM_SMCCC_VERSION_1_3)
                smccc_has_sve_hint = true;
+
+       if ((smccc_version >= ARM_SMCCC_VERSION_1_2) &&
+           (smccc_conduit != SMCCC_CONDUIT_NONE)) {
+               arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+                                    ARM_SMCCC_ARCH_SOC_ID, &res);
+               if ((s32)res.a0 >= 0) {
+                       arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
+                       smccc_soc_id_version = (s32)res.a0;
+                       arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
+                       smccc_soc_id_revision = (s32)res.a0;
+               }
+       }
 }
 
 enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
 }
 EXPORT_SYMBOL_GPL(arm_smccc_get_version);
 
+s32 arm_smccc_get_soc_id_version(void)
+{
+       return smccc_soc_id_version;
+}
+
+s32 arm_smccc_get_soc_id_revision(void)
+{
+       return smccc_soc_id_revision;
+}
+
 static int __init smccc_devices_init(void)
 {
        struct platform_device *pdev;
 
        if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
                return 0;
 
-       if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) {
-               pr_err("%s: invalid SMCCC conduit\n", __func__);
-               return -EOPNOTSUPP;
-       }
-
-       arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
-                            ARM_SMCCC_ARCH_SOC_ID, &res);
-
-       if ((int)res.a0 == SMCCC_RET_NOT_SUPPORTED) {
+       soc_id_version = arm_smccc_get_soc_id_version();
+       if (soc_id_version == SMCCC_RET_NOT_SUPPORTED) {
                pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
                return 0;
        }
 
-       if ((int)res.a0 < 0) {
-               pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n",
-                       res.a0);
-               return -EINVAL;
-       }
-
-       arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
-       if ((int)res.a0 < 0) {
+       if (soc_id_version < 0) {
                pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0);
                return -EINVAL;
        }
 
-       soc_id_version = res.a0;
-
-       arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
-       if ((int)res.a0 < 0) {
+       soc_id_rev = arm_smccc_get_soc_id_revision();
+       if (soc_id_rev < 0) {
                pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0);
                return -EINVAL;
        }
 
-       soc_id_rev = res.a0;
-
        soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
        if (!soc_dev_attr)
                return -ENOMEM;
 
 #include <linux/irqchip/arm-gic-common.h>
 #include <linux/irqchip/arm-gic-v3.h>
 #include <linux/irqchip/irq-partition-percpu.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/arm-smccc.h>
 
 #include <asm/cputype.h>
 #include <asm/exception.h>
 
 struct gic_chip_data {
        struct fwnode_handle    *fwnode;
+       phys_addr_t             dist_phys_base;
        void __iomem            *dist_base;
        struct redist_region    *redist_regions;
        struct rdists           rdists;
        struct partition_desc   **ppi_descs;
 };
 
+#define T241_CHIPS_MAX         4
+static void __iomem *t241_dist_base_alias[T241_CHIPS_MAX] __read_mostly;
+static DEFINE_STATIC_KEY_FALSE(gic_nvidia_t241_erratum);
+
 static struct gic_chip_data gic_data __read_mostly;
 static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
 
        }
 }
 
+static inline void __iomem *gic_dist_base_alias(struct irq_data *d)
+{
+       if (static_branch_unlikely(&gic_nvidia_t241_erratum)) {
+               irq_hw_number_t hwirq = irqd_to_hwirq(d);
+               u32 chip;
+
+               /*
+                * For the erratum T241-FABRIC-4, read accesses to GICD_In{E}
+                * registers are directed to the chip that owns the SPI. The
+                * the alias region can also be used for writes to the
+                * GICD_In{E} except GICD_ICENABLERn. Each chip has support
+                * for 320 {E}SPIs. Mappings for all 4 chips:
+                *    Chip0 = 32-351
+                *    Chip1 = 352-671
+                *    Chip2 = 672-991
+                *    Chip3 = 4096-4415
+                */
+               switch (__get_intid_range(hwirq)) {
+               case SPI_RANGE:
+                       chip = (hwirq - 32) / 320;
+                       break;
+               case ESPI_RANGE:
+                       chip = 3;
+                       break;
+               default:
+                       unreachable();
+               }
+               return t241_dist_base_alias[chip];
+       }
+
+       return gic_data.dist_base;
+}
+
 static inline void __iomem *gic_dist_base(struct irq_data *d)
 {
        switch (get_intid_range(d)) {
        if (gic_irq_in_rdist(d))
                base = gic_data_rdist_sgi_base();
        else
-               base = gic_data.dist_base;
+               base = gic_dist_base_alias(d);
 
        return !!(readl_relaxed(base + offset + (index / 32) * 4) & mask);
 }
        if (gic_irq_in_rdist(d))
                base = gic_data_rdist_sgi_base();
        else
-               base = gic_data.dist_base;
+               base = gic_dist_base_alias(d);
 
        offset = convert_offset_index(d, GICD_ICFGR, &index);
 
        return false;
 }
 
+#define T241_CHIPN_MASK                GENMASK_ULL(45, 44)
+#define T241_CHIP_GICDA_OFFSET 0x1580000
+#define SMCCC_SOC_ID_T241      0x036b0241
+
+static bool gic_enable_quirk_nvidia_t241(void *data)
+{
+       s32 soc_id = arm_smccc_get_soc_id_version();
+       unsigned long chip_bmask = 0;
+       phys_addr_t phys;
+       u32 i;
+
+       /* Check JEP106 code for NVIDIA T241 chip (036b:0241) */
+       if ((soc_id < 0) || (soc_id != SMCCC_SOC_ID_T241))
+               return false;
+
+       /* Find the chips based on GICR regions PHYS addr */
+       for (i = 0; i < gic_data.nr_redist_regions; i++) {
+               chip_bmask |= BIT(FIELD_GET(T241_CHIPN_MASK,
+                                 (u64)gic_data.redist_regions[i].phys_base));
+       }
+
+       if (hweight32(chip_bmask) < 3)
+               return false;
+
+       /* Setup GICD alias regions */
+       for (i = 0; i < ARRAY_SIZE(t241_dist_base_alias); i++) {
+               if (chip_bmask & BIT(i)) {
+                       phys = gic_data.dist_phys_base + T241_CHIP_GICDA_OFFSET;
+                       phys |= FIELD_PREP(T241_CHIPN_MASK, i);
+                       t241_dist_base_alias[i] = ioremap(phys, SZ_64K);
+                       WARN_ON_ONCE(!t241_dist_base_alias[i]);
+               }
+       }
+       static_branch_enable(&gic_nvidia_t241_erratum);
+       return true;
+}
+
 static const struct gic_quirk gic_quirks[] = {
        {
                .desc   = "GICv3: Qualcomm MSM8996 broken firmware",
                .mask   = 0xe8f00fff,
                .init   = gic_enable_quirk_cavium_38539,
        },
+       {
+               .desc   = "GICv3: NVIDIA erratum T241-FABRIC-4",
+               .iidr   = 0x0402043b,
+               .mask   = 0xffffffff,
+               .init   = gic_enable_quirk_nvidia_t241,
+       },
        {
        }
 };
                gic_chip.flags |= IRQCHIP_SUPPORTS_NMI;
 }
 
-static int __init gic_init_bases(void __iomem *dist_base,
+static int __init gic_init_bases(phys_addr_t dist_phys_base,
+                                void __iomem *dist_base,
                                 struct redist_region *rdist_regs,
                                 u32 nr_redist_regions,
                                 u64 redist_stride,
                pr_info("GIC: Using split EOI/Deactivate mode\n");
 
        gic_data.fwnode = handle;
+       gic_data.dist_phys_base = dist_phys_base;
        gic_data.dist_base = dist_base;
        gic_data.redist_regions = rdist_regs;
        gic_data.nr_redist_regions = nr_redist_regions;
        gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
                                                 &gic_data);
        gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
-       gic_data.rdists.has_rvpeid = true;
-       gic_data.rdists.has_vlpis = true;
-       gic_data.rdists.has_direct_lpi = true;
-       gic_data.rdists.has_vpend_valid_dirty = true;
+       if (!static_branch_unlikely(&gic_nvidia_t241_erratum)) {
+               /* Disable GICv4.x features for the erratum T241-FABRIC-4 */
+               gic_data.rdists.has_rvpeid = true;
+               gic_data.rdists.has_vlpis = true;
+               gic_data.rdists.has_direct_lpi = true;
+               gic_data.rdists.has_vpend_valid_dirty = true;
+       }
 
        if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
                err = -ENOMEM;
 
 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
 {
+       phys_addr_t dist_phys_base;
        void __iomem *dist_base;
        struct redist_region *rdist_regs;
        struct resource res;
                return PTR_ERR(dist_base);
        }
 
+       dist_phys_base = res.start;
+
        err = gic_validate_dist_version(dist_base);
        if (err) {
                pr_err("%pOF: no distributor detected, giving up\n", node);
 
        gic_enable_of_quirks(node, gic_quirks, &gic_data);
 
-       err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
-                            redist_stride, &node->fwnode);
+       err = gic_init_bases(dist_phys_base, dist_base, rdist_regs,
+                            nr_redist_regions, redist_stride, &node->fwnode);
        if (err)
                goto out_unmap_rdist;
 
                goto out_redist_unmap;
        }
 
-       err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
-                            acpi_data.nr_redist_regions, 0, gsi_domain_handle);
+       err = gic_init_bases(dist->base_address, acpi_data.dist_base,
+                            acpi_data.redist_regs, acpi_data.nr_redist_regions,
+                            0, gsi_domain_handle);
        if (err)
                goto out_fwhandle_free;