Add support for gladiator cache interconnect error detection. Snapshot of gladiator erp driver as of msm-4.14 commit <b9aea53430a7> (soc: qcom: Add snapshot of gladiator erp driver). Change-Id: I1ed5a45f68923e3591ba84198480a543d87d67f1 Signed-off-by: Abhimanyu Kapur <abhimany@codeaurora.org> Signed-off-by: Srinivas Ramana <sramana@codeaurora.org> [snaseem@codeaurora.org: Resolve restriction errors.] Signed-off-by: Shadab Naseem <snaseem@codeaurora.org>
1131 lines
32 KiB
C
1131 lines
32 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2015-2017,2019, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/cpu_pm.h>
|
|
#include <linux/platform_device.h>
|
|
#include <soc/qcom/scm.h>
|
|
#include <linux/of.h>
|
|
#include <linux/clk.h>
|
|
|
|
#define MODULE_NAME "gladiator_error_reporting"
|
|
|
|
#define INVALID_NUM 0xDEADBEEF
|
|
|
|
struct reg_off {
|
|
unsigned int gladiator_id_coreid;
|
|
unsigned int gladiator_id_revisionid;
|
|
unsigned int gladiator_faulten;
|
|
unsigned int gladiator_errvld;
|
|
unsigned int gladiator_errclr;
|
|
unsigned int gladiator_errlog0;
|
|
unsigned int gladiator_errlog1;
|
|
unsigned int gladiator_errlog2;
|
|
unsigned int gladiator_errlog3;
|
|
unsigned int gladiator_errlog4;
|
|
unsigned int gladiator_errlog5;
|
|
unsigned int gladiator_errlog6;
|
|
unsigned int gladiator_errlog7;
|
|
unsigned int gladiator_errlog8;
|
|
unsigned int observer_0_id_coreid;
|
|
unsigned int observer_0_id_revisionid;
|
|
unsigned int observer_0_faulten;
|
|
unsigned int observer_0_errvld;
|
|
unsigned int observer_0_errclr;
|
|
unsigned int observer_0_errlog0;
|
|
unsigned int observer_0_errlog1;
|
|
unsigned int observer_0_errlog2;
|
|
unsigned int observer_0_errlog3;
|
|
unsigned int observer_0_errlog4;
|
|
unsigned int observer_0_errlog5;
|
|
unsigned int observer_0_errlog6;
|
|
unsigned int observer_0_errlog7;
|
|
unsigned int observer_0_errlog8;
|
|
unsigned int observer_0_stallen;
|
|
};
|
|
|
|
struct reg_masks_shift {
|
|
unsigned int gld_trans_opcode_mask;
|
|
unsigned int gld_trans_opcode_shift;
|
|
unsigned int gld_error_type_mask;
|
|
unsigned int gld_error_type_shift;
|
|
unsigned int gld_len1_mask;
|
|
unsigned int gld_len1_shift;
|
|
unsigned int gld_trans_sourceid_mask;
|
|
unsigned int gld_trans_sourceid_shift;
|
|
unsigned int gld_trans_targetid_mask;
|
|
unsigned int gld_trans_targetid_shift;
|
|
unsigned int gld_errlog_error;
|
|
unsigned int gld_errlog5_error_type_mask;
|
|
unsigned int gld_errlog5_error_type_shift;
|
|
unsigned int gld_ace_port_parity_mask;
|
|
unsigned int gld_ace_port_parity_shift;
|
|
unsigned int gld_ace_port_disconnect_mask;
|
|
unsigned int gld_ace_port_disconnect_shift;
|
|
unsigned int gld_ace_port_directory_mask;
|
|
unsigned int gld_ace_port_directory_shift;
|
|
unsigned int gld_index_parity_mask;
|
|
unsigned int gld_index_parity_shift;
|
|
unsigned int obs_trans_opcode_mask;
|
|
unsigned int obs_trans_opcode_shift;
|
|
unsigned int obs_error_type_mask;
|
|
unsigned int obs_error_type_shift;
|
|
unsigned int obs_len1_mask;
|
|
unsigned int obs_len1_shift;
|
|
};
|
|
|
|
struct msm_gladiator_data {
|
|
void __iomem *gladiator_virt_base;
|
|
int erp_irq;
|
|
struct notifier_block pm_notifier_block;
|
|
struct clk *qdss_clk;
|
|
struct reg_off *reg_offs;
|
|
struct reg_masks_shift *reg_masks_shifts;
|
|
bool glad_v2;
|
|
bool glad_v3;
|
|
};
|
|
|
|
static int enable_panic_on_error;
|
|
module_param(enable_panic_on_error, int, 0000);
|
|
|
|
enum gld_trans_opcode {
|
|
GLD_RD,
|
|
GLD_RDX,
|
|
GLD_RDL,
|
|
GLD_RESERVED,
|
|
GLD_WR,
|
|
GLD_WRC,
|
|
GLD_PRE,
|
|
};
|
|
|
|
enum obs_trans_opcode {
|
|
OBS_RD,
|
|
OBS_RDW,
|
|
OBS_RDL,
|
|
OBS_RDX,
|
|
OBS_WR,
|
|
OBS_WRW,
|
|
OBS_WRC,
|
|
OBS_RESERVED,
|
|
OBS_PRE,
|
|
OBS_URG,
|
|
};
|
|
|
|
enum obs_err_code {
|
|
OBS_SLV,
|
|
OBS_DEC,
|
|
OBS_UNS,
|
|
OBS_DISC,
|
|
OBS_SEC,
|
|
OBS_HIDE,
|
|
OBS_TMO,
|
|
OBS_RSV,
|
|
};
|
|
|
|
enum err_log {
|
|
ID_COREID,
|
|
ID_REVISIONID,
|
|
FAULTEN,
|
|
ERRVLD,
|
|
ERRCLR,
|
|
ERR_LOG0,
|
|
ERR_LOG1,
|
|
ERR_LOG2,
|
|
ERR_LOG3,
|
|
ERR_LOG4,
|
|
ERR_LOG5,
|
|
ERR_LOG6,
|
|
ERR_LOG7,
|
|
ERR_LOG8,
|
|
STALLEN,
|
|
MAX_NUM,
|
|
};
|
|
|
|
enum type_logger_error {
|
|
DATA_TRANSFER_ERROR,
|
|
DVM_ERROR,
|
|
TX_ERROR,
|
|
TXR_ERROR,
|
|
DISCONNECT_ERROR,
|
|
DIRECTORY_ERROR,
|
|
PARITY_ERROR,
|
|
};
|
|
|
|
static void clear_gladiator_error(void __iomem *gladiator_virt_base,
|
|
struct reg_off *offs)
|
|
{
|
|
writel_relaxed(1, gladiator_virt_base + offs->gladiator_errclr);
|
|
writel_relaxed(1, gladiator_virt_base + offs->observer_0_errclr);
|
|
}
|
|
|
|
static inline void print_gld_transaction(unsigned int opc)
|
|
{
|
|
switch (opc) {
|
|
case GLD_RD:
|
|
pr_alert("Transaction type: READ\n");
|
|
break;
|
|
case GLD_RDX:
|
|
pr_alert("Transaction type: EXCLUSIVE READ\n");
|
|
break;
|
|
case GLD_RDL:
|
|
pr_alert("Transaction type: LINKED READ\n");
|
|
break;
|
|
case GLD_WR:
|
|
pr_alert("Transaction type: WRITE\n");
|
|
break;
|
|
case GLD_WRC:
|
|
pr_alert("Transaction type: CONDITIONAL WRITE\n");
|
|
break;
|
|
case GLD_PRE:
|
|
pr_alert("Transaction: Preamble packet of linked sequence\n");
|
|
break;
|
|
default:
|
|
pr_alert("Transaction type: Unknown; value:%u\n", opc);
|
|
}
|
|
}
|
|
|
|
static inline void print_gld_errtype(unsigned int errtype)
|
|
{
|
|
if (errtype == 0)
|
|
pr_alert("Error type: Snoop data transfer\n");
|
|
else if (errtype == 1)
|
|
pr_alert("Error type: DVM error\n");
|
|
else if (errtype == 3)
|
|
pr_alert("Error type: Disconnect, directory, or parity error\n");
|
|
else
|
|
pr_alert("Error type: Unknown; value:%u\n", errtype);
|
|
}
|
|
|
|
static void decode_gld_errlog0(u32 err_reg,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
unsigned int opc, errtype, len1;
|
|
|
|
opc = (err_reg & mask_shifts->gld_trans_opcode_mask) >>
|
|
mask_shifts->gld_trans_opcode_shift;
|
|
errtype = (err_reg & mask_shifts->gld_error_type_mask) >>
|
|
mask_shifts->gld_error_type_shift;
|
|
len1 = (err_reg & mask_shifts->gld_len1_mask) >>
|
|
mask_shifts->gld_len1_shift;
|
|
|
|
print_gld_transaction(opc);
|
|
print_gld_errtype(errtype);
|
|
pr_alert("number of payload bytes: %d\n", len1 + 1);
|
|
}
|
|
|
|
static void decode_gld_errlog1(u32 err_reg,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
if ((err_reg & mask_shifts->gld_errlog_error) ==
|
|
mask_shifts->gld_errlog_error)
|
|
pr_alert("Transaction issued on IO target generic interface\n");
|
|
else
|
|
pr_alert("Transaction source ID: %d\n",
|
|
(err_reg & mask_shifts->gld_trans_sourceid_mask)
|
|
>> mask_shifts->gld_trans_sourceid_shift);
|
|
}
|
|
|
|
static void decode_gld_errlog2(u32 err_reg,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
if ((err_reg & mask_shifts->gld_errlog_error) ==
|
|
mask_shifts->gld_errlog_error)
|
|
pr_alert("Error response coming from: external DVM network\n");
|
|
else
|
|
pr_alert("Error response coming from: Target ID: %d\n",
|
|
(err_reg & mask_shifts->gld_trans_targetid_mask)
|
|
>> mask_shifts->gld_trans_targetid_shift);
|
|
}
|
|
|
|
static void decode_ace_port_index(u32 type, u32 error,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
unsigned int port;
|
|
|
|
switch (type) {
|
|
case DISCONNECT_ERROR:
|
|
port = (error & mask_shifts->gld_ace_port_disconnect_mask)
|
|
>> mask_shifts->gld_ace_port_disconnect_shift;
|
|
pr_alert("ACE port index: %d\n", port);
|
|
break;
|
|
case DIRECTORY_ERROR:
|
|
port = (error & mask_shifts->gld_ace_port_directory_mask)
|
|
>> mask_shifts->gld_ace_port_directory_shift;
|
|
pr_alert("ACE port index: %d\n", port);
|
|
break;
|
|
case PARITY_ERROR:
|
|
port = (error & mask_shifts->gld_ace_port_parity_mask)
|
|
>> mask_shifts->gld_ace_port_parity_shift;
|
|
pr_alert("ACE port index: %d\n", port);
|
|
}
|
|
}
|
|
|
|
static void decode_index_parity(u32 error, struct reg_masks_shift *mask_shifts)
|
|
{
|
|
pr_alert("Index: %d\n",
|
|
(error & mask_shifts->gld_index_parity_mask)
|
|
>> mask_shifts->gld_index_parity_shift);
|
|
}
|
|
|
|
static void decode_gld_logged_error(u32 err_reg5,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
unsigned int log_err_type, i, value;
|
|
|
|
log_err_type = (err_reg5 & mask_shifts->gld_errlog5_error_type_mask)
|
|
>> mask_shifts->gld_errlog5_error_type_shift;
|
|
for (i = 0 ; i <= 6 ; i++) {
|
|
value = log_err_type & 0x1;
|
|
switch (i) {
|
|
case DATA_TRANSFER_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: Data transfer error\n");
|
|
break;
|
|
case DVM_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: DVM error\n");
|
|
break;
|
|
case TX_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: Tx error\n");
|
|
break;
|
|
case TXR_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: TxR error\n");
|
|
break;
|
|
case DISCONNECT_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: Disconnect error\n");
|
|
decode_ace_port_index(
|
|
DISCONNECT_ERROR,
|
|
err_reg5,
|
|
mask_shifts);
|
|
break;
|
|
case DIRECTORY_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: Directory error\n");
|
|
decode_ace_port_index(
|
|
DIRECTORY_ERROR,
|
|
err_reg5,
|
|
mask_shifts);
|
|
break;
|
|
case PARITY_ERROR:
|
|
if (value == 0)
|
|
continue;
|
|
pr_alert("Error type: Parity error\n");
|
|
decode_ace_port_index(PARITY_ERROR, err_reg5,
|
|
mask_shifts);
|
|
decode_index_parity(err_reg5, mask_shifts);
|
|
break;
|
|
}
|
|
log_err_type = log_err_type >> 1;
|
|
}
|
|
}
|
|
|
|
static void decode_gld_errlog(u32 err_reg, unsigned int err_log,
|
|
struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
switch (err_log) {
|
|
case ERR_LOG0:
|
|
decode_gld_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG1:
|
|
decode_gld_errlog1(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG2:
|
|
decode_gld_errlog2(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG3:
|
|
pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG4:
|
|
pr_alert("Upper 32-bits of error address: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG5:
|
|
pr_alert("Lower 32-bits of user: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG6:
|
|
pr_alert("Mid 32-bits(63-32) of user: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG7:
|
|
break;
|
|
case ERR_LOG8:
|
|
pr_alert("Upper 32-bits(95-64) of user: %08x\n", err_reg);
|
|
break;
|
|
default:
|
|
pr_alert("Invalid error register; reg num:%u\n", err_log);
|
|
}
|
|
}
|
|
|
|
static inline void print_obs_transaction(unsigned int opc)
|
|
{
|
|
switch (opc) {
|
|
case OBS_RD:
|
|
pr_alert("Transaction type: READ\n");
|
|
break;
|
|
case OBS_RDW:
|
|
pr_alert("Transaction type: WRAPPED READ\n");
|
|
break;
|
|
case OBS_RDL:
|
|
pr_alert("Transaction type: LINKED READ\n");
|
|
break;
|
|
case OBS_RDX:
|
|
pr_alert("Transaction type: EXCLUSIVE READ\n");
|
|
break;
|
|
case OBS_WR:
|
|
pr_alert("Transaction type: WRITE\n");
|
|
break;
|
|
case OBS_WRW:
|
|
pr_alert("Transaction type: WRAPPED WRITE\n");
|
|
break;
|
|
case OBS_WRC:
|
|
pr_alert("Transaction type: CONDITIONAL WRITE\n");
|
|
break;
|
|
case OBS_PRE:
|
|
pr_alert("Transaction: Preamble packet of linked sequence\n");
|
|
break;
|
|
case OBS_URG:
|
|
pr_alert("Transaction type: Urgency Packet\n");
|
|
break;
|
|
default:
|
|
pr_alert("Transaction type: Unknown; value:%u\n", opc);
|
|
}
|
|
}
|
|
|
|
static inline void print_obs_errcode(unsigned int errcode)
|
|
{
|
|
switch (errcode) {
|
|
case OBS_SLV:
|
|
pr_alert("Error code: Target error detected by slave\n");
|
|
pr_alert("Source: Target\n");
|
|
break;
|
|
case OBS_DEC:
|
|
pr_alert("Error code: Address decode error\n");
|
|
pr_alert("Source: Initiator NIU\n");
|
|
break;
|
|
case OBS_UNS:
|
|
pr_alert("Error code: Unsupported request\n");
|
|
pr_alert("Source: Target NIU\n");
|
|
break;
|
|
case OBS_DISC:
|
|
pr_alert("Error code: Disconnected target or domain\n");
|
|
pr_alert("Source: Power Disconnect\n");
|
|
break;
|
|
case OBS_SEC:
|
|
pr_alert("Error code: Security violation\n");
|
|
pr_alert("Source: Initiator NIU or Firewall\n");
|
|
break;
|
|
case OBS_HIDE:
|
|
pr_alert("Error :Hidden security violation, reported as OK\n");
|
|
pr_alert("Source: Firewall\n");
|
|
break;
|
|
case OBS_TMO:
|
|
pr_alert("Error code: Time-out\n");
|
|
pr_alert("Source: Target NIU\n");
|
|
break;
|
|
default:
|
|
pr_alert("Error code: Unknown; code:%u\n", errcode);
|
|
}
|
|
}
|
|
|
|
static void decode_obs_errlog0(u32 err_reg,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
unsigned int opc, errcode;
|
|
|
|
opc = (err_reg & mask_shifts->obs_trans_opcode_mask) >>
|
|
mask_shifts->obs_trans_opcode_shift;
|
|
errcode = (err_reg & mask_shifts->obs_error_type_mask) >>
|
|
mask_shifts->obs_error_type_shift;
|
|
|
|
print_obs_transaction(opc);
|
|
print_obs_errcode(errcode);
|
|
}
|
|
|
|
static void decode_obs_errlog0_len(u32 err_reg,
|
|
struct reg_masks_shift *mask_shifts)
|
|
{
|
|
unsigned int len1;
|
|
|
|
len1 = (err_reg & mask_shifts->obs_len1_mask) >>
|
|
mask_shifts->obs_len1_shift;
|
|
pr_alert("number of payload bytes: %d\n", len1 + 1);
|
|
}
|
|
|
|
static void decode_obs_errlog(u32 err_reg, unsigned int err_log,
|
|
struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
switch (err_log) {
|
|
case ERR_LOG0:
|
|
decode_obs_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
|
|
decode_obs_errlog0_len(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG1:
|
|
pr_alert("RouteId of the error: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG2:
|
|
/* reserved error log register */
|
|
break;
|
|
case ERR_LOG3:
|
|
pr_alert("Lower 32-bits of error address: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG4:
|
|
pr_alert("Upper 12-bits of error address: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG5:
|
|
pr_alert("Lower 13-bits of user: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG6:
|
|
/* reserved error log register */
|
|
break;
|
|
case ERR_LOG7:
|
|
pr_alert("Security filed of the logged error: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG8:
|
|
/* reserved error log register */
|
|
break;
|
|
case STALLEN:
|
|
pr_alert("stall mode of the error logger: %08x\n",
|
|
err_reg & 0x1);
|
|
break;
|
|
default:
|
|
pr_alert("Invalid error register; reg num:%u\n", err_log);
|
|
}
|
|
}
|
|
|
|
static void decode_obs_errlog_v3(u32 err_reg, unsigned int err_log,
|
|
struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
switch (err_log) {
|
|
case ERR_LOG0:
|
|
decode_obs_errlog0(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG1:
|
|
decode_obs_errlog0_len(err_reg, msm_gld_data->reg_masks_shifts);
|
|
break;
|
|
case ERR_LOG2:
|
|
pr_alert("Path of the error: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG3:
|
|
pr_alert("ExtID of the error: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG4:
|
|
pr_alert("ERRLOG2_LSB: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG5:
|
|
pr_alert("ERRLOG2_MSB: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG6:
|
|
pr_alert("ERRLOG3_LSB: %08x\n", err_reg);
|
|
break;
|
|
case ERR_LOG7:
|
|
pr_alert("ERRLOG3_MSB: %08x\n", err_reg);
|
|
break;
|
|
case FAULTEN:
|
|
pr_alert("stall mode of the error logger: %08x\n",
|
|
err_reg & 0x3);
|
|
break;
|
|
default:
|
|
pr_alert("Invalid error register; reg num:%u\n", err_log);
|
|
}
|
|
}
|
|
|
|
static u32 get_gld_offset(unsigned int err_log, struct reg_off *offs)
|
|
{
|
|
u32 offset = 0;
|
|
|
|
switch (err_log) {
|
|
case FAULTEN:
|
|
offset = offs->gladiator_faulten;
|
|
break;
|
|
case ERRVLD:
|
|
offset = offs->gladiator_errvld;
|
|
break;
|
|
case ERRCLR:
|
|
offset = offs->gladiator_errclr;
|
|
break;
|
|
case ERR_LOG0:
|
|
offset = offs->gladiator_errlog0;
|
|
break;
|
|
case ERR_LOG1:
|
|
offset = offs->gladiator_errlog1;
|
|
break;
|
|
case ERR_LOG2:
|
|
offset = offs->gladiator_errlog2;
|
|
break;
|
|
case ERR_LOG3:
|
|
offset = offs->gladiator_errlog3;
|
|
break;
|
|
case ERR_LOG4:
|
|
offset = offs->gladiator_errlog4;
|
|
break;
|
|
case ERR_LOG5:
|
|
offset = offs->gladiator_errlog5;
|
|
break;
|
|
case ERR_LOG6:
|
|
offset = offs->gladiator_errlog6;
|
|
break;
|
|
case ERR_LOG7:
|
|
offset = offs->gladiator_errlog7;
|
|
break;
|
|
case ERR_LOG8:
|
|
offset = offs->gladiator_errlog8;
|
|
break;
|
|
default:
|
|
pr_alert("Invalid gladiator error register; reg num:%u\n",
|
|
err_log);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
static u32 get_obs_offset(unsigned int err_log, struct reg_off *offs)
|
|
{
|
|
u32 offset = 0;
|
|
|
|
switch (err_log) {
|
|
case FAULTEN:
|
|
offset = offs->observer_0_faulten;
|
|
break;
|
|
case ERRVLD:
|
|
offset = offs->observer_0_errvld;
|
|
break;
|
|
case ERRCLR:
|
|
offset = offs->observer_0_errclr;
|
|
break;
|
|
case ERR_LOG0:
|
|
offset = offs->observer_0_errlog0;
|
|
break;
|
|
case ERR_LOG1:
|
|
offset = offs->observer_0_errlog1;
|
|
break;
|
|
case ERR_LOG2:
|
|
offset = offs->observer_0_errlog2;
|
|
break;
|
|
case ERR_LOG3:
|
|
offset = offs->observer_0_errlog3;
|
|
break;
|
|
case ERR_LOG4:
|
|
offset = offs->observer_0_errlog4;
|
|
break;
|
|
case ERR_LOG5:
|
|
offset = offs->observer_0_errlog5;
|
|
break;
|
|
case ERR_LOG6:
|
|
offset = offs->observer_0_errlog6;
|
|
break;
|
|
case ERR_LOG7:
|
|
offset = offs->observer_0_errlog7;
|
|
break;
|
|
case ERR_LOG8:
|
|
offset = offs->observer_0_errlog8;
|
|
break;
|
|
case STALLEN:
|
|
offset = offs->observer_0_stallen;
|
|
break;
|
|
default:
|
|
pr_alert("Invalid observer error register; reg num:%u\n",
|
|
err_log);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
static void decode_gld_errlog5(struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
unsigned int errtype;
|
|
u32 err_reg0, err_reg5;
|
|
struct reg_masks_shift *mask_shifts = msm_gld_data->reg_masks_shifts;
|
|
|
|
err_reg0 = readl_relaxed(msm_gld_data->gladiator_virt_base +
|
|
get_gld_offset(ERR_LOG0, msm_gld_data->reg_offs));
|
|
err_reg5 = readl_relaxed(msm_gld_data->gladiator_virt_base +
|
|
get_gld_offset(ERR_LOG5, msm_gld_data->reg_offs));
|
|
|
|
errtype = (err_reg0 & mask_shifts->gld_error_type_mask) >>
|
|
mask_shifts->gld_error_type_shift;
|
|
if (errtype == 3)
|
|
decode_gld_logged_error(err_reg5, mask_shifts);
|
|
else if (errtype == 0 || errtype == 1)
|
|
pr_alert("Lower 32-bits of user: %08x\n", err_reg5);
|
|
else
|
|
pr_alert("Error type: Unknown; value:%u\n", errtype);
|
|
}
|
|
|
|
static void dump_gld_err_regs(struct msm_gladiator_data *msm_gld_data,
|
|
unsigned int err_buf[MAX_NUM])
|
|
{
|
|
unsigned int err_log;
|
|
unsigned int start = FAULTEN;
|
|
unsigned int end = ERR_LOG8;
|
|
|
|
if (msm_gld_data->glad_v2 || msm_gld_data->glad_v3) {
|
|
start = FAULTEN;
|
|
end = ERR_LOG8;
|
|
}
|
|
|
|
pr_alert("Main log register data:\n");
|
|
for (err_log = start; err_log <= end; err_log++) {
|
|
err_buf[err_log] = readl_relaxed(
|
|
msm_gld_data->gladiator_virt_base +
|
|
get_gld_offset(err_log,
|
|
msm_gld_data->reg_offs));
|
|
pr_alert("%08x ", err_buf[err_log]);
|
|
}
|
|
}
|
|
|
|
static void dump_obsrv_err_regs(struct msm_gladiator_data *msm_gld_data,
|
|
unsigned int err_buf[MAX_NUM])
|
|
{
|
|
unsigned int err_log;
|
|
unsigned int start = ID_COREID;
|
|
unsigned int end = STALLEN;
|
|
|
|
if (msm_gld_data->glad_v2) {
|
|
start = ID_COREID;
|
|
end = STALLEN;
|
|
} else if (msm_gld_data->glad_v3) {
|
|
start = FAULTEN;
|
|
end = ERR_LOG7;
|
|
}
|
|
|
|
pr_alert("Observer log register data:\n");
|
|
for (err_log = start; err_log <= end; err_log++) {
|
|
err_buf[err_log] = readl_relaxed(
|
|
msm_gld_data->gladiator_virt_base +
|
|
get_obs_offset(
|
|
err_log,
|
|
msm_gld_data->reg_offs)
|
|
);
|
|
pr_alert("%08x ", err_buf[err_log]);
|
|
}
|
|
}
|
|
|
|
static void parse_gld_err_regs(struct msm_gladiator_data *msm_gld_data,
|
|
unsigned int err_buf[MAX_NUM])
|
|
{
|
|
unsigned int err_log;
|
|
|
|
pr_alert("Main error log register data:\n");
|
|
for (err_log = ERR_LOG0; err_log <= ERR_LOG8; err_log++) {
|
|
/* skip log register 7 as its reserved */
|
|
if (err_log == ERR_LOG7)
|
|
continue;
|
|
if (err_log == ERR_LOG5) {
|
|
decode_gld_errlog5(msm_gld_data);
|
|
continue;
|
|
}
|
|
decode_gld_errlog(err_buf[err_log], err_log,
|
|
msm_gld_data);
|
|
}
|
|
}
|
|
|
|
static void parse_obsrv_err_regs(struct msm_gladiator_data *msm_gld_data,
|
|
unsigned int err_buf[MAX_NUM])
|
|
{
|
|
unsigned int err_log;
|
|
|
|
pr_alert("Observor error log register data:\n");
|
|
if (msm_gld_data->glad_v2) {
|
|
for (err_log = ERR_LOG0; err_log <= STALLEN; err_log++) {
|
|
/* skip log register 2, 6 and 8 as they are reserved */
|
|
if ((err_log == ERR_LOG2) || (err_log == ERR_LOG6)
|
|
|| (err_log == ERR_LOG8))
|
|
continue;
|
|
decode_obs_errlog(err_buf[err_log], err_log,
|
|
msm_gld_data);
|
|
}
|
|
} else if (msm_gld_data->glad_v3) {
|
|
decode_obs_errlog_v3(err_buf[STALLEN], STALLEN,
|
|
msm_gld_data);
|
|
for (err_log = ERR_LOG0; err_log <= ERR_LOG7; err_log++) {
|
|
decode_obs_errlog_v3(err_buf[err_log], err_log,
|
|
msm_gld_data);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static irqreturn_t msm_gladiator_isr(int irq, void *dev_id)
|
|
{
|
|
unsigned int gld_err_buf[MAX_NUM], obs_err_buf[MAX_NUM];
|
|
|
|
struct msm_gladiator_data *msm_gld_data = dev_id;
|
|
|
|
/* Check validity */
|
|
bool gld_err_valid = readl_relaxed(msm_gld_data->gladiator_virt_base +
|
|
msm_gld_data->reg_offs->gladiator_errvld);
|
|
|
|
bool obsrv_err_valid = readl_relaxed(
|
|
msm_gld_data->gladiator_virt_base +
|
|
msm_gld_data->reg_offs->observer_0_errvld);
|
|
|
|
if (!gld_err_valid && !obsrv_err_valid) {
|
|
pr_err("%s Invalid Gladiator error reported, clear it\n",
|
|
__func__);
|
|
/* Clear IRQ */
|
|
clear_gladiator_error(msm_gld_data->gladiator_virt_base,
|
|
msm_gld_data->reg_offs);
|
|
return IRQ_HANDLED;
|
|
}
|
|
pr_alert("Gladiator Error Detected:\n");
|
|
if (gld_err_valid)
|
|
dump_gld_err_regs(msm_gld_data, gld_err_buf);
|
|
|
|
if (obsrv_err_valid)
|
|
dump_obsrv_err_regs(msm_gld_data, obs_err_buf);
|
|
|
|
if (gld_err_valid)
|
|
parse_gld_err_regs(msm_gld_data, gld_err_buf);
|
|
|
|
if (obsrv_err_valid)
|
|
parse_obsrv_err_regs(msm_gld_data, obs_err_buf);
|
|
|
|
/* Clear IRQ */
|
|
clear_gladiator_error(msm_gld_data->gladiator_virt_base,
|
|
msm_gld_data->reg_offs);
|
|
if (enable_panic_on_error)
|
|
panic("Gladiator Cache Interconnect Error Detected!\n");
|
|
else
|
|
WARN(1, "Gladiator Cache Interconnect Error Detected\n");
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static const struct of_device_id gladiator_erp_match_table[] = {
|
|
{ .compatible = "qcom,msm-gladiator-v2" },
|
|
{ .compatible = "qcom,msm-gladiator-v3" },
|
|
{},
|
|
};
|
|
|
|
static int parse_dt_node(struct platform_device *pdev,
|
|
struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
int ret = 0;
|
|
struct resource *res;
|
|
|
|
res = platform_get_resource_byname(pdev,
|
|
IORESOURCE_MEM, "gladiator_base");
|
|
if (!res)
|
|
return -ENODEV;
|
|
if (!devm_request_mem_region(&pdev->dev, res->start,
|
|
resource_size(res),
|
|
"msm-gladiator-erp")) {
|
|
|
|
dev_err(&pdev->dev, "%s cannot reserve gladiator erp region\n",
|
|
__func__);
|
|
return -ENXIO;
|
|
}
|
|
msm_gld_data->gladiator_virt_base = devm_ioremap(&pdev->dev,
|
|
res->start, resource_size(res));
|
|
if (!msm_gld_data->gladiator_virt_base) {
|
|
dev_err(&pdev->dev, "%s cannot map gladiator register space\n",
|
|
__func__);
|
|
return -ENXIO;
|
|
}
|
|
msm_gld_data->erp_irq = platform_get_irq(pdev, 0);
|
|
if (!msm_gld_data->erp_irq)
|
|
return -ENODEV;
|
|
|
|
/* clear existing errors before enabling the interrupt */
|
|
clear_gladiator_error(msm_gld_data->gladiator_virt_base,
|
|
msm_gld_data->reg_offs);
|
|
ret = devm_request_irq(&pdev->dev, msm_gld_data->erp_irq,
|
|
msm_gladiator_isr, IRQF_TRIGGER_HIGH,
|
|
"gladiator-error", msm_gld_data);
|
|
if (ret)
|
|
dev_err(&pdev->dev, "Failed to register irq handler\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void gladiator_irq_init(void __iomem *gladiator_virt_base,
|
|
struct reg_off *offs)
|
|
{
|
|
writel_relaxed(1, gladiator_virt_base + offs->gladiator_faulten);
|
|
writel_relaxed(1, gladiator_virt_base + offs->observer_0_faulten);
|
|
}
|
|
|
|
#define CCI_LEVEL 2
|
|
static int gladiator_erp_pm_callback(struct notifier_block *nb,
|
|
unsigned long val, void *data)
|
|
{
|
|
unsigned int level = (unsigned long) data;
|
|
struct msm_gladiator_data *msm_gld_data = container_of(nb,
|
|
struct msm_gladiator_data, pm_notifier_block);
|
|
|
|
if (level != CCI_LEVEL)
|
|
return NOTIFY_DONE;
|
|
|
|
switch (val) {
|
|
case CPU_CLUSTER_PM_EXIT:
|
|
gladiator_irq_init(msm_gld_data->gladiator_virt_base,
|
|
msm_gld_data->reg_offs);
|
|
break;
|
|
default:
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static void init_offsets_and_masks_v2(struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
msm_gld_data->reg_offs->gladiator_id_coreid = 0x0;
|
|
msm_gld_data->reg_offs->gladiator_id_revisionid = 0x4;
|
|
msm_gld_data->reg_offs->gladiator_faulten = 0x1010;
|
|
msm_gld_data->reg_offs->gladiator_errvld = 0x1014;
|
|
msm_gld_data->reg_offs->gladiator_errclr = 0x1018;
|
|
msm_gld_data->reg_offs->gladiator_errlog0 = 0x101C;
|
|
msm_gld_data->reg_offs->gladiator_errlog1 = 0x1020;
|
|
msm_gld_data->reg_offs->gladiator_errlog2 = 0x1024;
|
|
msm_gld_data->reg_offs->gladiator_errlog3 = 0x1028;
|
|
msm_gld_data->reg_offs->gladiator_errlog4 = 0x102C;
|
|
msm_gld_data->reg_offs->gladiator_errlog5 = 0x1030;
|
|
msm_gld_data->reg_offs->gladiator_errlog6 = 0x1034;
|
|
msm_gld_data->reg_offs->gladiator_errlog7 = 0x1038;
|
|
msm_gld_data->reg_offs->gladiator_errlog8 = 0x103C;
|
|
msm_gld_data->reg_offs->observer_0_id_coreid = 0x8000;
|
|
msm_gld_data->reg_offs->observer_0_id_revisionid = 0x8004;
|
|
msm_gld_data->reg_offs->observer_0_faulten = 0x8008;
|
|
msm_gld_data->reg_offs->observer_0_errvld = 0x800C;
|
|
msm_gld_data->reg_offs->observer_0_errclr = 0x8010;
|
|
msm_gld_data->reg_offs->observer_0_errlog0 = 0x8014;
|
|
msm_gld_data->reg_offs->observer_0_errlog1 = 0x8018;
|
|
msm_gld_data->reg_offs->observer_0_errlog2 = 0x801C;
|
|
msm_gld_data->reg_offs->observer_0_errlog3 = 0x8020;
|
|
msm_gld_data->reg_offs->observer_0_errlog4 = 0x8024;
|
|
msm_gld_data->reg_offs->observer_0_errlog5 = 0x8028;
|
|
msm_gld_data->reg_offs->observer_0_errlog6 = 0x802C;
|
|
msm_gld_data->reg_offs->observer_0_errlog7 = 0x8030;
|
|
msm_gld_data->reg_offs->observer_0_errlog8 = 0x8034;
|
|
msm_gld_data->reg_offs->observer_0_stallen = 0x8038;
|
|
|
|
msm_gld_data->reg_masks_shifts->gld_trans_opcode_mask = 0xE;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_opcode_shift = 1;
|
|
msm_gld_data->reg_masks_shifts->gld_error_type_mask = 0x700;
|
|
msm_gld_data->reg_masks_shifts->gld_error_type_shift = 8;
|
|
msm_gld_data->reg_masks_shifts->gld_len1_mask = 0xFFF;
|
|
msm_gld_data->reg_masks_shifts->gld_len1_shift = 16;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_sourceid_mask = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_sourceid_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_targetid_mask = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_targetid_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog_error = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_mask =
|
|
0xFF000000;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_shift = 24;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_parity_mask = 0xc000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_parity_shift = 14;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_mask = 0xf0000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_shift = 16;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_directory_mask = 0xf00000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_directory_shift = 20;
|
|
msm_gld_data->reg_masks_shifts->gld_index_parity_mask = 0x1FFF;
|
|
msm_gld_data->reg_masks_shifts->gld_index_parity_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->obs_trans_opcode_mask = 0x1E;
|
|
msm_gld_data->reg_masks_shifts->obs_trans_opcode_shift = 1;
|
|
msm_gld_data->reg_masks_shifts->obs_error_type_mask = 0x700;
|
|
msm_gld_data->reg_masks_shifts->obs_error_type_shift = 8;
|
|
msm_gld_data->reg_masks_shifts->obs_len1_mask = 0x7F0;
|
|
msm_gld_data->reg_masks_shifts->obs_len1_shift = 16;
|
|
}
|
|
|
|
static void init_offsets_and_masks_v3(struct msm_gladiator_data *msm_gld_data)
|
|
{
|
|
msm_gld_data->reg_offs->gladiator_id_coreid = 0x0;
|
|
msm_gld_data->reg_offs->gladiator_id_revisionid = 0x4;
|
|
msm_gld_data->reg_offs->gladiator_faulten = 0x1010;
|
|
msm_gld_data->reg_offs->gladiator_errvld = 0x1014;
|
|
msm_gld_data->reg_offs->gladiator_errclr = 0x1018;
|
|
msm_gld_data->reg_offs->gladiator_errlog0 = 0x101C;
|
|
msm_gld_data->reg_offs->gladiator_errlog1 = 0x1020;
|
|
msm_gld_data->reg_offs->gladiator_errlog2 = 0x1024;
|
|
msm_gld_data->reg_offs->gladiator_errlog3 = 0x1028;
|
|
msm_gld_data->reg_offs->gladiator_errlog4 = 0x102C;
|
|
msm_gld_data->reg_offs->gladiator_errlog5 = 0x1030;
|
|
msm_gld_data->reg_offs->gladiator_errlog6 = 0x1034;
|
|
msm_gld_data->reg_offs->gladiator_errlog7 = 0x1038;
|
|
msm_gld_data->reg_offs->gladiator_errlog8 = 0x103C;
|
|
msm_gld_data->reg_offs->observer_0_id_coreid = INVALID_NUM;
|
|
msm_gld_data->reg_offs->observer_0_id_revisionid = INVALID_NUM;
|
|
msm_gld_data->reg_offs->observer_0_faulten = 0x2008;
|
|
msm_gld_data->reg_offs->observer_0_errvld = 0x2010;
|
|
msm_gld_data->reg_offs->observer_0_errclr = 0x2018;
|
|
msm_gld_data->reg_offs->observer_0_errlog0 = 0x2020;
|
|
msm_gld_data->reg_offs->observer_0_errlog1 = 0x2024;
|
|
msm_gld_data->reg_offs->observer_0_errlog2 = 0x2028;
|
|
msm_gld_data->reg_offs->observer_0_errlog3 = 0x202C;
|
|
msm_gld_data->reg_offs->observer_0_errlog4 = 0x2030;
|
|
msm_gld_data->reg_offs->observer_0_errlog5 = 0x2034;
|
|
msm_gld_data->reg_offs->observer_0_errlog6 = 0x2038;
|
|
msm_gld_data->reg_offs->observer_0_errlog7 = 0x203C;
|
|
msm_gld_data->reg_offs->observer_0_errlog8 = INVALID_NUM;
|
|
msm_gld_data->reg_offs->observer_0_stallen = INVALID_NUM;
|
|
|
|
msm_gld_data->reg_masks_shifts->gld_trans_opcode_mask = 0xE;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_opcode_shift = 1;
|
|
msm_gld_data->reg_masks_shifts->gld_error_type_mask = 0x700;
|
|
msm_gld_data->reg_masks_shifts->gld_error_type_shift = 8;
|
|
msm_gld_data->reg_masks_shifts->gld_len1_mask = 0xFFF0000;
|
|
msm_gld_data->reg_masks_shifts->gld_len1_shift = 16;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_sourceid_mask = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_sourceid_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_targetid_mask = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_trans_targetid_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog_error = 0x7;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_mask =
|
|
0xFF000000;
|
|
msm_gld_data->reg_masks_shifts->gld_errlog5_error_type_shift = 24;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_parity_mask = 0xc000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_parity_shift = 14;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_mask = 0xf0000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_disconnect_shift = 16;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_directory_mask = 0xf00000;
|
|
msm_gld_data->reg_masks_shifts->gld_ace_port_directory_shift = 20;
|
|
msm_gld_data->reg_masks_shifts->gld_index_parity_mask = 0x1FFF;
|
|
msm_gld_data->reg_masks_shifts->gld_index_parity_shift = 0;
|
|
msm_gld_data->reg_masks_shifts->obs_trans_opcode_mask = 0x70;
|
|
msm_gld_data->reg_masks_shifts->obs_trans_opcode_shift = 4;
|
|
msm_gld_data->reg_masks_shifts->obs_error_type_mask = 0x700;
|
|
msm_gld_data->reg_masks_shifts->obs_error_type_shift = 8;
|
|
msm_gld_data->reg_masks_shifts->obs_len1_mask = 0x1FF;
|
|
msm_gld_data->reg_masks_shifts->obs_len1_shift = 0;
|
|
}
|
|
|
|
static int gladiator_erp_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = -1;
|
|
struct msm_gladiator_data *msm_gld_data;
|
|
|
|
msm_gld_data = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct msm_gladiator_data), GFP_KERNEL);
|
|
if (!msm_gld_data) {
|
|
ret = -ENOMEM;
|
|
goto bail;
|
|
}
|
|
|
|
msm_gld_data->reg_offs = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct reg_off), GFP_KERNEL);
|
|
msm_gld_data->reg_masks_shifts = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct reg_masks_shift), GFP_KERNEL);
|
|
|
|
if (!msm_gld_data->reg_offs || !msm_gld_data->reg_masks_shifts) {
|
|
ret = -ENOMEM;
|
|
goto bail;
|
|
}
|
|
|
|
msm_gld_data->glad_v2 = of_device_is_compatible(pdev->dev.of_node,
|
|
"qcom,msm-gladiator-v2");
|
|
msm_gld_data->glad_v3 = of_device_is_compatible(pdev->dev.of_node,
|
|
"qcom,msm-gladiator-v3");
|
|
|
|
if (msm_gld_data->glad_v2)
|
|
init_offsets_and_masks_v2(msm_gld_data);
|
|
else if (msm_gld_data->glad_v3)
|
|
init_offsets_and_masks_v3(msm_gld_data);
|
|
|
|
if (msm_gld_data->glad_v2) {
|
|
if (of_property_match_string(pdev->dev.of_node,
|
|
"clock-names", "atb_clk") >= 0) {
|
|
msm_gld_data->qdss_clk = devm_clk_get(&pdev->dev,
|
|
"atb_clk");
|
|
if (IS_ERR(msm_gld_data->qdss_clk)) {
|
|
dev_err(&pdev->dev, "Failed to get QDSS ATB clock\n");
|
|
goto bail;
|
|
}
|
|
} else {
|
|
dev_err(&pdev->dev, "No matching string of QDSS ATB clock\n");
|
|
goto bail;
|
|
}
|
|
|
|
ret = clk_prepare_enable(msm_gld_data->qdss_clk);
|
|
if (ret)
|
|
goto err_atb_clk;
|
|
}
|
|
|
|
ret = parse_dt_node(pdev, msm_gld_data);
|
|
if (ret)
|
|
goto bail;
|
|
msm_gld_data->pm_notifier_block.notifier_call =
|
|
gladiator_erp_pm_callback;
|
|
|
|
gladiator_irq_init(msm_gld_data->gladiator_virt_base,
|
|
msm_gld_data->reg_offs);
|
|
platform_set_drvdata(pdev, msm_gld_data);
|
|
cpu_pm_register_notifier(&msm_gld_data->pm_notifier_block);
|
|
#ifdef CONFIG_PANIC_ON_GLADIATOR_ERROR
|
|
enable_panic_on_error = 1;
|
|
#endif
|
|
dev_info(&pdev->dev, "MSM Gladiator Error Reporting Initialized\n");
|
|
return ret;
|
|
|
|
err_atb_clk:
|
|
clk_disable_unprepare(msm_gld_data->qdss_clk);
|
|
|
|
bail:
|
|
dev_err(&pdev->dev, "Probe failed bailing out\n");
|
|
return ret;
|
|
}
|
|
|
|
static int gladiator_erp_remove(struct platform_device *pdev)
|
|
{
|
|
struct msm_gladiator_data *msm_gld_data = platform_get_drvdata(pdev);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
cpu_pm_unregister_notifier(&msm_gld_data->pm_notifier_block);
|
|
clk_disable_unprepare(msm_gld_data->qdss_clk);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver gladiator_erp_driver = {
|
|
.probe = gladiator_erp_probe,
|
|
.remove = gladiator_erp_remove,
|
|
.driver = {
|
|
.name = MODULE_NAME,
|
|
.of_match_table = gladiator_erp_match_table,
|
|
},
|
|
};
|
|
|
|
static int __init init_gladiator_erp(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = scm_is_secure_device();
|
|
if (ret == 0) {
|
|
pr_info("Gladiator Error Reporting not available\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return platform_driver_register(&gladiator_erp_driver);
|
|
}
|
|
module_init(init_gladiator_erp);
|
|
|
|
static void __exit exit_gladiator_erp(void)
|
|
{
|
|
return platform_driver_unregister(&gladiator_erp_driver);
|
|
}
|
|
module_exit(exit_gladiator_erp);
|
|
|
|
MODULE_DESCRIPTION("Gladiator Error Reporting");
|
|
MODULE_LICENSE("GPL v2");
|