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