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/clk.h> 22#include <linux/coresight.h> 23#include <linux/amba/bus.h> 24 25#include "coresight-priv.h" 26 27#define FUNNEL_FUNCTL 0x000 28#define FUNNEL_PRICTL 0x004 29 30#define FUNNEL_HOLDTIME_MASK 0xf00 31#define FUNNEL_HOLDTIME_SHFT 0x8 32#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) 33 34/** 35 * struct funnel_drvdata - specifics associated to a funnel component 36 * @base: memory mapped base address for this component. 37 * @dev: the device entity associated to this component. 38 * @csdev: component vitals needed by the framework. 39 * @clk: the clock this component is associated to. 40 * @priority: port selection order. 41 */ 42struct funnel_drvdata { 43 void __iomem *base; 44 struct device *dev; 45 struct coresight_device *csdev; 46 struct clk *clk; 47 unsigned long priority; 48}; 49 50static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port) 51{ 52 u32 functl; 53 54 CS_UNLOCK(drvdata->base); 55 56 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 57 functl &= ~FUNNEL_HOLDTIME_MASK; 58 functl |= FUNNEL_HOLDTIME; 59 functl |= (1 << port); 60 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 61 writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); 62 63 CS_LOCK(drvdata->base); 64} 65 66static int funnel_enable(struct coresight_device *csdev, int inport, 67 int outport) 68{ 69 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 70 int ret; 71 72 ret = clk_prepare_enable(drvdata->clk); 73 if (ret) 74 return ret; 75 76 funnel_enable_hw(drvdata, inport); 77 78 dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); 79 return 0; 80} 81 82static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) 83{ 84 u32 functl; 85 86 CS_UNLOCK(drvdata->base); 87 88 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 89 functl &= ~(1 << inport); 90 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); 91 92 CS_LOCK(drvdata->base); 93} 94 95static void funnel_disable(struct coresight_device *csdev, int inport, 96 int outport) 97{ 98 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 99 100 funnel_disable_hw(drvdata, inport); 101 102 clk_disable_unprepare(drvdata->clk); 103 104 dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); 105} 106 107static const struct coresight_ops_link funnel_link_ops = { 108 .enable = funnel_enable, 109 .disable = funnel_disable, 110}; 111 112static const struct coresight_ops funnel_cs_ops = { 113 .link_ops = &funnel_link_ops, 114}; 115 116static ssize_t priority_show(struct device *dev, 117 struct device_attribute *attr, char *buf) 118{ 119 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 120 unsigned long val = drvdata->priority; 121 122 return sprintf(buf, "%#lx\n", val); 123} 124 125static ssize_t priority_store(struct device *dev, 126 struct device_attribute *attr, 127 const char *buf, size_t size) 128{ 129 int ret; 130 unsigned long val; 131 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 132 133 ret = kstrtoul(buf, 16, &val); 134 if (ret) 135 return ret; 136 137 drvdata->priority = val; 138 return size; 139} 140static DEVICE_ATTR_RW(priority); 141 142static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) 143{ 144 u32 functl; 145 146 CS_UNLOCK(drvdata->base); 147 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); 148 CS_LOCK(drvdata->base); 149 150 return functl; 151} 152 153static ssize_t funnel_ctrl_show(struct device *dev, 154 struct device_attribute *attr, char *buf) 155{ 156 int ret; 157 u32 val; 158 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); 159 160 ret = clk_prepare_enable(drvdata->clk); 161 if (ret) 162 return ret; 163 164 val = get_funnel_ctrl_hw(drvdata); 165 clk_disable_unprepare(drvdata->clk); 166 167 return sprintf(buf, "%#x\n", val); 168} 169static DEVICE_ATTR_RO(funnel_ctrl); 170 171static struct attribute *coresight_funnel_attrs[] = { 172 &dev_attr_funnel_ctrl.attr, 173 &dev_attr_priority.attr, 174 NULL, 175}; 176ATTRIBUTE_GROUPS(coresight_funnel); 177 178static int funnel_probe(struct amba_device *adev, const struct amba_id *id) 179{ 180 void __iomem *base; 181 struct device *dev = &adev->dev; 182 struct coresight_platform_data *pdata = NULL; 183 struct funnel_drvdata *drvdata; 184 struct resource *res = &adev->res; 185 struct coresight_desc *desc; 186 struct device_node *np = adev->dev.of_node; 187 188 if (np) { 189 pdata = of_get_coresight_platform_data(dev, np); 190 if (IS_ERR(pdata)) 191 return PTR_ERR(pdata); 192 adev->dev.platform_data = pdata; 193 } 194 195 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 196 if (!drvdata) 197 return -ENOMEM; 198 199 drvdata->dev = &adev->dev; 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 209 drvdata->clk = adev->pclk; 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 229static 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 237static struct amba_id funnel_ids[] = { 238 { 239 .id = 0x0003b908, 240 .mask = 0x0003ffff, 241 }, 242 { 0, 0}, 243}; 244 245static struct amba_driver funnel_driver = { 246 .drv = { 247 .name = "coresight-funnel", 248 .owner = THIS_MODULE, 249 }, 250 .probe = funnel_probe, 251 .remove = funnel_remove, 252 .id_table = funnel_ids, 253}; 254 255module_amba_driver(funnel_driver); 256 257MODULE_LICENSE("GPL v2"); 258MODULE_DESCRIPTION("CoreSight Funnel driver"); 259