coresight-tmc: add support to save TMC registers
On some devices TMC registers are not preserved across reset. Add support to save TMC registers to read TMC buffer after a crash. Change-Id: I5fb7e870ddece35159d1fe465d4b70d2a4c1ec35 Signed-off-by: Shashank Mittal <mittals@codeaurora.org>
This commit is contained in:
parent
71cfed4142
commit
1c93c19d4d
2 changed files with 84 additions and 7 deletions
|
@ -78,6 +78,8 @@ its hardware characteristcs.
|
||||||
|
|
||||||
* coresight-ctis: represents flush and reset CTIs for TMC buffer
|
* coresight-ctis: represents flush and reset CTIs for TMC buffer
|
||||||
|
|
||||||
|
* qcom,force-reg-dump: enables TMC reg dump support
|
||||||
|
|
||||||
* Required property for TPDAs:
|
* Required property for TPDAs:
|
||||||
|
|
||||||
* qcom,tpda-atid: must be present. Specifies the ATID for TPDA.
|
* qcom,tpda-atid: must be present. Specifies the ATID for TPDA.
|
||||||
|
|
|
@ -95,6 +95,9 @@
|
||||||
#define TMC_ETR_BAM_PIPE_INDEX 0
|
#define TMC_ETR_BAM_PIPE_INDEX 0
|
||||||
#define TMC_ETR_BAM_NR_PIPES 2
|
#define TMC_ETR_BAM_NR_PIPES 2
|
||||||
|
|
||||||
|
#define TMC_REG_DUMP_MAGIC_V2 (0x42445953)
|
||||||
|
#define TMC_REG_DUMP_VER (1)
|
||||||
|
|
||||||
enum tmc_config_type {
|
enum tmc_config_type {
|
||||||
TMC_CONFIG_TYPE_ETB,
|
TMC_CONFIG_TYPE_ETB,
|
||||||
TMC_CONFIG_TYPE_ETR,
|
TMC_CONFIG_TYPE_ETR,
|
||||||
|
@ -196,8 +199,13 @@ struct tmc_drvdata {
|
||||||
struct msm_dump_data buf_data;
|
struct msm_dump_data buf_data;
|
||||||
struct coresight_cti *cti_flush;
|
struct coresight_cti *cti_flush;
|
||||||
struct coresight_cti *cti_reset;
|
struct coresight_cti *cti_reset;
|
||||||
|
char *reg_buf;
|
||||||
|
bool force_reg_dump;
|
||||||
|
bool dump_reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void __tmc_reg_dump(struct tmc_drvdata *drvdata);
|
||||||
|
|
||||||
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
|
static void tmc_wait_for_ready(struct tmc_drvdata *drvdata)
|
||||||
{
|
{
|
||||||
/* Ensure formatter, unformatter and hardware fifo are empty */
|
/* Ensure formatter, unformatter and hardware fifo are empty */
|
||||||
|
@ -818,6 +826,11 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode)
|
||||||
tmc_etf_enable_hw(drvdata);
|
tmc_etf_enable_hw(drvdata);
|
||||||
}
|
}
|
||||||
drvdata->enable = true;
|
drvdata->enable = true;
|
||||||
|
if (drvdata->force_reg_dump) {
|
||||||
|
drvdata->dump_reg = true;
|
||||||
|
__tmc_reg_dump(drvdata);
|
||||||
|
drvdata->dump_reg = false;
|
||||||
|
}
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
dev_info(drvdata->dev, "TMC enabled\n");
|
dev_info(drvdata->dev, "TMC enabled\n");
|
||||||
|
@ -875,6 +888,7 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
|
||||||
|
|
||||||
tmc_flush_and_stop(drvdata);
|
tmc_flush_and_stop(drvdata);
|
||||||
tmc_etb_dump_hw(drvdata);
|
tmc_etb_dump_hw(drvdata);
|
||||||
|
__tmc_reg_dump(drvdata);
|
||||||
tmc_disable_hw(drvdata);
|
tmc_disable_hw(drvdata);
|
||||||
|
|
||||||
CS_LOCK(drvdata->base);
|
CS_LOCK(drvdata->base);
|
||||||
|
@ -967,6 +981,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
|
||||||
|
|
||||||
tmc_flush_and_stop(drvdata);
|
tmc_flush_and_stop(drvdata);
|
||||||
tmc_etr_dump_hw(drvdata);
|
tmc_etr_dump_hw(drvdata);
|
||||||
|
__tmc_reg_dump(drvdata);
|
||||||
tmc_disable_hw(drvdata);
|
tmc_disable_hw(drvdata);
|
||||||
|
|
||||||
CS_LOCK(drvdata->base);
|
CS_LOCK(drvdata->base);
|
||||||
|
@ -1656,10 +1671,64 @@ static int tmc_etf_set_buf_dump(struct tmc_drvdata *drvdata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __tmc_reg_dump(struct tmc_drvdata *drvdata)
|
||||||
|
{
|
||||||
|
uint32_t *reg_buf;
|
||||||
|
|
||||||
|
if (!drvdata->reg_buf)
|
||||||
|
return;
|
||||||
|
else if (!drvdata->dump_reg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
drvdata->reg_data.version = TMC_REG_DUMP_VER;
|
||||||
|
|
||||||
|
reg_buf = (uint32_t *)drvdata->reg_buf;
|
||||||
|
|
||||||
|
reg_buf[1] = readl_relaxed(drvdata->base + TMC_RSZ);
|
||||||
|
reg_buf[3] = readl_relaxed(drvdata->base + TMC_STS);
|
||||||
|
reg_buf[5] = readl_relaxed(drvdata->base + TMC_RRP);
|
||||||
|
reg_buf[6] = readl_relaxed(drvdata->base + TMC_RWP);
|
||||||
|
reg_buf[7] = readl_relaxed(drvdata->base + TMC_TRG);
|
||||||
|
reg_buf[8] = readl_relaxed(drvdata->base + TMC_CTL);
|
||||||
|
reg_buf[10] = readl_relaxed(drvdata->base + TMC_MODE);
|
||||||
|
reg_buf[11] = readl_relaxed(drvdata->base + TMC_LBUFLEVEL);
|
||||||
|
reg_buf[12] = readl_relaxed(drvdata->base + TMC_CBUFLEVEL);
|
||||||
|
reg_buf[13] = readl_relaxed(drvdata->base + TMC_BUFWM);
|
||||||
|
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||||
|
reg_buf[14] = readl_relaxed(drvdata->base + TMC_RRPHI);
|
||||||
|
reg_buf[15] = readl_relaxed(drvdata->base + TMC_RWPHI);
|
||||||
|
reg_buf[68] = readl_relaxed(drvdata->base + TMC_AXICTL);
|
||||||
|
reg_buf[70] = readl_relaxed(drvdata->base + TMC_DBALO);
|
||||||
|
reg_buf[71] = readl_relaxed(drvdata->base + TMC_DBAHI);
|
||||||
|
}
|
||||||
|
reg_buf[192] = readl_relaxed(drvdata->base + TMC_FFSR);
|
||||||
|
reg_buf[193] = readl_relaxed(drvdata->base + TMC_FFCR);
|
||||||
|
reg_buf[194] = readl_relaxed(drvdata->base + TMC_PSCR);
|
||||||
|
reg_buf[1000] = readl_relaxed(drvdata->base + CORESIGHT_CLAIMSET);
|
||||||
|
reg_buf[1001] = readl_relaxed(drvdata->base + CORESIGHT_CLAIMCLR);
|
||||||
|
reg_buf[1005] = readl_relaxed(drvdata->base + CORESIGHT_LSR);
|
||||||
|
reg_buf[1006] = readl_relaxed(drvdata->base + CORESIGHT_AUTHSTATUS);
|
||||||
|
reg_buf[1010] = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
||||||
|
reg_buf[1011] = readl_relaxed(drvdata->base + CORESIGHT_DEVTYPE);
|
||||||
|
reg_buf[1012] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR4);
|
||||||
|
reg_buf[1013] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR5);
|
||||||
|
reg_buf[1014] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR6);
|
||||||
|
reg_buf[1015] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR7);
|
||||||
|
reg_buf[1016] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0);
|
||||||
|
reg_buf[1017] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR1);
|
||||||
|
reg_buf[1018] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR2);
|
||||||
|
reg_buf[1019] = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR3);
|
||||||
|
reg_buf[1020] = readl_relaxed(drvdata->base + CORESIGHT_COMPIDR0);
|
||||||
|
reg_buf[1021] = readl_relaxed(drvdata->base + CORESIGHT_COMPIDR1);
|
||||||
|
reg_buf[1022] = readl_relaxed(drvdata->base + CORESIGHT_COMPIDR2);
|
||||||
|
reg_buf[1023] = readl_relaxed(drvdata->base + CORESIGHT_COMPIDR3);
|
||||||
|
|
||||||
|
drvdata->reg_data.magic = TMC_REG_DUMP_MAGIC_V2;
|
||||||
|
}
|
||||||
|
|
||||||
static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
|
static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
void *baddr;
|
|
||||||
struct amba_device *adev;
|
struct amba_device *adev;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev = drvdata->dev;
|
struct device *dev = drvdata->dev;
|
||||||
|
@ -1674,11 +1743,11 @@ static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
|
||||||
res = &adev->res;
|
res = &adev->res;
|
||||||
size = resource_size(res);
|
size = resource_size(res);
|
||||||
|
|
||||||
baddr = devm_kzalloc(dev, size, GFP_KERNEL);
|
drvdata->reg_buf = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||||
if (!baddr)
|
if (!drvdata->reg_buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
drvdata->reg_data.addr = virt_to_phys(baddr);
|
drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
|
||||||
drvdata->reg_data.len = size;
|
drvdata->reg_data.len = size;
|
||||||
|
|
||||||
dump_entry.id = MSM_DUMP_DATA_TMC_REG + count;
|
dump_entry.id = MSM_DUMP_DATA_TMC_REG + count;
|
||||||
|
@ -1686,10 +1755,13 @@ static int tmc_set_reg_dump(struct tmc_drvdata *drvdata)
|
||||||
|
|
||||||
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
|
ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
|
||||||
&dump_entry);
|
&dump_entry);
|
||||||
if (ret) {
|
/*
|
||||||
devm_kfree(dev, baddr);
|
* Don't free the buffer in case of error since it can
|
||||||
|
* still be used to dump registers as part of abort to
|
||||||
|
* aid post crash parsing.
|
||||||
|
*/
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
|
@ -1733,6 +1805,9 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
spin_lock_init(&drvdata->spinlock);
|
spin_lock_init(&drvdata->spinlock);
|
||||||
mutex_init(&drvdata->mem_lock);
|
mutex_init(&drvdata->mem_lock);
|
||||||
|
|
||||||
|
drvdata->force_reg_dump = of_property_read_bool(np,
|
||||||
|
"qcom,force-reg-dump");
|
||||||
|
|
||||||
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
||||||
drvdata->config_type = BMVAL(devid, 6, 7);
|
drvdata->config_type = BMVAL(devid, 6, 7);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue