blob: a3da8ffd8bfb42ec89afc7ee4823b2461da386f8 [file] [log] [blame]
Satyajit Desaicaa081f2017-06-15 14:54:27 -07001/* Copyright (c) 2011-2012, 2017, The Linux Foundation. All rights reserved.
Pratik Patel6e21e342014-11-03 11:07:39 -07002 *
Paul Gortmaker941943c2016-02-17 17:52:03 -07003 * Description: CoreSight Funnel driver
4 *
Pratik Patel6e21e342014-11-03 11:07:39 -07005 * 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>
Pratik Patel6e21e342014-11-03 11:07:39 -070016#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>
Linus Walleije2d08f92015-05-19 10:55:12 -060022#include <linux/pm_runtime.h>
Pratik Patel6e21e342014-11-03 11:07:39 -070023#include <linux/coresight.h>
24#include <linux/amba/bus.h>
Linus Walleij2e5f75d2015-05-19 10:55:17 -060025#include <linux/clk.h>
Satyajit Desaicaa081f2017-06-15 14:54:27 -070026#include <linux/of_address.h>
Pratik Patel6e21e342014-11-03 11:07:39 -070027
28#include "coresight-priv.h"
29
30#define FUNNEL_FUNCTL 0x000
31#define FUNNEL_PRICTL 0x004
32
33#define FUNNEL_HOLDTIME_MASK 0xf00
34#define FUNNEL_HOLDTIME_SHFT 0x8
35#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT)
36
37/**
38 * struct funnel_drvdata - specifics associated to a funnel component
39 * @base: memory mapped base address for this component.
40 * @dev: the device entity associated to this component.
Linus Walleij2e5f75d2015-05-19 10:55:17 -060041 * @atclk: optional clock for the core parts of the funnel.
Pratik Patel6e21e342014-11-03 11:07:39 -070042 * @csdev: component vitals needed by the framework.
Pratik Patel6e21e342014-11-03 11:07:39 -070043 * @priority: port selection order.
44 */
45struct funnel_drvdata {
46 void __iomem *base;
47 struct device *dev;
Linus Walleij2e5f75d2015-05-19 10:55:17 -060048 struct clk *atclk;
Pratik Patel6e21e342014-11-03 11:07:39 -070049 struct coresight_device *csdev;
Pratik Patel6e21e342014-11-03 11:07:39 -070050 unsigned long priority;
51};
52
53static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
54{
55 u32 functl;
56
57 CS_UNLOCK(drvdata->base);
58
59 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
60 functl &= ~FUNNEL_HOLDTIME_MASK;
61 functl |= FUNNEL_HOLDTIME;
62 functl |= (1 << port);
63 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
64 writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
65
66 CS_LOCK(drvdata->base);
67}
68
69static int funnel_enable(struct coresight_device *csdev, int inport,
70 int outport)
71{
72 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
Pratik Patel6e21e342014-11-03 11:07:39 -070073
Pratik Patel6e21e342014-11-03 11:07:39 -070074 funnel_enable_hw(drvdata, inport);
75
76 dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport);
77 return 0;
78}
79
80static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport)
81{
82 u32 functl;
83
84 CS_UNLOCK(drvdata->base);
85
86 functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
87 functl &= ~(1 << inport);
88 writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
89
90 CS_LOCK(drvdata->base);
91}
92
93static void funnel_disable(struct coresight_device *csdev, int inport,
94 int outport)
95{
96 struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
97
98 funnel_disable_hw(drvdata, inport);
Pratik Patel6e21e342014-11-03 11:07:39 -070099
100 dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport);
101}
102
103static const struct coresight_ops_link funnel_link_ops = {
104 .enable = funnel_enable,
105 .disable = funnel_disable,
106};
107
108static const struct coresight_ops funnel_cs_ops = {
109 .link_ops = &funnel_link_ops,
110};
111
112static 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
121static 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}
136static DEVICE_ATTR_RW(priority);
137
138static 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
149static ssize_t funnel_ctrl_show(struct device *dev,
150 struct device_attribute *attr, char *buf)
151{
Pratik Patel6e21e342014-11-03 11:07:39 -0700152 u32 val;
153 struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent);
154
Linus Walleije2d08f92015-05-19 10:55:12 -0600155 pm_runtime_get_sync(drvdata->dev);
Pratik Patel6e21e342014-11-03 11:07:39 -0700156
157 val = get_funnel_ctrl_hw(drvdata);
Linus Walleije2d08f92015-05-19 10:55:12 -0600158
159 pm_runtime_put(drvdata->dev);
Pratik Patel6e21e342014-11-03 11:07:39 -0700160
161 return sprintf(buf, "%#x\n", val);
162}
163static DEVICE_ATTR_RO(funnel_ctrl);
164
165static struct attribute *coresight_funnel_attrs[] = {
166 &dev_attr_funnel_ctrl.attr,
167 &dev_attr_priority.attr,
168 NULL,
169};
170ATTRIBUTE_GROUPS(coresight_funnel);
171
Satyajit Desaicaa081f2017-06-15 14:54:27 -0700172static int funnel_get_resource_byname(struct device_node *np,
173 char *ch_base, struct resource *res)
174{
175 const char *name = NULL;
176 int index = 0, found = 0;
177
178 while (!of_property_read_string_index(np, "reg-names", index, &name)) {
179 if (strcmp(ch_base, name)) {
180 index++;
181 continue;
182 }
183
184 /* We have a match and @index is where it's at */
185 found = 1;
186 break;
187 }
188
189 if (!found)
190 return -EINVAL;
191
192 return of_address_to_resource(np, index, res);
193}
194
Pratik Patel6e21e342014-11-03 11:07:39 -0700195static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
196{
Linus Walleij2e5f75d2015-05-19 10:55:17 -0600197 int ret;
Pratik Patel6e21e342014-11-03 11:07:39 -0700198 void __iomem *base;
199 struct device *dev = &adev->dev;
200 struct coresight_platform_data *pdata = NULL;
201 struct funnel_drvdata *drvdata;
Satyajit Desaicaa081f2017-06-15 14:54:27 -0700202 struct resource *res;
203 struct resource res_real;
Suzuki K Poulose94862952016-08-25 15:19:05 -0600204 struct coresight_desc desc = { 0 };
Pratik Patel6e21e342014-11-03 11:07:39 -0700205 struct device_node *np = adev->dev.of_node;
206
207 if (np) {
208 pdata = of_get_coresight_platform_data(dev, np);
209 if (IS_ERR(pdata))
210 return PTR_ERR(pdata);
211 adev->dev.platform_data = pdata;
212 }
213
214 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
215 if (!drvdata)
216 return -ENOMEM;
217
218 drvdata->dev = &adev->dev;
Linus Walleij2e5f75d2015-05-19 10:55:17 -0600219 drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
220 if (!IS_ERR(drvdata->atclk)) {
221 ret = clk_prepare_enable(drvdata->atclk);
222 if (ret)
223 return ret;
224 }
Pratik Patel6e21e342014-11-03 11:07:39 -0700225 dev_set_drvdata(dev, drvdata);
226
Satyajit Desaicaa081f2017-06-15 14:54:27 -0700227 if (of_property_read_bool(np, "qcom,duplicate-funnel")) {
228 ret = funnel_get_resource_byname(np, "funnel-base-real",
229 &res_real);
230 if (ret)
231 return ret;
232
233 res = &res_real;
234 base = devm_ioremap(dev, res->start, resource_size(res));
235 } else {
236 /* Validity of resource is already checked by the AMBA core */
237 res = &adev->res;
238 base = devm_ioremap_resource(dev, res);
239 }
Pratik Patel6e21e342014-11-03 11:07:39 -0700240 if (IS_ERR(base))
241 return PTR_ERR(base);
242
243 drvdata->base = base;
Linus Walleije2d08f92015-05-19 10:55:12 -0600244 pm_runtime_put(&adev->dev);
Pratik Patel6e21e342014-11-03 11:07:39 -0700245
Suzuki K Poulose94862952016-08-25 15:19:05 -0600246 desc.type = CORESIGHT_DEV_TYPE_LINK;
247 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
248 desc.ops = &funnel_cs_ops;
249 desc.pdata = pdata;
250 desc.dev = dev;
251 desc.groups = coresight_funnel_groups;
252 drvdata->csdev = coresight_register(&desc);
Pratik Patel6e21e342014-11-03 11:07:39 -0700253 if (IS_ERR(drvdata->csdev))
254 return PTR_ERR(drvdata->csdev);
255
Pratik Patel6e21e342014-11-03 11:07:39 -0700256 return 0;
257}
258
Linus Walleij2e5f75d2015-05-19 10:55:17 -0600259#ifdef CONFIG_PM
260static int funnel_runtime_suspend(struct device *dev)
261{
262 struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
263
264 if (drvdata && !IS_ERR(drvdata->atclk))
265 clk_disable_unprepare(drvdata->atclk);
266
267 return 0;
268}
269
270static int funnel_runtime_resume(struct device *dev)
271{
272 struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
273
274 if (drvdata && !IS_ERR(drvdata->atclk))
275 clk_prepare_enable(drvdata->atclk);
276
277 return 0;
278}
279#endif
280
281static const struct dev_pm_ops funnel_dev_pm_ops = {
282 SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
283};
284
Pratik Patel6e21e342014-11-03 11:07:39 -0700285static struct amba_id funnel_ids[] = {
286 {
287 .id = 0x0003b908,
288 .mask = 0x0003ffff,
289 },
290 { 0, 0},
291};
292
293static struct amba_driver funnel_driver = {
294 .drv = {
295 .name = "coresight-funnel",
296 .owner = THIS_MODULE,
Linus Walleij2e5f75d2015-05-19 10:55:17 -0600297 .pm = &funnel_dev_pm_ops,
Mathieu Poirierb15f0fb2016-02-02 14:14:00 -0700298 .suppress_bind_attrs = true,
Pratik Patel6e21e342014-11-03 11:07:39 -0700299 },
300 .probe = funnel_probe,
Pratik Patel6e21e342014-11-03 11:07:39 -0700301 .id_table = funnel_ids,
302};
Paul Gortmaker941943c2016-02-17 17:52:03 -0700303builtin_amba_driver(funnel_driver);