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; 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 = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 211 if (!desc) 212 return -ENOMEM; 213 214 desc->type = CORESIGHT_DEV_TYPE_LINK; 215 desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; 216 desc->ops = &funnel_cs_ops; 217 desc->pdata = pdata; 218 desc->dev = dev; 219 desc->groups = coresight_funnel_groups; 220 drvdata->csdev = coresight_register(desc); 221 if (IS_ERR(drvdata->csdev)) 222 return PTR_ERR(drvdata->csdev); 223 224 return 0; 225 } 226 227 #ifdef CONFIG_PM 228 static int funnel_runtime_suspend(struct device *dev) 229 { 230 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 231 232 if (drvdata && !IS_ERR(drvdata->atclk)) 233 clk_disable_unprepare(drvdata->atclk); 234 235 return 0; 236 } 237 238 static int funnel_runtime_resume(struct device *dev) 239 { 240 struct funnel_drvdata *drvdata = dev_get_drvdata(dev); 241 242 if (drvdata && !IS_ERR(drvdata->atclk)) 243 clk_prepare_enable(drvdata->atclk); 244 245 return 0; 246 } 247 #endif 248 249 static const struct dev_pm_ops funnel_dev_pm_ops = { 250 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) 251 }; 252 253 static struct amba_id funnel_ids[] = { 254 { 255 .id = 0x0003b908, 256 .mask = 0x0003ffff, 257 }, 258 { 0, 0}, 259 }; 260 261 static struct amba_driver funnel_driver = { 262 .drv = { 263 .name = "coresight-funnel", 264 .owner = THIS_MODULE, 265 .pm = &funnel_dev_pm_ops, 266 .suppress_bind_attrs = true, 267 }, 268 .probe = funnel_probe, 269 .id_table = funnel_ids, 270 }; 271 builtin_amba_driver(funnel_driver); 272