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:
Shashank Mittal 2016-06-15 13:39:57 -07:00 committed by Kyle Yan
parent 71cfed4142
commit 1c93c19d4d
2 changed files with 84 additions and 7 deletions

View file

@ -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.

View file

@ -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);