--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved
+ *
+ * The driver handles Error's from Control Backbone(CBB) version 2.0.
+ * generated due to illegal accesses. The driver prints debug information
+ * about failed transaction on receiving interrupt from Error Notifier.
+ * Error types supported by CBB2.0 are:
+ *   UNSUPPORTED_ERR, PWRDOWN_ERR, TIMEOUT_ERR, FIREWALL_ERR, DECODE_ERR,
+ *   SLAVE_ERR
+ */
+
+#include <linux/clk.h>
+#include <linux/cpufeature.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <soc/tegra/fuse.h>
+#include <soc/tegra/tegra-cbb.h>
+
+#define FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0     0x0
+#define FABRIC_EN_CFG_STATUS_0_0               0x40
+#define FABRIC_EN_CFG_ADDR_INDEX_0_0           0x60
+#define FABRIC_EN_CFG_ADDR_LOW_0               0x80
+#define FABRIC_EN_CFG_ADDR_HI_0                        0x84
+
+#define FABRIC_MN_MASTER_ERR_EN_0              0x200
+#define FABRIC_MN_MASTER_ERR_FORCE_0           0x204
+#define FABRIC_MN_MASTER_ERR_STATUS_0          0x208
+#define FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0 0x20c
+
+#define FABRIC_MN_MASTER_LOG_ERR_STATUS_0      0x300
+#define FABRIC_MN_MASTER_LOG_ADDR_LOW_0                0x304
+#define FABRIC_MN_MASTER_LOG_ADDR_HIGH_0       0x308
+#define FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0     0x30c
+#define FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0     0x310
+#define FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0     0x314
+#define FABRIC_MN_MASTER_LOG_USER_BITS0_0      0x318
+
+#define AXI_SLV_TIMEOUT_STATUS_0_0             0x8
+#define APB_BLOCK_TMO_STATUS_0                 0xc00
+#define APB_BLOCK_NUM_TMO_OFFSET               0x20
+
+#define FAB_EM_EL_MSTRID               GENMASK(29, 24)
+#define FAB_EM_EL_VQC                  GENMASK(17, 16)
+#define FAB_EM_EL_GRPSEC               GENMASK(14, 8)
+#define FAB_EM_EL_FALCONSEC            GENMASK(1, 0)
+
+#define FAB_EM_EL_FABID                        GENMASK(20, 16)
+#define FAB_EM_EL_SLAVEID              GENMASK(7, 0)
+
+#define FAB_EM_EL_ACCESSID             GENMASK(7, 0)
+
+#define FAB_EM_EL_AXCACHE              GENMASK(27, 24)
+#define FAB_EM_EL_AXPROT               GENMASK(22, 20)
+#define FAB_EM_EL_BURSTLENGTH          GENMASK(19, 12)
+#define FAB_EM_EL_BURSTTYPE            GENMASK(9, 8)
+#define FAB_EM_EL_BEATSIZE             GENMASK(6, 4)
+#define FAB_EM_EL_ACCESSTYPE           GENMASK(0, 0)
+
+#define USRBITS_MSTR_ID                        GENMASK(29, 24)
+
+#define REQ_SOCKET_ID                  GENMASK(27, 24)
+
+enum tegra234_cbb_fabric_ids {
+       CBB_FAB_ID,
+       SCE_FAB_ID,
+       RCE_FAB_ID,
+       DCE_FAB_ID,
+       AON_FAB_ID,
+       PSC_FAB_ID,
+       BPMP_FAB_ID,
+       FSI_FAB_ID,
+       MAX_FAB_ID,
+};
+
+struct tegra234_slave_lookup {
+       const char *name;
+       unsigned int offset;
+};
+
+struct tegra234_cbb_fabric {
+       const char *name;
+       phys_addr_t off_mask_erd;
+       bool erd_mask_inband_err;
+       const char * const *master_id;
+       unsigned int notifier_offset;
+       const struct tegra_cbb_error *errors;
+       const struct tegra234_slave_lookup *slave_map;
+};
+
+struct tegra234_cbb {
+       struct tegra_cbb base;
+
+       const struct tegra234_cbb_fabric *fabric;
+       struct resource *res;
+       void __iomem *regs;
+
+       int num_intr;
+       int sec_irq;
+
+       /* record */
+       void __iomem *mon;
+       unsigned int type;
+       u32 mask;
+       u64 access;
+       u32 mn_attr0;
+       u32 mn_attr1;
+       u32 mn_attr2;
+       u32 mn_user_bits;
+};
+
+static inline struct tegra234_cbb *to_tegra234_cbb(struct tegra_cbb *cbb)
+{
+       return container_of(cbb, struct tegra234_cbb, base);
+}
+
+static LIST_HEAD(cbb_list);
+static DEFINE_SPINLOCK(cbb_lock);
+
+static void tegra234_cbb_fault_enable(struct tegra_cbb *cbb)
+{
+       struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+       void __iomem *addr;
+
+       addr = priv->regs + priv->fabric->notifier_offset;
+       writel(0x1ff, addr + FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0);
+       dsb(sy);
+}
+
+static void tegra234_cbb_error_clear(struct tegra_cbb *cbb)
+{
+       struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+
+       writel(0x3f, priv->mon + FABRIC_MN_MASTER_ERR_STATUS_0);
+       dsb(sy);
+}
+
+static u32 tegra234_cbb_get_status(struct tegra_cbb *cbb)
+{
+       struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+       void __iomem *addr;
+       u32 value;
+
+       addr = priv->regs + priv->fabric->notifier_offset;
+       value = readl(addr + FABRIC_EN_CFG_STATUS_0_0);
+       dsb(sy);
+
+       return value;
+}
+
+static void tegra234_cbb_mask_serror(struct tegra234_cbb *cbb)
+{
+       writel(0x1, cbb->regs + cbb->fabric->off_mask_erd);
+       dsb(sy);
+}
+
+static u32 tegra234_cbb_get_tmo_slv(void __iomem *addr)
+{
+       u32 timeout;
+
+       timeout = readl(addr);
+       return timeout;
+}
+
+static void tegra234_cbb_tmo_slv(struct seq_file *file, const char *slave, void __iomem *addr,
+                                u32 status)
+{
+       tegra_cbb_print_err(file, "\t  %s : %#x\n", slave, status);
+}
+
+static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave,
+                                      void __iomem *base)
+{
+       unsigned int block = 0;
+       void __iomem *addr;
+       char name[64];
+       u32 status;
+
+       status = tegra234_cbb_get_tmo_slv(base);
+       if (status)
+               tegra_cbb_print_err(file, "\t  %s_BLOCK_TMO_STATUS : %#x\n", slave, status);
+
+       while (status) {
+               if (status & BIT(0)) {
+                       u32 timeout, clients, client = 0;
+
+                       addr = base + APB_BLOCK_NUM_TMO_OFFSET + (block * 4);
+                       timeout = tegra234_cbb_get_tmo_slv(addr);
+                       clients = timeout;
+
+                       while (timeout) {
+                               if (timeout & BIT(0)) {
+                                       if (clients != 0xffffffff)
+                                               clients &= BIT(client);
+
+                                       sprintf(name, "%s_BLOCK%d_TMO", slave, block);
+
+                                       tegra234_cbb_tmo_slv(file, name, addr, clients);
+                               }
+
+                               timeout >>= 1;
+                               client++;
+                       }
+               }
+
+               status >>= 1;
+               block++;
+       }
+}
+
+static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234_cbb *cbb,
+                                         u8 slave_id, u8 fab_id)
+{
+       const struct tegra234_slave_lookup *map = cbb->fabric->slave_map;
+       void __iomem *addr;
+
+       /*
+        * 1) Get slave node name and address mapping using slave_id.
+        * 2) Check if the timed out slave node is APB or AXI.
+        * 3) If AXI, then print timeout register and reset axi slave
+        *    using <FABRIC>_SN_<>_SLV_TIMEOUT_STATUS_0_0 register.
+        * 4) If APB, then perform an additional lookup to find the client
+        *    which timed out.
+        *      a) Get block number from the index of set bit in
+        *         <FABRIC>_SN_AXI2APB_<>_BLOCK_TMO_STATUS_0 register.
+        *      b) Get address of register repective to block number i.e.
+        *         <FABRIC>_SN_AXI2APB_<>_BLOCK<index-set-bit>_TMO_0.
+        *      c) Read the register in above step to get client_id which
+        *         timed out as per the set bits.
+        *      d) Reset the timedout client and print details.
+        *      e) Goto step-a till all bits are set.
+        */
+
+       addr = cbb->regs + map[slave_id].offset;
+
+       if (strstr(map[slave_id].name, "AXI2APB")) {
+               addr += APB_BLOCK_TMO_STATUS_0;
+
+               tegra234_cbb_lookup_apbslv(file, map[slave_id].name, addr);
+       } else {
+               char name[64];
+               u32 status;
+
+               addr += AXI_SLV_TIMEOUT_STATUS_0_0;
+
+               status = tegra234_cbb_get_tmo_slv(addr);
+               if (status) {
+                       sprintf(name, "%s_SLV_TIMEOUT_STATUS", map[slave_id].name);
+                       tegra234_cbb_tmo_slv(file, name, addr, status);
+               }
+       }
+}
+
+static void tegra234_cbb_print_error(struct seq_file *file, struct tegra234_cbb *cbb, u32 status,
+                                    u32 overflow)
+{
+       unsigned int type = 0;
+
+       if (status & (status - 1))
+               tegra_cbb_print_err(file, "\t  Multiple type of errors reported\n");
+
+       while (status) {
+               if (status & 0x1)
+                       tegra_cbb_print_err(file, "\t  Error Code\t\t: %s\n",
+                                           cbb->fabric->errors[type].code);
+
+               status >>= 1;
+               type++;
+       }
+
+       type = 0;
+
+       while (overflow) {
+               if (overflow & 0x1)
+                       tegra_cbb_print_err(file, "\t  Overflow\t\t: Multiple %s\n",
+                                           cbb->fabric->errors[type].code);
+
+               overflow >>= 1;
+               type++;
+       }
+}
+
+static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb)
+{
+       u8 cache_type, prot_type, burst_length, mstr_id, grpsec, vqc, falconsec, beat_size;
+       u8 access_type, access_id, slave_id, fab_id, burst_type;
+       char fabric_name[20];
+
+       mstr_id = FIELD_GET(FAB_EM_EL_MSTRID, cbb->mn_user_bits);
+       vqc = FIELD_GET(FAB_EM_EL_VQC, cbb->mn_user_bits);
+       grpsec = FIELD_GET(FAB_EM_EL_GRPSEC, cbb->mn_user_bits);
+       falconsec = FIELD_GET(FAB_EM_EL_FALCONSEC, cbb->mn_user_bits);
+
+       fab_id = FIELD_GET(FAB_EM_EL_FABID, cbb->mn_attr2);
+       slave_id = FIELD_GET(FAB_EM_EL_SLAVEID, cbb->mn_attr2);
+
+       access_id = FIELD_GET(FAB_EM_EL_ACCESSID, cbb->mn_attr1);
+
+       cache_type = FIELD_GET(FAB_EM_EL_AXCACHE, cbb->mn_attr0);
+       prot_type = FIELD_GET(FAB_EM_EL_AXPROT, cbb->mn_attr0);
+       burst_length = FIELD_GET(FAB_EM_EL_BURSTLENGTH, cbb->mn_attr0);
+       burst_type = FIELD_GET(FAB_EM_EL_BURSTTYPE, cbb->mn_attr0);
+       beat_size = FIELD_GET(FAB_EM_EL_BEATSIZE, cbb->mn_attr0);
+       access_type = FIELD_GET(FAB_EM_EL_ACCESSTYPE, cbb->mn_attr0);
+
+       tegra_cbb_print_err(file, "\n");
+       tegra_cbb_print_err(file, "\t  Error Code\t\t: %s\n",
+                           cbb->fabric->errors[cbb->type].code);
+
+       tegra_cbb_print_err(file, "\t  MASTER_ID\t\t: %s\n", cbb->fabric->master_id[mstr_id]);
+       tegra_cbb_print_err(file, "\t  Address\t\t: %#llx\n", cbb->access);
+
+       tegra_cbb_print_cache(file, cache_type);
+       tegra_cbb_print_prot(file, prot_type);
+
+       tegra_cbb_print_err(file, "\t  Access_Type\t\t: %s", (access_type) ? "Write\n" : "Read\n");
+       tegra_cbb_print_err(file, "\t  Access_ID\t\t: %#x", access_id);
+
+       if (fab_id == PSC_FAB_ID)
+               strcpy(fabric_name, "psc-fabric");
+       else if (fab_id == FSI_FAB_ID)
+               strcpy(fabric_name, "fsi-fabric");
+       else
+               strcpy(fabric_name, cbb->fabric->name);
+
+       tegra_cbb_print_err(file, "\t  Fabric\t\t: %s\n", fabric_name);
+       tegra_cbb_print_err(file, "\t  Slave_Id\t\t: %#x\n", slave_id);
+       tegra_cbb_print_err(file, "\t  Burst_length\t\t: %#x\n", burst_length);
+       tegra_cbb_print_err(file, "\t  Burst_type\t\t: %#x\n", burst_type);
+       tegra_cbb_print_err(file, "\t  Beat_size\t\t: %#x\n", beat_size);
+       tegra_cbb_print_err(file, "\t  VQC\t\t\t: %#x\n", vqc);
+       tegra_cbb_print_err(file, "\t  GRPSEC\t\t: %#x\n", grpsec);
+       tegra_cbb_print_err(file, "\t  FALCONSEC\t\t: %#x\n", falconsec);
+
+       if ((fab_id == PSC_FAB_ID) || (fab_id == FSI_FAB_ID))
+               return;
+
+       if (!strcmp(cbb->fabric->errors[cbb->type].code, "TIMEOUT_ERR")) {
+               tegra234_lookup_slave_timeout(file, cbb, slave_id, fab_id);
+               return;
+       }
+
+       tegra_cbb_print_err(file, "\t  Slave\t\t\t: %s\n", cbb->fabric->slave_map[slave_id].name);
+}
+
+static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb)
+{
+       u32 overflow, status, error;
+
+       status = readl(cbb->mon + FABRIC_MN_MASTER_ERR_STATUS_0);
+       if (!status) {
+               pr_err("Error Notifier received a spurious notification\n");
+               return -ENODATA;
+       }
+
+       if (status == 0xffffffff) {
+               pr_err("CBB registers returning all 1's which is invalid\n");
+               return -EINVAL;
+       }
+
+       overflow = readl(cbb->mon + FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0);
+
+       tegra234_cbb_print_error(file, cbb, status, overflow);
+
+       error = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ERR_STATUS_0);
+       if (!error) {
+               pr_info("Error Monitor doesn't have Error Logger\n");
+               return -EINVAL;
+       }
+
+       cbb->type = 0;
+
+       while (error) {
+               if (error & BIT(0)) {
+                       u32 hi, lo;
+
+                       hi = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_HIGH_0);
+                       lo = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_LOW_0);
+
+                       cbb->access = (u64)hi << 32 | lo;
+
+                       cbb->mn_attr0 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0);
+                       cbb->mn_attr1 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0);
+                       cbb->mn_attr2 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0);
+                       cbb->mn_user_bits = readl(cbb->mon + FABRIC_MN_MASTER_LOG_USER_BITS0_0);
+
+                       print_errlog_err(file, cbb);
+               }
+
+               cbb->type++;
+               error >>= 1;
+       }
+
+       return 0;
+}
+
+static int print_err_notifier(struct seq_file *file, struct tegra234_cbb *cbb, u32 status)
+{
+       unsigned int index = 0;
+       int err;
+
+       pr_crit("**************************************\n");
+       pr_crit("CPU:%d, Error:%s, Errmon:%d\n", smp_processor_id(),
+               cbb->fabric->name, status);
+
+       while (status) {
+               if (status & BIT(0)) {
+                       unsigned int notifier = cbb->fabric->notifier_offset;
+                       u32 hi, lo, mask = BIT(index);
+                       phys_addr_t addr;
+                       u64 offset;
+
+                       writel(mask, cbb->regs + notifier + FABRIC_EN_CFG_ADDR_INDEX_0_0);
+                       hi = readl(cbb->regs + notifier + FABRIC_EN_CFG_ADDR_HI_0);
+                       lo = readl(cbb->regs + notifier + FABRIC_EN_CFG_ADDR_LOW_0);
+
+                       addr = (u64)hi << 32 | lo;
+
+                       offset = addr - cbb->res->start;
+                       cbb->mon = cbb->regs + offset;
+                       cbb->mask = BIT(index);
+
+                       err = print_errmonX_info(file, cbb);
+                       tegra234_cbb_error_clear(&cbb->base);
+                       if (err)
+                               return err;
+               }
+
+               status >>= 1;
+               index++;
+       }
+
+       tegra_cbb_print_err(file, "\t**************************************\n");
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static DEFINE_MUTEX(cbb_debugfs_mutex);
+
+static int tegra234_cbb_debugfs_show(struct tegra_cbb *cbb, struct seq_file *file, void *data)
+{
+       int err = 0;
+
+       mutex_lock(&cbb_debugfs_mutex);
+
+       list_for_each_entry(cbb, &cbb_list, node) {
+               struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+               u32 status;
+
+               status = tegra_cbb_get_status(&priv->base);
+               if (status) {
+                       err = print_err_notifier(file, priv, status);
+                       if (err)
+                               break;
+               }
+       }
+
+       mutex_unlock(&cbb_debugfs_mutex);
+       return err;
+}
+#endif
+
+/*
+ * Handler for CBB errors
+ */
+static irqreturn_t tegra234_cbb_isr(int irq, void *data)
+{
+       bool is_inband_err = false;
+       struct tegra_cbb *cbb;
+       unsigned long flags;
+       u8 mstr_id;
+       int err;
+
+       spin_lock_irqsave(&cbb_lock, flags);
+
+       list_for_each_entry(cbb, &cbb_list, node) {
+               struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+               u32 status = tegra_cbb_get_status(cbb);
+
+               if (status && (irq == priv->sec_irq)) {
+                       tegra_cbb_print_err(NULL, "CPU:%d, Error: %s@%llx, irq=%d\n",
+                                           smp_processor_id(), priv->fabric->name,
+                                           priv->res->start, irq);
+
+                       err = print_err_notifier(NULL, priv, status);
+                       if (err)
+                               goto unlock;
+
+                       mstr_id =  FIELD_GET(USRBITS_MSTR_ID, priv->mn_user_bits);
+
+                       /*
+                        * If illegal request is from CCPLEX(id:0x1) master then call BUG() to
+                        * crash system.
+                        */
+                       if ((mstr_id == 0x1) && priv->fabric->off_mask_erd)
+                               is_inband_err = 1;
+               }
+       }
+
+unlock:
+       spin_unlock_irqrestore(&cbb_lock, flags);
+       WARN_ON(is_inband_err);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Register handler for CBB_SECURE interrupt for reporting errors
+ */
+static int tegra234_cbb_interrupt_enable(struct tegra_cbb *cbb)
+{
+       struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
+
+       if (priv->sec_irq) {
+               int err = devm_request_irq(cbb->dev, priv->sec_irq, tegra234_cbb_isr, 0,
+                                          dev_name(cbb->dev), priv);
+               if (err) {
+                       dev_err(cbb->dev, "failed to register interrupt %u: %d\n", priv->sec_irq,
+                               err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void tegra234_cbb_error_enable(struct tegra_cbb *cbb)
+{
+       tegra_cbb_fault_enable(cbb);
+}
+
+static const struct tegra_cbb_ops tegra234_cbb_ops = {
+       .get_status = tegra234_cbb_get_status,
+       .error_clear = tegra234_cbb_error_clear,
+       .fault_enable = tegra234_cbb_fault_enable,
+       .error_enable = tegra234_cbb_error_enable,
+       .interrupt_enable = tegra234_cbb_interrupt_enable,
+#ifdef CONFIG_DEBUG_FS
+       .debugfs_show = tegra234_cbb_debugfs_show,
+#endif
+};
+
+static const char * const tegra234_master_id[] = {
+       [0x00] = "TZ",
+       [0x01] = "CCPLEX",
+       [0x02] = "CCPMU",
+       [0x03] = "BPMP_FW",
+       [0x04] = "AON",
+       [0x05] = "SCE",
+       [0x06] = "GPCDMA_P",
+       [0x07] = "TSECA_NONSECURE",
+       [0x08] = "TSECA_LIGHTSECURE",
+       [0x09] = "TSECA_HEAVYSECURE",
+       [0x0a] = "CORESIGHT",
+       [0x0b] = "APE",
+       [0x0c] = "PEATRANS",
+       [0x0d] = "JTAGM_DFT",
+       [0x0e] = "RCE",
+       [0x0f] = "DCE",
+       [0x10] = "PSC_FW_USER",
+       [0x11] = "PSC_FW_SUPERVISOR",
+       [0x12] = "PSC_FW_MACHINE",
+       [0x13] = "PSC_BOOT",
+       [0x14] = "BPMP_BOOT",
+       [0x15] = "NVDEC_NONSECURE",
+       [0x16] = "NVDEC_LIGHTSECURE",
+       [0x17] = "NVDEC_HEAVYSECURE",
+       [0x18] = "CBB_INTERNAL",
+       [0x19] = "RSVD"
+};
+
+static const struct tegra_cbb_error tegra234_cbb_errors[] = {
+       {
+               .code = "SLAVE_ERR",
+               .desc = "Slave being accessed responded with an error"
+       }, {
+               .code = "DECODE_ERR",
+               .desc = "Attempt to access an address hole"
+       }, {
+               .code = "FIREWALL_ERR",
+               .desc = "Attempt to access a region which is firewall protected"
+       }, {
+               .code = "TIMEOUT_ERR",
+               .desc = "No response returned by slave"
+       }, {
+               .code = "PWRDOWN_ERR",
+               .desc = "Attempt to access a portion of fabric that is powered down"
+       }, {
+               .code = "UNSUPPORTED_ERR",
+               .desc = "Attempt to access a slave through an unsupported access"
+       }
+};
+
+static const struct tegra234_slave_lookup tegra234_aon_slave_map[] = {
+       { "AXI2APB", 0x00000 },
+       { "AST",     0x14000 },
+       { "CBB",     0x15000 },
+       { "CPU",     0x16000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_aon_fabric = {
+       .name = "aon-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_aon_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x17000,
+};
+
+static const struct tegra234_slave_lookup tegra234_bpmp_slave_map[] = {
+       { "AXI2APB", 0x00000 },
+       { "AST0",    0x15000 },
+       { "AST1",    0x16000 },
+       { "CBB",     0x17000 },
+       { "CPU",     0x18000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_bpmp_fabric = {
+       .name = "bpmp-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_bpmp_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x19000,
+};
+
+static const struct tegra234_slave_lookup tegra234_cbb_slave_map[] = {
+       { "AON",        0x40000 },
+       { "BPMP",       0x41000 },
+       { "CBB",        0x42000 },
+       { "HOST1X",     0x43000 },
+       { "STM",        0x44000 },
+       { "FSI",        0x45000 },
+       { "PSC",        0x46000 },
+       { "PCIE_C1",    0x47000 },
+       { "PCIE_C2",    0x48000 },
+       { "PCIE_C3",    0x49000 },
+       { "PCIE_C0",    0x4a000 },
+       { "PCIE_C4",    0x4b000 },
+       { "GPU",        0x4c000 },
+       { "SMMU0",      0x4d000 },
+       { "SMMU1",      0x4e000 },
+       { "SMMU2",      0x4f000 },
+       { "SMMU3",      0x50000 },
+       { "SMMU4",      0x51000 },
+       { "PCIE_C10",   0x52000 },
+       { "PCIE_C7",    0x53000 },
+       { "PCIE_C8",    0x54000 },
+       { "PCIE_C9",    0x55000 },
+       { "PCIE_C5",    0x56000 },
+       { "PCIE_C6",    0x57000 },
+       { "DCE",        0x58000 },
+       { "RCE",        0x59000 },
+       { "SCE",        0x5a000 },
+       { "AXI2APB_1",  0x70000 },
+       { "AXI2APB_10", 0x71000 },
+       { "AXI2APB_11", 0x72000 },
+       { "AXI2APB_12", 0x73000 },
+       { "AXI2APB_13", 0x74000 },
+       { "AXI2APB_14", 0x75000 },
+       { "AXI2APB_15", 0x76000 },
+       { "AXI2APB_16", 0x77000 },
+       { "AXI2APB_17", 0x78000 },
+       { "AXI2APB_18", 0x79000 },
+       { "AXI2APB_19", 0x7a000 },
+       { "AXI2APB_2",  0x7b000 },
+       { "AXI2APB_20", 0x7c000 },
+       { "AXI2APB_21", 0x7d000 },
+       { "AXI2APB_22", 0x7e000 },
+       { "AXI2APB_23", 0x7f000 },
+       { "AXI2APB_25", 0x80000 },
+       { "AXI2APB_26", 0x81000 },
+       { "AXI2APB_27", 0x82000 },
+       { "AXI2APB_28", 0x83000 },
+       { "AXI2APB_29", 0x84000 },
+       { "AXI2APB_30", 0x85000 },
+       { "AXI2APB_31", 0x86000 },
+       { "AXI2APB_32", 0x87000 },
+       { "AXI2APB_33", 0x88000 },
+       { "AXI2APB_34", 0x89000 },
+       { "AXI2APB_35", 0x92000 },
+       { "AXI2APB_4",  0x8b000 },
+       { "AXI2APB_5",  0x8c000 },
+       { "AXI2APB_6",  0x8d000 },
+       { "AXI2APB_7",  0x8e000 },
+       { "AXI2APB_8",  0x8f000 },
+       { "AXI2APB_9",  0x90000 },
+       { "AXI2APB_3",  0x91000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_cbb_fabric = {
+       .name = "cbb-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_cbb_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x60000,
+       .off_mask_erd = 0x3a004
+};
+
+static const struct tegra234_slave_lookup tegra234_dce_slave_map[] = {
+       { "AXI2APB", 0x00000 },
+       { "AST0",    0x15000 },
+       { "AST1",    0x16000 },
+       { "CPU",     0x18000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_dce_fabric = {
+       .name = "dce-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_dce_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x19000,
+};
+
+static const struct tegra234_slave_lookup tegra234_rce_slave_map[] = {
+       { "AXI2APB", 0x00000 },
+       { "AST0",    0x15000 },
+       { "AST1",    0x16000 },
+       { "CPU",     0x18000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_rce_fabric = {
+       .name = "rce-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_rce_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x19000,
+};
+
+static const struct tegra234_slave_lookup tegra234_sce_slave_map[] = {
+       { "AXI2APB", 0x00000 },
+       { "AST0",    0x15000 },
+       { "AST1",    0x16000 },
+       { "CBB",     0x17000 },
+       { "CPU",     0x18000 },
+};
+
+static const struct tegra234_cbb_fabric tegra234_sce_fabric = {
+       .name = "sce-fabric",
+       .master_id = tegra234_master_id,
+       .slave_map = tegra234_sce_slave_map,
+       .errors = tegra234_cbb_errors,
+       .notifier_offset = 0x19000,
+};
+
+static const struct of_device_id tegra234_cbb_dt_ids[] = {
+       { .compatible = "nvidia,tegra234-cbb-fabric", .data = &tegra234_cbb_fabric },
+       { .compatible = "nvidia,tegra234-aon-fabric", .data = &tegra234_aon_fabric },
+       { .compatible = "nvidia,tegra234-bpmp-fabric", .data = &tegra234_bpmp_fabric },
+       { .compatible = "nvidia,tegra234-dce-fabric", .data = &tegra234_dce_fabric },
+       { .compatible = "nvidia,tegra234-rce-fabric", .data = &tegra234_rce_fabric },
+       { .compatible = "nvidia,tegra234-sce-fabric", .data = &tegra234_sce_fabric },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tegra234_cbb_dt_ids);
+
+static int tegra234_cbb_probe(struct platform_device *pdev)
+{
+       const struct tegra234_cbb_fabric *fabric;
+       struct tegra234_cbb *cbb;
+       unsigned long flags = 0;
+       int err;
+
+       fabric = of_device_get_match_data(&pdev->dev);
+
+       cbb = devm_kzalloc(&pdev->dev, sizeof(*cbb), GFP_KERNEL);
+       if (!cbb)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&cbb->base.node);
+       cbb->base.ops = &tegra234_cbb_ops;
+       cbb->base.dev = &pdev->dev;
+       cbb->fabric = fabric;
+
+       cbb->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &cbb->res);
+       if (IS_ERR(cbb->regs))
+               return PTR_ERR(cbb->regs);
+
+       err = tegra_cbb_get_irq(pdev, NULL, &cbb->sec_irq);
+       if (err)
+               return err;
+
+       platform_set_drvdata(pdev, cbb);
+
+       spin_lock_irqsave(&cbb_lock, flags);
+       list_add(&cbb->base.node, &cbb_list);
+       spin_unlock_irqrestore(&cbb_lock, flags);
+
+       /* set ERD bit to mask SError and generate interrupt to report error */
+       if (cbb->fabric->off_mask_erd)
+               tegra234_cbb_mask_serror(cbb);
+
+       return tegra_cbb_register(&cbb->base);
+}
+
+static int tegra234_cbb_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev)
+{
+       struct tegra234_cbb *cbb = dev_get_drvdata(dev);
+
+       tegra234_cbb_error_enable(&cbb->base);
+
+       dev_dbg(dev, "%s resumed\n", cbb->fabric->name);
+
+       return 0;
+}
+
+static const struct dev_pm_ops tegra234_cbb_pm = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, tegra234_cbb_resume_noirq)
+};
+
+static struct platform_driver tegra234_cbb_driver = {
+       .probe = tegra234_cbb_probe,
+       .remove = tegra234_cbb_remove,
+       .driver = {
+               .name = "tegra234-cbb",
+               .of_match_table = tegra234_cbb_dt_ids,
+               .pm = &tegra234_cbb_pm,
+       },
+};
+
+static int __init tegra234_cbb_init(void)
+{
+       return platform_driver_register(&tegra234_cbb_driver);
+}
+pure_initcall(tegra234_cbb_init);
+
+static void __exit tegra234_cbb_exit(void)
+{
+       platform_driver_unregister(&tegra234_cbb_driver);
+}
+module_exit(tegra234_cbb_exit);
+
+MODULE_DESCRIPTION("Control Backbone 2.0 error handling driver for Tegra234");
+MODULE_LICENSE("GPL");