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