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 * @pclk: APB clock if present, otherwise NULL
40 * @csdev: component vitals needed by the framework.
41 * @priority: port selection order.
42 * @spinlock: serialize enable/disable operations.
44 struct funnel_drvdata
{
48 struct coresight_device
*csdev
;
49 unsigned long priority
;
53 static int dynamic_funnel_enable_hw(struct funnel_drvdata
*drvdata
, int port
)
57 struct coresight_device
*csdev
= drvdata
->csdev
;
59 CS_UNLOCK(drvdata
->base
);
61 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
62 /* Claim the device only when we enable the first slave */
63 if (!(functl
& FUNNEL_ENSx_MASK
)) {
64 rc
= coresight_claim_device_unlocked(csdev
);
69 functl
&= ~FUNNEL_HOLDTIME_MASK
;
70 functl
|= FUNNEL_HOLDTIME
;
71 functl
|= (1 << port
);
72 writel_relaxed(functl
, drvdata
->base
+ FUNNEL_FUNCTL
);
73 writel_relaxed(drvdata
->priority
, drvdata
->base
+ FUNNEL_PRICTL
);
75 CS_LOCK(drvdata
->base
);
79 static int funnel_enable(struct coresight_device
*csdev
,
80 struct coresight_connection
*in
,
81 struct coresight_connection
*out
)
84 struct funnel_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
86 bool first_enable
= false;
88 spin_lock_irqsave(&drvdata
->spinlock
, flags
);
89 if (atomic_read(&in
->dest_refcnt
) == 0) {
91 rc
= dynamic_funnel_enable_hw(drvdata
, in
->dest_port
);
96 atomic_inc(&in
->dest_refcnt
);
97 spin_unlock_irqrestore(&drvdata
->spinlock
, flags
);
100 dev_dbg(&csdev
->dev
, "FUNNEL inport %d enabled\n",
105 static void dynamic_funnel_disable_hw(struct funnel_drvdata
*drvdata
,
109 struct coresight_device
*csdev
= drvdata
->csdev
;
111 CS_UNLOCK(drvdata
->base
);
113 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
114 functl
&= ~(1 << inport
);
115 writel_relaxed(functl
, drvdata
->base
+ FUNNEL_FUNCTL
);
117 /* Disclaim the device if none of the slaves are now active */
118 if (!(functl
& FUNNEL_ENSx_MASK
))
119 coresight_disclaim_device_unlocked(csdev
);
121 CS_LOCK(drvdata
->base
);
124 static void funnel_disable(struct coresight_device
*csdev
,
125 struct coresight_connection
*in
,
126 struct coresight_connection
*out
)
128 struct funnel_drvdata
*drvdata
= dev_get_drvdata(csdev
->dev
.parent
);
130 bool last_disable
= false;
132 spin_lock_irqsave(&drvdata
->spinlock
, flags
);
133 if (atomic_dec_return(&in
->dest_refcnt
) == 0) {
135 dynamic_funnel_disable_hw(drvdata
, in
->dest_port
);
138 spin_unlock_irqrestore(&drvdata
->spinlock
, flags
);
141 dev_dbg(&csdev
->dev
, "FUNNEL inport %d disabled\n",
145 static const struct coresight_ops_link funnel_link_ops
= {
146 .enable
= funnel_enable
,
147 .disable
= funnel_disable
,
150 static const struct coresight_ops funnel_cs_ops
= {
151 .link_ops
= &funnel_link_ops
,
154 static ssize_t
priority_show(struct device
*dev
,
155 struct device_attribute
*attr
, char *buf
)
157 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
158 unsigned long val
= drvdata
->priority
;
160 return sprintf(buf
, "%#lx\n", val
);
163 static ssize_t
priority_store(struct device
*dev
,
164 struct device_attribute
*attr
,
165 const char *buf
, size_t size
)
169 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
171 ret
= kstrtoul(buf
, 16, &val
);
175 drvdata
->priority
= val
;
178 static DEVICE_ATTR_RW(priority
);
180 static u32
get_funnel_ctrl_hw(struct funnel_drvdata
*drvdata
)
184 CS_UNLOCK(drvdata
->base
);
185 functl
= readl_relaxed(drvdata
->base
+ FUNNEL_FUNCTL
);
186 CS_LOCK(drvdata
->base
);
191 static ssize_t
funnel_ctrl_show(struct device
*dev
,
192 struct device_attribute
*attr
, char *buf
)
195 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
->parent
);
197 pm_runtime_get_sync(dev
->parent
);
199 val
= get_funnel_ctrl_hw(drvdata
);
201 pm_runtime_put(dev
->parent
);
203 return sprintf(buf
, "%#x\n", val
);
205 static DEVICE_ATTR_RO(funnel_ctrl
);
207 static struct attribute
*coresight_funnel_attrs
[] = {
208 &dev_attr_funnel_ctrl
.attr
,
209 &dev_attr_priority
.attr
,
212 ATTRIBUTE_GROUPS(coresight_funnel
);
214 static int funnel_probe(struct device
*dev
, struct resource
*res
)
218 struct coresight_platform_data
*pdata
= NULL
;
219 struct funnel_drvdata
*drvdata
;
220 struct coresight_desc desc
= { 0 };
222 if (is_of_node(dev_fwnode(dev
)) &&
223 of_device_is_compatible(dev
->of_node
, "arm,coresight-funnel"))
224 dev_warn_once(dev
, "Uses OBSOLETE CoreSight funnel binding\n");
226 desc
.name
= coresight_alloc_device_name(&funnel_devs
, dev
);
230 drvdata
= devm_kzalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
234 drvdata
->atclk
= devm_clk_get(dev
, "atclk"); /* optional */
235 if (!IS_ERR(drvdata
->atclk
)) {
236 ret
= clk_prepare_enable(drvdata
->atclk
);
241 drvdata
->pclk
= coresight_get_enable_apb_pclk(dev
);
242 if (IS_ERR(drvdata
->pclk
))
246 * Map the device base for dynamic-funnel, which has been
247 * validated by AMBA core.
250 base
= devm_ioremap_resource(dev
, res
);
253 goto out_disable_clk
;
255 drvdata
->base
= base
;
256 desc
.groups
= coresight_funnel_groups
;
257 desc
.access
= CSDEV_ACCESS_IOMEM(base
);
260 dev_set_drvdata(dev
, drvdata
);
262 pdata
= coresight_get_platform_data(dev
);
264 ret
= PTR_ERR(pdata
);
265 goto out_disable_clk
;
267 dev
->platform_data
= pdata
;
269 spin_lock_init(&drvdata
->spinlock
);
270 desc
.type
= CORESIGHT_DEV_TYPE_LINK
;
271 desc
.subtype
.link_subtype
= CORESIGHT_DEV_SUBTYPE_LINK_MERG
;
272 desc
.ops
= &funnel_cs_ops
;
275 drvdata
->csdev
= coresight_register(&desc
);
276 if (IS_ERR(drvdata
->csdev
)) {
277 ret
= PTR_ERR(drvdata
->csdev
);
278 goto out_disable_clk
;
284 if (ret
&& !IS_ERR_OR_NULL(drvdata
->atclk
))
285 clk_disable_unprepare(drvdata
->atclk
);
286 if (ret
&& !IS_ERR_OR_NULL(drvdata
->pclk
))
287 clk_disable_unprepare(drvdata
->pclk
);
291 static int funnel_remove(struct device
*dev
)
293 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
295 coresight_unregister(drvdata
->csdev
);
301 static int funnel_runtime_suspend(struct device
*dev
)
303 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
305 if (drvdata
&& !IS_ERR(drvdata
->atclk
))
306 clk_disable_unprepare(drvdata
->atclk
);
308 if (drvdata
&& !IS_ERR_OR_NULL(drvdata
->pclk
))
309 clk_disable_unprepare(drvdata
->pclk
);
314 static int funnel_runtime_resume(struct device
*dev
)
316 struct funnel_drvdata
*drvdata
= dev_get_drvdata(dev
);
318 if (drvdata
&& !IS_ERR(drvdata
->atclk
))
319 clk_prepare_enable(drvdata
->atclk
);
321 if (drvdata
&& !IS_ERR_OR_NULL(drvdata
->pclk
))
322 clk_prepare_enable(drvdata
->pclk
);
327 static const struct dev_pm_ops funnel_dev_pm_ops
= {
328 SET_RUNTIME_PM_OPS(funnel_runtime_suspend
, funnel_runtime_resume
, NULL
)
331 static int funnel_platform_probe(struct platform_device
*pdev
)
333 struct resource
*res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
336 pm_runtime_get_noresume(&pdev
->dev
);
337 pm_runtime_set_active(&pdev
->dev
);
338 pm_runtime_enable(&pdev
->dev
);
340 ret
= funnel_probe(&pdev
->dev
, res
);
341 pm_runtime_put(&pdev
->dev
);
343 pm_runtime_disable(&pdev
->dev
);
348 static void funnel_platform_remove(struct platform_device
*pdev
)
350 struct funnel_drvdata
*drvdata
= dev_get_drvdata(&pdev
->dev
);
352 if (WARN_ON(!drvdata
))
355 funnel_remove(&pdev
->dev
);
356 pm_runtime_disable(&pdev
->dev
);
357 if (!IS_ERR_OR_NULL(drvdata
->pclk
))
358 clk_put(drvdata
->pclk
);
361 static const struct of_device_id funnel_match
[] = {
362 {.compatible
= "arm,coresight-static-funnel"},
366 MODULE_DEVICE_TABLE(of
, funnel_match
);
369 static const struct acpi_device_id funnel_acpi_ids
[] = {
370 {"ARMHC9FE", 0, 0, 0}, /* ARM Coresight Static Funnel */
371 {"ARMHC9FF", 0, 0, 0}, /* ARM CoreSight Dynamic Funnel */
375 MODULE_DEVICE_TABLE(acpi
, funnel_acpi_ids
);
378 static struct platform_driver funnel_driver
= {
379 .probe
= funnel_platform_probe
,
380 .remove
= funnel_platform_remove
,
382 .name
= "coresight-funnel",
383 /* THIS_MODULE is taken care of by platform_driver_register() */
384 .of_match_table
= funnel_match
,
385 .acpi_match_table
= ACPI_PTR(funnel_acpi_ids
),
386 .pm
= &funnel_dev_pm_ops
,
387 .suppress_bind_attrs
= true,
391 static int dynamic_funnel_probe(struct amba_device
*adev
,
392 const struct amba_id
*id
)
396 ret
= funnel_probe(&adev
->dev
, &adev
->res
);
398 pm_runtime_put(&adev
->dev
);
403 static void dynamic_funnel_remove(struct amba_device
*adev
)
405 funnel_remove(&adev
->dev
);
408 static const struct amba_id dynamic_funnel_ids
[] = {
414 /* Coresight SoC-600 */
421 MODULE_DEVICE_TABLE(amba
, dynamic_funnel_ids
);
423 static struct amba_driver dynamic_funnel_driver
= {
425 .name
= "coresight-dynamic-funnel",
426 .pm
= &funnel_dev_pm_ops
,
427 .suppress_bind_attrs
= true,
429 .probe
= dynamic_funnel_probe
,
430 .remove
= dynamic_funnel_remove
,
431 .id_table
= dynamic_funnel_ids
,
434 static int __init
funnel_init(void)
436 return coresight_init_driver("funnel", &dynamic_funnel_driver
, &funnel_driver
);
439 static void __exit
funnel_exit(void)
441 coresight_remove_driver(&dynamic_funnel_driver
, &funnel_driver
);
444 module_init(funnel_init
);
445 module_exit(funnel_exit
);
447 MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
448 MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
449 MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
450 MODULE_LICENSE("GPL v2");