/* Copyright (c) 2015-2017, 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 #include #include #include #include #include #include #include #include #include #include #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, .owner = THIS_MODULE, .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");