msm: mdss: register smmu context fault handler
Smmu context fault handler provides the fault iova information but does not provide any information about xin client. This patch registers the context fault handler in MDSS software to get the vmid/xin client information. It also dumps the registers for source associated with respective vmid client Change-Id: I2a833a4b5e81e36f4d7af23a3968c9755424b7a7 Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
This commit is contained in:
parent
f96563d68f
commit
fdef8552e6
9 changed files with 154 additions and 26 deletions
|
@ -392,6 +392,10 @@ Optional properties:
|
|||
Note that each tag matches with one of the
|
||||
regs-dump entries in the same order as they
|
||||
are defined.
|
||||
- qcom,regs-dump-xin-id-mdp: Array of VBIF clients ids (xins) corresponding
|
||||
to mdp block. Xin id property is not valid for mdp
|
||||
internal blocks like ctl, lm, dspp. It should set
|
||||
to 0xff for such blocks.
|
||||
|
||||
Fudge Factors: Fudge factors are used to boost demand for
|
||||
resources like bus bandswidth, clk rate etc. to
|
||||
|
@ -617,6 +621,8 @@ Subnode properties:
|
|||
"qcom,smmu_arm_mdp_sec" - arm smmu context bank device for
|
||||
secure mdp domain.
|
||||
- gdsc-mmagic-mdss-supply: Phandle for mmagic mdss supply regulator device node.
|
||||
- reg : offset and length of the register set for the device.
|
||||
- reg-names : names to refer to register sets related to this device
|
||||
- clocks: List of Phandles for clock device nodes
|
||||
needed by the device.
|
||||
- clock-names: List of clock names needed by the device.
|
||||
|
@ -639,6 +645,8 @@ Example:
|
|||
interrupts = <0 72 0>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
vdd-supply = <&gdsc_mdss>;
|
||||
batfet-supply = <&pm8941_chg_batif>;
|
||||
vdd-cx-supply = <&pm8841_s2_corner>;
|
||||
|
@ -778,6 +786,7 @@ Example:
|
|||
qcom,mdss-prefill-fbc-lines = <2>;
|
||||
qcom,mdss-idle-power-collapse-enabled;
|
||||
|
||||
qcom,regs-dump-xin-id-mdp = <0xff 0xff 0xff 0xff 0x0 0x0>;
|
||||
mdss_fb0: qcom,mdss_fb_primary {
|
||||
cell-index = <0>;
|
||||
compatible = "qcom,mdss-fb";
|
||||
|
@ -825,6 +834,8 @@ Example:
|
|||
smmu_mdp_sec: qcom,smmu_mdp_sec_cb {
|
||||
compatible = "qcom,smmu_mdp_sec";
|
||||
iommus = <&mdp_smmu 1>;
|
||||
reg = <0xd09000 0x000d00>,
|
||||
reg-names = "mmu_cb";
|
||||
gdsc-mmagic-mdss-supply = <&gdsc_mmagic_mdss>;
|
||||
clocks = <&clock_mmss clk_smmu_mdp_ahb_clk>,
|
||||
<&clock_mmss clk_smmu_mdp_axi_clk>;
|
||||
|
|
|
@ -201,6 +201,7 @@ struct mdss_smmu_client {
|
|||
struct reg_bus_client *reg_bus_clt;
|
||||
bool domain_attached;
|
||||
bool handoff_pending;
|
||||
void __iomem *mmu_base;
|
||||
};
|
||||
|
||||
struct mdss_mdp_qseed3_lut_tbl {
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
#define PANEL_CMD_MIN_TX_COUNT 2
|
||||
#define PANEL_DATA_NODE_LEN 80
|
||||
|
||||
#define INVALID_XIN_ID 0xFF
|
||||
|
||||
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
|
||||
|
||||
static int panel_debug_base_open(struct inode *inode, struct file *file)
|
||||
|
@ -630,12 +632,37 @@ error:
|
|||
}
|
||||
|
||||
static int parse_dt_xlog_dump_list(const u32 *arr, int count,
|
||||
struct list_head *xlog_dump_list, int total_names,
|
||||
struct platform_device *pdev, const char *name_prop)
|
||||
struct list_head *xlog_dump_list, struct platform_device *pdev,
|
||||
const char *name_prop, const char *xin_prop)
|
||||
{
|
||||
struct range_dump_node *xlog_node;
|
||||
u32 len;
|
||||
int i;
|
||||
int i, total_names, total_xin_ids, rc;
|
||||
u32 *offsets = NULL;
|
||||
|
||||
/* Get the property with the name of the ranges */
|
||||
total_names = of_property_count_strings(pdev->dev.of_node,
|
||||
name_prop);
|
||||
if (total_names < 0) {
|
||||
pr_warn("dump names not found. rc=%d\n", total_names);
|
||||
total_names = 0;
|
||||
}
|
||||
|
||||
of_find_property(pdev->dev.of_node, xin_prop, &total_xin_ids);
|
||||
if (total_xin_ids > 0) {
|
||||
total_xin_ids /= sizeof(u32);
|
||||
offsets = kcalloc(total_xin_ids, sizeof(u32), GFP_KERNEL);
|
||||
if (offsets) {
|
||||
rc = of_property_read_u32_array(pdev->dev.of_node,
|
||||
xin_prop, offsets, total_xin_ids);
|
||||
if (rc)
|
||||
total_xin_ids = 0;
|
||||
} else {
|
||||
total_xin_ids = 0;
|
||||
}
|
||||
} else {
|
||||
total_xin_ids = 0;
|
||||
}
|
||||
|
||||
for (i = 0, len = count * 2; i < len; i += 2) {
|
||||
xlog_node = kzalloc(sizeof(*xlog_node), GFP_KERNEL);
|
||||
|
@ -644,34 +671,33 @@ static int parse_dt_xlog_dump_list(const u32 *arr, int count,
|
|||
|
||||
xlog_node->offset.start = be32_to_cpu(arr[i]);
|
||||
xlog_node->offset.end = be32_to_cpu(arr[i + 1]);
|
||||
|
||||
parse_dump_range_name(pdev->dev.of_node, total_names, i/2,
|
||||
xlog_node->range_name,
|
||||
ARRAY_SIZE(xlog_node->range_name), name_prop);
|
||||
|
||||
if ((i / 2) < total_xin_ids)
|
||||
xlog_node->xin_id = offsets[i / 2];
|
||||
else
|
||||
xlog_node->xin_id = INVALID_XIN_ID;
|
||||
|
||||
list_add_tail(&xlog_node->head, xlog_dump_list);
|
||||
}
|
||||
|
||||
kfree(offsets);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdss_debug_register_dump_range(struct platform_device *pdev,
|
||||
struct mdss_debug_base *blk_base, const char *ranges_prop,
|
||||
const char *name_prop)
|
||||
const char *name_prop, const char *xin_prop)
|
||||
{
|
||||
int total_dump_names, mdp_len;
|
||||
int mdp_len;
|
||||
const u32 *mdp_arr;
|
||||
|
||||
if (!blk_base || !ranges_prop || !name_prop)
|
||||
return;
|
||||
|
||||
/* Get the property with the name of the ranges */
|
||||
total_dump_names = of_property_count_strings(pdev->dev.of_node,
|
||||
name_prop);
|
||||
if (total_dump_names < 0) {
|
||||
pr_warn("dump names not found. rc=%d\n", total_dump_names);
|
||||
total_dump_names = 0;
|
||||
}
|
||||
|
||||
mdp_arr = of_get_property(pdev->dev.of_node, ranges_prop,
|
||||
&mdp_len);
|
||||
if (!mdp_arr) {
|
||||
|
@ -680,9 +706,8 @@ void mdss_debug_register_dump_range(struct platform_device *pdev,
|
|||
} else {
|
||||
/* 2 is the number of entries per row to calculate the rows */
|
||||
mdp_len /= 2 * sizeof(u32);
|
||||
parse_dt_xlog_dump_list(mdp_arr, mdp_len,
|
||||
&blk_base->dump_list, total_dump_names, pdev,
|
||||
name_prop);
|
||||
parse_dt_xlog_dump_list(mdp_arr, mdp_len, &blk_base->dump_list,
|
||||
pdev, name_prop, xin_prop);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ struct range_dump_node {
|
|||
u32 *reg_dump; /* address for the mem dump */
|
||||
char range_name[40]; /* name of this range */
|
||||
struct dump_offset offset; /* range to dump */
|
||||
uint32_t xin_id; /* client xin id */
|
||||
};
|
||||
|
||||
#define DEFINE_MDSS_DEBUGFS_SEQ_FOPS(__prefix) \
|
||||
|
@ -143,7 +144,7 @@ int mdss_debug_register_base(const char *name, void __iomem *base,
|
|||
size_t max_offset, struct mdss_debug_base **dbg_blk);
|
||||
void mdss_debug_register_dump_range(struct platform_device *pdev,
|
||||
struct mdss_debug_base *blk_base, const char *ranges_prop,
|
||||
const char *name_prop);
|
||||
const char *name_prop, const char *xin_prop);
|
||||
int panel_debug_register_base(const char *name, void __iomem *base,
|
||||
size_t max_offset);
|
||||
int mdss_misr_set(struct mdss_data_type *mdata,
|
||||
|
@ -162,8 +163,13 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id,
|
|||
int mdss_create_xlog_debug(struct mdss_debug_data *mdd);
|
||||
void mdss_xlog(const char *name, int line, int flag, ...);
|
||||
void mdss_xlog_tout_handler_default(bool queue, const char *name, ...);
|
||||
u32 get_dump_range(struct dump_offset *range_node, size_t max_offset);
|
||||
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
|
||||
int len, u32 **dump_mem, bool from_isr);
|
||||
void mdss_mdp_debug_mid(u32 mid);
|
||||
#else
|
||||
struct mdss_debug_base;
|
||||
struct dump_offset;
|
||||
|
||||
static inline int mdss_debugfs_init(struct mdss_data_type *mdata) { return 0; }
|
||||
static inline int mdss_debugfs_remove(struct mdss_data_type *mdata)
|
||||
|
@ -174,7 +180,7 @@ static inline int mdss_debug_register_base(const char *name, void __iomem *base,
|
|||
size_t max_offset, struct mdss_debug_base **dbg_blk) { return 0; }
|
||||
static inline void mdss_debug_register_dump_range(struct platform_device *pdev,
|
||||
struct mdss_debug_base *blk_base, const char *ranges_prop,
|
||||
const char *name_prop) { }
|
||||
const char *name_prop, const char *xin_prop) { }
|
||||
static inline int panel_debug_register_base(const char *name,
|
||||
void __iomem *base,
|
||||
size_t max_offset)
|
||||
|
@ -203,6 +209,11 @@ static inline void mdss_xlog(const char *name, int line, int flag, ...) { }
|
|||
static inline void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) { }
|
||||
static inline void mdss_xlog_tout_handler_default(bool queue,
|
||||
const char *name, ...) { }
|
||||
u32 get_dump_range(struct dump_offset *range_node, size_t max_offset)
|
||||
{ return 0; }
|
||||
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
|
||||
int len, u32 **dump_mem, bool from_isr) { }
|
||||
void mdss_mdp_debug_mid(u32 mid) { }
|
||||
#endif
|
||||
|
||||
int mdss_dump_misr_data(char **buf, u32 size);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2014-2016, 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
|
||||
|
@ -405,8 +405,8 @@ static void mdss_dump_vbif_debug_bus(u32 bus_dump_flag,
|
|||
pr_info("========End VBIF Debug bus=========\n");
|
||||
}
|
||||
|
||||
static void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag,
|
||||
char *addr, int len, u32 **dump_mem)
|
||||
void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag, char *addr,
|
||||
int len, u32 **dump_mem, bool from_isr)
|
||||
{
|
||||
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
||||
bool in_log, in_mem;
|
||||
|
@ -440,7 +440,9 @@ static void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag,
|
|||
}
|
||||
}
|
||||
|
||||
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
|
||||
if (!from_isr)
|
||||
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
u32 x0, x4, x8, xc;
|
||||
|
||||
|
@ -462,7 +464,9 @@ static void mdss_dump_reg(const char *dump_name, u32 reg_dump_flag,
|
|||
|
||||
addr += 16;
|
||||
}
|
||||
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
|
||||
|
||||
if (!from_isr)
|
||||
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
|
||||
}
|
||||
|
||||
static void mdss_dump_reg_by_ranges(struct mdss_debug_base *dbg,
|
||||
|
@ -491,7 +495,8 @@ static void mdss_dump_reg_by_ranges(struct mdss_debug_base *dbg,
|
|||
addr, xlog_node->offset.start,
|
||||
xlog_node->offset.end);
|
||||
mdss_dump_reg((const char *)xlog_node->range_name,
|
||||
reg_dump_flag, addr, len, &xlog_node->reg_dump);
|
||||
reg_dump_flag, addr, len, &xlog_node->reg_dump,
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
/* If there is no list to dump ranges, dump all registers */
|
||||
|
@ -500,7 +505,7 @@ static void mdss_dump_reg_by_ranges(struct mdss_debug_base *dbg,
|
|||
addr = dbg->base;
|
||||
len = dbg->max_offset;
|
||||
mdss_dump_reg((const char *)dbg->name, reg_dump_flag, addr,
|
||||
len, &dbg->reg_dump);
|
||||
len, &dbg->reg_dump, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1358,7 +1358,7 @@ static int mdss_mdp_debug_init(struct platform_device *pdev,
|
|||
|
||||
mdss_debug_register_io("mdp", &mdata->mdss_io, &dbg_blk);
|
||||
mdss_debug_register_dump_range(pdev, dbg_blk, "qcom,regs-dump-mdp",
|
||||
"qcom,regs-dump-names-mdp");
|
||||
"qcom,regs-dump-names-mdp", "qcom,regs-dump-xin-id-mdp");
|
||||
|
||||
mdss_debug_register_io("vbif", &mdata->vbif_io, NULL);
|
||||
mdss_debug_register_io("vbif_nrt", &mdata->vbif_nrt_io, NULL);
|
||||
|
|
|
@ -971,6 +971,38 @@ void mdss_mdp_hw_rev_debug_caps_init(struct mdss_data_type *mdata)
|
|||
}
|
||||
}
|
||||
|
||||
void mdss_mdp_debug_mid(u32 mid)
|
||||
{
|
||||
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
||||
struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
|
||||
struct range_dump_node *xlog_node;
|
||||
struct mdss_debug_base *blk_base;
|
||||
char *addr;
|
||||
u32 len;
|
||||
|
||||
list_for_each_entry(blk_base, &mdd->base_list, head) {
|
||||
list_for_each_entry(xlog_node, &blk_base->dump_list, head) {
|
||||
if (xlog_node->xin_id != mid)
|
||||
continue;
|
||||
|
||||
len = get_dump_range(&xlog_node->offset,
|
||||
blk_base->max_offset);
|
||||
addr = blk_base->base + xlog_node->offset.start;
|
||||
pr_info("%s: mid:%d range_base=0x%pK start=0x%x end=0x%x\n",
|
||||
xlog_node->range_name, mid, addr,
|
||||
xlog_node->offset.start, xlog_node->offset.end);
|
||||
|
||||
/*
|
||||
* Next instruction assumes that MDP clocks are ON
|
||||
* because it is called from interrupt context
|
||||
*/
|
||||
mdss_dump_reg((const char *)xlog_node->range_name,
|
||||
MDSS_DBG_DUMP_IN_LOG, addr, len,
|
||||
&xlog_node->reg_dump, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __print_time(char *buf, u32 size, u64 ts)
|
||||
{
|
||||
unsigned long rem_ns = do_div(ts, NSEC_PER_SEC);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "mdss.h"
|
||||
#include "mdss_mdp.h"
|
||||
#include "mdss_smmu.h"
|
||||
#include "mdss_debug.h"
|
||||
|
||||
static DEFINE_MUTEX(mdp_iommu_lock);
|
||||
|
||||
|
@ -433,7 +434,34 @@ static void mdss_smmu_dsi_unmap_buffer_v2(dma_addr_t dma_addr, int domain,
|
|||
dma_unmap_single(mdss_smmu->dev, dma_addr, size, dir);
|
||||
}
|
||||
|
||||
int mdss_smmu_fault_handler(struct iommu_domain *domain, struct device *dev,
|
||||
unsigned long iova, int flags, void *user_data)
|
||||
{
|
||||
struct mdss_smmu_client *mdss_smmu =
|
||||
(struct mdss_smmu_client *)user_data;
|
||||
u32 fsynr1, mid, i;
|
||||
|
||||
if (!mdss_smmu || !mdss_smmu->mmu_base)
|
||||
goto end;
|
||||
|
||||
fsynr1 = readl_relaxed(mdss_smmu->mmu_base + SMMU_CBN_FSYNR1);
|
||||
mid = fsynr1 & 0xff;
|
||||
pr_err("mdss_smmu: iova:0x%lx flags:0x%x fsynr1: 0x%x mid: 0x%x\n",
|
||||
iova, flags, fsynr1, mid);
|
||||
|
||||
/* get domain id information */
|
||||
for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) {
|
||||
if (mdss_smmu == mdss_smmu_get_cb(i))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MDSS_IOMMU_MAX_DOMAIN)
|
||||
goto end;
|
||||
|
||||
mdss_mdp_debug_mid(mid);
|
||||
end:
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static void mdss_smmu_deinit_v2(struct mdss_data_type *mdata)
|
||||
{
|
||||
|
@ -536,6 +564,7 @@ int mdss_smmu_probe(struct platform_device *pdev)
|
|||
struct dss_module_power *mp;
|
||||
int disable_htw = 1;
|
||||
char name[MAX_CLIENT_NAME_LEN];
|
||||
const __be32 *address = NULL, *size = NULL;
|
||||
|
||||
if (!mdata) {
|
||||
pr_err("probe failed as mdata is not initialized\n");
|
||||
|
@ -651,6 +680,19 @@ int mdss_smmu_probe(struct platform_device *pdev)
|
|||
mdss_smmu->handoff_pending = true;
|
||||
|
||||
mdss_smmu->dev = dev;
|
||||
|
||||
address = of_get_address_by_name(pdev->dev.of_node, "mmu_cb", 0, 0);
|
||||
if (address) {
|
||||
size = address + 1;
|
||||
mdss_smmu->mmu_base = ioremap(be32_to_cpu(*address),
|
||||
be32_to_cpu(*size));
|
||||
if (mdss_smmu->mmu_base)
|
||||
iommu_set_fault_handler(mdss_smmu->mmu_mapping->domain,
|
||||
mdss_smmu_fault_handler, mdss_smmu);
|
||||
} else {
|
||||
pr_debug("unable to map context bank base\n");
|
||||
}
|
||||
|
||||
pr_info("iommu v2 domain[%d] mapping and clk register successful!\n",
|
||||
smmu_domain.domain);
|
||||
return 0;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mdss_debug.h"
|
||||
|
||||
#define MDSS_SMMU_COMPATIBLE "qcom,smmu"
|
||||
#define SMMU_CBN_FSYNR1 0x6c
|
||||
|
||||
struct mdss_iommu_map_type {
|
||||
char *client_name;
|
||||
|
|
Loading…
Add table
Reference in a new issue