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