blob: 26c8f75ac1fca2c013295e3f935b51f02ccec349 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp6/dsp_dump.c
2 *
3 * Copyright (C) 2009 Google, Inc.
4 * Author: Brian Swetland <swetland@google.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/io.h>
18#include <linux/fs.h>
19#include <linux/module.h>
20#include <linux/miscdevice.h>
21#include <linux/uaccess.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/delay.h>
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -070025#include <linux/platform_device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <asm/atomic.h>
27
Steve Mucklef132c6c2012-06-06 18:30:57 -070028#include <mach/proc_comm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include <mach/debug_mm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include <mach/qdsp6v2/dsp_debug.h>
31
32static wait_queue_head_t dsp_wait;
33static int dsp_has_crashed;
34static int dsp_wait_count;
35
36static atomic_t dsp_crash_count = ATOMIC_INIT(0);
37dsp_state_cb cb_ptr;
38
39void q6audio_dsp_not_responding(void)
40{
Patrick Lai035ea892013-01-05 21:23:29 -080041 int i;
42
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043 if (cb_ptr)
44 cb_ptr(DSP_STATE_CRASHED);
45 if (atomic_add_return(1, &dsp_crash_count) != 1) {
46 pr_err("q6audio_dsp_not_responding() \
47 - parking additional crasher...\n");
Patrick Lai035ea892013-01-05 21:23:29 -080048 for (i = 0; i < 600; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049 msleep(1000);
50 }
51 if (dsp_wait_count) {
52 dsp_has_crashed = 1;
53 wake_up(&dsp_wait);
54
55 while (dsp_has_crashed != 2)
56 wait_event(dsp_wait, dsp_has_crashed == 2);
57 } else {
58 pr_err("q6audio_dsp_not_responding() - no waiter?\n");
59 }
60 if (cb_ptr)
61 cb_ptr(DSP_STATE_CRASH_DUMP_DONE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062}
63
64static int dsp_open(struct inode *inode, struct file *file)
65{
66 return 0;
67}
68
69#define DSP_NMI_ADDR 0x28800010
70
71static ssize_t dsp_write(struct file *file, const char __user *buf,
72 size_t count, loff_t *pos)
73{
74 char cmd[32];
75 void __iomem *ptr;
Laura Abbottea3e7b62012-04-30 15:59:21 -070076 void *mem_buffer;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077
78 if (count >= sizeof(cmd))
79 return -EINVAL;
80 if (copy_from_user(cmd, buf, count))
81 return -EFAULT;
82 cmd[count] = 0;
83
84 if ((count > 1) && (cmd[count-1] == '\n'))
85 cmd[count-1] = 0;
86
87 if (!strcmp(cmd, "wait-for-crash")) {
88 while (!dsp_has_crashed) {
89 int res;
90 dsp_wait_count++;
91 res = wait_event_interruptible(dsp_wait,
92 dsp_has_crashed);
93 if (res < 0) {
94 dsp_wait_count--;
95 return res;
96 }
97 }
98 /* assert DSP NMI */
Laura Abbottea3e7b62012-04-30 15:59:21 -070099 mem_buffer = ioremap(DSP_NMI_ADDR, 0x16);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100 if (IS_ERR((void *)mem_buffer)) {
101 pr_err("%s:map_buffer failed, error = %ld\n", __func__,
102 PTR_ERR((void *)mem_buffer));
103 return -ENOMEM;
104 }
Laura Abbottea3e7b62012-04-30 15:59:21 -0700105 ptr = mem_buffer;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106 if (!ptr) {
107 pr_err("Unable to map DSP NMI\n");
108 return -EFAULT;
109 }
110 writel(0x1, (void *)ptr);
Laura Abbottea3e7b62012-04-30 15:59:21 -0700111 iounmap(mem_buffer);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 } else if (!strcmp(cmd, "boom")) {
113 q6audio_dsp_not_responding();
114 } else if (!strcmp(cmd, "continue-crash")) {
115 dsp_has_crashed = 2;
116 wake_up(&dsp_wait);
117 } else {
118 pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__,
119 __func__, cmd);
120 }
121
122 return count;
123}
124
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125static unsigned copy_ok_count;
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700126static uint32_t dsp_ram_size;
127static uint32_t dsp_ram_base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128
129static ssize_t dsp_read(struct file *file, char __user *buf,
130 size_t count, loff_t *pos)
131{
132 size_t actual = 0;
133 size_t mapsize = PAGE_SIZE;
134 unsigned addr;
135 void __iomem *ptr;
Laura Abbottea3e7b62012-04-30 15:59:21 -0700136 void *mem_buffer;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700138 if ((dsp_ram_base == 0) || (dsp_ram_size == 0)) {
139 pr_err("[%s:%s] Memory Invalid or not initialized, Base = 0x%x,"
140 " size = 0x%x\n", __MM_FILE__,
141 __func__, dsp_ram_base, dsp_ram_size);
142 return -EINVAL;
143 }
144
145 if (*pos >= dsp_ram_size)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 return 0;
147
148 if (*pos & (PAGE_SIZE - 1))
149 return -EINVAL;
150
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700151 addr = (*pos + dsp_ram_base);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152
153 /* don't blow up if we're unaligned */
154 if (addr & (PAGE_SIZE - 1))
155 mapsize *= 2;
156
157 while (count >= PAGE_SIZE) {
Laura Abbottea3e7b62012-04-30 15:59:21 -0700158 mem_buffer = ioremap(addr, mapsize);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 if (IS_ERR((void *)mem_buffer)) {
160 pr_err("%s:map_buffer failed, error = %ld\n",
161 __func__, PTR_ERR((void *)mem_buffer));
162 return -ENOMEM;
163 }
Laura Abbottea3e7b62012-04-30 15:59:21 -0700164 ptr = mem_buffer;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 if (!ptr) {
166 pr_err("[%s:%s] map error @ %x\n", __MM_FILE__,
167 __func__, addr);
168 return -EFAULT;
169 }
170 if (copy_to_user(buf, ptr, PAGE_SIZE)) {
Laura Abbottea3e7b62012-04-30 15:59:21 -0700171 iounmap(mem_buffer);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__,
173 __func__, buf);
174 return -EFAULT;
175 }
176 copy_ok_count += PAGE_SIZE;
Laura Abbottea3e7b62012-04-30 15:59:21 -0700177 iounmap(mem_buffer);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178 addr += PAGE_SIZE;
179 buf += PAGE_SIZE;
180 actual += PAGE_SIZE;
181 count -= PAGE_SIZE;
182 }
183
184 *pos += actual;
185 return actual;
186}
187
188static int dsp_release(struct inode *inode, struct file *file)
189{
190 return 0;
191}
192
193int dsp_debug_register(dsp_state_cb ptr)
194{
195 if (ptr == NULL)
196 return -EINVAL;
197 cb_ptr = ptr;
198
199 return 0;
200}
201
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700202static int dspcrashd_probe(struct platform_device *pdev)
203{
204 int rc = 0;
205 struct resource *res;
206 int *pdata;
207
208 pdata = pdev->dev.platform_data;
209 res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
210 "msm_dspcrashd");
211 if (!res) {
212 pr_err("%s: failed to get resources for dspcrashd\n", __func__);
213 return -ENODEV;
214 }
215
216 dsp_ram_base = res->start;
217 dsp_ram_size = res->end - res->start;
218 pr_info("%s: Platform driver values: Base = 0x%x, Size = 0x%x,"
219 "pdata = 0x%x\n", __func__,
220 dsp_ram_base, dsp_ram_size, *pdata);
221 return rc;
222}
223
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224static const struct file_operations dsp_fops = {
225 .owner = THIS_MODULE,
226 .open = dsp_open,
227 .read = dsp_read,
228 .write = dsp_write,
229 .release = dsp_release,
230};
231
232static struct miscdevice dsp_misc = {
233 .minor = MISC_DYNAMIC_MINOR,
234 .name = "dsp_debug",
235 .fops = &dsp_fops,
236};
237
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700238static struct platform_driver dspcrashd_driver = {
239 .probe = dspcrashd_probe,
240 .driver = { .name = "msm_dspcrashd"}
241};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242
243static int __init dsp_init(void)
244{
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700245 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246 init_waitqueue_head(&dsp_wait);
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700247 rc = platform_driver_register(&dspcrashd_driver);
248 if (IS_ERR_VALUE(rc)) {
249 pr_err("%s: platform_driver_register for dspcrashd failed\n",
250 __func__);
251 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 return misc_register(&dsp_misc);
253}
254
Swaminathan Sathappanb77c65e92011-09-30 18:36:09 -0700255static int __exit dsp_exit(void)
256{
257 platform_driver_unregister(&dspcrashd_driver);
258 return 0;
259}
260
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261device_initcall(dsp_init);