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 */ 42 struct funnel_drvdata { 43 void __iomem *base; 44 struct clk *atclk; 45 struct coresight_device *csdev; 46 unsigned long priority; 47 }; 48 49 static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port) 50 { 51 u32 functl; 52 int rc = 0; 53 54 CS_UNLOCK(drvdata->base); 55 56 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 57 /* Claim the device only when we enable the first slave */ 58 if (!(functl & FUNNEL_ENSx_MASK)) { 59 rc = coresight_claim_device_unlocked(drvdata->base); 60 if (rc) 61 goto done; 62 } 63 64 functl &= ~FUNNEL_HOLDTIME_MASK; 65 functl |= FUNNEL_HOLDTIME; 66 functl |= (1 << port); 67 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 68 writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); 69 done: 70 CS_LOCK(drvdata->base); 71 return rc; 72 } 73 74 static int funnel_enable(struct coresight_device *csdev, int inport, 75 int outport) 76 { 77 int rc = 0; 78 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 79 80 if (drvdata->base) 81 rc = dynamic_funnel_enable_hw(drvdata, inport); 82 83 if (!rc) 84 dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport); 85 return rc; 86 } 87 88 static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata, 89 int inport) 90 { 91 u32 functl; 92 93 CS_UNLOCK(drvdata->base); 94 95 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 96 functl &= ~(1 << inport); 97 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 98 99 /* Disclaim the device if none of the slaves are now active */ 100 if (!(functl & FUNNEL_ENSx_MASK)) 101 coresight_disclaim_device_unlocked(drvdata->base); 102 103 CS_LOCK(drvdata->base); 104 } 105 106 static void funnel_disable(struct coresight_device *csdev, int inport, 107 int outport) 108 { 109 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 110 111 if (drvdata->base) 112 dynamic_funnel_disable_hw(drvdata, inport); 113 114 dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport); 115 } 116 117 static const struct coresight_ops_link funnel_link_ops = { 118 .enable = funnel_enable, 119 .disable = funnel_disable, 120 }; 121 122 static const struct coresight_ops funnel_cs_ops = { 123 .link_ops = &funnel_link_ops, 124 }; 125 126 static ssize_t priority_show(struct device *dev, 127 struct device_attribute *attr, char *buf) 128 { 129 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 130 unsigned long val = drvdata->priority; 131 132 return sprintf(buf, "%#lx\n", val); 133 } 134 135 static ssize_t priority_store(struct device *dev, 136 struct device_attribute *attr, 137 const char *buf, size_t size) 138 { 139 int ret; 140 unsigned long val; 141 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 142 143 ret = kstrtoul(buf, 16, &val); 144 if (ret) 145 return ret; 146 147 drvdata->priority = val; 148 return size; 149 } 150 static DEVICE_ATTR_RW(priority); 151 152 static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) 153 { 154 u32 functl; 155 156 CS_UNLOCK(drvdata->base); 157 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 158 CS_LOCK(drvdata->base); 159 160 return functl; 161 } 162 163 static ssize_t funnel_ctrl_show(struct device *dev, 164 struct device_attribute *attr, char *buf) 165 { 166 u32 val; 167 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 168 169 pm_runtime_get_sync(dev->parent); 170 171 val = get_funnel_ctrl_hw(drvdata); 172 173 pm_runtime_put(dev->parent); 174 175 return sprintf(buf, "%#x\n", val); 176 } 177 static DEVICE_ATTR_RO(funnel_ctrl); 178 179 static struct attribute *coresight_funnel_attrs[] = { 180 &dev_attr_funnel_ctrl.attr, 181 &dev_attr_priority.attr, 182 NULL, 183 }; 184 ATTRIBUTE_GROUPS(coresight_funnel); 185 186 static int funnel_probe(struct device *dev, struct resource *res) 187 { 188 int ret; 189 void __iomem *base; 190 struct coresight_platform_data *pdata = NULL; 191 struct funnel_drvdata *drvdata; 192 struct coresight_desc desc = { 0 }; 193 194 if (is_of_node(dev_fwnode(dev)) && 195 of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) 196 dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n"); 197 198 desc.name = coresight_alloc_device_name(&funnel_devs, dev); 199 if (!desc.name) 200 return -ENOMEM; 201 202 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 203 if (!drvdata) 204 return -ENOMEM; 205 206 drvdata->atclk = devm_clk_get(dev, "atclk"); /* optional */ 207 if (!IS_ERR(drvdata->atclk)) { 208 ret = clk_prepare_enable(drvdata->atclk); 209 if (ret) 210 return ret; 211 } 212 213 /* 214 * Map the device base for dynamic-funnel, which has been 215 * validated by AMBA core. 216 */ 217 if (res) { 218 base = devm_ioremap_resource(dev, res); 219 if (IS_ERR(base)) { 220 ret = PTR_ERR(base); 221 goto out_disable_clk; 222 } 223 drvdata->base = base; 224 desc.groups = coresight_funnel_groups; 225 } 226 227 dev_set_drvdata(dev, drvdata); 228 229 pdata = coresight_get_platform_data(dev); 230 if (IS_ERR(pdata)) { 231 ret = PTR_ERR(pdata); 232 goto out_disable_clk; 233 } 234 dev->platform_data = pdata; 235 236 desc.type = CORESIGHT_DEV_TYPE_LINK; 237 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; 238 desc.ops = &funnel_cs_ops; 239 desc.pdata = pdata; 240 desc.dev = dev; 241 drvdata->csdev = coresight_register(&desc); 242 if (IS_ERR(drvdata->csdev)) { 243 ret = PTR_ERR(drvdata->csdev); 244 goto out_disable_clk; 245 } 246 247 pm_runtime_put(dev); 248 ret = 0; 249 250 out_disable_clk: 251 if (ret && !IS_ERR_OR_NULL(drvdata->atclk)) 252 clk_disable_unprepare(drvdata->atclk); 253 return ret; 254 } 255 256 #ifdef CONFIG_PM 257 static int funnel_runtime_suspend(struct device *dev) 258 { 259 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 260 261 if (drvdata && !IS_ERR(drvdata->atclk)) 262 clk_disable_unprepare(drvdata->atclk); 263 264 return 0; 265 } 266 267 static int funnel_runtime_resume(struct device *dev) 268 { 269 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 270 271 if (drvdata && !IS_ERR(drvdata->atclk)) 272 clk_prepare_enable(drvdata->atclk); 273 274 return 0; 275 } 276 #endif 277 278 static const struct dev_pm_ops funnel_dev_pm_ops = { 279 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) 280 }; 281 282 static int static_funnel_probe(struct platform_device *pdev) 283 { 284 int ret; 285 286 pm_runtime_get_noresume(&pdev->dev); 287 pm_runtime_set_active(&pdev->dev); 288 pm_runtime_enable(&pdev->dev); 289 290 /* Static funnel do not have programming base */ 291 ret = funnel_probe(&pdev->dev, NULL); 292 293 if (ret) { 294 pm_runtime_put_noidle(&pdev->dev); 295 pm_runtime_disable(&pdev->dev); 296 } 297 298 return ret; 299 } 300 301 static const struct of_device_id static_funnel_match[] = { 302 {.compatible = "arm,coresight-static-funnel"}, 303 {} 304 }; 305 306 #ifdef CONFIG_ACPI 307 static const struct acpi_device_id static_funnel_ids[] = { 308 {"ARMHC9FE", 0}, 309 {}, 310 }; 311 #endif 312 313 static struct platform_driver static_funnel_driver = { 314 .probe = static_funnel_probe, 315 .driver = { 316 .name = "coresight-static-funnel", 317 .of_match_table = static_funnel_match, 318 .acpi_match_table = ACPI_PTR(static_funnel_ids), 319 .pm = &funnel_dev_pm_ops, 320 .suppress_bind_attrs = true, 321 }, 322 }; 323 builtin_platform_driver(static_funnel_driver); 324 325 static int dynamic_funnel_probe(struct amba_device *adev, 326 const struct amba_id *id) 327 { 328 return funnel_probe(&adev->dev, &adev->res); 329 } 330 331 static const struct amba_id dynamic_funnel_ids[] = { 332 { 333 .id = 0x000bb908, 334 .mask = 0x000fffff, 335 }, 336 { 337 /* Coresight SoC-600 */ 338 .id = 0x000bb9eb, 339 .mask = 0x000fffff, 340 }, 341 { 0, 0}, 342 }; 343 344 static struct amba_driver dynamic_funnel_driver = { 345 .drv = { 346 .name = "coresight-dynamic-funnel", 347 .owner = THIS_MODULE, 348 .pm = &funnel_dev_pm_ops, 349 .suppress_bind_attrs = true, 350 }, 351 .probe = dynamic_funnel_probe, 352 .id_table = dynamic_funnel_ids, 353 }; 354 builtin_amba_driver(dynamic_funnel_driver); 355