summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight/coresight-funnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-funnel.c')
-rw-r--r--drivers/hwtracing/coresight/coresight-funnel.c269
1 files changed, 211 insertions, 58 deletions
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 927925151509..3b248e54471a 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -5,6 +5,7 @@
* Description: CoreSight Funnel driver
*/
+#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -12,6 +13,8 @@
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
@@ -27,33 +30,38 @@
#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
#define FUNNEL_ENSx_MASK 0xff
+DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
+
/**
* struct funnel_drvdata - specifics associated to a funnel component
* @base: memory mapped base address for this component.
- * @dev: the device entity associated to this component.
* @atclk: optional clock for the core parts of the funnel.
+ * @pclk: APB clock if present, otherwise NULL
* @csdev: component vitals needed by the framework.
* @priority: port selection order.
+ * @spinlock: serialize enable/disable operations.
*/
struct funnel_drvdata {
void __iomem *base;
- struct device *dev;
struct clk *atclk;
+ struct clk *pclk;
struct coresight_device *csdev;
unsigned long priority;
+ raw_spinlock_t spinlock;
};
-static int funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
+static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
{
u32 functl;
int rc = 0;
+ struct coresight_device *csdev = drvdata->csdev;
CS_UNLOCK(drvdata->base);
functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
/* Claim the device only when we enable the first slave */
if (!(functl & FUNNEL_ENSx_MASK)) {
- rc = coresight_claim_device_unlocked(drvdata->base);
+ rc = coresight_claim_device_unlocked(csdev);
if (rc)
goto done;
}
@@ -68,22 +76,37 @@ done:
return rc;
}
-static int funnel_enable(struct coresight_device *csdev, int inport,
- int outport)
+static int funnel_enable(struct coresight_device *csdev,
+ struct coresight_connection *in,
+ struct coresight_connection *out)
{
- int rc;
+ int rc = 0;
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
- rc = funnel_enable_hw(drvdata, inport);
-
+ unsigned long flags;
+ bool first_enable = false;
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (in->dest_refcnt == 0) {
+ if (drvdata->base)
+ rc = dynamic_funnel_enable_hw(drvdata, in->dest_port);
+ if (!rc)
+ first_enable = true;
+ }
if (!rc)
- dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
+ in->dest_refcnt++;
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ if (first_enable)
+ dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n",
+ in->dest_port);
return rc;
}
-static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
+static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata,
+ int inport)
{
u32 functl;
+ struct coresight_device *csdev = drvdata->csdev;
CS_UNLOCK(drvdata->base);
@@ -93,19 +116,30 @@ static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
/* Disclaim the device if none of the slaves are now active */
if (!(functl & FUNNEL_ENSx_MASK))
- coresight_disclaim_device_unlocked(drvdata->base);
+ coresight_disclaim_device_unlocked(csdev);
CS_LOCK(drvdata->base);
}
-static void funnel_disable(struct coresight_device *csdev, int inport,
- int outport)
+static void funnel_disable(struct coresight_device *csdev,
+ struct coresight_connection *in,
+ struct coresight_connection *out)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+ unsigned long flags;
+ bool last_disable = false;
+
+ raw_spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (--in->dest_refcnt == 0) {
+ if (drvdata->base)
+ dynamic_funnel_disable_hw(drvdata, in->dest_port);
+ last_disable = true;
+ }
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
- funnel_disable_hw(drvdata, inport);
-
- dev_dbg(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
+ if (last_disable)
+ dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n",
+ in->dest_port);
}
static const struct coresight_ops_link funnel_link_ops = {
@@ -160,11 +194,11 @@ static ssize_t funnel_ctrl_show(struct device *dev,
u32 val;
struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
- pm_runtime_get_sync(drvdata->dev);
+ pm_runtime_get_sync(dev->parent);
val = get_funnel_ctrl_hw(drvdata);
- pm_runtime_put(drvdata->dev);
+ pm_runtime_put(dev->parent);
return sprintf(buf, "%#x\n", val);
}
@@ -177,54 +211,72 @@ static struct attribute *coresight_funnel_attrs[] = {
};
ATTRIBUTE_GROUPS(coresight_funnel);
-static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
+static int funnel_probe(struct device *dev, struct resource *res)
{
- int ret;
void __iomem *base;
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata = NULL;
struct funnel_drvdata *drvdata;
- struct resource *res = &adev->res;
struct coresight_desc desc = { 0 };
- struct device_node *np = adev->dev.of_node;
+ int ret;
- if (np) {
- pdata = of_get_coresight_platform_data(dev, np);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- adev->dev.platform_data = pdata;
- }
+ if (is_of_node(dev_fwnode(dev)) &&
+ of_device_is_compatible(dev->of_node, "arm,coresight-funnel"))
+ dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n");
+
+ desc.name = coresight_alloc_device_name(&funnel_devs, dev);
+ if (!desc.name)
+ return -ENOMEM;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- drvdata->dev = &adev->dev;
- drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
- if (!IS_ERR(drvdata->atclk)) {
- ret = clk_prepare_enable(drvdata->atclk);
- if (ret)
- return ret;
+ ret = coresight_get_enable_clocks(dev, &drvdata->pclk, &drvdata->atclk);
+ if (ret)
+ return ret;
+
+ /*
+ * Map the device base for dynamic-funnel, which has been
+ * validated by AMBA core.
+ */
+ if (res) {
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ drvdata->base = base;
+ desc.groups = coresight_funnel_groups;
+ desc.access = CSDEV_ACCESS_IOMEM(base);
+ coresight_clear_self_claim_tag(&desc.access);
}
+
dev_set_drvdata(dev, drvdata);
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ pdata = coresight_get_platform_data(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
- drvdata->base = base;
- pm_runtime_put(&adev->dev);
+ dev->platform_data = pdata;
+ raw_spin_lock_init(&drvdata->spinlock);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
desc.ops = &funnel_cs_ops;
desc.pdata = pdata;
desc.dev = dev;
- desc.groups = coresight_funnel_groups;
drvdata->csdev = coresight_register(&desc);
+ if (IS_ERR(drvdata->csdev))
+ return PTR_ERR(drvdata->csdev);
- return PTR_ERR_OR_ZERO(drvdata->csdev);
+ return 0;
+}
+
+static int funnel_remove(struct device *dev)
+{
+ struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
+
+ coresight_unregister(drvdata->csdev);
+
+ return 0;
}
#ifdef CONFIG_PM
@@ -232,8 +284,8 @@ static int funnel_runtime_suspend(struct device *dev)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
- if (drvdata && !IS_ERR(drvdata->atclk))
- clk_disable_unprepare(drvdata->atclk);
+ clk_disable_unprepare(drvdata->atclk);
+ clk_disable_unprepare(drvdata->pclk);
return 0;
}
@@ -241,11 +293,17 @@ static int funnel_runtime_suspend(struct device *dev)
static int funnel_runtime_resume(struct device *dev)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(drvdata->pclk);
+ if (ret)
+ return ret;
- if (drvdata && !IS_ERR(drvdata->atclk))
- clk_prepare_enable(drvdata->atclk);
+ ret = clk_prepare_enable(drvdata->atclk);
+ if (ret)
+ clk_disable_unprepare(drvdata->pclk);
- return 0;
+ return ret;
}
#endif
@@ -253,7 +311,82 @@ static const struct dev_pm_ops funnel_dev_pm_ops = {
SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
};
-static const struct amba_id funnel_ids[] = {
+static int funnel_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = funnel_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void funnel_platform_remove(struct platform_device *pdev)
+{
+ struct funnel_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!drvdata))
+ return;
+
+ funnel_remove(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id funnel_match[] = {
+ {.compatible = "arm,coresight-static-funnel"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, funnel_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id funnel_acpi_ids[] = {
+ {"ARMHC9FE", 0, 0, 0}, /* ARM Coresight Static Funnel */
+ {"ARMHC9FF", 0, 0, 0}, /* ARM CoreSight Dynamic Funnel */
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, funnel_acpi_ids);
+#endif
+
+static struct platform_driver funnel_driver = {
+ .probe = funnel_platform_probe,
+ .remove = funnel_platform_remove,
+ .driver = {
+ .name = "coresight-funnel",
+ /* THIS_MODULE is taken care of by platform_driver_register() */
+ .of_match_table = funnel_match,
+ .acpi_match_table = ACPI_PTR(funnel_acpi_ids),
+ .pm = &funnel_dev_pm_ops,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int dynamic_funnel_probe(struct amba_device *adev,
+ const struct amba_id *id)
+{
+ int ret;
+
+ ret = funnel_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
+static void dynamic_funnel_remove(struct amba_device *adev)
+{
+ funnel_remove(&adev->dev);
+}
+
+static const struct amba_id dynamic_funnel_ids[] = {
{
.id = 0x000bb908,
.mask = 0x000fffff,
@@ -263,17 +396,37 @@ static const struct amba_id funnel_ids[] = {
.id = 0x000bb9eb,
.mask = 0x000fffff,
},
- { 0, 0},
+ { 0, 0, NULL },
};
-static struct amba_driver funnel_driver = {
+MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);
+
+static struct amba_driver dynamic_funnel_driver = {
.drv = {
- .name = "coresight-funnel",
- .owner = THIS_MODULE,
+ .name = "coresight-dynamic-funnel",
.pm = &funnel_dev_pm_ops,
.suppress_bind_attrs = true,
},
- .probe = funnel_probe,
- .id_table = funnel_ids,
+ .probe = dynamic_funnel_probe,
+ .remove = dynamic_funnel_remove,
+ .id_table = dynamic_funnel_ids,
};
-builtin_amba_driver(funnel_driver);
+
+static int __init funnel_init(void)
+{
+ return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver,
+ THIS_MODULE);
+}
+
+static void __exit funnel_exit(void)
+{
+ coresight_remove_driver(&dynamic_funnel_driver, &funnel_driver);
+}
+
+module_init(funnel_init);
+module_exit(funnel_exit);
+
+MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
+MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
+MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
+MODULE_LICENSE("GPL v2");