blob: 4f75bcdd243039de61e664534439f27bb07a53f7 [file] [log] [blame]
Aparna Das3b8a7082013-04-02 13:51:13 -07001/* Copyright (c) 2013, 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/device.h>
17#include <linux/platform_device.h>
18#include <linux/io.h>
19#include <linux/err.h>
20#include <linux/slab.h>
21#include <linux/mutex.h>
22#include <linux/clk.h>
23#include <linux/of_coresight.h>
24#include <linux/coresight.h>
25#include <linux/of.h>
26
27#include "coresight-priv.h"
28
29struct hwevent_mux {
30 phys_addr_t start;
31 phys_addr_t end;
32};
33
34struct hwevent_drvdata {
35 struct device *dev;
36 struct coresight_device *csdev;
37 struct clk *clk;
38 struct mutex mutex;
39 int nr_hclk;
40 struct clk **hclk;
41 int nr_hmux;
42 struct hwevent_mux *hmux;
43 bool enable;
44};
45
46static int hwevent_enable(struct hwevent_drvdata *drvdata)
47{
48 int ret, i;
49
50 mutex_lock(&drvdata->mutex);
51
52 if (drvdata->enable)
53 goto out;
54
55 ret = clk_prepare_enable(drvdata->clk);
56 if (ret)
57 goto err0;
58 for (i = 0; i < drvdata->nr_hclk; i++) {
59 ret = clk_prepare_enable(drvdata->hclk[i]);
60 if (ret)
61 goto err1;
62 }
63 drvdata->enable = true;
64 dev_info(drvdata->dev, "Hardware Event driver enabled\n");
65out:
66 mutex_unlock(&drvdata->mutex);
67 return 0;
68err1:
69 clk_disable_unprepare(drvdata->clk);
70 for (i--; i >= 0; i--)
71 clk_disable_unprepare(drvdata->hclk[i]);
72err0:
73 mutex_unlock(&drvdata->mutex);
74 return ret;
75}
76
77static void hwevent_disable(struct hwevent_drvdata *drvdata)
78{
79 int i;
80
81 mutex_lock(&drvdata->mutex);
82
83 if (!drvdata->enable)
84 goto out;
85
86 drvdata->enable = false;
87 clk_disable_unprepare(drvdata->clk);
88 for (i = 0; i < drvdata->nr_hclk; i++)
89 clk_disable_unprepare(drvdata->hclk[i]);
90 dev_info(drvdata->dev, "Hardware Event driver disabled\n");
91out:
92 mutex_unlock(&drvdata->mutex);
93}
94
95static ssize_t hwevent_show_enable(struct device *dev,
96 struct device_attribute *attr, char *buf)
97{
98 struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
99 unsigned long val = drvdata->enable;
100
101 return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
102}
103
104static ssize_t hwevent_store_enable(struct device *dev,
105 struct device_attribute *attr,
106 const char *buf, size_t size)
107{
108 struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
109 unsigned long val;
110 int ret = 0;
111
112 if (sscanf(buf, "%lx", &val) != 1)
113 return -EINVAL;
114
115 if (val)
116 ret = hwevent_enable(drvdata);
117 else
118 hwevent_disable(drvdata);
119
120 if (ret)
121 return ret;
122 return size;
123}
124static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, hwevent_show_enable,
125 hwevent_store_enable);
126
127static ssize_t hwevent_store_setreg(struct device *dev,
128 struct device_attribute *attr,
129 const char *buf, size_t size)
130{
131 struct hwevent_drvdata *drvdata = dev_get_drvdata(dev->parent);
132 void *hwereg;
Pushkar Joshic791fb32013-07-22 15:19:42 -0700133 unsigned long long addr;
134 unsigned long val;
Aparna Das3b8a7082013-04-02 13:51:13 -0700135 int ret, i;
136
Pushkar Joshic791fb32013-07-22 15:19:42 -0700137 if (sscanf(buf, "%llx %lx", &addr, &val) != 2)
Aparna Das3b8a7082013-04-02 13:51:13 -0700138 return -EINVAL;
139
140 mutex_lock(&drvdata->mutex);
141
142 if (!drvdata->enable) {
143 dev_err(dev, "Hardware Event driver not enabled\n");
144 ret = -EINVAL;
145 goto err;
146 }
147
148 for (i = 0; i < drvdata->nr_hmux; i++) {
149 if ((addr >= drvdata->hmux[i].start) &&
150 (addr < drvdata->hmux[i].end)) {
151 hwereg = devm_ioremap(dev,
152 drvdata->hmux[i].start,
153 drvdata->hmux[i].end -
154 drvdata->hmux[i].start);
155 if (!hwereg) {
Pushkar Joshic791fb32013-07-22 15:19:42 -0700156 dev_err(dev, "unable to map address 0x%llx\n",
Aparna Das3b8a7082013-04-02 13:51:13 -0700157 addr);
158 ret = -ENOMEM;
159 goto err;
160 }
161 writel_relaxed(val, hwereg + addr -
162 drvdata->hmux[i].start);
163 /* Ensure writes to hwevent control registers
164 are completed before unmapping the address
165 */
166 mb();
167 devm_iounmap(dev, hwereg);
168 break;
169 }
170 }
171
172 if (i == drvdata->nr_hmux) {
173 ret = coresight_csr_hwctrl_set(addr, val);
174 if (ret) {
175 dev_err(dev, "invalid mux control register address\n");
176 ret = -EINVAL;
177 goto err;
178 }
179 }
180
181 mutex_unlock(&drvdata->mutex);
182 return size;
183err:
184 mutex_unlock(&drvdata->mutex);
185 return ret;
186}
187static DEVICE_ATTR(setreg, S_IWUSR, NULL, hwevent_store_setreg);
188
189static struct attribute *hwevent_attrs[] = {
190 &dev_attr_enable.attr,
191 &dev_attr_setreg.attr,
192 NULL,
193};
194
195static struct attribute_group hwevent_attr_grp = {
196 .attrs = hwevent_attrs,
197};
198
199static const struct attribute_group *hwevent_attr_grps[] = {
200 &hwevent_attr_grp,
201 NULL,
202};
203
204static int __devinit hwevent_probe(struct platform_device *pdev)
205{
206 struct device *dev = &pdev->dev;
207 struct hwevent_drvdata *drvdata;
208 struct coresight_desc *desc;
209 struct coresight_platform_data *pdata;
210 struct resource *res;
211 int ret, i;
212 const char *hmux_name, *hclk_name;
213
Pratik Patel49306402013-06-14 01:02:08 -0700214 if (coresight_fuse_access_disabled())
215 return -EPERM;
216
Aparna Das3b8a7082013-04-02 13:51:13 -0700217 if (pdev->dev.of_node) {
218 pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
219 if (IS_ERR(pdata))
220 return PTR_ERR(pdata);
221 pdev->dev.platform_data = pdata;
222 }
223
224 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
225 if (!drvdata)
226 return -ENOMEM;
227 drvdata->dev = &pdev->dev;
228 platform_set_drvdata(pdev, drvdata);
229
230 if (pdev->dev.of_node)
231 drvdata->nr_hmux = of_property_count_strings(pdev->dev.of_node,
232 "reg-names");
233
234 if (drvdata->nr_hmux > 0) {
235 drvdata->hmux = devm_kzalloc(dev, drvdata->nr_hmux *
236 sizeof(*drvdata->hmux),
237 GFP_KERNEL);
238 if (!drvdata->hmux)
239 return -ENOMEM;
240 for (i = 0; i < drvdata->nr_hmux; i++) {
241 ret = of_property_read_string_index(pdev->dev.of_node,
242 "reg-names", i,
243 &hmux_name);
244 if (ret)
245 return ret;
246 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
247 hmux_name);
248 if (!res)
249 return -ENODEV;
250 drvdata->hmux[i].start = res->start;
251 drvdata->hmux[i].end = res->end;
252 }
253 } else if (drvdata->nr_hmux < 0) {
254 return drvdata->nr_hmux;
255 } else {
256 /* return error if reg-names in dt node is empty string */
257 return -ENODEV;
258 }
259
260 mutex_init(&drvdata->mutex);
261
262 drvdata->clk = devm_clk_get(dev, "core_clk");
263 if (IS_ERR(drvdata->clk))
264 return PTR_ERR(drvdata->clk);
265
266 ret = clk_set_rate(drvdata->clk, CORESIGHT_CLK_RATE_TRACE);
267 if (ret)
268 return ret;
269
270 if (pdev->dev.of_node)
271 drvdata->nr_hclk = of_property_count_strings(pdev->dev.of_node,
272 "qcom,hwevent-clks");
273 if (drvdata->nr_hclk > 0) {
274 drvdata->hclk = devm_kzalloc(dev, drvdata->nr_hclk *
275 sizeof(*drvdata->hclk),
276 GFP_KERNEL);
277 if (!drvdata->hclk)
278 return -ENOMEM;
279 for (i = 0; i < drvdata->nr_hclk; i++) {
280 ret = of_property_read_string_index(pdev->dev.of_node,
281 "qcom,hwevent-clks",
282 i, &hclk_name);
283 if (ret)
284 return ret;
285 drvdata->hclk[i] = devm_clk_get(dev, hclk_name);
286 if (IS_ERR(drvdata->hclk[i]))
287 return PTR_ERR(drvdata->hclk[i]);
288 }
289 }
290
291 desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
292 if (!desc)
293 return -ENOMEM;
294
295 desc->type = CORESIGHT_DEV_TYPE_NONE;
296 desc->pdata = pdev->dev.platform_data;
297 desc->dev = &pdev->dev;
298 desc->groups = hwevent_attr_grps;
299 desc->owner = THIS_MODULE;
300 drvdata->csdev = coresight_register(desc);
301 if (IS_ERR(drvdata->csdev))
302 return PTR_ERR(drvdata->csdev);
303
304 dev_info(dev, "Hardware Event driver initialized\n");
305 return 0;
306}
307
308static int __devexit hwevent_remove(struct platform_device *pdev)
309{
310 struct hwevent_drvdata *drvdata = platform_get_drvdata(pdev);
311
312 coresight_unregister(drvdata->csdev);
313 return 0;
314}
315
316static struct of_device_id hwevent_match[] = {
317 {.compatible = "qcom,coresight-hwevent"},
318 {}
319};
320
321static struct platform_driver hwevent_driver = {
322 .probe = hwevent_probe,
323 .remove = __devexit_p(hwevent_remove),
324 .driver = {
325 .name = "coresight-hwevent",
326 .owner = THIS_MODULE,
327 .of_match_table = hwevent_match,
328 },
329};
330
331static int __init hwevent_init(void)
332{
333 return platform_driver_register(&hwevent_driver);
334}
335module_init(hwevent_init);
336
337static void __exit hwevent_exit(void)
338{
339 platform_driver_unregister(&hwevent_driver);
340}
341module_exit(hwevent_exit);
342
343MODULE_LICENSE("GPL v2");
344MODULE_DESCRIPTION("CoreSight Hardware Event driver");