0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/acpi.h>
0009 #include <linux/kernel.h>
0010 #include <linux/init.h>
0011 #include <linux/types.h>
0012 #include <linux/device.h>
0013 #include <linux/err.h>
0014 #include <linux/fs.h>
0015 #include <linux/slab.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/pm_runtime.h>
0019 #include <linux/coresight.h>
0020 #include <linux/amba/bus.h>
0021 #include <linux/clk.h>
0022
0023 #include "coresight-priv.h"
0024
0025 #define FUNNEL_FUNCTL 0x000
0026 #define FUNNEL_PRICTL 0x004
0027
0028 #define FUNNEL_HOLDTIME_MASK 0xf00
0029 #define FUNNEL_HOLDTIME_SHFT 0x8
0030 #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
0031 #define FUNNEL_ENSx_MASK 0xff
0032
0033 DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 struct funnel_drvdata {
0044 void __iomem *base;
0045 struct clk *atclk;
0046 struct coresight_device *csdev;
0047 unsigned long priority;
0048 spinlock_t spinlock;
0049 };
0050
0051 static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
0052 {
0053 u32 functl;
0054 int rc = 0;
0055 struct coresight_device *csdev = drvdata->csdev;
0056
0057 CS_UNLOCK(drvdata->base);
0058
0059 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
0060
0061 if (!(functl & FUNNEL_ENSx_MASK)) {
0062 rc = coresight_claim_device_unlocked(csdev);
0063 if (rc)
0064 goto done;
0065 }
0066
0067 functl &= ~FUNNEL_HOLDTIME_MASK;
0068 functl |= FUNNEL_HOLDTIME;
0069 functl |= (1 << port);
0070 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
0071 writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
0072 done:
0073 CS_LOCK(drvdata->base);
0074 return rc;
0075 }
0076
0077 static int funnel_enable(struct coresight_device *csdev, int inport,
0078 int outport)
0079 {
0080 int rc = 0;
0081 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
0082 unsigned long flags;
0083 bool first_enable = false;
0084
0085 spin_lock_irqsave(&drvdata->spinlock, flags);
0086 if (atomic_read(&csdev->refcnt[inport]) == 0) {
0087 if (drvdata->base)
0088 rc = dynamic_funnel_enable_hw(drvdata, inport);
0089 if (!rc)
0090 first_enable = true;
0091 }
0092 if (!rc)
0093 atomic_inc(&csdev->refcnt[inport]);
0094 spin_unlock_irqrestore(&drvdata->spinlock, flags);
0095
0096 if (first_enable)
0097 dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport);
0098 return rc;
0099 }
0100
0101 static void dynamic_funnel_disable_hw(struct funnel_drvdata *drvdata,
0102 int inport)
0103 {
0104 u32 functl;
0105 struct coresight_device *csdev = drvdata->csdev;
0106
0107 CS_UNLOCK(drvdata->base);
0108
0109 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
0110 functl &= ~(1 << inport);
0111 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
0112
0113
0114 if (!(functl & FUNNEL_ENSx_MASK))
0115 coresight_disclaim_device_unlocked(csdev);
0116
0117 CS_LOCK(drvdata->base);
0118 }
0119
0120 static void funnel_disable(struct coresight_device *csdev, int inport,
0121 int outport)
0122 {
0123 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
0124 unsigned long flags;
0125 bool last_disable = false;
0126
0127 spin_lock_irqsave(&drvdata->spinlock, flags);
0128 if (atomic_dec_return(&csdev->refcnt[inport]) == 0) {
0129 if (drvdata->base)
0130 dynamic_funnel_disable_hw(drvdata, inport);
0131 last_disable = true;
0132 }
0133 spin_unlock_irqrestore(&drvdata->spinlock, flags);
0134
0135 if (last_disable)
0136 dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport);
0137 }
0138
0139 static const struct coresight_ops_link funnel_link_ops = {
0140 .enable = funnel_enable,
0141 .disable = funnel_disable,
0142 };
0143
0144 static const struct coresight_ops funnel_cs_ops = {
0145 .link_ops = &funnel_link_ops,
0146 };
0147
0148 static ssize_t priority_show(struct device *dev,
0149 struct device_attribute *attr, char *buf)
0150 {
0151 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
0152 unsigned long val = drvdata->priority;
0153
0154 return sprintf(buf, "%#lx\n", val);
0155 }
0156
0157 static ssize_t priority_store(struct device *dev,
0158 struct device_attribute *attr,
0159 const char *buf, size_t size)
0160 {
0161 int ret;
0162 unsigned long val;
0163 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
0164
0165 ret = kstrtoul(buf, 16, &val);
0166 if (ret)
0167 return ret;
0168
0169 drvdata->priority = val;
0170 return size;
0171 }
0172 static DEVICE_ATTR_RW(priority);
0173
0174 static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata)
0175 {
0176 u32 functl;
0177
0178 CS_UNLOCK(drvdata->base);
0179 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
0180 CS_LOCK(drvdata->base);
0181
0182 return functl;
0183 }
0184
0185 static ssize_t funnel_ctrl_show(struct device *dev,
0186 struct device_attribute *attr, char *buf)
0187 {
0188 u32 val;
0189 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
0190
0191 pm_runtime_get_sync(dev->parent);
0192
0193 val = get_funnel_ctrl_hw(drvdata);
0194
0195 pm_runtime_put(dev->parent);
0196
0197 return sprintf(buf, "%#x\n", val);
0198 }
0199 static DEVICE_ATTR_RO(funnel_ctrl);
0200
0201 static struct attribute *coresight_funnel_attrs[] = {
0202 &dev_attr_funnel_ctrl.attr,
0203 &dev_attr_priority.attr,
0204 NULL,
0205 };
0206 ATTRIBUTE_GROUPS(coresight_funnel);
0207
0208 static int funnel_probe(struct device *dev, struct resource *res)
0209 {
0210 int ret;
0211 void __iomem *base;
0212 struct coresight_platform_data *pdata = NULL;
0213 struct funnel_drvdata *drvdata;
0214 struct coresight_desc desc = { 0 };
0215
0216 if (is_of_node(dev_fwnode(dev)) &&
0217 of_device_is_compatible(dev->of_node, "arm,coresight-funnel"))
0218 dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n");
0219
0220 desc.name = coresight_alloc_device_name(&funnel_devs, dev);
0221 if (!desc.name)
0222 return -ENOMEM;
0223
0224 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
0225 if (!drvdata)
0226 return -ENOMEM;
0227
0228 drvdata->atclk = devm_clk_get(dev, "atclk");
0229 if (!IS_ERR(drvdata->atclk)) {
0230 ret = clk_prepare_enable(drvdata->atclk);
0231 if (ret)
0232 return ret;
0233 }
0234
0235
0236
0237
0238
0239 if (res) {
0240 base = devm_ioremap_resource(dev, res);
0241 if (IS_ERR(base)) {
0242 ret = PTR_ERR(base);
0243 goto out_disable_clk;
0244 }
0245 drvdata->base = base;
0246 desc.groups = coresight_funnel_groups;
0247 desc.access = CSDEV_ACCESS_IOMEM(base);
0248 }
0249
0250 dev_set_drvdata(dev, drvdata);
0251
0252 pdata = coresight_get_platform_data(dev);
0253 if (IS_ERR(pdata)) {
0254 ret = PTR_ERR(pdata);
0255 goto out_disable_clk;
0256 }
0257 dev->platform_data = pdata;
0258
0259 spin_lock_init(&drvdata->spinlock);
0260 desc.type = CORESIGHT_DEV_TYPE_LINK;
0261 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
0262 desc.ops = &funnel_cs_ops;
0263 desc.pdata = pdata;
0264 desc.dev = dev;
0265 drvdata->csdev = coresight_register(&desc);
0266 if (IS_ERR(drvdata->csdev)) {
0267 ret = PTR_ERR(drvdata->csdev);
0268 goto out_disable_clk;
0269 }
0270
0271 pm_runtime_put(dev);
0272 ret = 0;
0273
0274 out_disable_clk:
0275 if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
0276 clk_disable_unprepare(drvdata->atclk);
0277 return ret;
0278 }
0279
0280 static int funnel_remove(struct device *dev)
0281 {
0282 struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
0283
0284 coresight_unregister(drvdata->csdev);
0285
0286 return 0;
0287 }
0288
0289 #ifdef CONFIG_PM
0290 static int funnel_runtime_suspend(struct device *dev)
0291 {
0292 struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
0293
0294 if (drvdata && !IS_ERR(drvdata->atclk))
0295 clk_disable_unprepare(drvdata->atclk);
0296
0297 return 0;
0298 }
0299
0300 static int funnel_runtime_resume(struct device *dev)
0301 {
0302 struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
0303
0304 if (drvdata && !IS_ERR(drvdata->atclk))
0305 clk_prepare_enable(drvdata->atclk);
0306
0307 return 0;
0308 }
0309 #endif
0310
0311 static const struct dev_pm_ops funnel_dev_pm_ops = {
0312 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
0313 };
0314
0315 static int static_funnel_probe(struct platform_device *pdev)
0316 {
0317 int ret;
0318
0319 pm_runtime_get_noresume(&pdev->dev);
0320 pm_runtime_set_active(&pdev->dev);
0321 pm_runtime_enable(&pdev->dev);
0322
0323
0324 ret = funnel_probe(&pdev->dev, NULL);
0325
0326 if (ret) {
0327 pm_runtime_put_noidle(&pdev->dev);
0328 pm_runtime_disable(&pdev->dev);
0329 }
0330
0331 return ret;
0332 }
0333
0334 static int static_funnel_remove(struct platform_device *pdev)
0335 {
0336 funnel_remove(&pdev->dev);
0337 pm_runtime_disable(&pdev->dev);
0338 return 0;
0339 }
0340
0341 static const struct of_device_id static_funnel_match[] = {
0342 {.compatible = "arm,coresight-static-funnel"},
0343 {}
0344 };
0345
0346 MODULE_DEVICE_TABLE(of, static_funnel_match);
0347
0348 #ifdef CONFIG_ACPI
0349 static const struct acpi_device_id static_funnel_ids[] = {
0350 {"ARMHC9FE", 0},
0351 {},
0352 };
0353
0354 MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
0355 #endif
0356
0357 static struct platform_driver static_funnel_driver = {
0358 .probe = static_funnel_probe,
0359 .remove = static_funnel_remove,
0360 .driver = {
0361 .name = "coresight-static-funnel",
0362
0363 .of_match_table = static_funnel_match,
0364 .acpi_match_table = ACPI_PTR(static_funnel_ids),
0365 .pm = &funnel_dev_pm_ops,
0366 .suppress_bind_attrs = true,
0367 },
0368 };
0369
0370 static int dynamic_funnel_probe(struct amba_device *adev,
0371 const struct amba_id *id)
0372 {
0373 return funnel_probe(&adev->dev, &adev->res);
0374 }
0375
0376 static void dynamic_funnel_remove(struct amba_device *adev)
0377 {
0378 funnel_remove(&adev->dev);
0379 }
0380
0381 static const struct amba_id dynamic_funnel_ids[] = {
0382 {
0383 .id = 0x000bb908,
0384 .mask = 0x000fffff,
0385 },
0386 {
0387
0388 .id = 0x000bb9eb,
0389 .mask = 0x000fffff,
0390 },
0391 { 0, 0},
0392 };
0393
0394 MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);
0395
0396 static struct amba_driver dynamic_funnel_driver = {
0397 .drv = {
0398 .name = "coresight-dynamic-funnel",
0399 .owner = THIS_MODULE,
0400 .pm = &funnel_dev_pm_ops,
0401 .suppress_bind_attrs = true,
0402 },
0403 .probe = dynamic_funnel_probe,
0404 .remove = dynamic_funnel_remove,
0405 .id_table = dynamic_funnel_ids,
0406 };
0407
0408 static int __init funnel_init(void)
0409 {
0410 int ret;
0411
0412 ret = platform_driver_register(&static_funnel_driver);
0413 if (ret) {
0414 pr_info("Error registering platform driver\n");
0415 return ret;
0416 }
0417
0418 ret = amba_driver_register(&dynamic_funnel_driver);
0419 if (ret) {
0420 pr_info("Error registering amba driver\n");
0421 platform_driver_unregister(&static_funnel_driver);
0422 }
0423
0424 return ret;
0425 }
0426
0427 static void __exit funnel_exit(void)
0428 {
0429 platform_driver_unregister(&static_funnel_driver);
0430 amba_driver_unregister(&dynamic_funnel_driver);
0431 }
0432
0433 module_init(funnel_init);
0434 module_exit(funnel_exit);
0435
0436 MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
0437 MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
0438 MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
0439 MODULE_LICENSE("GPL v2");