coresight: implement runtime pc save control

Add runtime control for enabling/disabling program counter save feature.
Enabling program counter save feature will enable pc to be saved on
reset but implies ETM being left powered on. So we provide user runtime
control to enable the feature when debugging or disable it while taking
power measurements.

Change-Id: Ib007da851ee7f3b0fac195da62aac7def68cc67a
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
This commit is contained in:
Pratik Patel 2012-09-28 23:21:46 -07:00 committed by Iliyan Malchev
parent cae5ad6a24
commit c5036e4eb2
3 changed files with 176 additions and 3 deletions

View file

@ -31,6 +31,7 @@ Optional properties:
component
- coresight-child-ports : list of input port numbers of the children
- coresight-default-sink : represents the default compile time CoreSight sink
- qcom,pc-save : program counter save implemented
Examples:
@ -103,4 +104,5 @@ Examples:
coresight-outports = <0>;
coresight-child-list = <&funnel_kpss>;
coresight-child-ports = <0>;
qcom,pc-save;
};

View file

@ -114,6 +114,15 @@ config CORESIGHT_ETM_DEFAULT_ENABLE
If unsure, say 'N' here to avoid potential power and performance
penalty.
config CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE
bool "Turn on PC saving by default"
depends on CORESIGHT_ETM
help
Turns on program counter saving on reset by default. Otherwise,
PC saving is disabled by default but can be enabled via sysfs.
If unsure, say 'N' here to avoid potential power penalty.
endif
config CORESIGHT_EVENT

View file

