diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-replicator.c')
| -rw-r--r-- | drivers/hwtracing/coresight/coresight-replicator.c | 382 |
1 files changed, 332 insertions, 50 deletions
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index feac98315471..e6472658235d 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * * Description: CoreSight Replicator driver */ +#include <linux/acpi.h> +#include <linux/amba/bus.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -12,39 +14,182 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/clk.h> #include <linux/of.h> #include <linux/coresight.h> #include "coresight-priv.h" +#define REPLICATOR_IDFILTER0 0x000 +#define REPLICATOR_IDFILTER1 0x004 + +DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); + /** * struct replicator_drvdata - specifics associated to a replicator component - * @dev: the device entity associated with this component + * @base: memory mapped base address for this component. Also indicates + * whether this one is programmable or not. * @atclk: optional clock for the core parts of the replicator. + * @pclk: APB clock if present, otherwise NULL * @csdev: component vitals needed by the framework + * @spinlock: serialize enable/disable operations. + * @check_idfilter_val: check if the context is lost upon clock removal. */ struct replicator_drvdata { - struct device *dev; + void __iomem *base; struct clk *atclk; + struct clk *pclk; struct coresight_device *csdev; + raw_spinlock_t spinlock; + bool check_idfilter_val; }; -static int replicator_enable(struct coresight_device *csdev, int inport, - int outport) +static void dynamic_replicator_reset(struct replicator_drvdata *drvdata) +{ + struct coresight_device *csdev = drvdata->csdev; + + CS_UNLOCK(drvdata->base); + + if (!coresight_claim_device_unlocked(csdev)) { + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); + coresight_disclaim_device_unlocked(csdev); + } + + CS_LOCK(drvdata->base); +} + +/* + * replicator_reset : Reset the replicator configuration to sane values. + */ +static void replicator_reset(struct replicator_drvdata *drvdata) +{ + if (drvdata->base) + dynamic_replicator_reset(drvdata); +} + +static int dynamic_replicator_enable(struct replicator_drvdata *drvdata, + int inport, int outport) +{ + int rc = 0; + u32 id0val, id1val; + struct coresight_device *csdev = drvdata->csdev; + + CS_UNLOCK(drvdata->base); + + id0val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0); + id1val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1); + + /* + * Some replicator designs lose context when AMBA clocks are removed, + * so have a check for this. + */ + if (drvdata->check_idfilter_val && id0val == 0x0 && id1val == 0x0) + id0val = id1val = 0xff; + + if (id0val == 0xff && id1val == 0xff) + rc = coresight_claim_device_unlocked(csdev); + + if (!rc) { + switch (outport) { + case 0: + id0val = 0x0; + break; + case 1: + id1val = 0x0; + break; + default: + WARN_ON(1); + rc = -EINVAL; + } + } + + /* Ensure that the outport is enabled. */ + if (!rc) { + writel_relaxed(id0val, drvdata->base + REPLICATOR_IDFILTER0); + writel_relaxed(id1val, drvdata->base + REPLICATOR_IDFILTER1); + } + + CS_LOCK(drvdata->base); + + return rc; +} + +static int replicator_enable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) { + int rc = 0; struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool first_enable = false; - dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); - return 0; + raw_spin_lock_irqsave(&drvdata->spinlock, flags); + if (out->src_refcnt == 0) { + if (drvdata->base) + rc = dynamic_replicator_enable(drvdata, in->dest_port, + out->src_port); + if (!rc) + first_enable = true; + } + if (!rc) + out->src_refcnt++; + raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); + + if (first_enable) + dev_dbg(&csdev->dev, "REPLICATOR enabled\n"); + return rc; } -static void replicator_disable(struct coresight_device *csdev, int inport, - int outport) +static void dynamic_replicator_disable(struct replicator_drvdata *drvdata, + int inport, int outport) +{ + u32 reg; + struct coresight_device *csdev = drvdata->csdev; + + switch (outport) { + case 0: + reg = REPLICATOR_IDFILTER0; + break; + case 1: + reg = REPLICATOR_IDFILTER1; + break; + default: + WARN_ON(1); + return; + } + + CS_UNLOCK(drvdata->base); + + /* disable the flow of ATB data through port */ + writel_relaxed(0xff, drvdata->base + reg); + + if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) && + (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff)) + coresight_disclaim_device_unlocked(csdev); + CS_LOCK(drvdata->base); +} + +static void replicator_disable(struct coresight_device *csdev, + struct coresight_connection *in, + struct coresight_connection *out) { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool last_disable = false; - dev_dbg(drvdata->dev, "REPLICATOR disabled\n"); + raw_spin_lock_irqsave(&drvdata->spinlock, flags); + if (--out->src_refcnt == 0) { + if (drvdata->base) + dynamic_replicator_disable(drvdata, in->dest_port, + out->src_port); + last_disable = true; + } + raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); + + if (last_disable) + dev_dbg(&csdev->dev, "REPLICATOR disabled\n"); } static const struct coresight_ops_link replicator_link_ops = { @@ -56,69 +201,130 @@ static const struct coresight_ops replicator_cs_ops = { .link_ops = &replicator_link_ops, }; -static int replicator_probe(struct platform_device *pdev) +static struct attribute *replicator_mgmt_attrs[] = { + coresight_simple_reg32(idfilter0, REPLICATOR_IDFILTER0), + coresight_simple_reg32(idfilter1, REPLICATOR_IDFILTER1), + NULL, +}; + +static const struct attribute_group replicator_mgmt_group = { + .attrs = replicator_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *replicator_groups[] = { + &replicator_mgmt_group, + NULL, +}; + +static int replicator_probe(struct device *dev, struct resource *res) { - int ret; - struct device *dev = &pdev->dev; struct coresight_platform_data *pdata = NULL; struct replicator_drvdata *drvdata; struct coresight_desc desc = { 0 }; - struct device_node *np = pdev->dev.of_node; + void __iomem *base; + int ret; - if (np) { - pdata = of_get_coresight_platform_data(dev, np); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - pdev->dev.platform_data = pdata; - } + if (is_of_node(dev_fwnode(dev)) && + of_device_is_compatible(dev->of_node, "arm,coresight-replicator")) + dev_warn_once(dev, + "Uses OBSOLETE CoreSight replicator binding\n"); + + desc.name = coresight_alloc_device_name(&replicator_devs, dev); + if (!desc.name) + return -ENOMEM; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->dev = &pdev->dev; - drvdata->atclk = devm_clk_get(&pdev->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-replicator, 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 = replicator_groups; + desc.access = CSDEV_ACCESS_IOMEM(base); + coresight_clear_self_claim_tag(&desc.access); } - pm_runtime_get_noresume(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - platform_set_drvdata(pdev, drvdata); + if (fwnode_property_present(dev_fwnode(dev), + "qcom,replicator-loses-context")) + drvdata->check_idfilter_val = true; + + dev_set_drvdata(dev, drvdata); + + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + dev->platform_data = pdata; + + raw_spin_lock_init(&drvdata->spinlock); desc.type = CORESIGHT_DEV_TYPE_LINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; desc.ops = &replicator_cs_ops; - desc.pdata = pdev->dev.platform_data; - desc.dev = &pdev->dev; + desc.pdata = dev->platform_data; + desc.dev = dev; + drvdata->csdev = coresight_register(&desc); - if (IS_ERR(drvdata->csdev)) { - ret = PTR_ERR(drvdata->csdev); - goto out_disable_pm; - } + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); - pm_runtime_put(&pdev->dev); + replicator_reset(drvdata); + return 0; +} +static int replicator_remove(struct device *dev) +{ + struct replicator_drvdata *drvdata = dev_get_drvdata(dev); + + coresight_unregister(drvdata->csdev); return 0; +} -out_disable_pm: - if (!IS_ERR(drvdata->atclk)) - clk_disable_unprepare(drvdata->atclk); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_disable(&pdev->dev); +static int replicator_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 = replicator_probe(&pdev->dev, res); + pm_runtime_put(&pdev->dev); + if (ret) + pm_runtime_disable(&pdev->dev); return ret; } +static void replicator_platform_remove(struct platform_device *pdev) +{ + struct replicator_drvdata *drvdata = dev_get_drvdata(&pdev->dev); + + if (WARN_ON(!drvdata)) + return; + + replicator_remove(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { struct replicator_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; } @@ -126,11 +332,17 @@ static int replicator_runtime_suspend(struct device *dev) static int replicator_runtime_resume(struct device *dev) { struct replicator_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 @@ -141,16 +353,86 @@ static const struct dev_pm_ops replicator_dev_pm_ops = { static const struct of_device_id replicator_match[] = { {.compatible = "arm,coresight-replicator"}, + {.compatible = "arm,coresight-static-replicator"}, {} }; +MODULE_DEVICE_TABLE(of, replicator_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id replicator_acpi_ids[] = { + {"ARMHC985", 0, 0, 0}, /* ARM CoreSight Static Replicator */ + {"ARMHC98D", 0, 0, 0}, /* ARM CoreSight Dynamic Replicator */ + {} +}; + +MODULE_DEVICE_TABLE(acpi, replicator_acpi_ids); +#endif + static struct platform_driver replicator_driver = { - .probe = replicator_probe, + .probe = replicator_platform_probe, + .remove = replicator_platform_remove, .driver = { .name = "coresight-replicator", - .of_match_table = replicator_match, + /* THIS_MODULE is taken care of by platform_driver_register() */ + .of_match_table = of_match_ptr(replicator_match), + .acpi_match_table = ACPI_PTR(replicator_acpi_ids), .pm = &replicator_dev_pm_ops, .suppress_bind_attrs = true, }, }; -builtin_platform_driver(replicator_driver); + +static int dynamic_replicator_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ret = replicator_probe(&adev->dev, &adev->res); + if (!ret) + pm_runtime_put(&adev->dev); + + return ret; +} + +static void dynamic_replicator_remove(struct amba_device *adev) +{ + replicator_remove(&adev->dev); +} + +static const struct amba_id dynamic_replicator_ids[] = { + CS_AMBA_ID(0x000bb909), + CS_AMBA_ID(0x000bb9ec), /* Coresight SoC-600 */ + {}, +}; + +MODULE_DEVICE_TABLE(amba, dynamic_replicator_ids); + +static struct amba_driver dynamic_replicator_driver = { + .drv = { + .name = "coresight-dynamic-replicator", + .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = dynamic_replicator_probe, + .remove = dynamic_replicator_remove, + .id_table = dynamic_replicator_ids, +}; + +static int __init replicator_init(void) +{ + return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver, + THIS_MODULE); +} + +static void __exit replicator_exit(void) +{ + coresight_remove_driver(&dynamic_replicator_driver, &replicator_driver); +} + +module_init(replicator_init); +module_exit(replicator_exit); + +MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>"); +MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); +MODULE_DESCRIPTION("Arm CoreSight Replicator Driver"); +MODULE_LICENSE("GPL v2"); |
