// 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/acpi.h>
#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