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