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