2 * linux/arch/arm/mach-omap/dsp/dsp_common.c
4 * OMAP DSP driver static part
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/13: DSP Gateway version 3.3
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>
33 #include <linux/clk.h>
34 #include <linux/mutex.h>
35 #include <linux/interrupt.h>
37 #include <asm/tlbflush.h>
39 #include <asm/arch/dsp.h>
40 #include <asm/arch/tc.h>
41 #include "dsp_common.h"
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;
53 unsigned short icrmask;
59 int (*mem_req_cb)(void);
60 void (*mem_rel_cb)(void);
62 struct cpustat cpustat = {
63 .stat = CPUSTAT_RESET,
67 int dsp_set_rstvect(unsigned long adr)
69 unsigned long *dst_adr;
71 if (adr >= DSPSPACE_SIZE)
74 dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT);
76 *dst_adr = ((adr & 0xffff) << 16) | (adr >> 16);
80 omap_writew(MPUI_DSP_BOOT_CONFIG_DIRECT, MPUI_DSP_BOOT_CONFIG);
85 static void simple_load_code(unsigned char *src_c, unsigned short *dst, int len)
88 unsigned short *src = (unsigned short *)src_c;
91 /* len must be multiple of 2. */
96 for (i = 0; i < len_w; i++) {
98 *dst = ((*src & 0x00ff) << 8) |
99 ((*src & 0xff00) >> 8);
105 /* program size must be multiple of 2 */
106 #define GBL_IDLE_TEXT_SIZE 52
107 #define GBL_IDLE_TEXT_INIT { \
109 0x3c, 0x4a, /* 0x3c4a: MOV 0x4, AR2 */ \
110 0xf4, 0x41, 0xfc, 0xff, /* 0xf441fcff: AND 0xfcff, *AR2 */ \
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 */ \
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 */ \
134 /* program size must be multiple of 2 */
135 #define CPU_IDLE_TEXT_SIZE 48
136 #define CPU_IDLE_TEXT_INIT(icrh, icrl) { \
138 0x3c, 0x4b, /* 0x3c4b: MOV 0x4, AR3 */ \
139 0xf4, 0x61, 0xfc, 0xff, /* 0xf461fcff: AND 0xfcff, *AR3 */ \
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 */ \
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.
168 static unsigned long idle_boot_base = DSP_BOOT_ADR_MPUI;
170 static void dsp_gbl_idle(void)
172 unsigned char idle_text[GBL_IDLE_TEXT_SIZE] = GBL_IDLE_TEXT_INIT;
175 clk_enable(api_ck_handle);
178 omap_writew(MPUI_DSP_BOOT_CONFIG_IDLE, MPUI_DSP_BOOT_CONFIG);
180 simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
182 if (idle_boot_base == DSP_BOOT_ADR_MPUI)
183 omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG);
185 dsp_set_rstvect(idle_boot_base);
188 udelay(100); /* to make things stable */
189 clk_disable(api_ck_handle);
192 static void dsp_cpu_idle(void)
194 unsigned short icr_tmp;
195 unsigned char icrh, icrl;
198 clk_enable(api_ck_handle);
202 * DMA should not sleep for DARAM/SARAM access
203 * DPLL should not sleep while any other domain is active
205 icr_tmp = cpustat.icrmask & ~(DSPREG_ICR_DMA_IDLE_DOMAIN |
206 DSPREG_ICR_DPLL_IDLE_DOMAIN);
208 icrl = icr_tmp & 0xff;
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),
214 if (idle_boot_base == DSP_BOOT_ADR_MPUI)
215 omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG);
217 dsp_set_rstvect(idle_boot_base);
219 udelay(100); /* to make things stable */
220 clk_disable(api_ck_handle);
223 void dsp_set_idle_boot_base(unsigned long adr, size_t size)
225 if (adr == idle_boot_base)
227 idle_boot_base = adr;
228 if ((size < GBL_IDLE_TEXT_SIZE) ||
229 (size < CPU_IDLE_TEXT_SIZE)) {
231 "omapdsp: size for idle program is not enough!\n");
235 /* restart idle program with new base address */
236 if (cpustat.stat == CPUSTAT_GBL_IDLE)
238 if (cpustat.stat == CPUSTAT_CPU_IDLE)
242 static int init_done;
244 static int __init omap_dsp_init(void)
246 mutex_init(&cpustat.lock);
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;
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;
269 if (dspmem_size == 0) {
270 printk(KERN_ERR "omapdsp: unsupported omap architecture.\n");
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);
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);
286 /* This is needed for McBSP init, released in late_initcall */
287 clk_enable(api_ck_handle);
295 printk(KERN_INFO "omap_dsp_init() done\n");
299 static int dsp_late_init(void)
301 clk_disable(api_ck_handle);
304 late_initcall(dsp_late_init);
306 static void dsp_cpustat_update(void)
311 if (cpustat.req == CPUSTAT_RUN) {
312 if (cpustat.stat < CPUSTAT_RUN) {
314 clk_enable(api_ck_handle);
317 cpustat.stat = CPUSTAT_RUN;
318 enable_irq(INT_DSP_MMU);
323 /* cpustat.stat < CPUSTAT_RUN */
325 if (cpustat.stat == CPUSTAT_RUN) {
326 disable_irq(INT_DSP_MMU);
327 clk_disable(api_ck_handle);
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.
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) {
341 cpustat.stat = CPUSTAT_CPU_IDLE;
347 * when ARM only needs MPUI access, MPUI can be HOM and
350 if ((cpustat.req == CPUSTAT_GBL_IDLE) ||
351 (cpustat.usecount.mpui > 0)) {
352 if (cpustat.stat != CPUSTAT_GBL_IDLE) {
354 cpustat.stat = CPUSTAT_GBL_IDLE;
360 * no user, no request
362 if (cpustat.stat != CPUSTAT_RESET) {
364 cpustat.stat = CPUSTAT_RESET;
368 void dsp_cpustat_request(enum e_cpustat req)
370 mutex_lock(&cpustat.lock);
372 dsp_cpustat_update();
373 mutex_unlock(&cpustat.lock);
376 enum e_cpustat dsp_cpustat_get_stat(void)
381 unsigned short dsp_cpustat_get_icrmask(void)
383 return cpustat.icrmask;
386 void dsp_cpustat_set_icrmask(unsigned short mask)
388 mutex_lock(&cpustat.lock);
389 cpustat.icrmask = mask;
390 dsp_cpustat_update();
391 mutex_unlock(&cpustat.lock);
394 void omap_dsp_request_mpui(void)
396 mutex_lock(&cpustat.lock);
397 if (cpustat.usecount.mpui++ == 0)
398 dsp_cpustat_update();
399 mutex_unlock(&cpustat.lock);
402 void omap_dsp_release_mpui(void)
404 mutex_lock(&cpustat.lock);
405 if (cpustat.usecount.mpui-- == 0) {
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;
412 if (cpustat.usecount.mpui == 0)
413 dsp_cpustat_update();
414 mutex_unlock(&cpustat.lock);
417 int omap_dsp_request_mem(void)
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--;
430 dsp_cpustat_update();
433 mutex_unlock(&cpustat.lock);
439 * release_mem will be delayed.
441 static void do_release_mem(void)
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();
450 mutex_unlock(&cpustat.lock);
453 static DECLARE_WORK(mem_rel_work, (void (*)(void *))do_release_mem, NULL);
455 int omap_dsp_release_mem(void)
457 mutex_lock(&cpustat.lock);
459 /* cancel previous release work */
460 cancel_delayed_work(&mem_rel_work);
461 cpustat.usecount.mem_delayed = 0;
463 if (cpustat.usecount.mem-- == 0) {
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;
470 if (cpustat.usecount.mem == 0) {
471 cpustat.usecount.mem_delayed = 1;
472 schedule_delayed_work(&mem_rel_work, HZ);
475 mutex_unlock(&cpustat.lock);
480 void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void))
482 mutex_lock(&cpustat.lock);
484 cpustat.mem_req_cb = req_cb;
485 cpustat.mem_rel_cb = rel_cb;
488 * This function must be called while mem is enabled!
490 BUG_ON(cpustat.usecount.mem == 0);
492 mutex_unlock(&cpustat.lock);
495 void dsp_unregister_mem_cb(void)
497 mutex_lock(&cpustat.lock);
498 cpustat.mem_req_cb = NULL;
499 cpustat.mem_rel_cb = NULL;
500 mutex_unlock(&cpustat.lock);
504 * Audio power control function prototypes and defaults
505 * (To be overridden with board specific functions)
507 static void generic_audio_pwr_up_request(int stage)
509 printk(KERN_ERR "audio power-up request function is not defined.\n");
512 void (*omap_dsp_audio_pwr_up_request)(int stage) = generic_audio_pwr_up_request;
513 EXPORT_SYMBOL(omap_dsp_audio_pwr_up_request);
515 static void generic_audio_pwr_down_request(int stage)
517 printk(KERN_ERR "audio power-down request function is not defined.\n");
520 void (*omap_dsp_audio_pwr_down_request)(int stage) = generic_audio_pwr_down_request;
521 EXPORT_SYMBOL(omap_dsp_audio_pwr_down_request);
523 arch_initcall(omap_dsp_init);
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);
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);
548 EXPORT_SYMBOL(__cpu_flush_kern_tlb_range);
549 EXPORT_SYMBOL(cpu_architecture);
550 EXPORT_SYMBOL(pmd_clear_bad);