1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. |
4 | * |
5 | * Description: CoreSight Replicator driver |
6 | */ |
7 | |
8 | #include <linux/acpi.h> |
9 | #include <linux/amba/bus.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/device.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/io.h> |
14 | #include <linux/err.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/property.h> |
18 | #include <linux/clk.h> |
19 | #include <linux/of.h> |
20 | #include <linux/coresight.h> |
21 | |
22 | #include "coresight-priv.h" |
23 | |
24 | #define REPLICATOR_IDFILTER0 0x000 |
25 | #define REPLICATOR_IDFILTER1 0x004 |
26 | |
27 | DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator" ); |
28 | |
29 | /** |
30 | * struct replicator_drvdata - specifics associated to a replicator component |
31 | * @base: memory mapped base address for this component. Also indicates |
32 | * whether this one is programmable or not. |
33 | * @atclk: optional clock for the core parts of the replicator. |
34 | * @csdev: component vitals needed by the framework |
35 | * @spinlock: serialize enable/disable operations. |
36 | * @check_idfilter_val: check if the context is lost upon clock removal. |
37 | */ |
38 | struct replicator_drvdata { |
39 | void __iomem *base; |
40 | struct clk *atclk; |
41 | struct coresight_device *csdev; |
42 | spinlock_t spinlock; |
43 | bool check_idfilter_val; |
44 | }; |
45 | |
46 | static void dynamic_replicator_reset(struct replicator_drvdata *drvdata) |
47 | { |
48 | struct coresight_device *csdev = drvdata->csdev; |
49 | |
50 | CS_UNLOCK(addr: drvdata->base); |
51 | |
52 | if (!coresight_claim_device_unlocked(csdev)) { |
53 | writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); |
54 | writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); |
55 | coresight_disclaim_device_unlocked(csdev); |
56 | } |
57 | |
58 | CS_LOCK(addr: drvdata->base); |
59 | } |
60 | |
61 | /* |
62 | * replicator_reset : Reset the replicator configuration to sane values. |
63 | */ |
64 | static inline void replicator_reset(struct replicator_drvdata *drvdata) |
65 | { |
66 | if (drvdata->base) |
67 | dynamic_replicator_reset(drvdata); |
68 | } |
69 | |
70 | static int dynamic_replicator_enable(struct replicator_drvdata *drvdata, |
71 | int inport, int outport) |
72 | { |
73 | int rc = 0; |
74 | u32 id0val, id1val; |
75 | struct coresight_device *csdev = drvdata->csdev; |
76 | |
77 | CS_UNLOCK(addr: drvdata->base); |
78 | |
79 | id0val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0); |
80 | id1val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1); |
81 | |
82 | /* |
83 | * Some replicator designs lose context when AMBA clocks are removed, |
84 | * so have a check for this. |
85 | */ |
86 | if (drvdata->check_idfilter_val && id0val == 0x0 && id1val == 0x0) |
87 | id0val = id1val = 0xff; |
88 | |
89 | if (id0val == 0xff && id1val == 0xff) |
90 | rc = coresight_claim_device_unlocked(csdev); |
91 | |
92 | if (!rc) { |
93 | switch (outport) { |
94 | case 0: |
95 | id0val = 0x0; |
96 | break; |
97 | case 1: |
98 | id1val = 0x0; |
99 | break; |
100 | default: |
101 | WARN_ON(1); |
102 | rc = -EINVAL; |
103 | } |
104 | } |
105 | |
106 | /* Ensure that the outport is enabled. */ |
107 | if (!rc) { |
108 | writel_relaxed(id0val, drvdata->base + REPLICATOR_IDFILTER0); |
109 | writel_relaxed(id1val, drvdata->base + REPLICATOR_IDFILTER1); |
110 | } |
111 | |
112 | CS_LOCK(addr: drvdata->base); |
113 | |
114 | return rc; |
115 | } |
116 | |
117 | static int replicator_enable(struct coresight_device *csdev, |
118 | struct coresight_connection *in, |
119 | struct coresight_connection *out) |
120 | { |
121 | int rc = 0; |
122 | struct replicator_drvdata *drvdata = dev_get_drvdata(dev: csdev->dev.parent); |
123 | unsigned long flags; |
124 | bool first_enable = false; |
125 | |
126 | spin_lock_irqsave(&drvdata->spinlock, flags); |
127 | if (atomic_read(v: &out->src_refcnt) == 0) { |
128 | if (drvdata->base) |
129 | rc = dynamic_replicator_enable(drvdata, inport: in->dest_port, |
130 | outport: out->src_port); |
131 | if (!rc) |
132 | first_enable = true; |
133 | } |
134 | if (!rc) |
135 | atomic_inc(v: &out->src_refcnt); |
136 | spin_unlock_irqrestore(lock: &drvdata->spinlock, flags); |
137 | |
138 | if (first_enable) |
139 | dev_dbg(&csdev->dev, "REPLICATOR enabled\n" ); |
140 | return rc; |
141 | } |
142 | |
143 | static void dynamic_replicator_disable(struct replicator_drvdata *drvdata, |
144 | int inport, int outport) |
145 | { |
146 | u32 reg; |
147 | struct coresight_device *csdev = drvdata->csdev; |
148 | |
149 | switch (outport) { |
150 | case 0: |
151 | reg = REPLICATOR_IDFILTER0; |
152 | break; |
153 | case 1: |
154 | reg = REPLICATOR_IDFILTER1; |
155 | break; |
156 | default: |
157 | WARN_ON(1); |
158 | return; |
159 | } |
160 | |
161 | CS_UNLOCK(addr: drvdata->base); |
162 | |
163 | /* disable the flow of ATB data through port */ |
164 | writel_relaxed(0xff, drvdata->base + reg); |
165 | |
166 | if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) && |
167 | (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff)) |
168 | coresight_disclaim_device_unlocked(csdev); |
169 | CS_LOCK(addr: drvdata->base); |
170 | } |
171 | |
172 | static void replicator_disable(struct coresight_device *csdev, |
173 | struct coresight_connection *in, |
174 | struct coresight_connection *out) |
175 | { |
176 | struct replicator_drvdata *drvdata = dev_get_drvdata(dev: csdev->dev.parent); |
177 | unsigned long flags; |
178 | bool last_disable = false; |
179 | |
180 | spin_lock_irqsave(&drvdata->spinlock, flags); |
181 | if (atomic_dec_return(v: &out->src_refcnt) == 0) { |
182 | if (drvdata->base) |
183 | dynamic_replicator_disable(drvdata, inport: in->dest_port, |
184 | outport: out->src_port); |
185 | last_disable = true; |
186 | } |
187 | spin_unlock_irqrestore(lock: &drvdata->spinlock, flags); |
188 | |
189 | if (last_disable) |
190 | dev_dbg(&csdev->dev, "REPLICATOR disabled\n" ); |
191 | } |
192 | |
193 | static const struct coresight_ops_link replicator_link_ops = { |
194 | .enable = replicator_enable, |
195 | .disable = replicator_disable, |
196 | }; |
197 | |
198 | static const struct coresight_ops replicator_cs_ops = { |
199 | .link_ops = &replicator_link_ops, |
200 | }; |
201 | |
202 | static struct attribute *replicator_mgmt_attrs[] = { |
203 | coresight_simple_reg32(idfilter0, REPLICATOR_IDFILTER0), |
204 | coresight_simple_reg32(idfilter1, REPLICATOR_IDFILTER1), |
205 | NULL, |
206 | }; |
207 | |
208 | static const struct attribute_group replicator_mgmt_group = { |
209 | .attrs = replicator_mgmt_attrs, |
210 | .name = "mgmt" , |
211 | }; |
212 | |
213 | static const struct attribute_group *replicator_groups[] = { |
214 | &replicator_mgmt_group, |
215 | NULL, |
216 | }; |
217 | |
218 | static int replicator_probe(struct device *dev, struct resource *res) |
219 | { |
220 | int ret = 0; |
221 | struct coresight_platform_data *pdata = NULL; |
222 | struct replicator_drvdata *drvdata; |
223 | struct coresight_desc desc = { 0 }; |
224 | void __iomem *base; |
225 | |
226 | if (is_of_node(dev_fwnode(dev)) && |
227 | of_device_is_compatible(device: dev->of_node, "arm,coresight-replicator" )) |
228 | dev_warn_once(dev, |
229 | "Uses OBSOLETE CoreSight replicator binding\n" ); |
230 | |
231 | desc.name = coresight_alloc_device_name(devs: &replicator_devs, dev); |
232 | if (!desc.name) |
233 | return -ENOMEM; |
234 | |
235 | drvdata = devm_kzalloc(dev, size: sizeof(*drvdata), GFP_KERNEL); |
236 | if (!drvdata) |
237 | return -ENOMEM; |
238 | |
239 | drvdata->atclk = devm_clk_get(dev, id: "atclk" ); /* optional */ |
240 | if (!IS_ERR(ptr: drvdata->atclk)) { |
241 | ret = clk_prepare_enable(clk: drvdata->atclk); |
242 | if (ret) |
243 | return ret; |
244 | } |
245 | |
246 | /* |
247 | * Map the device base for dynamic-replicator, which has been |
248 | * validated by AMBA core |
249 | */ |
250 | if (res) { |
251 | base = devm_ioremap_resource(dev, res); |
252 | if (IS_ERR(ptr: base)) { |
253 | ret = PTR_ERR(ptr: base); |
254 | goto out_disable_clk; |
255 | } |
256 | drvdata->base = base; |
257 | desc.groups = replicator_groups; |
258 | desc.access = CSDEV_ACCESS_IOMEM(base); |
259 | } |
260 | |
261 | if (fwnode_property_present(dev_fwnode(dev), |
262 | propname: "qcom,replicator-loses-context" )) |
263 | drvdata->check_idfilter_val = true; |
264 | |
265 | dev_set_drvdata(dev, data: drvdata); |
266 | |
267 | pdata = coresight_get_platform_data(dev); |
268 | if (IS_ERR(ptr: pdata)) { |
269 | ret = PTR_ERR(ptr: pdata); |
270 | goto out_disable_clk; |
271 | } |
272 | dev->platform_data = pdata; |
273 | |
274 | spin_lock_init(&drvdata->spinlock); |
275 | desc.type = CORESIGHT_DEV_TYPE_LINK; |
276 | desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; |
277 | desc.ops = &replicator_cs_ops; |
278 | desc.pdata = dev->platform_data; |
279 | desc.dev = dev; |
280 | |
281 | drvdata->csdev = coresight_register(desc: &desc); |
282 | if (IS_ERR(ptr: drvdata->csdev)) { |
283 | ret = PTR_ERR(ptr: drvdata->csdev); |
284 | goto out_disable_clk; |
285 | } |
286 | |
287 | replicator_reset(drvdata); |
288 | pm_runtime_put(dev); |
289 | |
290 | out_disable_clk: |
291 | if (ret && !IS_ERR_OR_NULL(ptr: drvdata->atclk)) |
292 | clk_disable_unprepare(clk: drvdata->atclk); |
293 | return ret; |
294 | } |
295 | |
296 | static int replicator_remove(struct device *dev) |
297 | { |
298 | struct replicator_drvdata *drvdata = dev_get_drvdata(dev); |
299 | |
300 | coresight_unregister(csdev: drvdata->csdev); |
301 | return 0; |
302 | } |
303 | |
304 | static int static_replicator_probe(struct platform_device *pdev) |
305 | { |
306 | int ret; |
307 | |
308 | pm_runtime_get_noresume(dev: &pdev->dev); |
309 | pm_runtime_set_active(dev: &pdev->dev); |
310 | pm_runtime_enable(dev: &pdev->dev); |
311 | |
312 | /* Static replicators do not have programming base */ |
313 | ret = replicator_probe(dev: &pdev->dev, NULL); |
314 | |
315 | if (ret) { |
316 | pm_runtime_put_noidle(dev: &pdev->dev); |
317 | pm_runtime_disable(dev: &pdev->dev); |
318 | } |
319 | |
320 | return ret; |
321 | } |
322 | |
323 | static void static_replicator_remove(struct platform_device *pdev) |
324 | { |
325 | replicator_remove(dev: &pdev->dev); |
326 | pm_runtime_disable(dev: &pdev->dev); |
327 | } |
328 | |
329 | #ifdef CONFIG_PM |
330 | static int replicator_runtime_suspend(struct device *dev) |
331 | { |
332 | struct replicator_drvdata *drvdata = dev_get_drvdata(dev); |
333 | |
334 | if (drvdata && !IS_ERR(ptr: drvdata->atclk)) |
335 | clk_disable_unprepare(clk: drvdata->atclk); |
336 | |
337 | return 0; |
338 | } |
339 | |
340 | static int replicator_runtime_resume(struct device *dev) |
341 | { |
342 | struct replicator_drvdata *drvdata = dev_get_drvdata(dev); |
343 | |
344 | if (drvdata && !IS_ERR(ptr: drvdata->atclk)) |
345 | clk_prepare_enable(clk: drvdata->atclk); |
346 | |
347 | return 0; |
348 | } |
349 | #endif |
350 | |
351 | static const struct dev_pm_ops replicator_dev_pm_ops = { |
352 | SET_RUNTIME_PM_OPS(replicator_runtime_suspend, |
353 | replicator_runtime_resume, NULL) |
354 | }; |
355 | |
356 | static const struct of_device_id static_replicator_match[] = { |
357 | {.compatible = "arm,coresight-replicator" }, |
358 | {.compatible = "arm,coresight-static-replicator" }, |
359 | {} |
360 | }; |
361 | |
362 | MODULE_DEVICE_TABLE(of, static_replicator_match); |
363 | |
364 | #ifdef CONFIG_ACPI |
365 | static const struct acpi_device_id static_replicator_acpi_ids[] = { |
366 | {"ARMHC985" , 0, 0, 0}, /* ARM CoreSight Static Replicator */ |
367 | {} |
368 | }; |
369 | |
370 | MODULE_DEVICE_TABLE(acpi, static_replicator_acpi_ids); |
371 | #endif |
372 | |
373 | static struct platform_driver static_replicator_driver = { |
374 | .probe = static_replicator_probe, |
375 | .remove_new = static_replicator_remove, |
376 | .driver = { |
377 | .name = "coresight-static-replicator" , |
378 | /* THIS_MODULE is taken care of by platform_driver_register() */ |
379 | .of_match_table = of_match_ptr(static_replicator_match), |
380 | .acpi_match_table = ACPI_PTR(static_replicator_acpi_ids), |
381 | .pm = &replicator_dev_pm_ops, |
382 | .suppress_bind_attrs = true, |
383 | }, |
384 | }; |
385 | |
386 | static int dynamic_replicator_probe(struct amba_device *adev, |
387 | const struct amba_id *id) |
388 | { |
389 | return replicator_probe(dev: &adev->dev, res: &adev->res); |
390 | } |
391 | |
392 | static void dynamic_replicator_remove(struct amba_device *adev) |
393 | { |
394 | replicator_remove(dev: &adev->dev); |
395 | } |
396 | |
397 | static const struct amba_id dynamic_replicator_ids[] = { |
398 | CS_AMBA_ID(0x000bb909), |
399 | CS_AMBA_ID(0x000bb9ec), /* Coresight SoC-600 */ |
400 | {}, |
401 | }; |
402 | |
403 | MODULE_DEVICE_TABLE(amba, dynamic_replicator_ids); |
404 | |
405 | static struct amba_driver dynamic_replicator_driver = { |
406 | .drv = { |
407 | .name = "coresight-dynamic-replicator" , |
408 | .pm = &replicator_dev_pm_ops, |
409 | .owner = THIS_MODULE, |
410 | .suppress_bind_attrs = true, |
411 | }, |
412 | .probe = dynamic_replicator_probe, |
413 | .remove = dynamic_replicator_remove, |
414 | .id_table = dynamic_replicator_ids, |
415 | }; |
416 | |
417 | static int __init replicator_init(void) |
418 | { |
419 | int ret; |
420 | |
421 | ret = platform_driver_register(&static_replicator_driver); |
422 | if (ret) { |
423 | pr_info("Error registering platform driver\n" ); |
424 | return ret; |
425 | } |
426 | |
427 | ret = amba_driver_register(drv: &dynamic_replicator_driver); |
428 | if (ret) { |
429 | pr_info("Error registering amba driver\n" ); |
430 | platform_driver_unregister(&static_replicator_driver); |
431 | } |
432 | |
433 | return ret; |
434 | } |
435 | |
436 | static void __exit replicator_exit(void) |
437 | { |
438 | platform_driver_unregister(&static_replicator_driver); |
439 | amba_driver_unregister(drv: &dynamic_replicator_driver); |
440 | } |
441 | |
442 | module_init(replicator_init); |
443 | module_exit(replicator_exit); |
444 | |
445 | MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>" ); |
446 | MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>" ); |
447 | MODULE_DESCRIPTION("Arm CoreSight Replicator Driver" ); |
448 | MODULE_LICENSE("GPL v2" ); |
449 | |