linux/drivers/hwtracing/coresight/coresight-funnel.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
   4 *
   5 * Description: CoreSight Funnel driver
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/init.h>
  10#include <linux/types.h>
  11#include <linux/device.h>
  12#include <linux/err.h>
  13#include <linux/fs.h>
  14#include <linux/slab.h>
  15#include <linux/of.h>
  16#include <linux/platform_device.h>
  17#include <linux/pm_runtime.h>
  18#include <linux/coresight.h>
  19#include <linux/amba/bus.h>
  20#include <linux/clk.h>
  21
  22#include "coresight-priv.h"
  23
  24#define FUNNEL_FUNCTL           0x000
  25#define FUNNEL_PRICTL           0x004
  26
  27#define FUNNEL_HOLDTIME_MASK    0xf00
  28#define FUNNEL_HOLDTIME_SHFT    0x8
  29#define FUNNEL_HOLDTIME         (0x7 << FUNNEL_HOLDTIME_SHFT)
  30#define FUNNEL_ENSx_MASK        0xff
  31
  32DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
  33
  34/**
  35 * struct funnel_drvdata - specifics associated to a funnel component
  36 * @base:       memory mapped base address for this component.
  37 * @atclk:      optional clock for the core parts of the funnel.
  38 * @csdev:      component vitals needed by the framework.
  39 * @priority:   port selection order.
  40 */
  41struct funnel_drvdata {
  42        void __iomem            *base;
  43        struct clk              *atclk;
  44        struct coresight_device *csdev;
  45        unsigned long           priority;
  46};
  47
  48static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
  49{
  50        u32 functl;
  51        int rc = 0;
  52
  53        CS_UNLOCK(drvdata->base);
  54
  55        functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
  56        /* Claim the device only when we enable the first slave */
  57        if (!(functl & FUNNEL_ENSx_MASK)) {
  58                rc = coresight_claim_device_unlocked(drvdata->base);
  59                if (rc)
  60                        goto done;
  61        }
  62
  63        functl &= ~FUNNEL_HOLDTIME_MASK;
  64        functl |= FUNNEL_HOLDTIME;
  65        functl |= (1 << port);
  66        writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
  67        writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
  68done:
  69        CS_LOCK(drvdata->base);
  70        return rc;
  71}
  72
  73static int funnel_enable(struct coresight_device *csdev, int inport,
  74                         int outport)
  75{
  76        int rc = 0;
  77        struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
  78
  79        if (drvdata->base)
  80                rc = dynamic_funnel_enable_hw(drvdata, inport);
  81
  82        if (!rc)
  83                dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport);
  84        return rc;
  85}
  86
  87static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata,
  88                                      int inport)
  89{
  90        u32 functl;
  91
  92        CS_UNLOCK(drvdata->base);
  93
  94        functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
  95        functl &= ~(1 << inport);
  96        writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
  97
  98        /* Disclaim the device if none of the slaves are now active */
  99        if (!(functl & FUNNEL_ENSx_MASK))
 100                coresight_disclaim_device_unlocked(drvdata->base);
 101
 102        CS_LOCK(drvdata->base);
 103}
 104
 105static void funnel_disable(struct coresight_device *csdev, int inport,
 106                           int outport)
 107{
 108        struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 109
 110        if (drvdata->base)
 111                dynamic_funnel_disable_hw(drvdata, inport);
 112
 113        dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
 114}
 115
 116static const struct coresight_ops_link funnel_link_ops = {
 117        .enable         = funnel_enable,
 118        .disable        = funnel_disable,
 119};
 120
 121static const struct coresight_ops funnel_cs_ops = {
 122        .link_ops       = &funnel_link_ops,
 123};
 124
 125static ssize_t priority_show(struct device *dev,
 126                             struct device_attribute *attr, char *buf)
 127{
 128        struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
 129        unsigned long val = drvdata->priority;
 130
 131        return sprintf(buf, "%#lx\n", val);
 132}
 133
 134static ssize_t priority_store(struct device *dev,
 135                              struct device_attribute *attr,
 136                              const char *buf, size_t size)
 137{
 138        int ret;
 139        unsigned long val;
 140        struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
 141
 142        ret = kstrtoul(buf, 16, &val);
 143        if (ret)
 144                return ret;
 145
 146        drvdata->priority = val;
 147        return size;
 148}
 149static DEVICE_ATTR_RW(priority);
 150
 151static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
 152{
 153        u32 functl;
 154
 155        CS_UNLOCK(drvdata->base);
 156        functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
 157        CS_LOCK(drvdata->base);
 158
 159        return functl;
 160}
 161
 162static ssize_t funnel_ctrl_show(struct device *dev,
 163                             struct device_attribute *attr, char *buf)
 164{
 165        u32 val;
 166        struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
 167
 168        pm_runtime_get_sync(dev->parent);
 169
 170        val = get_funnel_ctrl_hw(drvdata);
 171
 172        pm_runtime_put(dev->parent);
 173
 174        return sprintf(buf, "%#x\n", val);
 175}
 176static DEVICE_ATTR_RO(funnel_ctrl);
 177
 178static struct attribute *coresight_funnel_attrs[] = {
 179        &dev_attr_funnel_ctrl.attr,
 180        &dev_attr_priority.attr,
 181        NULL,
 182};
 183ATTRIBUTE_GROUPS(coresight_funnel);
 184
 185static int funnel_probe(struct device *dev, struct resource *res)
 186{
 187        int ret;
 188        void __iomem *base;
 189        struct coresight_platform_data *pdata = NULL;
 190        struct funnel_drvdata *drvdata;
 191        struct coresight_desc desc = { 0 };
 192
 193        if (is_of_node(dev_fwnode(dev)) &&
 194            of_device_is_compatible(dev->of_node, "arm,coresight-funnel"))
 195                pr_warn_once("Uses OBSOLETE CoreSight funnel binding\n");
 196
 197        desc.name = coresight_alloc_device_name(&funnel_devs, dev);
 198        if (!desc.name)
 199                return -ENOMEM;
 200
 201        drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
 202        if (!drvdata)
 203                return -ENOMEM;
 204
 205        drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */
 206        if (!IS_ERR(drvdata->atclk)) {
 207                ret = clk_prepare_enable(drvdata->atclk);
 208                if (ret)
 209                        return ret;
 210        }
 211
 212        /*
 213         * Map the device base for dynamic-funnel, which has been
 214         * validated by AMBA core.
 215         */
 216        if (res) {
 217                base = devm_ioremap_resource(dev, res);
 218                if (IS_ERR(base)) {
 219                        ret = PTR_ERR(base);
 220                        goto out_disable_clk;
 221                }
 222                drvdata->base = base;
 223                desc.groups = coresight_funnel_groups;
 224        }
 225
 226        dev_set_drvdata(dev, drvdata);
 227
 228        pdata = coresight_get_platform_data(dev);
 229        if (IS_ERR(pdata)) {
 230                ret = PTR_ERR(pdata);
 231                goto out_disable_clk;
 232        }
 233        dev->platform_data = pdata;
 234
 235        desc.type = CORESIGHT_DEV_TYPE_LINK;
 236        desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
 237        desc.ops = &funnel_cs_ops;
 238        desc.pdata = pdata;
 239        desc.dev = dev;
 240        drvdata->csdev = coresight_register(&desc);
 241        if (IS_ERR(drvdata->csdev)) {
 242                ret = PTR_ERR(drvdata->csdev);
 243                goto out_disable_clk;
 244        }
 245
 246        pm_runtime_put(dev);
 247        ret = 0;
 248
 249out_disable_clk:
 250        if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
 251                clk_disable_unprepare(drvdata->atclk);
 252        return ret;
 253}
 254
 255#ifdef CONFIG_PM
 256static int funnel_runtime_suspend(struct device *dev)
 257{
 258        struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
 259
 260        if (drvdata && !IS_ERR(drvdata->atclk))
 261                clk_disable_unprepare(drvdata->atclk);
 262
 263        return 0;
 264}
 265
 266static int funnel_runtime_resume(struct device *dev)
 267{
 268        struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
 269
 270        if (drvdata && !IS_ERR(drvdata->atclk))
 271                clk_prepare_enable(drvdata->atclk);
 272
 273        return 0;
 274}
 275#endif
 276
 277static const struct dev_pm_ops funnel_dev_pm_ops = {
 278        SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
 279};
 280
 281static int static_funnel_probe(struct platform_device *pdev)
 282{
 283        int ret;
 284
 285        pm_runtime_get_noresume(&pdev->dev);
 286        pm_runtime_set_active(&pdev->dev);
 287        pm_runtime_enable(&pdev->dev);
 288
 289        /* Static funnel do not have programming base */
 290        ret = funnel_probe(&pdev->dev, NULL);
 291
 292        if (ret) {
 293                pm_runtime_put_noidle(&pdev->dev);
 294                pm_runtime_disable(&pdev->dev);
 295        }
 296
 297        return ret;
 298}
 299
 300static const struct of_device_id static_funnel_match[] = {
 301        {.compatible = "arm,coresight-static-funnel"},
 302        {}
 303};
 304
 305static struct platform_driver static_funnel_driver = {
 306        .probe          = static_funnel_probe,
 307        .driver         = {
 308                .name   = "coresight-static-funnel",
 309                .of_match_table = static_funnel_match,
 310                .pm     = &funnel_dev_pm_ops,
 311                .suppress_bind_attrs = true,
 312        },
 313};
 314builtin_platform_driver(static_funnel_driver);
 315
 316static int dynamic_funnel_probe(struct amba_device *adev,
 317                                const struct amba_id *id)
 318{
 319        return funnel_probe(&adev->dev, &adev->res);
 320}
 321
 322static const struct amba_id dynamic_funnel_ids[] = {
 323        {
 324                .id     = 0x000bb908,
 325                .mask   = 0x000fffff,
 326        },
 327        {
 328                /* Coresight SoC-600 */
 329                .id     = 0x000bb9eb,
 330                .mask   = 0x000fffff,
 331        },
 332        { 0, 0},
 333};
 334
 335static struct amba_driver dynamic_funnel_driver = {
 336        .drv = {
 337                .name   = "coresight-dynamic-funnel",
 338                .owner  = THIS_MODULE,
 339                .pm     = &funnel_dev_pm_ops,
 340                .suppress_bind_attrs = true,
 341        },
 342        .probe          = dynamic_funnel_probe,
 343        .id_table       = dynamic_funnel_ids,
 344};
 345builtin_amba_driver(dynamic_funnel_driver);
 346