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 #ifdef CONFIG_PM 278 static int funnel_runtime_suspend(struct device *dev) 279 { 280 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 281 282 if (drvdata && !IS_ERR(drvdata->atclk)) 283 clk_disable_unprepare(drvdata->atclk); 284 285 return 0; 286 } 287 288 static int funnel_runtime_resume(struct device *dev) 289 { 290 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 291 292 if (drvdata && !IS_ERR(drvdata->atclk)) 293 clk_prepare_enable(drvdata->atclk); 294 295 return 0; 296 } 297 #endif 298 299 static const struct dev_pm_ops funnel_dev_pm_ops = { 300 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) 301 }; 302 303 static int static_funnel_probe(struct platform_device *pdev) 304 { 305 int ret; 306 307 pm_runtime_get_noresume(&pdev->dev); 308 pm_runtime_set_active(&pdev->dev); 309 pm_runtime_enable(&pdev->dev); 310 311 /* Static funnel do not have programming base */ 312 ret = funnel_probe(&pdev->dev, NULL); 313 314 if (ret) { 315 pm_runtime_put_noidle(&pdev->dev); 316 pm_runtime_disable(&pdev->dev); 317 } 318 319 return ret; 320 } 321 322 static const struct of_device_id static_funnel_match[] = { 323 {.compatible = "arm,coresight-static-funnel"}, 324 {} 325 }; 326 327 #ifdef CONFIG_ACPI 328 static const struct acpi_device_id static_funnel_ids[] = { 329 {"ARMHC9FE", 0}, 330 {}, 331 }; 332 #endif 333 334 static struct platform_driver static_funnel_driver = { 335 .probe = static_funnel_probe, 336 .driver = { 337 .name = "coresight-static-funnel", 338 .of_match_table = static_funnel_match, 339 .acpi_match_table = ACPI_PTR(static_funnel_ids), 340 .pm = &funnel_dev_pm_ops, 341 .suppress_bind_attrs = true, 342 }, 343 }; 344 builtin_platform_driver(static_funnel_driver); 345 346 static int dynamic_funnel_probe(struct amba_device *adev, 347 const struct amba_id *id) 348 { 349 return funnel_probe(&adev->dev, &adev->res); 350 } 351 352 static const struct amba_id dynamic_funnel_ids[] = { 353 { 354 .id = 0x000bb908, 355 .mask = 0x000fffff, 356 }, 357 { 358 /* Coresight SoC-600 */ 359 .id = 0x000bb9eb, 360 .mask = 0x000fffff, 361 }, 362 { 0, 0}, 363 }; 364 365 static struct amba_driver dynamic_funnel_driver = { 366 .drv = { 367 .name = "coresight-dynamic-funnel", 368 .owner = THIS_MODULE, 369 .pm = &funnel_dev_pm_ops, 370 .suppress_bind_attrs = true, 371 }, 372 .probe = dynamic_funnel_probe, 373 .id_table = dynamic_funnel_ids, 374 }; 375 builtin_amba_driver(dynamic_funnel_driver); 376