]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/dsp/dsp_common.c
378d564fbaa5ecedeb5212d9662551be2e1b35d2
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / dsp / dsp_common.c
1 /*
2  * linux/arch/arm/mach-omap/dsp/dsp_common.c
3  *
4  * OMAP DSP driver static part
5  *
6  * Copyright (C) 2002-2005 Nokia Corporation
7  *
8  * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
9  *
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.
14  *
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.
19  *
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
23  *
24  * 2005/06/13:  DSP Gateway version 3.3
25  */
26
27 #include <linux/module.h>
28 #include <linux/errno.h>
29 #include <linux/init.h>
30 #include <linux/sched.h>
31 #include <linux/delay.h>
32 #include <linux/mm.h>
33 #include <linux/clk.h>
34 #include <linux/mutex.h>
35 #include <linux/interrupt.h>
36 #include <asm/io.h>
37 #include <asm/tlbflush.h>
38 #include <asm/irq.h>
39 #include <asm/arch/dsp.h>
40 #include <asm/arch/tc.h>
41 #include "dsp_common.h"
42
43 struct clk *dsp_ck_handle;
44 struct clk *api_ck_handle;
45 unsigned long dspmem_base, dspmem_size,
46               daram_base, daram_size,
47               saram_base, saram_size;
48
49 struct cpustat {
50         struct mutex lock;
51         enum e_cpustat stat;
52         enum e_cpustat req;
53         unsigned short icrmask;
54         struct {
55                 int mpui;
56                 int mem;
57                 int mem_delayed;
58         } usecount;
59         int (*mem_req_cb)(void);
60         void (*mem_rel_cb)(void);
61 };
62 struct cpustat cpustat = {
63         .stat = CPUSTAT_RESET,
64         .icrmask = 0xffff,
65 };
66
67 int dsp_set_rstvect(unsigned long adr)
68 {
69         unsigned long *dst_adr;
70
71         if (adr >= DSPSPACE_SIZE)
72                 return -EINVAL;
73
74         dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT);
75         /* word swap */
76         *dst_adr = ((adr & 0xffff) << 16) | (adr >> 16);
77         /* fill 8 bytes! */
78         *(dst_adr+1) = 0;
79         /* direct boot */
80         omap_writew(MPUI_DSP_BOOT_CONFIG_DIRECT, MPUI_DSP_BOOT_CONFIG);
81
82         return 0;
83 }
84
85 static void simple_load_code(unsigned char *src_c, unsigned short *dst, int len)
86 {
87         int i;
88         unsigned short *src = (unsigned short *)src_c;
89         int len_w;
90
91         /* len must be multiple of 2. */
92         if (len & 1)
93                 BUG();
94
95         len_w = len / 2;
96         for (i = 0; i < len_w; i++) {
97                 /* byte swap copy */
98                 *dst = ((*src & 0x00ff) << 8) |
99                        ((*src & 0xff00) >> 8);
100                 src++;
101                 dst++;
102         }
103 }
104
105 /* program size must be multiple of 2 */
106 #define GBL_IDLE_TEXT_SIZE      52
107 #define GBL_IDLE_TEXT_INIT { \
108         /* SAM */ \
109         0x3c, 0x4a,                     /* 0x3c4a:     MOV 0x4, AR2 */ \
110         0xf4, 0x41, 0xfc, 0xff,         /* 0xf441fcff: AND 0xfcff, *AR2 */ \
111         /* disable WDT */ \
112         0x76, 0x34, 0x04, 0xb8,         /* 0x763404b8: MOV 0x3404, AR3 */ \
113         0xfb, 0x61, 0x00, 0xf5,         /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \
114         0x9a,                           /* 0x9a:       PORT */ \
115         0xfb, 0x61, 0x00, 0xa0,         /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \
116         0x9a,                           /* 0x9a:       PORT */ \
117         /* *IER0 = 0, *IER1 = 0 */ \
118         0x3c, 0x0b,                     /* 0x3c0b:     MOV 0x0, AR3 */ \
119         0xe6, 0x61, 0x00,               /* 0xe66100:   MOV 0, *AR3 */ \
120         0x76, 0x00, 0x45, 0xb8,         /* 0x76004508: MOV 0x45, AR3 */ \
121         0xe6, 0x61, 0x00,               /* 0xe66100:   MOV 0, *AR3 */ \
122         /* *ICR = 0xffff */ \
123         0x3c, 0x1b,                     /* 0x3c1b:     MOV 0x1, AR3 */ \
124         0xfb, 0x61, 0xff, 0xff,         /* 0xfb61ffff: MOV 0xffff, *AR3 */ \
125         0x9a,                           /* 0x9a:       PORT */ \
126         /* HOM */ \
127         0xf5, 0x41, 0x03, 0x00,         /* 0xf5410300: OR 0x0300, *AR2 */ \
128         /* idle and loop forever */ \
129         0x7a, 0x00, 0x00, 0x0c,         /* 0x7a00000c: IDLE */ \
130         0x4a, 0x7a,                     /* 0x4a7a:     B -6 (infinite loop) */ \
131         0x20, 0x20, 0x20,               /* 0x20:       NOP */ \
132 }
133
134 /* program size must be multiple of 2 */
135 #define CPU_IDLE_TEXT_SIZE      48
136 #define CPU_IDLE_TEXT_INIT(icrh, icrl) { \
137         /* SAM */ \
138         0x3c, 0x4b,                     /* 0x3c4b:     MOV 0x4, AR3 */ \
139         0xf4, 0x61, 0xfc, 0xff,         /* 0xf461fcff: AND 0xfcff, *AR3 */ \
140         /* disable WDT */ \
141         0x76, 0x34, 0x04, 0xb8,         /* 0x763404b8: MOV 0x3404, AR3 */ \
142         0xfb, 0x61, 0x00, 0xf5,         /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \
143         0x9a,                           /* 0x9a:       PORT */ \
144         0xfb, 0x61, 0x00, 0xa0,         /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \
145         0x9a,                           /* 0x9a:       PORT */ \
146         /* *IER0 = 0, *IER1 = 0 */ \
147         0x3c, 0x0b,                     /* 0x3c0b:     MOV 0x0, AR3 */ \
148         0xe6, 0x61, 0x00,               /* 0xe66100:   MOV 0, *AR3 */ \
149         0x76, 0x00, 0x45, 0xb8,         /* 0x76004508: MOV 0x45, AR3 */ \
150         0xe6, 0x61, 0x00,               /* 0xe66100:   MOV 0, *AR3 */ \
151         /* set ICR = icr */ \
152         0x3c, 0x1b,                     /* 0x3c1b:     MOV AR3 0x1 */ \
153         0xfb, 0x61, (icrh), (icrl),     /* 0xfb61****: MOV *AR3, icr */ \
154         0x9a,                           /* 0x9a:       PORT */ \
155         /* idle and loop forever */ \
156         0x7a, 0x00, 0x00, 0x0c,         /* 0x7a00000c: IDLE */ \
157         0x4a, 0x7a,                     /* 0x4a7a:     B -6 (infinite loop) */ \
158         0x20, 0x20, 0x20                /* 0x20: nop */ \
159 }
160
161 /*
162  * idle_boot base:
163  * Initialized with DSP_BOOT_ADR_MPUI (=0x010000).
164  * This value is used before DSP Gateway driver is initialized.
165  * DSP Gateway driver will overwrite this value with other value,
166  * to avoid confliction with the user program.
167  */
168 static unsigned long idle_boot_base = DSP_BOOT_ADR_MPUI;
169
170 static void dsp_gbl_idle(void)
171 {
172         unsigned char idle_text[GBL_IDLE_TEXT_SIZE] = GBL_IDLE_TEXT_INIT;
173
174         __dsp_reset();
175         clk_enable(api_ck_handle);
176
177 #if 0
178         omap_writew(MPUI_DSP_BOOT_CONFIG_IDLE, MPUI_DSP_BOOT_CONFIG);
179 #endif
180         simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
181                          GBL_IDLE_TEXT_SIZE);
182         if (idle_boot_base == DSP_BOOT_ADR_MPUI)
183                 omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG);
184         else
185                 dsp_set_rstvect(idle_boot_base);
186
187         __dsp_run();
188         udelay(100);    /* to make things stable */
189         clk_disable(api_ck_handle);
190 }
191
192 static void dsp_cpu_idle(void)
193 {
194         unsigned short icr_tmp;
195         unsigned char icrh, icrl;
196
197         __dsp_reset();
198         clk_enable(api_ck_handle);
199
200         /*
201          * icr settings:
202          * DMA should not sleep for DARAM/SARAM access
203          * DPLL should not sleep while any other domain is active
204          */
205         icr_tmp = cpustat.icrmask & ~(DSPREG_ICR_DMA_IDLE_DOMAIN |
206                                       DSPREG_ICR_DPLL_IDLE_DOMAIN);
207         icrh = icr_tmp >> 8;
208         icrl = icr_tmp & 0xff;
209         {
210                 unsigned char idle_text[CPU_IDLE_TEXT_SIZE] = CPU_IDLE_TEXT_INIT(icrh, icrl);
211                 simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
212                                  CPU_IDLE_TEXT_SIZE);
213         }
214         if (idle_boot_base == DSP_BOOT_ADR_MPUI)
215                 omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG);
216         else
217                 dsp_set_rstvect(idle_boot_base);
218         __dsp_run();
219         udelay(100);    /* to make things stable */
220         clk_disable(api_ck_handle);
221 }
222
223 void dsp_set_idle_boot_base(unsigned long adr, size_t size)
224 {
225         if (adr == idle_boot_base)
226                 return;
227         idle_boot_base = adr;
228         if ((size < GBL_IDLE_TEXT_SIZE) ||
229             (size < CPU_IDLE_TEXT_SIZE)) {
230                 printk(KERN_ERR
231                        "omapdsp: size for idle program is not enough!\n");
232                 BUG();
233         }
234
235         /* restart idle program with new base address */
236         if (cpustat.stat == CPUSTAT_GBL_IDLE)
237                 dsp_gbl_idle();
238         if (cpustat.stat == CPUSTAT_CPU_IDLE)
239                 dsp_cpu_idle();
240 }
241
242 static int init_done;
243
244 static int __init omap_dsp_init(void)
245 {
246         mutex_init(&cpustat.lock);
247
248         dspmem_size = 0;
249 #ifdef CONFIG_ARCH_OMAP15XX
250         if (cpu_is_omap1510()) {
251                 dspmem_base = OMAP1510_DSP_BASE;
252                 dspmem_size = OMAP1510_DSP_SIZE;
253                 daram_base = OMAP1510_DARAM_BASE;
254                 daram_size = OMAP1510_DARAM_SIZE;
255                 saram_base = OMAP1510_SARAM_BASE;
256                 saram_size = OMAP1510_SARAM_SIZE;
257         }
258 #endif
259 #ifdef CONFIG_ARCH_OMAP16XX
260         if (cpu_is_omap16xx()) {
261                 dspmem_base = OMAP16XX_DSP_BASE;
262                 dspmem_size = OMAP16XX_DSP_SIZE;
263                 daram_base = OMAP16XX_DARAM_BASE;
264                 daram_size = OMAP16XX_DARAM_SIZE;
265                 saram_base = OMAP16XX_SARAM_BASE;
266                 saram_size = OMAP16XX_SARAM_SIZE;
267         }
268 #endif
269         if (dspmem_size == 0) {
270                 printk(KERN_ERR "omapdsp: unsupported omap architecture.\n");
271                 return -ENODEV;
272         }
273
274         dsp_ck_handle = clk_get(0, "dsp_ck");
275         if (IS_ERR(dsp_ck_handle)) {
276                 printk(KERN_ERR "omapdsp: could not acquire dsp_ck handle.\n");
277                 return PTR_ERR(dsp_ck_handle);
278         }
279
280         api_ck_handle = clk_get(0, "api_ck");
281         if (IS_ERR(api_ck_handle)) {
282                 printk(KERN_ERR "omapdsp: could not acquire api_ck handle.\n");
283                 return PTR_ERR(api_ck_handle);
284         }
285
286         /* This is needed for McBSP init, released in late_initcall */
287         clk_enable(api_ck_handle);
288
289         __dsp_enable();
290         mpui_byteswap_off();
291         mpui_wordswap_on();
292         tc_wordswap();
293
294         init_done = 1;
295         printk(KERN_INFO "omap_dsp_init() done\n");
296         return 0;
297 }
298
299 static int dsp_late_init(void)
300 {
301         clk_disable(api_ck_handle);
302         return 0;
303 }
304 late_initcall(dsp_late_init);
305
306 static void dsp_cpustat_update(void)
307 {
308         if (!init_done)
309                 omap_dsp_init();
310
311         if (cpustat.req == CPUSTAT_RUN) {
312                 if (cpustat.stat < CPUSTAT_RUN) {
313                         __dsp_reset();
314                         clk_enable(api_ck_handle);
315                         udelay(10);
316                         __dsp_run();
317                         cpustat.stat = CPUSTAT_RUN;
318                         enable_irq(INT_DSP_MMU);
319                 }
320                 return;
321         }
322
323         /* cpustat.stat < CPUSTAT_RUN */
324
325         if (cpustat.stat == CPUSTAT_RUN) {
326                 disable_irq(INT_DSP_MMU);
327                 clk_disable(api_ck_handle);
328         }
329
330         /*
331          * (1) when ARM wants DARAM access, MPUI should be SAM and
332          *     DSP needs to be on.
333          * (2) if any bits of icr is masked, we can not enter global idle.
334          */
335         if ((cpustat.req == CPUSTAT_CPU_IDLE) ||
336             (cpustat.usecount.mem > 0) ||
337             (cpustat.usecount.mem_delayed > 0) ||
338             ((cpustat.usecount.mpui > 0) && (cpustat.icrmask != 0xffff))) {
339                 if (cpustat.stat != CPUSTAT_CPU_IDLE) {
340                         dsp_cpu_idle();
341                         cpustat.stat = CPUSTAT_CPU_IDLE;
342                 }
343                 return;
344         }
345
346         /*
347          * when ARM only needs MPUI access, MPUI can be HOM and
348          * DSP can be idling.
349          */
350         if ((cpustat.req == CPUSTAT_GBL_IDLE) ||
351             (cpustat.usecount.mpui > 0)) {
352                 if (cpustat.stat != CPUSTAT_GBL_IDLE) {
353                         dsp_gbl_idle();
354                         cpustat.stat = CPUSTAT_GBL_IDLE;
355                 }
356                 return;
357         }
358
359         /*
360          * no user, no request
361          */
362         if (cpustat.stat != CPUSTAT_RESET) {
363                 __dsp_reset();
364                 cpustat.stat = CPUSTAT_RESET;
365         }
366 }
367
368 void dsp_cpustat_request(enum e_cpustat req)
369 {
370         mutex_lock(&cpustat.lock);
371         cpustat.req = req;
372         dsp_cpustat_update();
373         mutex_unlock(&cpustat.lock);
374 }
375
376 enum e_cpustat dsp_cpustat_get_stat(void)
377 {
378         return cpustat.stat;
379 }
380
381 unsigned short dsp_cpustat_get_icrmask(void)
382 {
383         return cpustat.icrmask;
384 }
385
386 void dsp_cpustat_set_icrmask(unsigned short mask)
387 {
388         mutex_lock(&cpustat.lock);
389         cpustat.icrmask = mask;
390         dsp_cpustat_update();
391         mutex_unlock(&cpustat.lock);
392 }
393
394 void omap_dsp_request_mpui(void)
395 {
396         mutex_lock(&cpustat.lock);
397         if (cpustat.usecount.mpui++ == 0)
398                 dsp_cpustat_update();
399         mutex_unlock(&cpustat.lock);
400 }
401
402 void omap_dsp_release_mpui(void)
403 {
404         mutex_lock(&cpustat.lock);
405         if (cpustat.usecount.mpui-- == 0) {
406                 printk(KERN_ERR
407                        "omapdsp: unbalanced mpui request/release detected.\n"
408                        "         cpustat.usecount.mpui is going to be "
409                        "less than zero! ... fixed to be zero.\n");
410                 cpustat.usecount.mpui = 0;
411         }
412         if (cpustat.usecount.mpui == 0)
413                 dsp_cpustat_update();
414         mutex_unlock(&cpustat.lock);
415 }
416
417 int omap_dsp_request_mem(void)
418 {
419         int ret = 0;
420
421         mutex_lock(&cpustat.lock);
422         if ((cpustat.usecount.mem++ == 0) &&
423             (cpustat.usecount.mem_delayed == 0)) {
424                 if (cpustat.mem_req_cb) {
425                         if ((ret = cpustat.mem_req_cb()) < 0) {
426                                 cpustat.usecount.mem--;
427                                 goto out;
428                         }
429                 }
430                 dsp_cpustat_update();
431         }
432 out:
433         mutex_unlock(&cpustat.lock);
434
435         return ret;
436 }
437
438 /*
439  * release_mem will be delayed.
440  */
441 static void do_release_mem(void)
442 {
443         mutex_lock(&cpustat.lock);
444         cpustat.usecount.mem_delayed = 0;
445         if (cpustat.usecount.mem == 0) {
446                 dsp_cpustat_update();
447                 if (cpustat.mem_rel_cb)
448                         cpustat.mem_rel_cb();
449         }
450         mutex_unlock(&cpustat.lock);
451 }
452
453 static DECLARE_WORK(mem_rel_work, (void (*)(void *))do_release_mem, NULL);
454
455 int omap_dsp_release_mem(void)
456 {
457         mutex_lock(&cpustat.lock);
458
459         /* cancel previous release work */
460         cancel_delayed_work(&mem_rel_work);
461         cpustat.usecount.mem_delayed = 0;
462
463         if (cpustat.usecount.mem-- == 0) {
464                 printk(KERN_ERR
465                        "omapdsp: unbalanced memory request/release detected.\n"
466                        "         cpustat.usecount.mem is going to be "
467                        "less than zero! ... fixed to be zero.\n");
468                 cpustat.usecount.mem = 0;
469         }
470         if (cpustat.usecount.mem == 0) {
471                 cpustat.usecount.mem_delayed = 1;
472                 schedule_delayed_work(&mem_rel_work, HZ);
473         }
474
475         mutex_unlock(&cpustat.lock);
476
477         return 0;
478 }
479
480 void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void))
481 {
482         mutex_lock(&cpustat.lock);
483
484         cpustat.mem_req_cb = req_cb;
485         cpustat.mem_rel_cb = rel_cb;
486
487         /*
488          * This function must be called while mem is enabled!
489          */
490         BUG_ON(cpustat.usecount.mem == 0);
491
492         mutex_unlock(&cpustat.lock);
493 }
494
495 void dsp_unregister_mem_cb(void)
496 {
497         mutex_lock(&cpustat.lock);
498         cpustat.mem_req_cb = NULL;
499         cpustat.mem_rel_cb = NULL;
500         mutex_unlock(&cpustat.lock);
501 }
502
503 /*
504  * Audio power control function prototypes and defaults
505  * (To be overridden with board specific functions)
506  */
507 static void generic_audio_pwr_up_request(int stage)
508 {
509         printk(KERN_ERR "audio power-up request function is not defined.\n");
510 }
511
512 void (*omap_dsp_audio_pwr_up_request)(int stage) = generic_audio_pwr_up_request;
513 EXPORT_SYMBOL(omap_dsp_audio_pwr_up_request);
514
515 static void generic_audio_pwr_down_request(int stage)
516 {
517         printk(KERN_ERR "audio power-down request function is not defined.\n");
518 }
519
520 void (*omap_dsp_audio_pwr_down_request)(int stage) = generic_audio_pwr_down_request;
521 EXPORT_SYMBOL(omap_dsp_audio_pwr_down_request);
522
523 arch_initcall(omap_dsp_init);
524
525 EXPORT_SYMBOL(omap_dsp_request_mpui);
526 EXPORT_SYMBOL(omap_dsp_release_mpui);
527 EXPORT_SYMBOL(omap_dsp_request_mem);
528 EXPORT_SYMBOL(omap_dsp_release_mem);
529
530 #ifdef CONFIG_OMAP_DSP_MODULE
531 EXPORT_SYMBOL(dsp_ck_handle);
532 EXPORT_SYMBOL(api_ck_handle);
533 EXPORT_SYMBOL(dspmem_base);
534 EXPORT_SYMBOL(dspmem_size);
535 EXPORT_SYMBOL(daram_base);
536 EXPORT_SYMBOL(daram_size);
537 EXPORT_SYMBOL(saram_base);
538 EXPORT_SYMBOL(saram_size);
539 EXPORT_SYMBOL(dsp_set_rstvect);
540 EXPORT_SYMBOL(dsp_set_idle_boot_base);
541 EXPORT_SYMBOL(dsp_cpustat_request);
542 EXPORT_SYMBOL(dsp_cpustat_get_stat);
543 EXPORT_SYMBOL(dsp_cpustat_get_icrmask);
544 EXPORT_SYMBOL(dsp_cpustat_set_icrmask);
545 EXPORT_SYMBOL(dsp_register_mem_cb);
546 EXPORT_SYMBOL(dsp_unregister_mem_cb);
547
548 EXPORT_SYMBOL(__cpu_flush_kern_tlb_range);
549 EXPORT_SYMBOL(cpu_architecture);
550 EXPORT_SYMBOL(pmd_clear_bad);
551 #endif