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