cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

coresight-funnel.c (10443B)


      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/acpi.h>
      9#include <linux/kernel.h>
     10#include <linux/init.h>
     11#include <linux/types.h>
     12#include <linux/device.h>
     13#include <linux/err.h>
     14#include <linux/fs.h>
     15#include <linux/slab.h>
     16#include <linux/of.h>
     17#include <linux/platform_device.h>
     18#include <linux/pm_runtime.h>
     19#include <linux/coresight.h>
     20#include <linux/amba/bus.h>
     21#include <linux/clk.h>
     22
     23#include "coresight-priv.h"
     24
     25#define FUNNEL_FUNCTL		0x000
     26#define FUNNEL_PRICTL		0x004
     27
     28#define FUNNEL_HOLDTIME_MASK	0xf00
     29#define FUNNEL_HOLDTIME_SHFT	0x8
     30#define FUNNEL_HOLDTIME		(0x7 << FUNNEL_HOLDTIME_SHFT)
     31#define FUNNEL_ENSx_MASK	0xff
     32
     33DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
     34
     35/**
     36 * struct funnel_drvdata - specifics associated to a funnel component
     37 * @base:	memory mapped base address for this component.
     38 * @atclk:	optional clock for the core parts of the funnel.
     39 * @csdev:	component vitals needed by the framework.
     40 * @priority:	port selection order.
     41 * @spinlock:	serialize enable/disable operations.
     42 */
     43struct funnel_drvdata {
     44	void __iomem		*base;
     45	struct clk		*atclk;
     46	struct coresight_device	*csdev;
     47	unsigned long		priority;
     48	spinlock_t		spinlock;
     49};
     50
     51static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
     52{
     53	u32 functl;
     54	int rc = 0;
     55	struct coresight_device *csdev = drvdata->csdev;
     56
     57	CS_UNLOCK(drvdata->base);
     58
     59	functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
     60	/* Claim the device only when we enable the first slave */
     61	if (!(functl & FUNNEL_ENSx_MASK)) {
     62		rc = coresight_claim_device_unlocked(csdev);
     63		if (rc)
     64			goto done;
     65	}
     66
     67	functl &= ~FUNNEL_HOLDTIME_MASK;
     68	functl |= FUNNEL_HOLDTIME;
     69	functl |= (1 << port);
     70	writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
     71	writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
     72done:
     73	CS_LOCK(drvdata->base);
     74	return rc;
     75}
     76
     77static int funnel_enable(struct coresight_device *csdev, int inport,
     78			 int outport)
     79{
     80	int rc = 0;
     81	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
     82	unsigned long flags;
     83	bool first_enable = false;
     84
     85	spin_lock_irqsave(&drvdata->spinlock, flags);
     86	if (atomic_read(&csdev->refcnt[inport]) == 0) {
     87		if (drvdata->base)
     88			rc = dynamic_funnel_enable_hw(drvdata, inport);
     89		if (!rc)
     90			first_enable = true;
     91	}
     92	if (!rc)
     93		atomic_inc(&csdev->refcnt[inport]);
     94	spin_unlock_irqrestore(&drvdata->spinlock, flags);
     95
     96	if (first_enable)
     97		dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport);
     98	return rc;
     99}
    100
    101static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata,
    102				      int inport)
    103{
    104	u32 functl;
    105	struct coresight_device *csdev = drvdata->csdev;
    106
    107	CS_UNLOCK(drvdata->base);
    108
    109	functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
    110	functl &= ~(1 << inport);
    111	writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
    112
    113	/* Disclaim the device if none of the slaves are now active */
    114	if (!(functl & FUNNEL_ENSx_MASK))
    115		coresight_disclaim_device_unlocked(csdev);
    116
    117	CS_LOCK(drvdata->base);
    118}
    119
    120static void funnel_disable(struct coresight_device *csdev, int inport,
    121			   int outport)
    122{
    123	struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
    124	unsigned long flags;
    125	bool last_disable = false;
    126
    127	spin_lock_irqsave(&drvdata->spinlock, flags);
    128	if (atomic_dec_return(&csdev->refcnt[inport]) == 0) {
    129		if (drvdata->base)
    130			dynamic_funnel_disable_hw(drvdata, inport);
    131		last_disable = true;
    132	}
    133	spin_unlock_irqrestore(&drvdata->spinlock, flags);
    134
    135	if (last_disable)
    136		dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
    137}
    138
    139static const struct coresight_ops_link funnel_link_ops = {
    140	.enable		= funnel_enable,
    141	.disable	= funnel_disable,
    142};
    143
    144static const struct coresight_ops funnel_cs_ops = {
    145	.link_ops	= &funnel_link_ops,
    146};
    147
    148static ssize_t priority_show(struct device *dev,
    149			     struct device_attribute *attr, char *buf)
    150{
    151	struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
    152	unsigned long val = drvdata->priority;
    153
    154	return sprintf(buf, "%#lx\n", val);
    155}
    156
    157static ssize_t priority_store(struct device *dev,
    158			      struct device_attribute *attr,
    159			      const char *buf, size_t size)
    160{
    161	int ret;
    162	unsigned long val;
    163	struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
    164
    165	ret = kstrtoul(buf, 16, &val);
    166	if (ret)
    167		return ret;
    168
    169	drvdata->priority = val;
    170	return size;
    171}
    172static DEVICE_ATTR_RW(priority);
    173
    174static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
    175{
    176	u32 functl;
    177
    178	CS_UNLOCK(drvdata->base);
    179	functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
    180	CS_LOCK(drvdata->base);
    181
    182	return functl;
    183}
    184
    185static ssize_t funnel_ctrl_show(struct device *dev,
    186			     struct device_attribute *attr, char *buf)
    187{
    188	u32 val;
    189	struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
    190
    191	pm_runtime_get_sync(dev->parent);
    192
    193	val = get_funnel_ctrl_hw(drvdata);
    194
    195	pm_runtime_put(dev->parent);
    196
    197	return sprintf(buf, "%#x\n", val);
    198}
    199static DEVICE_ATTR_RO(funnel_ctrl);
    200
    201static struct attribute *coresight_funnel_attrs[] = {
    202	&dev_attr_funnel_ctrl.attr,
    203	&dev_attr_priority.attr,
    204	NULL,
    205};
    206ATTRIBUTE_GROUPS(coresight_funnel);
    207
    208static int funnel_probe(struct device *dev, struct resource *res)
    209{
    210	int ret;
    211	void __iomem *base;
    212	struct coresight_platform_data *pdata = NULL;
    213	struct funnel_drvdata *drvdata;
    214	struct coresight_desc desc = { 0 };
    215
    216	if (is_of_node(dev_fwnode(dev)) &&
    217	    of_device_is_compatible(dev->of_node, "arm,coresight-funnel"))
    218		dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n");
    219
    220	desc.name = coresight_alloc_device_name(&funnel_devs, dev);
    221	if (!desc.name)
    222		return -ENOMEM;
    223
    224	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
    225	if (!drvdata)
    226		return -ENOMEM;
    227
    228	drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */
    229	if (!IS_ERR(drvdata->atclk)) {
    230		ret = clk_prepare_enable(drvdata->atclk);
    231		if (ret)
    232			return ret;
    233	}
    234
    235	/*
    236	 * Map the device base for dynamic-funnel, which has been
    237	 * validated by AMBA core.
    238	 */
    239	if (res) {
    240		base = devm_ioremap_resource(dev, res);
    241		if (IS_ERR(base)) {
    242			ret = PTR_ERR(base);
    243			goto out_disable_clk;
    244		}
    245		drvdata->base = base;
    246		desc.groups = coresight_funnel_groups;
    247		desc.access = CSDEV_ACCESS_IOMEM(base);
    248	}
    249
    250	dev_set_drvdata(dev, drvdata);
    251
    252	pdata = coresight_get_platform_data(dev);
    253	if (IS_ERR(pdata)) {
    254		ret = PTR_ERR(pdata);
    255		goto out_disable_clk;
    256	}
    257	dev->platform_data = pdata;
    258
    259	spin_lock_init(&drvdata->spinlock);
    260	desc.type = CORESIGHT_DEV_TYPE_LINK;
    261	desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
    262	desc.ops = &funnel_cs_ops;
    263	desc.pdata = pdata;
    264	desc.dev = dev;
    265	drvdata->csdev = coresight_register(&desc);
    266	if (IS_ERR(drvdata->csdev)) {
    267		ret = PTR_ERR(drvdata->csdev);
    268		goto out_disable_clk;
    269	}
    270
    271	pm_runtime_put(dev);
    272	ret = 0;
    273
    274out_disable_clk:
    275	if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
    276		clk_disable_unprepare(drvdata->atclk);
    277	return ret;
    278}
    279
    280static int funnel_remove(struct device *dev)
    281{
    282	struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
    283
    284	coresight_unregister(drvdata->csdev);
    285
    286	return 0;
    287}
    288
    289#ifdef CONFIG_PM
    290static int funnel_runtime_suspend(struct device *dev)
    291{
    292	struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
    293
    294	if (drvdata && !IS_ERR(drvdata->atclk))
    295		clk_disable_unprepare(drvdata->atclk);
    296
    297	return 0;
    298}
    299
    300static int funnel_runtime_resume(struct device *dev)
    301{
    302	struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
    303
    304	if (drvdata && !IS_ERR(drvdata->atclk))
    305		clk_prepare_enable(drvdata->atclk);
    306
    307	return 0;
    308}
    309#endif
    310
    311static const struct dev_pm_ops funnel_dev_pm_ops = {
    312	SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
    313};
    314
    315static int static_funnel_probe(struct platform_device *pdev)
    316{
    317	int ret;
    318
    319	pm_runtime_get_noresume(&pdev->dev);
    320	pm_runtime_set_active(&pdev->dev);
    321	pm_runtime_enable(&pdev->dev);
    322
    323	/* Static funnel do not have programming base */
    324	ret = funnel_probe(&pdev->dev, NULL);
    325
    326	if (ret) {
    327		pm_runtime_put_noidle(&pdev->dev);
    328		pm_runtime_disable(&pdev->dev);
    329	}
    330
    331	return ret;
    332}
    333
    334static int static_funnel_remove(struct platform_device *pdev)
    335{
    336	funnel_remove(&pdev->dev);
    337	pm_runtime_disable(&pdev->dev);
    338	return 0;
    339}
    340
    341static const struct of_device_id static_funnel_match[] = {
    342	{.compatible = "arm,coresight-static-funnel"},
    343	{}
    344};
    345
    346MODULE_DEVICE_TABLE(of, static_funnel_match);
    347
    348#ifdef CONFIG_ACPI
    349static const struct acpi_device_id static_funnel_ids[] = {
    350	{"ARMHC9FE", 0},
    351	{},
    352};
    353
    354MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
    355#endif
    356
    357static struct platform_driver static_funnel_driver = {
    358	.probe          = static_funnel_probe,
    359	.remove          = static_funnel_remove,
    360	.driver         = {
    361		.name   = "coresight-static-funnel",
    362		/* THIS_MODULE is taken care of by platform_driver_register() */
    363		.of_match_table = static_funnel_match,
    364		.acpi_match_table = ACPI_PTR(static_funnel_ids),
    365		.pm	= &funnel_dev_pm_ops,
    366		.suppress_bind_attrs = true,
    367	},
    368};
    369
    370static int dynamic_funnel_probe(struct amba_device *adev,
    371				const struct amba_id *id)
    372{
    373	return funnel_probe(&adev->dev, &adev->res);
    374}
    375
    376static void dynamic_funnel_remove(struct amba_device *adev)
    377{
    378	funnel_remove(&adev->dev);
    379}
    380
    381static const struct amba_id dynamic_funnel_ids[] = {
    382	{
    383		.id     = 0x000bb908,
    384		.mask   = 0x000fffff,
    385	},
    386	{
    387		/* Coresight SoC-600 */
    388		.id     = 0x000bb9eb,
    389		.mask   = 0x000fffff,
    390	},
    391	{ 0, 0},
    392};
    393
    394MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);
    395
    396static struct amba_driver dynamic_funnel_driver = {
    397	.drv = {
    398		.name	= "coresight-dynamic-funnel",
    399		.owner	= THIS_MODULE,
    400		.pm	= &funnel_dev_pm_ops,
    401		.suppress_bind_attrs = true,
    402	},
    403	.probe		= dynamic_funnel_probe,
    404	.remove		= dynamic_funnel_remove,
    405	.id_table	= dynamic_funnel_ids,
    406};
    407
    408static int __init funnel_init(void)
    409{
    410	int ret;
    411
    412	ret = platform_driver_register(&static_funnel_driver);
    413	if (ret) {
    414		pr_info("Error registering platform driver\n");
    415		return ret;
    416	}
    417
    418	ret = amba_driver_register(&dynamic_funnel_driver);
    419	if (ret) {
    420		pr_info("Error registering amba driver\n");
    421		platform_driver_unregister(&static_funnel_driver);
    422	}
    423
    424	return ret;
    425}
    426
    427static void __exit funnel_exit(void)
    428{
    429	platform_driver_unregister(&static_funnel_driver);
    430	amba_driver_unregister(&dynamic_funnel_driver);
    431}
    432
    433module_init(funnel_init);
    434module_exit(funnel_exit);
    435
    436MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
    437MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
    438MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
    439MODULE_LICENSE("GPL v2");