diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt index c58407365c92..f8606182262e 100644 --- a/Documentation/devicetree/bindings/coresight/coresight.txt +++ b/Documentation/devicetree/bindings/coresight/coresight.txt @@ -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; }; diff --git a/drivers/coresight/Kconfig b/drivers/coresight/Kconfig index 19b88a217dd1..c77df9578998 100644 --- a/drivers/coresight/Kconfig +++ b/drivers/coresight/Kconfig @@ -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 diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c index e0d2aa07d2d9..d076e18a4748 100644 --- a/drivers/coresight/coresight-etm.c +++ b/drivers/coresight/coresight-etm.c @@ -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);