mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
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:
parent
cae5ad6a24
commit
c5036e4eb2
3 changed files with 176 additions and 3 deletions
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue