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 33 DEFINE_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 */ 43 struct 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 51 static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port) 52 { 53 u32 functl; 54 int rc = 0; 55 56 CS_UNLOCK(drvdata->base); 57 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); 62 if (rc) 63 goto done; 64 } 65 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); 71 done: 72 CS_LOCK(drvdata->base); 73 return rc; 74 } 75 76 static int funnel_enable(struct coresight_device *csdev, int inport, 77 int outport) 78 { 79 int rc = 0; 80 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 81 unsigned long flags; 82 bool first_enable = false; 83 84 spin_lock_irqsave(&drvdata->spinlock, flags); 85 if (atomic_read(&csdev->refcnt[inport]) == 0) { 86 if (drvdata->base) 87 rc = dynamic_funnel_enable_hw(drvdata, inport); 88 if (!rc) 89 first_enable = true; 90 } 91 if (!rc) 92 atomic_inc(&csdev->refcnt[inport]); 93 spin_unlock_irqrestore(&drvdata->spinlock, flags); 94 95 if (first_enable) 96 dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport); 97 return rc; 98 } 99 100 static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata, 101 int inport) 102 { 103 u32 functl; 104 105 CS_UNLOCK(drvdata->base); 106 107 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 108 functl &= ~(1 << inport); 109 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 110 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); 114 115 CS_LOCK(drvdata->base); 116 } 117 118 static void funnel_disable(struct coresight_device *csdev, int inport, 119 int outport) 120 { 121 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 122 unsigned long flags; 123 bool last_disable = false; 124 125 spin_lock_irqsave(&drvdata->spinlock, flags); 126 if (atomic_dec_return(&csdev->refcnt[inport]) == 0) { 127 if (drvdata->base) 128 dynamic_funnel_disable_hw(drvdata, inport); 129 last_disable = true; 130 } 131 spin_unlock_irqrestore(&drvdata->spinlock, flags); 132 133 if (last_disable) 134 dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport); 135 } 136 137 static const struct coresight_ops_link funnel_link_ops = { 138 .enable = funnel_enable, 139 .disable = funnel_disable, 140 }; 141 142 static const struct coresight_ops funnel_cs_ops = { 143 .link_ops = &funnel_link_ops, 144 }; 145 146 static ssize_t priority_show(struct device *dev, 147 struct device_attribute *attr, char *buf) 148 { 149 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 150 unsigned long val = drvdata->priority; 151 152 return sprintf(buf, "%#lx\n", val); 153 } 154 155 static ssize_t priority_store(struct device *dev, 156 struct device_attribute *attr, 157 const char *buf, size_t size) 158 { 159 int ret; 160 unsigned long val; 161 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 162 163 ret = kstrtoul(buf, 16, &val); 164 if (ret) 165 return ret; 166 167 drvdata->priority = val; 168 return size; 169 } 170 static DEVICE_ATTR_RW(priority); 171 172 static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) 173 { 174 u32 functl; 175 176 CS_UNLOCK(drvdata->base); 177 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 178 CS_LOCK(drvdata->base); 179 180 return functl; 181 } 182 183 static ssize_t funnel_ctrl_show(struct device *dev, 184 struct device_attribute *attr, char *buf) 185 { 186 u32 val; 187 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 188 189 pm_runtime_get_sync(dev->parent); 190 191 val = get_funnel_ctrl_hw(drvdata); 192 193 pm_runtime_put(dev->parent); 194 195 return sprintf(buf, "%#x\n", val); 196 } 197 static DEVICE_ATTR_RO(funnel_ctrl); 198 199 static struct attribute *coresight_funnel_attrs[] = { 200 &dev_attr_funnel_ctrl.attr, 201 &dev_attr_priority.attr, 202 NULL, 203 }; 204 ATTRIBUTE_GROUPS(coresight_funnel); 205 206 static int funnel_probe(struct device *dev, struct resource *res) 207 { 208 int ret; 209 void __iomem *base; 210 struct coresight_platform_data *pdata = NULL; 211 struct funnel_drvdata *drvdata; 212 struct coresight_desc desc = { 0 }; 213 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"); 217 218 desc.name = coresight_alloc_device_name(&funnel_devs, dev); 219 if (!desc.name) 220 return -ENOMEM; 221 222 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 223 if (!drvdata) 224 return -ENOMEM; 225 226 drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */ 227 if (!IS_ERR(drvdata->atclk)) { 228 ret = clk_prepare_enable(drvdata->atclk); 229 if (ret) 230 return ret; 231 } 232 233 /* 234 * Map the device base for dynamic-funnel, which has been 235 * validated by AMBA core. 236 */ 237 if (res) { 238 base = devm_ioremap_resource(dev, res); 239 if (IS_ERR(base)) { 240 ret = PTR_ERR(base); 241 goto out_disable_clk; 242 } 243 drvdata->base = base; 244 desc.groups = coresight_funnel_groups; 245 } 246 247 dev_set_drvdata(dev, drvdata); 248 249 pdata = coresight_get_platform_data(dev); 250 if (IS_ERR(pdata)) { 251 ret = PTR_ERR(pdata); 252 goto out_disable_clk; 253 } 254 dev->platform_data = pdata; 255 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; 260 desc.pdata = pdata; 261 desc.dev = dev; 262 drvdata->csdev = coresight_register(&desc); 263 if (IS_ERR(drvdata->csdev)) { 264 ret = PTR_ERR(drvdata->csdev); 265 goto out_disable_clk; 266 } 267 268 pm_runtime_put(dev); 269 ret = 0; 270 271 out_disable_clk: 272 if (ret && !IS_ERR_OR_NULL(drvdata->atclk)) 273 clk_disable_unprepare(drvdata->atclk); 274 return ret; 275 } 276 277 static int funnel_remove(struct device *dev) 278 { 279 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 280 281 coresight_unregister(drvdata->csdev); 282 283 return 0; 284 } 285 286 #ifdef CONFIG_PM 287 static int funnel_runtime_suspend(struct device *dev) 288 { 289 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 290 291 if (drvdata && !IS_ERR(drvdata->atclk)) 292 clk_disable_unprepare(drvdata->atclk); 293 294 return 0; 295 } 296 297 static int funnel_runtime_resume(struct device *dev) 298 { 299 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 300 301 if (drvdata && !IS_ERR(drvdata->atclk)) 302 clk_prepare_enable(drvdata->atclk); 303 304 return 0; 305 } 306 #endif 307 308 static const struct dev_pm_ops funnel_dev_pm_ops = { 309 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) 310 }; 311 312 static int static_funnel_probe(struct platform_device *pdev) 313 { 314 int ret; 315 316 pm_runtime_get_noresume(&pdev->dev); 317 pm_runtime_set_active(&pdev->dev); 318 pm_runtime_enable(&pdev->dev); 319 320 /* Static funnel do not have programming base */ 321 ret = funnel_probe(&pdev->dev, NULL); 322 323 if (ret) { 324 pm_runtime_put_noidle(&pdev->dev); 325 pm_runtime_disable(&pdev->dev); 326 } 327 328 return ret; 329 } 330 331 static int static_funnel_remove(struct platform_device *pdev) 332 { 333 funnel_remove(&pdev->dev); 334 pm_runtime_disable(&pdev->dev); 335 return 0; 336 } 337 338 static const struct of_device_id static_funnel_match[] = { 339 {.compatible = "arm,coresight-static-funnel"}, 340 {} 341 }; 342 343 MODULE_DEVICE_TABLE(of, static_funnel_match); 344 345 #ifdef CONFIG_ACPI 346 static const struct acpi_device_id static_funnel_ids[] = { 347 {"ARMHC9FE", 0}, 348 {}, 349 }; 350 351 MODULE_DEVICE_TABLE(acpi, static_funnel_ids); 352 #endif 353 354 static struct platform_driver static_funnel_driver = { 355 .probe = static_funnel_probe, 356 .remove = static_funnel_remove, 357 .driver = { 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, 364 }, 365 }; 366 367 static int dynamic_funnel_probe(struct amba_device *adev, 368 const struct amba_id *id) 369 { 370 return funnel_probe(&adev->dev, &adev->res); 371 } 372 373 static int dynamic_funnel_remove(struct amba_device *adev) 374 { 375 return funnel_remove(&adev->dev); 376 } 377 378 static const struct amba_id dynamic_funnel_ids[] = { 379 { 380 .id = 0x000bb908, 381 .mask = 0x000fffff, 382 }, 383 { 384 /* Coresight SoC-600 */ 385 .id = 0x000bb9eb, 386 .mask = 0x000fffff, 387 }, 388 { 0, 0}, 389 }; 390 391 MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids); 392 393 static struct amba_driver dynamic_funnel_driver = { 394 .drv = { 395 .name = "coresight-dynamic-funnel", 396 .owner = THIS_MODULE, 397 .pm = &funnel_dev_pm_ops, 398 .suppress_bind_attrs = true, 399 }, 400 .probe = dynamic_funnel_probe, 401 .remove = dynamic_funnel_remove, 402 .id_table = dynamic_funnel_ids, 403 }; 404 405 static int __init funnel_init(void) 406 { 407 int ret; 408 409 ret = platform_driver_register(&static_funnel_driver); 410 if (ret) { 411 pr_info("Error registering platform driver\n"); 412 return ret; 413 } 414 415 ret = amba_driver_register(&dynamic_funnel_driver); 416 if (ret) { 417 pr_info("Error registering amba driver\n"); 418 platform_driver_unregister(&static_funnel_driver); 419 } 420 421 return ret; 422 } 423 424 static void __exit funnel_exit(void) 425 { 426 platform_driver_unregister(&static_funnel_driver); 427 amba_driver_unregister(&dynamic_funnel_driver); 428 } 429 430 module_init(funnel_init); 431 module_exit(funnel_exit); 432 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"); 437