summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorDhaval Patel <pdhaval@codeaurora.org>2016-03-18 10:45:25 -0700
committerKyle Yan <kyan@codeaurora.org>2016-04-28 16:40:45 -0700
commitfdef8552e64e9b26841d66f24aab41579f2c607c (patch)
tree073e3c512891bf55ecfeb15f127e4e151063605a /drivers/video/fbdev
parentf96563d68fc3a11ca6076bbcbc26441b5a35ef11 (diff)
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>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c57
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.h15
-rw-r--r--drivers/video/fbdev/msm/mdss_debug_xlog.c19
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c2
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_debug.c32
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c42
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.h1
8 files changed, 143 insertions, 26 deletions
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index f92f3ae40d74..2c26a80bbc85 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -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 {
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 82144ec653f9..08d57e9de244 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -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);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h
index b0dcaf1109c6..35ae81e4709e 100644
--- a/drivers/video/fbdev/msm/mdss_debug.h
+++ b/drivers/video/fbdev/msm/mdss_debug.h
@@ -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);
diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c
index db0c1f2333ac..c696832a96e8 100644
--- a/drivers/video/fbdev/msm/mdss_debug_xlog.c
+++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c
@@ -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);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 84ddef637826..b66830d7d7a5 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -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);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 8436156ccfef..943a454a906e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -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);
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 9aa2c8386b17..9906f10a7911 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -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;
diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h
index 4000644e00b8..19ec3d2d1e2d 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.h
+++ b/drivers/video/fbdev/msm/mdss_smmu.h
@@ -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;