1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
5 * Description: CoreSight Funnel driver
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>
15 #include <linux/slab.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>
23 #include "coresight-priv.h"
25 #define FUNNEL_FUNCTL 0x000
26 #define FUNNEL_PRICTL 0x004
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
33 DEFINE_CORESIGHT_DEVLIST(funnel_devs
, "funnel");
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.
43 struct funnel_drvdata
{
46 struct coresight_device
*csdev
;
47 unsigned long priority
;
51 static int dynamic_funnel_enable_hw(struct funnel_drvdata
*drvdata
, int port
)
56 CS_UNLOCK(drvdata
->base
);
58 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
59 /* Claim the device only when we enable the first slave */
60 if (!(functl
& FUNNEL_ENSx_MASK
)) {
61 rc
= coresight_claim_device_unlocked(drvdata
->base
);
66 functl
&= ~FUNNEL_HOLDTIME_MASK
;
67 functl
|= FUNNEL_HOLDTIME
;
68 functl
|= (1 << port
);
69 writel_relaxed(functl
, drvdata
->base
+ FUNNEL_FUNCTL
);
70 writel_relaxed(drvdata
->priority
, drvdata
->base
+ FUNNEL_PRICTL
);
72 CS_LOCK(drvdata
->base
);
76 static int funnel_enable(struct coresight_device
*csdev
, int inport
,
80 struct funnel_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
82 bool first_enable
= false;
84 spin_lock_irqsave(&drvdata
->spinlock
, flags
);
85 if (atomic_read(&csdev
->refcnt
[inport
]) == 0) {
87 rc
= dynamic_funnel_enable_hw(drvdata
, inport
);
92 atomic_inc(&csdev
->refcnt
[inport
]);
93 spin_unlock_irqrestore(&drvdata
->spinlock
, flags
);
96 dev_dbg(&csdev
->dev
, "FUNNEL inport %d enabled\n", inport
);
100 static void dynamic_funnel_disable_hw(struct funnel_drvdata
*drvdata
,
105 CS_UNLOCK(drvdata
->base
);
107 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
108 functl
&= ~(1 << inport
);
109 writel_relaxed(functl
, drvdata
->base
+ FUNNEL_FUNCTL
);
111 /* Disclaim the device if none of the slaves are now active */
112 if (!(functl
& FUNNEL_ENSx_MASK
))
113 coresight_disclaim_device_unlocked(drvdata
->base
);
115 CS_LOCK(drvdata
->base
);
118 static void funnel_disable(struct coresight_device
*csdev
, int inport
,
121 struct funnel_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
123 bool last_disable
= false;
125 spin_lock_irqsave(&drvdata
->spinlock
, flags
);
126 if (atomic_dec_return(&csdev
->refcnt
[inport
]) == 0) {
128 dynamic_funnel_disable_hw(drvdata
, inport
);
131 spin_unlock_irqrestore(&drvdata
->spinlock
, flags
);
134 dev_dbg(&csdev
->dev
, "FUNNEL inport %d disabled\n", inport
);
137 static const struct coresight_ops_link funnel_link_ops
= {
138 .enable
= funnel_enable
,
139 .disable
= funnel_disable
,
142 static const struct coresight_ops funnel_cs_ops
= {
143 .link_ops
= &funnel_link_ops
,
146 static ssize_t
priority_show(struct device
*dev
,
147 struct device_attribute
*attr
, char *buf
)
149 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
150 unsigned long val
= drvdata
->priority
;
152 return sprintf(buf
, "%#lx\n", val
);
155 static ssize_t
priority_store(struct device
*dev
,
156 struct device_attribute
*attr
,
157 const char *buf
, size_t size
)
161 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
163 ret
= kstrtoul(buf
, 16, &val
);
167 drvdata
->priority
= val
;
170 static DEVICE_ATTR_RW(priority
);
172 static u32
get_funnel_ctrl_hw(struct funnel_drvdata
*drvdata
)
176 CS_UNLOCK(drvdata
->base
);
177 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
178 CS_LOCK(drvdata
->base
);
183 static ssize_t
funnel_ctrl_show(struct device
*dev
,
184 struct device_attribute
*attr
, char *buf
)
187 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
189 pm_runtime_get_sync(dev
->parent
);
191 val
= get_funnel_ctrl_hw(drvdata
);
193 pm_runtime_put(dev
->parent
);
195 return sprintf(buf
, "%#x\n", val
);
197 static DEVICE_ATTR_RO(funnel_ctrl
);
199 static struct attribute
*coresight_funnel_attrs
[] = {
200 &dev_attr_funnel_ctrl
.attr
,
201 &dev_attr_priority
.attr
,
204 ATTRIBUTE_GROUPS(coresight_funnel
);
206 static int funnel_probe(struct device
*dev
, struct resource
*res
)
210 struct coresight_platform_data
*pdata
= NULL
;
211 struct funnel_drvdata
*drvdata
;
212 struct coresight_desc desc
= { 0 };
214 if (is_of_node(dev_fwnode(dev
)) &&
215 of_device_is_compatible(dev
->of_node
, "arm,coresight-funnel"))
216 dev_warn_once(dev
, "Uses OBSOLETE CoreSight funnel binding\n");
218 desc
.name
= coresight_alloc_device_name(&funnel_devs
, dev
);
222 drvdata
= devm_kzalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
226 drvdata
->atclk
= devm_clk_get(dev
, "atclk"); /* optional */
227 if (!IS_ERR(drvdata
->atclk
)) {
228 ret
= clk_prepare_enable(drvdata
->atclk
);
234 * Map the device base for dynamic-funnel, which has been
235 * validated by AMBA core.
238 base
= devm_ioremap_resource(dev
, res
);
241 goto out_disable_clk
;
243 drvdata
->base
= base
;
244 desc
.groups
= coresight_funnel_groups
;
247 dev_set_drvdata(dev
, drvdata
);
249 pdata
= coresight_get_platform_data(dev
);
251 ret
= PTR_ERR(pdata
);
252 goto out_disable_clk
;
254 dev
->platform_data
= pdata
;
256 spin_lock_init(&drvdata
->spinlock
);
257 desc
.type
= CORESIGHT_DEV_TYPE_LINK
;
258 desc
.subtype
.link_subtype
= CORESIGHT_DEV_SUBTYPE_LINK_MERG
;
259 desc
.ops
= &funnel_cs_ops
;
262 drvdata
->csdev
= coresight_register(&desc
);
263 if (IS_ERR(drvdata
->csdev
)) {
264 ret
= PTR_ERR(drvdata
->csdev
);
265 goto out_disable_clk
;
272 if (ret
&& !IS_ERR_OR_NULL(drvdata
->atclk
))
273 clk_disable_unprepare(drvdata
->atclk
);
277 static int funnel_remove(struct device
*dev
)
279 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
281 coresight_unregister(drvdata
->csdev
);
287 static int funnel_runtime_suspend(struct device
*dev
)
289 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
291 if (drvdata
&& !IS_ERR(drvdata
->atclk
))
292 clk_disable_unprepare(drvdata
->atclk
);
297 static int funnel_runtime_resume(struct device
*dev
)
299 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
301 if (drvdata
&& !IS_ERR(drvdata
->atclk
))
302 clk_prepare_enable(drvdata
->atclk
);
308 static const struct dev_pm_ops funnel_dev_pm_ops
= {
309 SET_RUNTIME_PM_OPS(funnel_runtime_suspend
, funnel_runtime_resume
, NULL
)
312 static int static_funnel_probe(struct platform_device
*pdev
)
316 pm_runtime_get_noresume(&pdev
->dev
);
317 pm_runtime_set_active(&pdev
->dev
);
318 pm_runtime_enable(&pdev
->dev
);
320 /* Static funnel do not have programming base */
321 ret
= funnel_probe(&pdev
->dev
, NULL
);
324 pm_runtime_put_noidle(&pdev
->dev
);
325 pm_runtime_disable(&pdev
->dev
);
331 static int static_funnel_remove(struct platform_device
*pdev
)
333 funnel_remove(&pdev
->dev
);
334 pm_runtime_disable(&pdev
->dev
);
338 static const struct of_device_id static_funnel_match
[] = {
339 {.compatible
= "arm,coresight-static-funnel"},
343 MODULE_DEVICE_TABLE(of
, static_funnel_match
);
346 static const struct acpi_device_id static_funnel_ids
[] = {
351 MODULE_DEVICE_TABLE(acpi
, static_funnel_ids
);
354 static struct platform_driver static_funnel_driver
= {
355 .probe
= static_funnel_probe
,
356 .remove
= static_funnel_remove
,
358 .name
= "coresight-static-funnel",
359 /* THIS_MODULE is taken care of by platform_driver_register() */
360 .of_match_table
= static_funnel_match
,
361 .acpi_match_table
= ACPI_PTR(static_funnel_ids
),
362 .pm
= &funnel_dev_pm_ops
,
363 .suppress_bind_attrs
= true,
367 static int dynamic_funnel_probe(struct amba_device
*adev
,
368 const struct amba_id
*id
)
370 return funnel_probe(&adev
->dev
, &adev
->res
);
373 static int dynamic_funnel_remove(struct amba_device
*adev
)
375 return funnel_remove(&adev
->dev
);
378 static const struct amba_id dynamic_funnel_ids
[] = {
384 /* Coresight SoC-600 */
391 MODULE_DEVICE_TABLE(amba
, dynamic_funnel_ids
);
393 static struct amba_driver dynamic_funnel_driver
= {
395 .name
= "coresight-dynamic-funnel",
396 .owner
= THIS_MODULE
,
397 .pm
= &funnel_dev_pm_ops
,
398 .suppress_bind_attrs
= true,
400 .probe
= dynamic_funnel_probe
,
401 .remove
= dynamic_funnel_remove
,
402 .id_table
= dynamic_funnel_ids
,
405 static int __init
funnel_init(void)
409 ret
= platform_driver_register(&static_funnel_driver
);
411 pr_info("Error registering platform driver\n");
415 ret
= amba_driver_register(&dynamic_funnel_driver
);
417 pr_info("Error registering amba driver\n");
418 platform_driver_unregister(&static_funnel_driver
);
424 static void __exit
funnel_exit(void)
426 platform_driver_unregister(&static_funnel_driver
);
427 amba_driver_unregister(&dynamic_funnel_driver
);
430 module_init(funnel_init
);
431 module_exit(funnel_exit
);
433 MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
434 MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
435 MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
436 MODULE_LICENSE("GPL v2");