Add a CoreSight funnel driver for the gtrace framework, derived from drivers/hwtracing/coresight/coresight-funnel.c.
Signed-off-by: Zane Leung liangzhen@linux.spacemit.com --- drivers/hwtracing/gtrace/Kconfig | 13 + drivers/hwtracing/gtrace/Makefile | 1 + drivers/hwtracing/gtrace/coresight-funnel.c | 430 ++++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 drivers/hwtracing/gtrace/coresight-funnel.c
diff --git a/drivers/hwtracing/gtrace/Kconfig b/drivers/hwtracing/gtrace/Kconfig index 5dce4474db7c..081af9c0c670 100644 --- a/drivers/hwtracing/gtrace/Kconfig +++ b/drivers/hwtracing/gtrace/Kconfig @@ -101,3 +101,16 @@ config GTRACE_CORESIGHT
To compile this driver as a module, choose M here: the module will be called coresight. + +config CORESIGHT_FUNNEL + tristate "CoreSight Trace Funnel driver" + depends on GTRACE_CORESIGHT + default y + help + This driver provides support for the CoreSight funnel component. + The funnel merges trace data from multiple input ports into a single + output stream. It supports both static funnels (platform bus, + arm,coresight-static-funnel) and dynamic funnels (AMBA bus). + + To compile this driver as a module, choose M here: the module + will be called coresight-funnel. diff --git a/drivers/hwtracing/gtrace/Makefile b/drivers/hwtracing/gtrace/Makefile index 0833a40c397c..d2745c961a75 100644 --- a/drivers/hwtracing/gtrace/Makefile +++ b/drivers/hwtracing/gtrace/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o obj-$(CONFIG_RVTRACE_ATBBRIDGE) += rvtrace-atbbridge.o obj-$(CONFIG_GTRACE_CORESIGHT) += coresight-platform.o +obj-$(CONFIG_CORESIGHT_FUNNEL) += coresight-funnel.o diff --git a/drivers/hwtracing/gtrace/coresight-funnel.c b/drivers/hwtracing/gtrace/coresight-funnel.c new file mode 100644 index 000000000000..e7a3f185da56 --- /dev/null +++ b/drivers/hwtracing/gtrace/coresight-funnel.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Funnel driver for the gtrace framework. + * Derived from drivers/hwtracing/coresight/coresight-funnel.c. + */ + +#include <linux/acpi.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#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/amba/bus.h> +#include <linux/clk.h> + +#include "coresight.h" + +#define FUNNEL_FUNCTL 0x000 +#define FUNNEL_PRICTL 0x004 + +#define FUNNEL_HOLDTIME_MASK 0xf00 +#define FUNNEL_HOLDTIME_SHFT 0x8 +#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) +#define FUNNEL_ENSx_MASK 0xff + +/** + * struct funnel_drvdata - specifics associated to a funnel component + * @base: memory mapped base address for this component. + * @atclk: optional clock for the core parts of the funnel. + * @pclk: APB clock if present, otherwise NULL + * @comp: component vitals needed by the framework. + * @priority: port selection order. + */ +struct funnel_drvdata { + void __iomem *base; + struct clk *atclk; + struct clk *pclk; + struct gtrace_component *comp; + unsigned long priority; +}; + +static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port) +{ + u32 functl; + int rc = 0; + struct gtrace_component *comp = drvdata->comp; + + 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(comp); + if (rc) + goto done; + } + + functl &= ~FUNNEL_HOLDTIME_MASK; + functl |= FUNNEL_HOLDTIME; + functl |= (1 << port); + writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); + writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); +done: + CS_LOCK(drvdata->base); + return rc; +} + +static int funnel_enable(struct gtrace_component *comp, + struct gtrace_connection *in, + struct gtrace_connection *out) +{ + int rc = 0; + struct funnel_drvdata *drvdata = dev_get_drvdata(comp->dev.parent); + bool first_enable = false; + + if (in->dest_refcnt == 0) { + if (drvdata->base) + rc = dynamic_funnel_enable_hw(drvdata, in->dest_port); + if (!rc) + first_enable = true; + } + if (!rc) + in->dest_refcnt++; + + if (first_enable) + dev_dbg(&comp->dev, "FUNNEL inport %d enabled\n", + in->dest_port); + return rc; +} + +static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata, + int inport) +{ + u32 functl; + struct gtrace_component *comp = drvdata->comp; + + CS_UNLOCK(drvdata->base); + + functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); + functl &= ~(1 << inport); + writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); + + /* Disclaim the device if none of the slaves are now active */ + if (!(functl & FUNNEL_ENSx_MASK)) + coresight_disclaim_device_unlocked(comp); + + CS_LOCK(drvdata->base); +} + +static void funnel_disable(struct gtrace_component *comp, + struct gtrace_connection *in, + struct gtrace_connection *out) +{ + struct funnel_drvdata *drvdata = dev_get_drvdata(comp->dev.parent); + bool last_disable = false; + + if (--in->dest_refcnt == 0) { + if (drvdata->base) + dynamic_funnel_disable_hw(drvdata, in->dest_port); + last_disable = true; + } + + if (last_disable) + dev_dbg(&comp->dev, "FUNNEL inport %d disabled\n", + in->dest_port); +} + +static ssize_t priority_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->priority; + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t priority_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->priority = val; + return size; +} +static DEVICE_ATTR_RW(priority); + +static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) +{ + u32 functl; + + CS_UNLOCK(drvdata->base); + functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); + CS_LOCK(drvdata->base); + + return functl; +} + +static ssize_t funnel_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); + + pm_runtime_get_sync(dev->parent); + + val = get_funnel_ctrl_hw(drvdata); + + pm_runtime_put(dev->parent); + + return sprintf(buf, "%#x\n", val); +} +static DEVICE_ATTR_RO(funnel_ctrl); + +static struct attribute *coresight_funnel_attrs[] = { + &dev_attr_funnel_ctrl.attr, + &dev_attr_priority.attr, + NULL, +}; +ATTRIBUTE_GROUPS(coresight_funnel); + +static int funnel_probe(struct device *dev, struct resource *res) +{ + void __iomem *base; + struct gtrace_platform_data *pdata = NULL; + struct funnel_drvdata *drvdata; + struct gtrace_component_id id = { + .type = GTRACE_CORESIGHT_FUNNEL, + .version = CORESIGHT_COMPONENT_ID_VERSION, + }; + int ret; + + 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"); + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + ret = coresight_get_enable_clocks(dev, &drvdata->pclk, &drvdata->atclk); + if (ret) + return ret; + + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + /* + * 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; + pdata->groups = coresight_funnel_groups; + pdata->io_mem = true; + pdata->base = base; + coresight_clear_self_claim_tag(pdata); + } + + dev_set_drvdata(dev, drvdata); + + drvdata->comp = gtrace_register_component(&id, "cs_funnel", pdata); + if (IS_ERR(drvdata->comp)) + return PTR_ERR(drvdata->comp); + + return 0; +} + +static void funnel_remove(struct device *dev) +{ + struct funnel_drvdata *drvdata = dev_get_drvdata(dev); + + gtrace_unregister_component(drvdata->comp); +} + +#ifdef CONFIG_PM +static int funnel_runtime_suspend(struct device *dev) +{ + struct funnel_drvdata *drvdata = dev_get_drvdata(dev); + + clk_disable_unprepare(drvdata->atclk); + clk_disable_unprepare(drvdata->pclk); + + return 0; +} + +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; + + ret = clk_prepare_enable(drvdata->atclk); + if (ret) + clk_disable_unprepare(drvdata->pclk); + + return ret; +} +#endif + +static const struct dev_pm_ops funnel_dev_pm_ops = { + SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) +}; + +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, + }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9eb, + .mask = 0x000fffff, + }, + { 0, 0, NULL }, +}; + +MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids); + +static struct amba_driver dynamic_funnel_driver = { + .drv = { + .name = "coresight-dynamic-funnel", + .pm = &funnel_dev_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = dynamic_funnel_probe, + .remove = dynamic_funnel_remove, + .id_table = dynamic_funnel_ids, +}; + +static const struct gtrace_component_id coresight_funnel_ids[] = { + { .type = GTRACE_CORESIGHT_FUNNEL, + .version = CORESIGHT_COMPONENT_ID_VERSION, }, + {}, +}; + +static struct gtrace_driver coresight_funnel_driver = { + .id_table = coresight_funnel_ids, + .start_port = funnel_enable, + .stop_port = funnel_disable, + .driver = { + .name = "coresight-funnel", + }, +}; + +static int __init funnel_init(void) +{ + int ret; + + ret = gtrace_register_driver(&coresight_funnel_driver); + if (ret) + return ret; + + ret = coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver); + if (ret) { + gtrace_unregister_driver(&coresight_funnel_driver); + return ret; + } + + return 0; +} + +static void __exit funnel_exit(void) +{ + coresight_remove_driver(&dynamic_funnel_driver, &funnel_driver); + gtrace_unregister_driver(&coresight_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_AUTHOR("Zane Leung liangzhen@linux.spacemit.com"); +MODULE_DESCRIPTION("Arm CoreSight Funnel Driver"); +MODULE_LICENSE("GPL v2");