@ -177,6 +177,15 @@ module_param_named(
boot_enable, boot_enable, int, S_IRUGO
);
#ifdef CONFIG_CORESIGHT_ETM_PCSAVE_DEFAULT_ENABLE
static int boot_pcsave_enable = 1;
#else
static int boot_pcsave_enable;
#endif
module_param_named(
boot_pcsave_enable, boot_pcsave_enable, int, S_IRUGO
);
struct etm_drvdata {
void __iomem *base;
struct device *dev;
@ -194,6 +203,7 @@ struct etm_drvdata {
uint8_t reset;
uint32_t mode;
uint32_t ctrl;
uint8_t ctrl_pwrdwn;
uint32_t trigger_event;
uint32_t startstop_ctrl;
uint32_t enable_event;
@ -220,6 +230,9 @@ struct etm_drvdata {
uint32_t ctxid_mask;
uint32_t sync_freq;
uint32_t timestamp_event;
uint8_t pdcr_pwrup;
bool pcsave_impl;
bool pcsave_enable;
};
static struct etm_drvdata *etm0drvdata;
@ -326,6 +339,70 @@ static void etm_clr_prog(struct etm_drvdata *drvdata)
etm_readl(drvdata, ETMSR));
}
static void etm_save_pwrdwn(struct etm_drvdata *drvdata)
{
drvdata->ctrl_pwrdwn = BVAL(etm_readl(drvdata, ETMCR), 0);
}
static void etm_restore_pwrdwn(struct etm_drvdata *drvdata)
{
uint32_t etmcr;
etmcr = etm_readl(drvdata, ETMCR);
etmcr = (etmcr & ~BIT(0)) | drvdata->ctrl_pwrdwn;
etm_writel(drvdata, etmcr, ETMCR);
}
static void etm_save_pwrup(struct etm_drvdata *drvdata)
{
drvdata->pdcr_pwrup = BVAL(etm_readl_mm(drvdata, ETMPDCR), 3);
}
static void etm_restore_pwrup(struct etm_drvdata *drvdata)
{
uint32_t etmpdcr;
etmpdcr = etm_readl_mm(drvdata, ETMPDCR);
etmpdcr = (etmpdcr & ~BIT(3)) | (drvdata->pdcr_pwrup << 3);
etm_writel_mm(drvdata, etmpdcr, ETMPDCR);
}
static void etm_enable_pcsave(void *info)
{
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
etm_save_pwrup(drvdata);
/*
* ETMPDCR is only accessible via memory mapped interface and so use
* it first to enable power/clock to allow subsequent cp14 accesses.
*/
etm_set_pwrup(drvdata);
etm_clr_pwrdwn(drvdata);
etm_restore_pwrup(drvdata);
ETM_LOCK(drvdata);
}
static void etm_disable_pcsave(void *info)
{
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
etm_save_pwrup(drvdata);
/*
* ETMPDCR is only accessible via memory mapped interface and so use
* it first to enable power/clock to allow subsequent cp14 accesses.
*/
etm_set_pwrup(drvdata);
etm_set_pwrdwn(drvdata);
etm_restore_pwrup(drvdata);
ETM_LOCK(drvdata);
}
static void __etm_enable(void *info)
{
int i;
@ -333,8 +410,13 @@ static void __etm_enable(void *info)
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
/* Vote for ETM power/clock enable */
/*
* Vote for ETM power/clock enable. ETMPDCR is only accessible via
* memory mapped interface and so use it first to enable power/clock
* to allow subsequent cp14 accesses.
*/
etm_set_pwrup(drvdata);
etm_save_pwrdwn(drvdata);
/*
* Clear power down bit since when this bit is set writes to
* certain registers might be ignored.
@ -381,6 +463,7 @@ static void __etm_enable(void *info)
etm_writel(drvdata, 0x00000000, ETMVMIDCVR);
etm_clr_prog(drvdata);
etm_restore_pwrdwn(drvdata);
ETM_LOCK(drvdata);
dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
@ -418,12 +501,18 @@ static void __etm_disable(void *info)
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
etm_save_pwrdwn(drvdata);
/*
* Clear power down bit since when this bit is set writes to
* certain registers might be ignored.
*/
etm_clr_pwrdwn(drvdata);
etm_set_prog(drvdata);
/* program trace enable to low by using always false event */
etm_writel(drvdata, 0x6F | BIT(14), ETMTEEVR);
etm_set_pwrdwn(drvdata);
etm_restore_pwrdwn(drvdata);
/* Vote for ETM power/clock disable */
etm_clr_pwrup(drvdata);
ETM_LOCK(drvdata);
@ -1380,6 +1469,7 @@ static ssize_t etm_show_sync_freq(struct device *dev,
{
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->sync_freq;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
@ -1425,6 +1515,60 @@ static ssize_t etm_store_timestamp_event(struct device *dev,
static DEVICE_ATTR(timestamp_event, S_IRUGO | S_IWUSR, etm_show_timestamp_event,
etm_store_timestamp_event);
static ssize_t etm_show_pcsave(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
val = drvdata->pcsave_enable;
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static int __etm_store_pcsave(struct etm_drvdata *drvdata, unsigned long val)
{
int ret;
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
mutex_lock(&drvdata->mutex);
if (val) {
smp_call_function_single(drvdata->cpu, etm_enable_pcsave,
drvdata, 1);
drvdata->pcsave_enable = true;
} else {
smp_call_function_single(drvdata->cpu, etm_disable_pcsave,
drvdata, 1);
drvdata->pcsave_enable = false;
}
mutex_unlock(&drvdata->mutex);
clk_disable_unprepare(drvdata->clk);
return 0;
}
static ssize_t etm_store_pcsave(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
int ret;
if (sscanf(buf, "%lx", &val) != 1)
return -EINVAL;
ret = __etm_store_pcsave(drvdata, val);
if (ret)
return ret;
return size;
}
static DEVICE_ATTR(pcsave, S_IRUGO | S_IWUSR, etm_show_pcsave,
etm_store_pcsave);
static struct attribute *etm_attrs[] = {
&dev_attr_nr_addr_cmp.attr,
&dev_attr_nr_cntr.attr,
@ -1505,7 +1649,11 @@ static void __devinit etm_init_arch_data(void *info)
struct etm_drvdata *drvdata = info;
ETM_UNLOCK(drvdata);
/* Vote for ETM power/clock enable */
/*
* Vote for ETM power/clock enable. ETMPDCR is only accessible via
* memory mapped interface and so use it first to enable power/clock
* to allow subsequent cp14 accesses.
*/
etm_set_pwrup(drvdata);
/*
* Clear power down bit since when this bit is set writes to
@ -1697,11 +1845,24 @@ static int __devinit etm_probe(struct platform_device *pdev)
goto err0;
}
if (pdev->dev.of_node)
drvdata->pcsave_impl = of_property_read_bool(pdev->dev.of_node,
"qcom,pc-save");
if (drvdata->pcsave_impl) {
ret = device_create_file(&drvdata->csdev->dev,
&dev_attr_pcsave);
if (ret)
dev_err(dev, "ETM pcsave dev node creation failed\n");
}
dev_info(dev, "ETM initialized\n");
if (boot_enable)
coresight_enable(drvdata->csdev);
if (drvdata->pcsave_impl && boot_pcsave_enable)
__etm_store_pcsave(drvdata, true);
return 0;
err1:
clk_disable_unprepare(drvdata->clk);
@ -1715,6 +1876,7 @@ static int __devexit etm_remove(struct platform_device *pdev)
{
struct etm_drvdata *drvdata = platform_get_drvdata(pdev);
device_remove_file(&drvdata->csdev->dev, &dev_attr_pcsave);
coresight_unregister(drvdata->csdev);
wake_lock_destroy(&drvdata->wake_lock);
mutex_destroy(&drvdata->mutex);