linux-stable/drivers/soc/tegra/cbb/tegra234-cbb.c
Rob Herring 8297603c79 soc/tegra: Explicitly include correct DT includes
The DT of_device.h and of_platform.h date back to the separate
of_platform_bus_type before it as merged into the regular platform bus.
As part of that merge prepping Arm DT support 13 years ago, they
"temporarily" include each other. They also include platform_device.h
and of.h. As a result, there's a pretty much random mix of those include
files used throughout the tree. In order to detangle these headers and
replace the implicit includes with struct declarations, users need to
explicitly include the correct includes.

Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
2023-07-21 17:12:19 +02:00

1210 lines
32 KiB
C

// 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(CCPLEX_MSTRID)))
return true;
return false;
}
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 (type >= cbb->fabric->max_errors) {
tegra_cbb_print_err(file, "\t Wrong type index:%u, status:%u\n",
type, status);
return;
}
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 (type >= cbb->fabric->max_errors) {
tegra_cbb_print_err(file, "\t Wrong type index:%u, overflow:%u\n",
type, overflow);
return;
}
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, requester_socket_id, local_socket_id, slave_id, fab_id;
char fabric_name[20];
bool is_numa = false;
u8 burst_type;
if (num_possible_nodes() > 1)
is_numa = true;
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);
/*
* For SOC with multiple NUMA nodes, print cross socket access
* errors only if initiator/master_id is CCPLEX, CPMU or GPU.
*/
if (is_numa) {
local_socket_id = numa_node_id();
requester_socket_id = FIELD_GET(REQ_SOCKET_ID, cbb->mn_attr2);
if (requester_socket_id != local_socket_id) {
if ((mstr_id != 0x1) && (mstr_id != 0x2) && (mstr_id != 0xB))
return;
}
}
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");
if (cbb->type < cbb->fabric->max_errors)
tegra_cbb_print_err(file, "\t Error Code\t\t: %s\n",
cbb->fabric->errors[cbb->type].code);
else
tegra_cbb_print_err(file, "\t Wrong type index:%u\n", cbb->type);
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);
if (is_numa) {
tegra_cbb_print_err(file, "\t Requester_Socket_Id\t: %#x\n",
requester_socket_id);
tegra_cbb_print_err(file, "\t Local_Socket_Id\t: %#x\n",
local_socket_id);
tegra_cbb_print_err(file, "\t No. of NUMA_NODES\t: %#x\n",
num_possible_nodes());
}
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 (slave_id >= cbb->fabric->max_slaves) {
tegra_cbb_print_err(file, "\t Invalid slave_id:%d\n", slave_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@0x%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;
/*
* If illegal request is from CCPLEX(id:0x1) master then call WARN()
*/
if (priv->fabric->off_mask_erd) {
mstr_id = FIELD_GET(USRBITS_MSTR_ID, priv->mn_user_bits);
if (mstr_id == CCPLEX_MSTRID)
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,
.max_slaves = ARRAY_SIZE(tegra234_aon_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x17000,
.firewall_base = 0x30000,
.firewall_ctl = 0x8d0,
.firewall_wr_ctl = 0x8c8,
};
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,
.max_slaves = ARRAY_SIZE(tegra234_bpmp_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x19000,
.firewall_base = 0x30000,
.firewall_ctl = 0x8f0,
.firewall_wr_ctl = 0x8e8,
};
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,
.max_slaves = ARRAY_SIZE(tegra234_cbb_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x60000,
.off_mask_erd = 0x3a004,
.firewall_base = 0x10000,
.firewall_ctl = 0x23f0,
.firewall_wr_ctl = 0x23e8,
};
static const struct tegra234_slave_lookup tegra234_common_slave_map[] = {
{ "AXI2APB", 0x00000 },
{ "AST0", 0x15000 },
{ "AST1", 0x16000 },
{ "CBB", 0x17000 },
{ "RSVD", 0x00000 },
{ "CPU", 0x18000 },
};
static const struct tegra234_cbb_fabric tegra234_dce_fabric = {
.name = "dce-fabric",
.master_id = tegra234_master_id,
.slave_map = tegra234_common_slave_map,
.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x19000,
.firewall_base = 0x30000,
.firewall_ctl = 0x290,
.firewall_wr_ctl = 0x288,
};
static const struct tegra234_cbb_fabric tegra234_rce_fabric = {
.name = "rce-fabric",
.master_id = tegra234_master_id,
.slave_map = tegra234_common_slave_map,
.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x19000,
.firewall_base = 0x30000,
.firewall_ctl = 0x290,
.firewall_wr_ctl = 0x288,
};
static const struct tegra234_cbb_fabric tegra234_sce_fabric = {
.name = "sce-fabric",
.master_id = tegra234_master_id,
.slave_map = tegra234_common_slave_map,
.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
.errors = tegra234_cbb_errors,
.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
.notifier_offset = 0x19000,
.firewall_base = 0x30000,
.firewall_ctl = 0x290,
.firewall_wr_ctl = 0x288,
};
static const char * const tegra241_master_id[] = {
[0x0] = "TZ",
[0x1] = "CCPLEX",
[0x2] = "CCPMU",
[0x3] = "BPMP_FW",
[0x4] = "PSC_FW_USER",
[0x5] = "PSC_FW_SUPERVISOR",
[0x6] = "PSC_FW_MACHINE",
[0x7] = "PSC_BOOT",
[0x8] = "BPMP_BOOT",
[0x9] = "JTAGM_DFT",
[0xa] = "CORESIGHT",
[0xb] = "GPU",
[0xc] = "PEATRANS",
[0xd ... 0x3f] = "RSVD"
};
/*
* Possible causes for Slave and Timeout errors.
* SLAVE_ERR:
* Slave being accessed responded with an error. Slave could return
* an error for various cases :
* Unsupported access, clamp setting when power gated, register
* level firewall(SCR), address hole within the slave, etc
*
* TIMEOUT_ERR:
* No response returned by slave. Can be due to slave being clock
* gated, under reset, powered down or slave inability to respond
* for an internal slave issue
*/
static const struct tegra_cbb_error tegra241_cbb_errors[] = {
{
.code = "SLAVE_ERR",
.desc = "Slave being accessed responded with an error."
}, {
.code = "DECODE_ERR",
.desc = "Attempt to access an address hole or Reserved region of memory."
}, {
.code = "FIREWALL_ERR",
.desc = "Attempt to access a region which is firewalled."
}, {
.code = "TIMEOUT_ERR",
.desc = "No response returned by slave."
}, {
.code = "PWRDOWN_ERR",
.desc = "Attempt to access a portion of the fabric that is powered down."
}, {
.code = "UNSUPPORTED_ERR",
.desc = "Attempt to access a slave through an unsupported access."
}, {
.code = "POISON_ERR",
.desc = "Slave responds with poison error to indicate error in data."
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "RSVD"
}, {
.code = "NO_SUCH_ADDRESS_ERR",
.desc = "The address belongs to the pri_target range but there is no register "
"implemented at the address."
}, {
.code = "TASK_ERR",
.desc = "Attempt to update a PRI task when the current task has still not "
"completed."
}, {
.code = "EXTERNAL_ERR",
.desc = "Indicates that an external PRI register access met with an error due to "
"any issue in the unit."
}, {
.code = "INDEX_ERR",
.desc = "Applicable to PRI index aperture pair, when the programmed index is "
"outside the range defined in the manual."
}, {
.code = "RESET_ERR",
.desc = "Target in Reset Error: Attempt to access a SubPri or external PRI "
"register but they are in reset."
}, {
.code = "REGISTER_RST_ERR",
.desc = "Attempt to access a PRI register but the register is partial or "
"completely in reset."
}, {
.code = "POWER_GATED_ERR",
.desc = "Returned by external PRI client when the external access goes to a power "
"gated domain."
}, {
.code = "SUBPRI_FS_ERR",
.desc = "Subpri is floorswept: Attempt to access a subpri through the main pri "
"target but subPri logic is floorswept."
}, {
.code = "SUBPRI_CLK_OFF_ERR",
.desc = "Subpri clock is off: Attempt to access a subpri through the main pri "
"target but subPris clock is gated/off."
},
};
static const struct tegra234_slave_lookup tegra241_cbb_slave_map[] = {
{ "RSVD", 0x00000 },
{ "PCIE_C8", 0x51000 },
{ "PCIE_C9", 0x52000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "AON", 0x5b000 },
{ "BPMP", 0x5c000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "PSC", 0x5d000 },
{ "STM", 0x5e000 },
{ "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_4", 0x87000 },
{ "AXI2APB_5", 0x88000 },
{ "AXI2APB_6", 0x89000 },
{ "AXI2APB_7", 0x8a000 },
{ "AXI2APB_8", 0x8b000 },
{ "AXI2APB_9", 0x8c000 },
{ "AXI2APB_3", 0x8d000 },
{ "AXI2APB_21", 0x7d000 },
{ "AXI2APB_22", 0x7e000 },
{ "AXI2APB_23", 0x7f000 },
{ "AXI2APB_24", 0x80000 },
{ "AXI2APB_25", 0x81000 },
{ "AXI2APB_26", 0x82000 },
{ "AXI2APB_27", 0x83000 },
{ "AXI2APB_28", 0x84000 },
{ "PCIE_C4", 0x53000 },
{ "PCIE_C5", 0x54000 },
{ "PCIE_C6", 0x55000 },
{ "PCIE_C7", 0x56000 },
{ "PCIE_C2", 0x57000 },
{ "PCIE_C3", 0x58000 },
{ "PCIE_C0", 0x59000 },
{ "PCIE_C1", 0x5a000 },
{ "CCPLEX", 0x50000 },
{ "AXI2APB_29", 0x85000 },
{ "AXI2APB_30", 0x86000 },
{ "CBB_CENTRAL", 0x00000 },
{ "AXI2APB_31", 0x8E000 },
{ "AXI2APB_32", 0x8F000 },
};
static const struct tegra234_cbb_fabric tegra241_cbb_fabric = {
.name = "cbb-fabric",
.master_id = tegra241_master_id,
.slave_map = tegra241_cbb_slave_map,
.max_slaves = ARRAY_SIZE(tegra241_cbb_slave_map),
.errors = tegra241_cbb_errors,
.max_errors = ARRAY_SIZE(tegra241_cbb_errors),
.notifier_offset = 0x60000,
.off_mask_erd = 0x40004,
.firewall_base = 0x20000,
.firewall_ctl = 0x2370,
.firewall_wr_ctl = 0x2368,
};
static const struct tegra234_slave_lookup tegra241_bpmp_slave_map[] = {
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "RSVD", 0x00000 },
{ "CBB", 0x15000 },
{ "CPU", 0x16000 },
{ "AXI2APB", 0x00000 },
{ "DBB0", 0x17000 },
{ "DBB1", 0x18000 },
};
static const struct tegra234_cbb_fabric tegra241_bpmp_fabric = {
.name = "bpmp-fabric",
.master_id = tegra241_master_id,
.slave_map = tegra241_bpmp_slave_map,
.max_slaves = ARRAY_SIZE(tegra241_bpmp_slave_map),
.errors = tegra241_cbb_errors,
.max_errors = ARRAY_SIZE(tegra241_cbb_errors),
.notifier_offset = 0x19000,
.firewall_base = 0x30000,
.firewall_ctl = 0x8f0,
.firewall_wr_ctl = 0x8e8,
};
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);
struct tegra234_cbb_acpi_uid {
const char *hid;
const char *uid;
const struct tegra234_cbb_fabric *fabric;
};
static const struct tegra234_cbb_acpi_uid tegra234_cbb_acpi_uids[] = {
{ "NVDA1070", "1", &tegra241_cbb_fabric },
{ "NVDA1070", "2", &tegra241_bpmp_fabric },
{ },
};
static const struct
tegra234_cbb_fabric *tegra234_cbb_acpi_get_fabric(struct acpi_device *adev)
{
const struct tegra234_cbb_acpi_uid *entry;
for (entry = tegra234_cbb_acpi_uids; entry->hid; entry++) {
if (acpi_dev_hid_uid_match(adev, entry->hid, entry->uid))
return entry->fabric;
}
return NULL;
}
static const struct acpi_device_id tegra241_cbb_acpi_ids[] = {
{ "NVDA1070" },
{ },
};
MODULE_DEVICE_TABLE(acpi, tegra241_cbb_acpi_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;
if (pdev->dev.of_node) {
fabric = of_device_get_match_data(&pdev->dev);
} else {
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
if (!device)
return -ENODEV;
fabric = tegra234_cbb_acpi_get_fabric(device);
if (!fabric) {
dev_err(&pdev->dev, "no device match found\n");
return -ENODEV;
}
}
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);
/*
* Don't enable error reporting for a Fabric if write to it's registers
* is blocked by CBB firewall.
*/
if (!tegra234_cbb_write_access_allowed(pdev, cbb)) {
dev_info(&pdev->dev, "error reporting not enabled due to firewall\n");
return 0;
}
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 __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,
.driver = {
.name = "tegra234-cbb",
.of_match_table = tegra234_cbb_dt_ids,
.acpi_match_table = tegra241_cbb_acpi_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");