d549133776a0d93b43d6c0a9a89291cc797f890f
[sgx.git] / pvr / pvr_debugfs.c
1 /*
2  * Copyright (c) 2010-2011 Imre Deak <imre.deak@nokia.com>
3  * Copyright (c) 2010-2011 Luc Verhaegen <libv@codethink.co.uk>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19
20 /*
21  *
22  * Debugfs interface living in pvr/ subdirectory.
23  *
24  */
25 #include <linux/kernel.h>
26 #include <linux/debugfs.h>
27 #include <linux/vmalloc.h>
28 #include <linux/mutex.h>
29 #include <linux/uaccess.h>
30
31 #include "img_types.h"
32 #include "servicesext.h"
33 #include "services.h"
34 #include "sgxinfokm.h"
35 #include "syscommon.h"
36 #include "pvr_bridge_km.h"
37 #include "sgxutils.h"
38 #include "pvr_debugfs.h"
39
40 static struct dentry *debugfs_dir;
41 static u32 pvr_reset;
42
43 /*
44  *
45  */
46 static struct PVRSRV_DEVICE_NODE *get_sgx_node(void)
47 {
48         struct SYS_DATA *sysdata;
49         struct PVRSRV_DEVICE_NODE *node;
50
51         if (SysAcquireData(&sysdata) != PVRSRV_OK)
52                 return NULL;
53
54         for (node = sysdata->psDeviceNodeList; node; node = node->psNext)
55                 if (node->sDevId.eDeviceType == PVRSRV_DEVICE_TYPE_SGX)
56                         break;
57
58         return node;
59 }
60
61 static int pvr_debugfs_reset(void *data, u64 val)
62 {
63         struct PVRSRV_DEVICE_NODE *node;
64         enum PVRSRV_ERROR err;
65         int r = 0;
66
67         if (val != 1)
68                 return 0;
69
70         pvr_lock();
71
72         if (pvr_is_disabled()) {
73                 r = -ENODEV;
74                 goto exit;
75         }
76
77         node = get_sgx_node();
78         if (!node) {
79                 r =  -ENODEV;
80                 goto exit;
81         }
82
83         err = PVRSRVSetDevicePowerStateKM(node->sDevId.ui32DeviceIndex,
84                                           PVRSRV_POWER_STATE_D0);
85         if (err != PVRSRV_OK) {
86                 r = -EIO;
87                 goto exit;
88         }
89
90         HWRecoveryResetSGX(node);
91
92         SGXTestActivePowerEvent(node);
93 exit:
94         pvr_unlock();
95
96         return r;
97 }
98
99 static int pvr_debugfs_set(void *data, u64 val)
100 {
101         u32 *var = data;
102
103         if (var == &pvr_reset)
104                 return pvr_debugfs_reset(data, val);
105
106         BUG();
107 }
108
109 DEFINE_SIMPLE_ATTRIBUTE(pvr_debugfs_fops, NULL, pvr_debugfs_set, "%llu\n");
110
111 /*
112  *
113  */
114 struct edm_buf_info {
115         size_t len;
116         char data[1];
117 };
118
119 static int pvr_debugfs_edm_open(struct inode *inode, struct file *file)
120 {
121         struct PVRSRV_DEVICE_NODE *node;
122         struct PVRSRV_SGXDEV_INFO *sgx_info;
123         struct edm_buf_info *bi;
124         size_t size;
125
126         /* Take a snapshot of the EDM trace buffer */
127         size = SGXMK_TRACE_BUFFER_SIZE * SGXMK_TRACE_BUF_STR_LEN;
128         bi = vmalloc(sizeof(*bi) + size);
129         if (!bi)
130                 return -ENOMEM;
131
132         node = get_sgx_node();
133         sgx_info = node->pvDevice;
134         bi->len = snprint_edm_trace(sgx_info, bi->data, size);
135         file->private_data = bi;
136
137         return 0;
138 }
139
140 static int pvr_debugfs_edm_release(struct inode *inode, struct file *file)
141 {
142         vfree(file->private_data);
143
144         return 0;
145 }
146
147 static ssize_t pvr_debugfs_edm_read(struct file *file, char __user *buffer,
148                                 size_t count, loff_t *ppos)
149 {
150         struct edm_buf_info *bi = file->private_data;
151
152         return simple_read_from_buffer(buffer, count, ppos, bi->data, bi->len);
153 }
154
155 static const struct file_operations pvr_debugfs_edm_fops = {
156         .owner          = THIS_MODULE,
157         .open           = pvr_debugfs_edm_open,
158         .read           = pvr_debugfs_edm_read,
159         .release        = pvr_debugfs_edm_release,
160 };
161
162
163 /*
164  *
165  * HW Recovery dumping support.
166  *
167  */
168 static struct mutex hwrec_mutex[1];
169 static struct timeval hwrec_time;
170 static int hwrec_open_count;
171 static DECLARE_WAIT_QUEUE_HEAD(hwrec_wait_queue);
172 static int hwrec_event;
173
174 /* add extra locking to keep us from overwriting things during dumping. */
175 static int hwrec_event_open_count;
176 static int hwrec_event_file_lock;
177
178 void
179 pvr_hwrec_dump(void)
180 {
181         mutex_lock(hwrec_mutex);
182
183         if (hwrec_open_count || hwrec_event_file_lock) {
184                 pr_err("%s: previous hwrec dump is still locked!\n", __func__);
185                 mutex_unlock(hwrec_mutex);
186                 return;
187         }
188
189         do_gettimeofday(&hwrec_time);
190         pr_info("HW Recovery dump generated at %010ld%06ld\n",
191                 hwrec_time.tv_sec, hwrec_time.tv_usec);
192
193         hwrec_event = 1;
194
195         mutex_unlock(hwrec_mutex);
196
197         wake_up_interruptible(&hwrec_wait_queue);
198 }
199
200 /*
201  * helpers.
202  */
203 static int
204 hwrec_file_open(struct inode *inode, struct file *filp)
205 {
206         mutex_lock(hwrec_mutex);
207
208         hwrec_open_count++;
209
210         mutex_unlock(hwrec_mutex);
211         return 0;
212 }
213
214 static int
215 hwrec_file_release(struct inode *inode, struct file *filp)
216 {
217         mutex_lock(hwrec_mutex);
218
219         hwrec_open_count--;
220
221         mutex_unlock(hwrec_mutex);
222         return 0;
223 }
224
225 /*
226  * Provides a hwrec timestamp for unique dumping.
227  */
228 static ssize_t
229 hwrec_time_read(struct file *filp, char __user *buf, size_t size,
230                 loff_t *f_pos)
231 {
232         char tmp[20];
233
234         mutex_lock(hwrec_mutex);
235         snprintf(tmp, sizeof(tmp), "%010ld%06ld",
236                  hwrec_time.tv_sec, hwrec_time.tv_usec);
237         mutex_unlock(hwrec_mutex);
238
239         return simple_read_from_buffer(buf, size, f_pos, tmp, strlen(tmp));
240 }
241
242 static const struct file_operations hwrec_time_fops = {
243         .owner = THIS_MODULE,
244         .llseek = no_llseek,
245         .read = hwrec_time_read,
246         .open = hwrec_file_open,
247         .release = hwrec_file_release,
248 };
249
250 /*
251  * Blocks the reader until a HWRec happens.
252  */
253 static int
254 hwrec_event_open(struct inode *inode, struct file *filp)
255 {
256         int ret;
257
258         mutex_lock(hwrec_mutex);
259
260         if (hwrec_event_open_count)
261                 ret = -EUSERS;
262         else {
263                 hwrec_event_open_count++;
264                 ret = 0;
265         }
266
267         mutex_unlock(hwrec_mutex);
268
269         return ret;
270 }
271
272 static int
273 hwrec_event_release(struct inode *inode, struct file *filp)
274 {
275         mutex_lock(hwrec_mutex);
276
277         hwrec_event_open_count--;
278
279         mutex_unlock(hwrec_mutex);
280
281         return 0;
282 }
283
284
285 static ssize_t
286 hwrec_event_read(struct file *filp, char __user *buf, size_t size,
287                  loff_t *f_pos)
288 {
289         int ret = 0;
290
291         mutex_lock(hwrec_mutex);
292
293         hwrec_event_file_lock = 0;
294
295         mutex_unlock(hwrec_mutex);
296
297         ret = wait_event_interruptible(hwrec_wait_queue, hwrec_event);
298         if (!ret) {
299                 mutex_lock(hwrec_mutex);
300
301                 hwrec_event = 0;
302                 hwrec_event_file_lock = 1;
303
304                 mutex_unlock(hwrec_mutex);
305         }
306
307         return ret;
308 }
309
310 static const struct file_operations hwrec_event_fops = {
311         .owner = THIS_MODULE,
312         .llseek = no_llseek,
313         .read = hwrec_event_read,
314         .open = hwrec_event_open,
315         .release = hwrec_event_release,
316 };
317
318 /*
319  *
320  */
321 int pvr_debugfs_init(void)
322 {
323         mutex_init(hwrec_mutex);
324
325         debugfs_dir = debugfs_create_dir("pvr", NULL);
326         if (!debugfs_dir)
327                 return -ENODEV;
328
329         if (!debugfs_create_file("reset_sgx", S_IWUSR, debugfs_dir, &pvr_reset,
330                                  &pvr_debugfs_fops)) {
331                 debugfs_remove(debugfs_dir);
332                 return -ENODEV;
333         }
334
335         if (!debugfs_create_file("edm_trace", S_IRUGO, debugfs_dir, NULL,
336                                  &pvr_debugfs_edm_fops)) {
337                 debugfs_remove_recursive(debugfs_dir);
338                 return -ENODEV;
339         }
340
341         if (!debugfs_create_file("hwrec_event", S_IRUSR, debugfs_dir, NULL,
342                                  &hwrec_event_fops)) {
343                 debugfs_remove_recursive(debugfs_dir);
344                 return -ENODEV;
345         }
346
347         if (!debugfs_create_file("hwrec_time", S_IRUSR, debugfs_dir, NULL,
348                                  &hwrec_time_fops)) {
349                 debugfs_remove_recursive(debugfs_dir);
350                 return -ENODEV;
351         }
352
353         return 0;
354 }
355
356 void pvr_debugfs_cleanup(void)
357 {
358         debugfs_remove_recursive(debugfs_dir);
359 }