2 * linux/arch/arm/mach-omap/dsp/dsp_core.c
6 * Copyright (C) 2002-2005 Nokia Corporation
8 * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * 2005/06/07: DSP Gateway version 3.3
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/slab.h>
30 #include <linux/device.h>
31 #include <linux/devfs_fs_kernel.h>
32 #include <linux/sched.h>
33 #include <linux/interrupt.h>
34 #include <linux/proc_fs.h>
35 #include <asm/uaccess.h>
37 #include <asm/signal.h>
38 #include <asm/delay.h>
40 #include <asm/arch/dsp.h>
41 #include "hardware_dsp.h"
46 MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
47 MODULE_DESCRIPTION("OMAP DSP driver module");
48 MODULE_LICENSE("GPL");
50 enum mbseq_check_level {
51 MBSEQ_CHECK_NONE, /* no check */
52 MBSEQ_CHECK_VERBOSE, /* discard the illegal command and
54 MBSEQ_CHECK_SILENT, /* discard the illegal command */
57 static enum mbseq_check_level mbseq_check_level = MBSEQ_CHECK_VERBOSE;
59 static int mbx1_valid;
60 static struct sync_seq *mbseq;
61 static unsigned short mbseq_expect_tmp;
62 static unsigned short *mbseq_expect = &mbseq_expect_tmp;
67 extern void mbx1_wdsnd(struct mbcmd *mb);
68 extern void mbx1_wdreq(struct mbcmd *mb);
69 extern void mbx1_bksnd(struct mbcmd *mb);
70 extern void mbx1_bkreq(struct mbcmd *mb);
71 extern void mbx1_bkyld(struct mbcmd *mb);
72 extern void mbx1_bksndp(struct mbcmd *mb);
73 extern void mbx1_bkreqp(struct mbcmd *mb);
74 extern void mbx1_tctl(struct mbcmd *mb);
75 extern void mbx1_poll(struct mbcmd *mb);
76 #ifdef OLD_BINARY_SUPPORT
78 extern void mbx1_wdt(struct mbcmd *mb);
80 extern void mbx1_suspend(struct mbcmd *mb);
81 static void mbx1_kfunc(struct mbcmd *mb);
82 extern void mbx1_tcfg(struct mbcmd *mb);
83 extern void mbx1_tadd(struct mbcmd *mb);
84 extern void mbx1_tdel(struct mbcmd *mb);
85 extern void mbx1_dspcfg(struct mbcmd *mb);
86 extern void mbx1_regrw(struct mbcmd *mb);
87 extern void mbx1_getvar(struct mbcmd *mb);
88 extern void mbx1_err(struct mbcmd *mb);
89 extern void mbx1_dbg(struct mbcmd *mb);
91 static const struct cmdinfo
92 cif_null = { "Unknown", CMD_L_TYPE_NULL, NULL },
93 cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbx1_wdsnd },
94 cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbx1_wdreq },
95 cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbx1_bksnd },
96 cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbx1_bkreq },
97 cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbx1_bkyld },
98 cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbx1_bksndp },
99 cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbx1_bkreqp },
100 cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbx1_tctl },
101 cif_poll = { "POLL", CMD_L_TYPE_NULL, mbx1_poll },
102 #ifdef OLD_BINARY_SUPPORT
104 cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbx1_wdt },
106 cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL },
107 cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL },
108 cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbx1_suspend },
109 cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbx1_kfunc },
110 cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbx1_tcfg },
111 cif_tadd = { "TADD", CMD_L_TYPE_TID, mbx1_tadd },
112 cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbx1_tdel },
113 cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL },
114 cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbx1_dspcfg },
115 cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbx1_regrw },
116 cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbx1_getvar },
117 cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL },
118 cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbx1_err },
119 cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbx1_dbg };
121 const struct cmdinfo *cmdinfo[128] = {
122 /*00*/ &cif_null, &cif_null, &cif_null, &cif_null,
123 &cif_null, &cif_null, &cif_null, &cif_null,
124 &cif_null, &cif_null, &cif_null, &cif_null,
125 &cif_null, &cif_null, &cif_null, &cif_null,
126 /*10*/ &cif_wdsnd, &cif_wdreq, &cif_null, &cif_null,
127 &cif_null, &cif_null, &cif_null, &cif_null,
128 &cif_null, &cif_null, &cif_null, &cif_null,
129 &cif_null, &cif_null, &cif_null, &cif_null,
130 /*20*/ &cif_bksnd, &cif_bkreq, &cif_null, &cif_bkyld,
131 &cif_bksndp, &cif_bkreqp, &cif_null, &cif_null,
132 &cif_null, &cif_null, &cif_null, &cif_null,
133 &cif_null, &cif_null, &cif_null, &cif_null,
134 /*30*/ &cif_tctl, &cif_null, &cif_poll, &cif_null,
135 &cif_null, &cif_null, &cif_null, &cif_null,
136 &cif_null, &cif_null, &cif_null, &cif_null,
137 &cif_null, &cif_null, &cif_null, &cif_null,
138 /*40*/ &cif_null, &cif_null, &cif_null, &cif_null,
139 &cif_null, &cif_null, &cif_null, &cif_null,
140 &cif_null, &cif_null, &cif_null, &cif_null,
141 &cif_null, &cif_null, &cif_null, &cif_null,
142 #ifdef OLD_BINARY_SUPPORT
144 /*50*/ &cif_wdt, &cif_runlevel, &cif_pm, &cif_suspend,
146 /*50*/ &cif_null, &cif_runlevel, &cif_pm, &cif_suspend,
148 &cif_kfunc, &cif_null, &cif_null, &cif_null,
149 &cif_null, &cif_null, &cif_null, &cif_null,
150 &cif_null, &cif_null, &cif_null, &cif_null,
151 /*60*/ &cif_tcfg, &cif_null, &cif_tadd, &cif_tdel,
152 &cif_null, &cif_tstop, &cif_null, &cif_null,
153 &cif_null, &cif_null, &cif_null, &cif_null,
154 &cif_null, &cif_null, &cif_null, &cif_null,
155 /*70*/ &cif_dspcfg, &cif_null, &cif_regrw, &cif_null,
156 &cif_getvar, &cif_setvar, &cif_null, &cif_null,
157 &cif_err, &cif_dbg, &cif_null, &cif_null,
158 &cif_null, &cif_null, &cif_null, &cif_null
161 int sync_with_dsp(unsigned short *syncwd, unsigned short tid, int try_cnt)
165 if (*(volatile unsigned short *)syncwd == tid)
168 for (try = 0; try < try_cnt; try++) {
170 if (*(volatile unsigned short *)syncwd == tid) {
173 "omapdsp: sync_with_dsp(): try = %d\n", try);
182 static __inline__ int mbsync_irq_save(unsigned long *flags, int try_cnt)
186 local_irq_save(*flags);
187 if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0)
190 * mailbox is busy. wait for some usecs...
192 local_irq_restore(*flags);
193 for (cnt = 0; cnt < try_cnt; cnt++) {
195 local_irq_save(*flags);
196 if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0) /* success! */
198 local_irq_restore(*flags);
205 #ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
206 #define print_mb_busy_abort(mb) \
208 "mbx: mailbox is busy. %s is aborting.\n", cmd_name(*mb))
209 #define print_mb_mmu_abort(mb) \
211 "mbx: mmu interrupt is set. %s is aborting.\n", cmd_name(*mb))
212 #else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */
213 #define print_mb_busy_abort(mb) do {} while(0)
214 #define print_mb_mmu_abort(mb) do {} while(0)
215 #endif /* !CONFIG_OMAP_DSP_MBCMD_VERBOSE */
217 int __mbcmd_send(struct mbcmd *mb)
219 struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb;
223 * DSP mailbox interrupt latency must be less than 1ms.
225 if (mbsync_irq_save(&flags, 1000) < 0) {
226 print_mb_busy_abort(mb);
231 mb->seq = mbseq->ad_arm;
235 mblog_add(mb, DIR_A2D);
236 mblog_printcmd(mb, DIR_A2D);
238 omap_writew(mb_hw->data, MAILBOX_ARM2DSP1);
239 omap_writew(mb_hw->cmd, MAILBOX_ARM2DSP1b);
241 local_irq_restore(flags);
246 * __dsp_mbcmd_send(): mailbox dispatcher
248 int __dsp_mbcmd_send(struct mbcmd *mb, struct mb_exarg *arg, int recovery_flag)
250 static DECLARE_MUTEX(mbsend_sem);
254 * while MMU fault is set,
255 * only recovery command can be executed
257 if (dsp_err_mmu_isset() && !recovery_flag) {
258 print_mb_mmu_abort(mb);
262 if (down_interruptible(&mbsend_sem) < 0)
265 if (arg) { /* we have extra argument */
269 * even if ipbuf_sys_ad is in DSP internal memory,
270 * dsp_mem_enable() never cause to call PM mailbox command
271 * because in that case DSP memory should be always enabled.
272 * (see ipbuf_sys_hold_mem_active in ipbuf.c)
274 * Therefore, we can call this function here safely.
276 dsp_mem_enable(ipbuf_sys_ad);
277 if (sync_with_dsp(&ipbuf_sys_ad->s, OMAP_DSP_TID_FREE, 10) < 0) {
278 printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
279 dsp_mem_disable(ipbuf_sys_ad);
283 for (i = 0; i < arg->argc; i++) {
284 ipbuf_sys_ad->d[i] = arg->argv[i];
286 ipbuf_sys_ad->s = arg->tid;
287 dsp_mem_disable(ipbuf_sys_ad);
290 ret = __mbcmd_send(mb);
297 int __dsp_mbcmd_send_and_wait(struct mbcmd *mb, struct mb_exarg *arg,
298 wait_queue_head_t *q)
301 DECLARE_WAITQUEUE(wait, current);
303 add_wait_queue(q, &wait);
304 current_state = current->state;
305 set_current_state(TASK_INTERRUPTIBLE);
306 if (dsp_mbcmd_send_exarg(mb, arg) < 0) {
307 set_current_state(current_state);
308 remove_wait_queue(q, &wait);
311 schedule_timeout(DSP_TIMEOUT);
312 set_current_state(current_state);
313 remove_wait_queue(q, &wait);
318 int __dsp_mbsend(unsigned char cmdh, unsigned char cmdl, unsigned short data,
323 mbcmd_set(mb, cmdh, cmdl, data);
324 return __dsp_mbcmd_send(&mb, NULL, recovery_flag);
327 static int mbsync_hold_mem_active;
329 void dsp_mb_start(void)
331 mbx1_valid = 1; /* start interpreting */
332 mbseq_expect_tmp = 0;
335 void dsp_mb_stop(void)
337 mbx1_valid = 0; /* stop interpreting */
338 if (mbsync_hold_mem_active) {
339 dsp_mem_disable((void *)daram_base);
340 mbsync_hold_mem_active = 0;
343 mbseq_expect = &mbseq_expect_tmp;
346 int dsp_mb_config(void *p)
350 if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
352 if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
354 "omapdsp: mbseq is placed in DSP internal memory.\n"
355 " It will prevent DSP from idling.\n");
356 mbsync_hold_mem_active = 1;
358 * dsp_mem_enable() never fails because
359 * it has been already enabled in dspcfg process and
360 * this will just increment the usecount.
362 dsp_mem_enable((void *)daram_base);
365 local_irq_save(flags);
367 mbseq->da_arm = mbseq_expect_tmp;
368 mbseq_expect = &mbseq->da_arm;
369 local_irq_restore(flags);
379 struct mbcmd mb[MBQ_DEPTH];
386 #define mbq_inc(p) do { if (++(p) == MBQ_DEPTH) (p) = 0; } while(0)
391 static void do_mbx1(void)
395 disable_irq(INT_D2A_MB1);
396 if ((mbq.rp == mbq.wp) && !mbq.full)
398 enable_irq(INT_D2A_MB1);
403 mb = &mbq.mb[mbq.rp];
405 mblog_add(mb, DIR_D2A);
406 mblog_printcmd(mb, DIR_D2A);
409 * call handler for each command
411 if (cmdinfo[mb->cmd_h]->handler)
412 cmdinfo[mb->cmd_h]->handler(mb);
413 else if (cmdinfo[mb->cmd_h] != &cif_null)
414 printk(KERN_ERR "mbx: %s is not allowed from DSP.\n",
418 "mbx: Unrecognized command: "
419 "cmd=0x%04x, data=0x%04x\n",
420 ((struct mbcmd_hw *)mb)->cmd & 0x7fff, mb->data);
422 disable_irq(INT_D2A_MB1);
424 if (mbq.rp == mbq.wp)
426 /* if mbq has been full, now we have a room. */
429 enable_irq(INT_D2A_MB1);
431 enable_irq(INT_D2A_MB1);
435 static DECLARE_WORK(mbx1_work, (void (*)(void *))do_mbx1, NULL);
438 * kernel function dispatcher
440 extern void mbx1_fbctl_disable(void);
442 static void mbx1_kfunc_fbctl(unsigned short data)
445 case OMAP_DSP_MBCMD_FBCTL_DISABLE:
446 mbx1_fbctl_disable();
450 "mailbox: Unknown FBCTL from DSP: 0x%04x\n", data);
454 static void mbx1_kfunc(struct mbcmd *mb)
457 case OMAP_DSP_MBCMD_KFUNC_FBCTL:
458 mbx1_kfunc_fbctl(mb->data);
463 "mailbox: Unknown kfunc from DSP: 0x%02x\n", mb->cmd_l);
468 * mailbox interrupt handler
470 static irqreturn_t mbx1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
475 } *mb = (void *)&mbq.mb[mbq.wp];
477 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
478 mb->hw.data = omap_readw(MAILBOX_DSP2ARM1);
479 mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM1b);
480 #elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
481 mb->hw.data = omap_readw(MAILBOX_DSP2ARM2);
482 mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM2b);
485 /* if mbx1 has not been validated yet, discard. */
489 if (mb->sw.seq != (*mbseq_expect & 1)) {
490 switch (mbseq_check_level) {
491 case MBSEQ_CHECK_NONE:
493 case MBSEQ_CHECK_VERBOSE:
495 "mbx: illegal seq bit!!! ignoring this command."
496 " (%04x:%04x)\n", mb->hw.cmd, mb->hw.data);
498 case MBSEQ_CHECK_SILENT:
506 if (mbq.wp == mbq.rp) { /* mbq is full */
508 disable_irq(INT_D2A_MB1);
510 schedule_work(&mbx1_work);
515 static irqreturn_t mbx2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
517 unsigned short cmd, data;
519 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
520 data = omap_readw(MAILBOX_DSP2ARM2);
521 cmd = omap_readw(MAILBOX_DSP2ARM2b);
522 #elif (INT_D2A_MB1 == INT_DSP_MAILBOX2)
523 data = omap_readw(MAILBOX_DSP2ARM1);
524 cmd = omap_readw(MAILBOX_DSP2ARM1b);
527 "mailbox2 interrupt! cmd=%04x, data=%04x\n", cmd, data);
533 static void mpuio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
535 printk(KERN_INFO "MPUIO interrupt!\n");
539 #ifdef CONFIG_PROC_FS
540 struct proc_dir_entry *procdir_dsp = NULL;
542 static void dsp_create_procdir_dsp(void)
544 procdir_dsp = proc_mkdir("dsp", 0);
545 if (procdir_dsp == NULL) {
547 "omapdsp: failed to register proc directory: dsp\n");
551 static void dsp_remove_procdir_dsp(void)
554 remove_proc_entry("dsp", 0);
556 #else /* CONFIG_PROC_FS */
557 #define dsp_create_procdir_dsp() do { } while (0)
558 #define dsp_remove_procdir_dsp() do { } while (0)
559 #endif /* CONFIG_PROC_FS */
561 extern irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id,
562 struct pt_regs *regs);
564 extern int dsp_ctl_core_init(void);
565 extern void dsp_ctl_core_exit(void);
566 extern void dsp_ctl_init(void);
567 extern void dsp_ctl_exit(void);
568 extern int dsp_mem_init(void);
569 extern void dsp_mem_exit(void);
570 extern void mblog_init(void);
571 extern void mblog_exit(void);
572 extern int dsp_taskmod_init(void);
573 extern void dsp_taskmod_exit(void);
578 static void dsp_dev_release(struct device *dev)
585 #if (INT_D2A_MB1 == INT_DSP_MAILBOX1)
586 # define INT_D2A_MB2 INT_DSP_MAILBOX2
587 #elif(INT_D2A_MB1 == INT_DSP_MAILBOX2) /* swap MB1 and MB2 */
588 # define INT_D2A_MB2 INT_DSP_MAILBOX1
591 static int __init dsp_drv_probe(struct device *dev)
595 printk(KERN_INFO "OMAP DSP driver initialization\n");
597 //__dsp_enable(); // XXX
599 dsp_create_procdir_dsp();
601 if ((ret = dsp_ctl_core_init()) < 0)
603 if ((ret = dsp_mem_init()) < 0)
607 if ((ret = dsp_taskmod_init()) < 0)
611 * mailbox interrupt handlers registration
613 ret = request_irq(INT_D2A_MB1, mbx1_interrupt, SA_INTERRUPT, "dsp",
617 "failed to register mailbox1 interrupt: %d\n", ret);
621 ret = request_irq(INT_D2A_MB2, mbx2_interrupt, SA_INTERRUPT, "dsp",
625 "failed to register mailbox2 interrupt: %d\n", ret);
629 ret = request_irq(INT_DSP_MMU, dsp_mmu_interrupt, SA_INTERRUPT, "dsp",
633 "failed to register DSP MMU interrupt: %d\n", ret);
637 /* MMU interrupt is not enabled until DSP runs */
638 disable_irq(INT_DSP_MMU);
641 ret = request_irq(INT_MPUIO, mpuio_interrupt, SA_INTERRUPT, "dsp", dev);
644 "failed to register MPUIO interrupt: %d\n", ret);
652 free_irq(INT_D2A_MB2, dev);
654 free_irq(INT_D2A_MB1, dev);
664 dsp_remove_procdir_dsp();
666 //__dsp_disable(); // XXX
670 static int dsp_drv_remove(struct device *dev)
672 dsp_cpustat_request(CPUSTAT_RESET);
675 free_irq(INT_MPUIO, dev);
677 free_irq(INT_DSP_MMU, dev);
678 free_irq(INT_D2A_MB2, dev);
679 free_irq(INT_D2A_MB1, dev);
681 /* recover disable_depth */
682 enable_irq(INT_DSP_MMU);
691 dsp_remove_procdir_dsp();
693 //__dsp_disable(); // XXX
699 static int dsp_drv_suspend(struct device *dev, u32 state, u32 level)
703 case SUSPEND_DISABLE:
704 case SUSPEND_SAVE_STATE:
706 case SUSPEND_POWER_DOWN:
714 static int dsp_drv_resume(struct device *dev, u32 level)
717 case RESUME_POWER_ON:
720 case RESUME_RESTORE_STATE:
727 #endif /* CONFIG_PM */
729 static struct resource dsp_resources[] = {
731 .start = INT_DSP_MAILBOX1,
732 .flags = IORESOURCE_IRQ,
735 .start = INT_DSP_MAILBOX2,
736 .flags = IORESOURCE_IRQ,
739 .start = INT_DSP_MMU,
740 .flags = IORESOURCE_IRQ,
744 struct platform_device dsp_device = {
748 .release = dsp_dev_release,
750 .num_resources = ARRAY_SIZE(&dsp_resources),
751 .resource = dsp_resources,
754 static struct device_driver dsp_driver = {
756 .bus = &platform_bus_type,
757 .probe = dsp_drv_probe,
758 .remove = dsp_drv_remove,
760 .suspend = dsp_drv_suspend,
761 .resume = dsp_drv_resume,
765 static int __init omap_dsp_mod_init(void)
769 ret = platform_device_register(&dsp_device);
771 printk(KERN_ERR "failed to register the DSP device: %d\n", ret);
775 ret = driver_register(&dsp_driver);
777 printk(KERN_ERR "failed to register the DSP driver: %d\n", ret);
784 platform_device_unregister(&dsp_device);
789 static void __exit omap_dsp_mod_exit(void)
791 driver_unregister(&dsp_driver);
792 platform_device_unregister(&dsp_device);
795 module_init(omap_dsp_mod_init);
796 module_exit(omap_dsp_mod_exit);