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");