// 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/platform_device.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/ioport.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)
#define CCPLEX_MSTRID 0x1
#define FIREWALL_APERTURE_SZ 0x10000
/* Write firewall check enable */
#define WEN 0x20000
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;
phys_addr_t firewall_base;
unsigned int firewall_ctl;
unsigned int firewall_wr_ctl;
const char * const *master_id;
unsigned int notifier_offset;
const struct tegra_cbb_error *errors;
const int max_errors;
const struct tegra234_slave_lookup *slave_map;
const int max_slaves;
};
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 bool
tegra234_cbb_write_access_allowed(struct platform_device *pdev, struct tegra234_cbb *cbb)
{
u32 val;
if (!cbb->fabric->firewall_base ||
!cbb->fabric->firewall_ctl ||
!cbb->fabric->firewall_wr_ctl) {
dev_info(&pdev->dev, "SoC data missing for firewall\n");
return false;
}
if ((cbb->fabric->firewall_ctl > FIREWALL_APERTURE_SZ) ||
(cbb->fabric->firewall_wr_ctl > FIREWALL_APERTURE_SZ)) {
dev_err(&pdev->dev, "wrong firewall offset value\n");
return false;
}
val = readl(cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_ctl);
/*
* If the firewall check feature for allowing or blocking the
* write accesses through the firewall of a fabric is disabled
* then CCPLEX can write to the registers of that fabric.
*/
if (!(val & WEN))
return true;
/*
* If the firewall check is enabled then check whether CCPLEX
* has write access to the fabric's error notifier registers
*/
val = readl(cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_wr_ctl);
if (val & (BIT(<