blob: d4f7940085a4f46501145d45ded4dfee9f738501 [file] [log] [blame]
Pratik Patel7831c082011-06-08 21:44:37 -07001/* Copyright (c) 2011, Code Aurora Forum. 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/platform_device.h>
16#include <linux/io.h>
17#include <linux/err.h>
18#include <linux/fs.h>
19#include <linux/miscdevice.h>
20#include <linux/uaccess.h>
21#include <linux/slab.h>
22#include <linux/delay.h>
23
24#include "qdss.h"
25
26#define etb_writel(etb, val, off) __raw_writel((val), etb.base + off)
27#define etb_readl(etb, off) __raw_readl(etb.base + off)
28
29#define ETB_RAM_DEPTH_REG (0x004)
30#define ETB_STATUS_REG (0x00C)
31#define ETB_RAM_READ_DATA_REG (0x010)
32#define ETB_RAM_READ_POINTER (0x014)
33#define ETB_RAM_WRITE_POINTER (0x018)
34#define ETB_TRG (0x01C)
35#define ETB_CTL_REG (0x020)
36#define ETB_RWD_REG (0x024)
37#define ETB_FFSR (0x300)
38#define ETB_FFCR (0x304)
39#define ETB_ITMISCOP0 (0xEE0)
40#define ETB_ITTRFLINACK (0xEE4)
41#define ETB_ITTRFLIN (0xEE8)
42#define ETB_ITATBDATA0 (0xEEC)
43#define ETB_ITATBCTR2 (0xEF0)
44#define ETB_ITATBCTR1 (0xEF4)
45#define ETB_ITATBCTR0 (0xEF8)
46
47
48#define BYTES_PER_WORD 4
49#define ETB_SIZE_WORDS 4096
50
51#define ETB_LOCK() \
52do { \
53 mb(); \
54 etb_writel(etb, MAGIC2, CS_LAR); \
55} while (0)
56#define ETB_UNLOCK() \
57do { \
58 etb_writel(etb, MAGIC1, CS_LAR); \
59 mb(); \
60} while (0)
61
62struct etb_ctx {
63 uint8_t *buf;
64 void __iomem *base;
65 bool enabled;
66 bool reading;
67 struct mutex lock;
68 atomic_t in_use;
69 struct device *dev;
70};
71
72static struct etb_ctx etb;
73
74static void __etb_enable(void)
75{
76 int i;
77
78 ETB_UNLOCK();
79
80 etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER);
81 for (i = 0; i < ETB_SIZE_WORDS; i++)
82 etb_writel(etb, 0x0, ETB_RWD_REG);
83
84 etb_writel(etb, 0x0, ETB_RAM_WRITE_POINTER);
85 etb_writel(etb, 0x0, ETB_RAM_READ_POINTER);
86
87 etb_writel(etb, BIT(13) | BIT(0), ETB_FFCR);
88 etb_writel(etb, BIT(0), ETB_CTL_REG);
89
90 ETB_LOCK();
91}
92
93void etb_enable(void)
94{
95 mutex_lock(&etb.lock);
96 __etb_enable();
97 etb.enabled = true;
98 dev_info(etb.dev, "etb enabled\n");
99 mutex_unlock(&etb.lock);
100}
101
102static void __etb_disable(void)
103{
104 int count;
105
106 ETB_UNLOCK();
107
108 etb_writel(etb, BIT(12) | BIT(13), ETB_FFCR);
109 etb_writel(etb, 0x0, ETB_CTL_REG);
110
111 for (count = TIMEOUT_US; BVAL(etb_readl(etb, ETB_FFSR), 1) != 1
112 && count > 0; count--)
113 udelay(1);
114 WARN(count == 0, "timeout while disabling etb\n");
115
116 ETB_LOCK();
117}
118
119void etb_disable(void)
120{
121 mutex_lock(&etb.lock);
122 __etb_disable();
123 etb.enabled = false;
124 dev_info(etb.dev, "etb disabled\n");
125 mutex_unlock(&etb.lock);
126}
127
128static void __etb_dump(void)
129{
130 int i;
131 uint8_t *buf_ptr;
132 uint32_t read_data;
133 uint32_t read_ptr;
134 uint32_t write_ptr;
135
136 ETB_UNLOCK();
137
138 read_ptr = etb_readl(etb, ETB_RAM_READ_POINTER);
139 write_ptr = etb_readl(etb, ETB_RAM_WRITE_POINTER);
140
141 if ((etb_readl(etb, ETB_STATUS_REG) & BIT(0)) == 0)
142 etb_writel(etb, 0x0, ETB_RAM_READ_POINTER);
143 else
144 etb_writel(etb, write_ptr, ETB_RAM_READ_POINTER);
145
146 buf_ptr = etb.buf;
147 for (i = 0; i < ETB_SIZE_WORDS; i++) {
148 read_data = etb_readl(etb, ETB_RAM_READ_DATA_REG);
149 *buf_ptr = read_data >> 0;
150 buf_ptr++;
151 *buf_ptr = read_data >> 8;
152 buf_ptr++;
153 *buf_ptr = read_data >> 16;
154 buf_ptr++;
155 *buf_ptr = read_data >> 24;
156 buf_ptr++;
157 }
158
159 etb_writel(etb, read_ptr, ETB_RAM_READ_POINTER);
160
161 ETB_LOCK();
162}
163
164void etb_dump(void)
165{
166 mutex_lock(&etb.lock);
167 if (etb.enabled) {
168 __etb_disable();
169 __etb_dump();
170 __etb_enable();
171
172 dev_info(etb.dev, "etb dumped\n");
173 }
174 mutex_unlock(&etb.lock);
175}
176
177static int etb_open(struct inode *inode, struct file *file)
178{
179 if (atomic_cmpxchg(&etb.in_use, 0, 1))
180 return -EBUSY;
181
182 dev_dbg(etb.dev, "%s: successfully opened\n", __func__);
183 return 0;
184}
185
186static ssize_t etb_read(struct file *file, char __user *data,
187 size_t len, loff_t *ppos)
188{
189 if (etb.reading == false) {
190 etb_dump();
191 etb.reading = true;
192 }
193
194 if (*ppos + len > ETB_SIZE_WORDS * BYTES_PER_WORD)
195 len = ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos;
196
197 if (copy_to_user(data, etb.buf + *ppos, len)) {
198 dev_dbg(etb.dev, "%s: copy_to_user failed\n", __func__);
199 return -EFAULT;
200 }
201
202 *ppos += len;
203
204 dev_dbg(etb.dev, "%s: %d bytes copied, %d bytes left\n",
205 __func__, len, (int) (ETB_SIZE_WORDS * BYTES_PER_WORD - *ppos));
206
207 return len;
208}
209
210static int etb_release(struct inode *inode, struct file *file)
211{
212 etb.reading = false;
213
214 atomic_set(&etb.in_use, 0);
215
216 dev_dbg(etb.dev, "%s: released\n", __func__);
217
218 return 0;
219}
220
221static const struct file_operations etb_fops = {
222 .owner = THIS_MODULE,
223 .open = etb_open,
224 .read = etb_read,
225 .release = etb_release,
226};
227
228static struct miscdevice etb_misc = {
229 .name = "msm_etb",
230 .minor = MISC_DYNAMIC_MINOR,
231 .fops = &etb_fops,
232};
233
234static int __devinit etb_probe(struct platform_device *pdev)
235{
236 int ret;
237 struct resource *res;
238
239 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
240 if (!res) {
241 ret = -EINVAL;
242 goto err_res;
243 }
244
245 etb.base = ioremap_nocache(res->start, resource_size(res));
246 if (!etb.base) {
247 ret = -EINVAL;
248 goto err_ioremap;
249 }
250
251 etb.dev = &pdev->dev;
252
253 ret = misc_register(&etb_misc);
254 if (ret)
255 goto err_misc;
256
257 etb.buf = kzalloc(ETB_SIZE_WORDS * BYTES_PER_WORD, GFP_KERNEL);
258 if (!etb.buf) {
259 ret = -ENOMEM;
260 goto err_alloc;
261 }
262
263 mutex_init(&etb.lock);
264
265 return 0;
266
267err_alloc:
268 misc_deregister(&etb_misc);
269err_misc:
270 iounmap(etb.base);
271err_ioremap:
272err_res:
273 return ret;
274}
275
276static int __devexit etb_remove(struct platform_device *pdev)
277{
278 if (etb.enabled)
279 etb_disable();
280 mutex_destroy(&etb.lock);
281 kfree(etb.buf);
282 misc_deregister(&etb_misc);
283 iounmap(etb.base);
284
285 return 0;
286}
287
288static struct platform_driver etb_driver = {
289 .probe = etb_probe,
290 .remove = __devexit_p(etb_remove),
291 .driver = {
292 .name = "msm_etb",
293 },
294};
295
296static int __init etb_init(void)
297{
298 return platform_driver_register(&etb_driver);
299}
300module_init(etb_init);
301
302static void __exit etb_exit(void)
303{
304 platform_driver_unregister(&etb_driver);
305}
306module_exit(etb_exit);
307
308MODULE_LICENSE("GPL v2");
309MODULE_DESCRIPTION("Coresight Embedded Trace Buffer");