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/kernel.h> 9 #include <linux/init.h> 10 #include <linux/types.h> 11 #include <linux/device.h> 12 #include <linux/err.h> 13 #include <linux/fs.h> 14 #include <linux/slab.h> 15 #include <linux/pm_runtime.h> 16 #include <linux/coresight.h> 17 #include <linux/amba/bus.h> 18 #include <linux/clk.h> 19 20 #include "coresight-priv.h" 21 22 #define FUNNEL_FUNCTL 0x000 23 #define FUNNEL_PRICTL 0x004 24 25 #define FUNNEL_HOLDTIME_MASK 0xf00 26 #define FUNNEL_HOLDTIME_SHFT 0x8 27 #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) 28 #define FUNNEL_ENSx_MASK 0xff 29 30 /** 31 * struct funnel_drvdata - specifics associated to a funnel component 32 * @base: memory mapped base address for this component. 33 * @dev: the device entity associated to this component. 34 * @atclk: optional clock for the core parts of the funnel. 35 * @csdev: component vitals needed by the framework. 36 * @priority: port selection order. 37 */ 38 struct funnel_drvdata { 39 void __iomem *base; 40 struct device *dev; 41 struct clk *atclk; 42 struct coresight_device *csdev; 43 unsigned long priority; 44 }; 45 46 static int funnel_enable_hw(struct funnel_drvdata *drvdata, int port) 47 { 48 u32 functl; 49 int rc = 0; 50 51 CS_UNLOCK(drvdata->base); 52 53 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 54 /* Claim the device only when we enable the first slave */ 55 if (!(functl & FUNNEL_ENSx_MASK)) { 56 rc = coresight_claim_device_unlocked(drvdata->base); 57 if (rc) 58 goto done; 59 } 60 61 functl &= ~FUNNEL_HOLDTIME_MASK; 62 functl |= FUNNEL_HOLDTIME; 63 functl |= (1 << port); 64 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 65 writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); 66 done: 67 CS_LOCK(drvdata->base); 68 return rc; 69 } 70 71 static int funnel_enable(struct coresight_device *csdev, int inport, 72 int outport) 73 { 74 int rc; 75 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 76 77 rc = funnel_enable_hw(drvdata, inport); 78 79 if (!rc) 80 dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport); 81 return rc; 82 } 83 84 static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) 85 { 86 u32 functl; 87 88 CS_UNLOCK(drvdata->base); 89 90 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 91 functl &= ~(1 << inport); 92 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 93 94 /* Disclaim the device if none of the slaves are now active */ 95 if (!(functl & FUNNEL_ENSx_MASK)) 96 coresight_disclaim_device_unlocked(drvdata->base); 97 98 CS_LOCK(drvdata->base); 99 } 100 101 static void funnel_disable(struct coresight_device *csdev, int inport, 102 int outport) 103 { 104 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 105 106 funnel_disable_hw(drvdata, inport); 107 108 dev_dbg(drvdata->dev, "FUNNEL inport %d disabled\n", inport); 109 } 110 111 static const struct coresight_ops_link funnel_link_ops = { 112 .enable = funnel_enable, 113 .disable = funnel_disable, 114 }; 115 116 static const struct coresight_ops funnel_cs_ops = { 117 .link_ops = &funnel_link_ops, 118 }; 119 120 static ssize_t priority_show(struct device *dev, 121 struct device_attribute *attr, char *buf) 122 { 123 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 124 unsigned long val = drvdata->priority; 125 126 return sprintf(buf, "%#lx\n", val); 127 } 128 129 static ssize_t priority_store(struct device *dev, 130 struct device_attribute *attr, 131 const char *buf, size_t size) 132 { 133 int ret; 134 unsigned long val; 135 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 136 137 ret = kstrtoul(buf, 16, &val); 138 if (ret) 139 return ret; 140 141 drvdata->priority = val; 142 return size; 143 } 144 static DEVICE_ATTR_RW(priority); 145 146 static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) 147 { 148 u32 functl; 149 150 CS_UNLOCK(drvdata->base); 151 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 152 CS_LOCK(drvdata->base); 153 154 return functl; 155 } 156 157 static ssize_t funnel_ctrl_show(struct device *dev, 158 struct device_attribute *attr, char *buf) 159 { 160 u32 val; 161 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 162 163 pm_runtime_get_sync(drvdata->dev); 164 165 val = get_funnel_ctrl_hw(drvdata); 166 167 pm_runtime_put(drvdata->dev); 168 169 return sprintf(buf, "%#x\n", val); 170 } 171 static DEVICE_ATTR_RO(funnel_ctrl); 172 173 static struct attribute *coresight_funnel_attrs[] = { 174 &dev_attr_funnel_ctrl.attr, 175 &dev_attr_priority.attr, 176 NULL, 177 }; 178 ATTRIBUTE_GROUPS(coresight_funnel); 179 180 static int funnel_probe(struct amba_device *adev, const struct amba_id *id) 181 { 182 int ret; 183 void __iomem *base; 184 struct device *dev = &adev->dev; 185 struct coresight_platform_data *pdata = NULL; 186 struct funnel_drvdata *drvdata; 187 struct resource *res = &adev->res; 188 struct coresight_desc desc = { 0 }; 189 struct device_node *np = adev->dev.of_node; 190 191 if (np) { 192 pdata = of_get_coresight_platform_data(dev, np); 193 if (IS_ERR(pdata)) 194 return PTR_ERR(pdata); 195 adev->dev.platform_data = pdata; 196 } 197 198 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 199 if (!drvdata) 200 return -ENOMEM; 201 202 drvdata->dev = &adev->dev; 203 drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ 204 if (!IS_ERR(drvdata->atclk)) { 205 ret = clk_prepare_enable(drvdata->atclk); 206 if (ret) 207 return ret; 208 } 209 dev_set_drvdata(dev, drvdata); 210 211 /* Validity for the resource is already checked by the AMBA core */ 212 base = devm_ioremap_resource(dev, res); 213 if (IS_ERR(base)) 214 return PTR_ERR(base); 215 216 drvdata->base = base; 217 pm_runtime_put(&adev->dev); 218 219 desc.type = CORESIGHT_DEV_TYPE_LINK; 220 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; 221 desc.ops = &funnel_cs_ops; 222 desc.pdata = pdata; 223 desc.dev = dev; 224 desc.groups = coresight_funnel_groups; 225 drvdata->csdev = coresight_register(&desc); 226 227 return PTR_ERR_OR_ZERO(drvdata->csdev); 228 } 229 230 #ifdef CONFIG_PM 231 static int funnel_runtime_suspend(struct device *dev) 232 { 233 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 234 235 if (drvdata && !IS_ERR(drvdata->atclk)) 236 clk_disable_unprepare(drvdata->atclk); 237 238 return 0; 239 } 240 241 static int funnel_runtime_resume(struct device *dev) 242 { 243 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 244 245 if (drvdata && !IS_ERR(drvdata->atclk)) 246 clk_prepare_enable(drvdata->atclk); 247 248 return 0; 249 } 250 #endif 251 252 static const struct dev_pm_ops funnel_dev_pm_ops = { 253 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) 254 }; 255 256 static const struct amba_id funnel_ids[] = { 257 { 258 .id = 0x000bb908, 259 .mask = 0x000fffff, 260 }, 261 { 262 /* Coresight SoC-600 */ 263 .id = 0x000bb9eb, 264 .mask = 0x000fffff, 265 }, 266 { 0, 0}, 267 }; 268 269 static struct amba_driver funnel_driver = { 270 .drv = { 271 .name = "coresight-funnel", 272 .owner = THIS_MODULE, 273 .pm = &funnel_dev_pm_ops, 274 .suppress_bind_attrs = true, 275 }, 276 .probe = funnel_probe, 277 .id_table = funnel_ids, 278 }; 279 builtin_amba_driver(funnel_driver); 280