diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-funnel.c')
| -rw-r--r-- | drivers/hwtracing/coresight/coresight-funnel.c | 301 |
1 files changed, 233 insertions, 68 deletions
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 860fe6ef5632..3b248e54471a 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -1,17 +1,11 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * Description: CoreSight Funnel driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ +#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -19,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> @@ -32,53 +28,85 @@ #define FUNNEL_HOLDTIME_MASK 0xf00 #define FUNNEL_HOLDTIME_SHFT 0x8 #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 void 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(csdev); + 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 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 = 0; struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - funnel_enable_hw(drvdata, inport); - - dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); - return 0; + 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) + 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); @@ -86,17 +114,32 @@ static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) 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(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_info(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 = { @@ -151,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); } @@ -168,51 +211,58 @@ 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); @@ -220,13 +270,22 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) 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 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; } @@ -234,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; - if (drvdata && !IS_ERR(drvdata->atclk)) - clk_prepare_enable(drvdata->atclk); + ret = clk_prepare_enable(drvdata->pclk); + if (ret) + return ret; - return 0; + ret = clk_prepare_enable(drvdata->atclk); + if (ret) + clk_disable_unprepare(drvdata->pclk); + + return ret; } #endif @@ -246,22 +311,122 @@ static const struct dev_pm_ops funnel_dev_pm_ops = { SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) }; -static 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, + }, { - .id = 0x0003b908, - .mask = 0x0003ffff, + /* Coresight SoC-600 */ + .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"); |
