summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorDaniel Ankers <dan@weirdo.org.uk>2007-03-04 20:06:41 +0000
committerDaniel Ankers <dan@weirdo.org.uk>2007-03-04 20:06:41 +0000
commit82f9056988331572e01231d70fadc64b7ab76c6f (patch)
tree9f1d33b904516fd5eeac2067e4afb32ce5e990df /firmware
parent74e572c9d600247ee795b206da3715f6af442a25 (diff)
downloadrockbox-82f9056988331572e01231d70fadc64b7ab76c6f.tar.gz
rockbox-82f9056988331572e01231d70fadc64b7ab76c6f.zip
Dual core support for PP502x players (iPod G4 and later, iriver h10, Sansa - iPod G3 will be coming soon.) This allows threads to be run on either core provided that all communications between the cores is done using uncached memory. There should be no significant change in battery life from doing this. Documentation (on the RockboxKernel wiki page) will follow shortly.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12601 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/backlight.c3
-rw-r--r--firmware/common/dircache.c3
-rw-r--r--firmware/drivers/ata.c3
-rw-r--r--firmware/drivers/ata_mmc.c3
-rw-r--r--firmware/drivers/lcd-16bit.c3
-rw-r--r--firmware/drivers/lcd-1bit-vert.c3
-rw-r--r--firmware/drivers/lcd-2bit-horz.c3
-rw-r--r--firmware/drivers/lcd-2bit-vert.c3
-rw-r--r--firmware/drivers/lcd-player.c3
-rw-r--r--firmware/drivers/lcd-remote-1bit-v.c3
-rw-r--r--firmware/drivers/lcd-remote-2bit-vi.c3
-rw-r--r--firmware/export/config.h39
-rw-r--r--firmware/export/pp5002.h3
-rw-r--r--firmware/export/pp5020.h9
-rw-r--r--firmware/export/thread.h9
-rw-r--r--firmware/kernel.c67
-rw-r--r--firmware/mpeg.c3
-rw-r--r--firmware/pcm_record.c3
-rw-r--r--firmware/powermgmt.c3
-rw-r--r--firmware/system.c237
-rw-r--r--firmware/target/arm/crt0-pp.S17
-rw-r--r--firmware/target/arm/sandisk/sansa-e200/ata-e200.c3
-rw-r--r--firmware/thread.c78
-rw-r--r--firmware/usb.c3
24 files changed, 340 insertions, 167 deletions
diff --git a/firmware/backlight.c b/firmware/backlight.c
index a273b94a38..1e8dd46b2d 100644
--- a/firmware/backlight.c
+++ b/firmware/backlight.c
@@ -582,7 +582,8 @@ void backlight_init(void)
582 582
583 create_thread(backlight_thread, backlight_stack, 583 create_thread(backlight_thread, backlight_stack,
584 sizeof(backlight_stack), backlight_thread_name 584 sizeof(backlight_stack), backlight_thread_name
585 IF_PRIO(, PRIORITY_SYSTEM)); 585 IF_PRIO(, PRIORITY_SYSTEM)
586 IF_COP(, CPU, false));
586 tick_add_task(backlight_tick); 587 tick_add_task(backlight_tick);
587} 588}
588 589
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index fd9796c0d8..f78fa2d0cf 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -701,7 +701,8 @@ void dircache_init(void)
701 701
702 queue_init(&dircache_queue, true); 702 queue_init(&dircache_queue, true);
703 create_thread(dircache_thread, dircache_stack, 703 create_thread(dircache_thread, dircache_stack,
704 sizeof(dircache_stack), dircache_thread_name IF_PRIO(, PRIORITY_BACKGROUND)); 704 sizeof(dircache_stack), dircache_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
705 IF_COP(, CPU, false));
705} 706}
706 707
707/** 708/**
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 21376ab9a7..76c0090a12 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -991,7 +991,8 @@ int ata_init(void)
991 last_disk_activity = current_tick; 991 last_disk_activity = current_tick;
992 create_thread(ata_thread, ata_stack, 992 create_thread(ata_thread, ata_stack,
993 sizeof(ata_stack), ata_thread_name 993 sizeof(ata_stack), ata_thread_name
994 IF_PRIO(, PRIORITY_SYSTEM)); 994 IF_PRIO(, PRIORITY_SYSTEM)
995 IF_COP(, CPU, false));
995 initialized = true; 996 initialized = true;
996 997
997 } 998 }
diff --git a/firmware/drivers/ata_mmc.c b/firmware/drivers/ata_mmc.c
index a549624b2c..377d2444bf 100644
--- a/firmware/drivers/ata_mmc.c
+++ b/firmware/drivers/ata_mmc.c
@@ -1173,7 +1173,8 @@ int ata_init(void)
1173 1173
1174 queue_init(&mmc_queue, true); 1174 queue_init(&mmc_queue, true);
1175 create_thread(mmc_thread, mmc_stack, 1175 create_thread(mmc_thread, mmc_stack,
1176 sizeof(mmc_stack), mmc_thread_name IF_PRIO(, PRIORITY_SYSTEM)); 1176 sizeof(mmc_stack), mmc_thread_name IF_PRIO(, PRIORITY_SYSTEM)
1177 IF_COP(, CPU, false));
1177 tick_add_task(mmc_tick); 1178 tick_add_task(mmc_tick);
1178 initialized = true; 1179 initialized = true;
1179 } 1180 }
diff --git a/firmware/drivers/lcd-16bit.c b/firmware/drivers/lcd-16bit.c
index d545bf3fe4..7269e54dcd 100644
--- a/firmware/drivers/lcd-16bit.c
+++ b/firmware/drivers/lcd-16bit.c
@@ -89,7 +89,8 @@ void lcd_init(void)
89 89
90 create_thread(scroll_thread, scroll_stack, 90 create_thread(scroll_thread, scroll_stack,
91 sizeof(scroll_stack), scroll_name 91 sizeof(scroll_stack), scroll_name
92 IF_PRIO(, PRIORITY_USER_INTERFACE)); 92 IF_PRIO(, PRIORITY_USER_INTERFACE)
93 IF_COP(, CPU, false));
93} 94}
94 95
95/*** parameter handling ***/ 96/*** parameter handling ***/
diff --git a/firmware/drivers/lcd-1bit-vert.c b/firmware/drivers/lcd-1bit-vert.c
index 62dfab0180..64c1ace292 100644
--- a/firmware/drivers/lcd-1bit-vert.c
+++ b/firmware/drivers/lcd-1bit-vert.c
@@ -68,7 +68,8 @@ void lcd_init(void)
68 /* Call device specific init */ 68 /* Call device specific init */
69 lcd_init_device(); 69 lcd_init_device();
70 create_thread(scroll_thread, scroll_stack, 70 create_thread(scroll_thread, scroll_stack,
71 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 71 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
72 IF_COP(, CPU, false));
72} 73}
73 74
74/*** parameter handling ***/ 75/*** parameter handling ***/
diff --git a/firmware/drivers/lcd-2bit-horz.c b/firmware/drivers/lcd-2bit-horz.c
index 9ba52e1ba9..475e466c42 100644
--- a/firmware/drivers/lcd-2bit-horz.c
+++ b/firmware/drivers/lcd-2bit-horz.c
@@ -79,7 +79,8 @@ void lcd_init(void)
79 /* Call device specific init */ 79 /* Call device specific init */
80 lcd_init_device(); 80 lcd_init_device();
81 create_thread(scroll_thread, scroll_stack, 81 create_thread(scroll_thread, scroll_stack,
82 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 82 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
83 IF_COP(, CPU, false));
83} 84}
84 85
85/*** parameter handling ***/ 86/*** parameter handling ***/
diff --git a/firmware/drivers/lcd-2bit-vert.c b/firmware/drivers/lcd-2bit-vert.c
index 7b3352b9d6..7a49f35312 100644
--- a/firmware/drivers/lcd-2bit-vert.c
+++ b/firmware/drivers/lcd-2bit-vert.c
@@ -82,7 +82,8 @@ void lcd_init(void)
82 /* Call device specific init */ 82 /* Call device specific init */
83 lcd_init_device(); 83 lcd_init_device();
84 create_thread(scroll_thread, scroll_stack, 84 create_thread(scroll_thread, scroll_stack,
85 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 85 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
86 IF_COP(, CPU, false));
86} 87}
87 88
88/*** parameter handling ***/ 89/*** parameter handling ***/
diff --git a/firmware/drivers/lcd-player.c b/firmware/drivers/lcd-player.c
index 8ca81473fc..c863c9f188 100644
--- a/firmware/drivers/lcd-player.c
+++ b/firmware/drivers/lcd-player.c
@@ -610,7 +610,8 @@ void lcd_init (void)
610 lcd_set_contrast(lcd_default_contrast()); 610 lcd_set_contrast(lcd_default_contrast());
611 611
612 create_thread(scroll_thread, scroll_stack, 612 create_thread(scroll_thread, scroll_stack,
613 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 613 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
614 IF_COP(, CPU, false));
614} 615}
615 616
616void lcd_jump_scroll (int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */ 617void lcd_jump_scroll (int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
diff --git a/firmware/drivers/lcd-remote-1bit-v.c b/firmware/drivers/lcd-remote-1bit-v.c
index c81ccc83c9..0aa3d890f7 100644
--- a/firmware/drivers/lcd-remote-1bit-v.c
+++ b/firmware/drivers/lcd-remote-1bit-v.c
@@ -903,5 +903,6 @@ void lcd_remote_init(void)
903 queue_init(&remote_scroll_queue, false); 903 queue_init(&remote_scroll_queue, false);
904#endif 904#endif
905 create_thread(scroll_thread, scroll_stack, 905 create_thread(scroll_thread, scroll_stack,
906 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 906 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
907 IF_COP(, CPU, false));
907} 908}
diff --git a/firmware/drivers/lcd-remote-2bit-vi.c b/firmware/drivers/lcd-remote-2bit-vi.c
index 281cbc2189..e0f6b35004 100644
--- a/firmware/drivers/lcd-remote-2bit-vi.c
+++ b/firmware/drivers/lcd-remote-2bit-vi.c
@@ -1241,5 +1241,6 @@ void lcd_remote_init(void)
1241 queue_init(&remote_scroll_queue, false); 1241 queue_init(&remote_scroll_queue, false);
1242#endif 1242#endif
1243 create_thread(scroll_thread, scroll_stack, 1243 create_thread(scroll_thread, scroll_stack,
1244 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)); 1244 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
1245 IF_COP(, CPU, false));
1245} 1246}
diff --git a/firmware/export/config.h b/firmware/export/config.h
index d848d16155..13fb77661d 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -273,21 +273,6 @@
273/* define for all cpus from PP family */ 273/* define for all cpus from PP family */
274#if (CONFIG_CPU == PP5002) || (CONFIG_CPU == PP5020) || (CONFIG_CPU == PP5024) 274#if (CONFIG_CPU == PP5002) || (CONFIG_CPU == PP5020) || (CONFIG_CPU == PP5024)
275#define CPU_PP 275#define CPU_PP
276
277/* PP family has dual cores */
278#if 0
279/* Keep it as single core until dual core support is ready */
280#define NUM_CORES 2
281#define CURRENT_CORE current_core()
282#endif
283
284#define NUM_CORES 1
285#define CURRENT_CORE 0
286
287#define COP_REBOOT 0x00000001
288#else
289#define NUM_CORES 1
290#define CURRENT_CORE 0
291#endif 276#endif
292 277
293/* define for all cpus from ARM family */ 278/* define for all cpus from ARM family */
@@ -348,4 +333,28 @@
348#define IRAM_LCDFRAMEBUFFER 333#define IRAM_LCDFRAMEBUFFER
349#endif 334#endif
350 335
336/* Dual core support - not yet working on the 3G iPod */
337#if defined(CPU_PP) && CONFIG_CPU != PP5002
338#define NUM_CORES 2
339#define CURRENT_CORE current_core()
340/* Hopefully at some point we will learn how to mark areas of main memory as
341 * not to be cached. Until then, use IRAM for variables shared across cores */
342#define NOCACHEBSS_ATTR IBSS_ATTR
343#define NOCACHEDATA_ATTR IDATA_ATTR
344
345#define IF_COP(empty, x, y) , x, y
346
347/* Defines for inter-core messaging */
348#define COP_REBOOT 1
349
350#else
351#define NUM_CORES 1
352#define CURRENT_CORE CPU
353#define NOCACHEBSS_ATTR
354#define NOCACHEDATA_ATTR
355
356#define IF_COP(empty, x, y)
357
358#endif /* Processor specific */
359
351#endif 360#endif
diff --git a/firmware/export/pp5002.h b/firmware/export/pp5002.h
index ef131fe6d6..9636313390 100644
--- a/firmware/export/pp5002.h
+++ b/firmware/export/pp5002.h
@@ -73,6 +73,9 @@
73#define CPU_INT_STAT (*(volatile unsigned long*)(0xcf001000)) 73#define CPU_INT_STAT (*(volatile unsigned long*)(0xcf001000))
74#define CPU_INT_EN (*(volatile unsigned long*)(0xcf001024)) 74#define CPU_INT_EN (*(volatile unsigned long*)(0xcf001024))
75#define CPU_INT_CLR (*(volatile unsigned long*)(0xcf001028)) 75#define CPU_INT_CLR (*(volatile unsigned long*)(0xcf001028))
76#define COP_INT_STAT (*(volatile unsigned long*)(0xcf001010)) /* A guess */
77#define COP_INT_EN (*(volatile unsigned long*)(0xcf001034))
78#define COP_INT_CLR (*(volatile unsigned long*)(0xcf001038))
76 79
77#define USB2D_IDENT (*(volatile unsigned long*)(0xc5000000)) 80#define USB2D_IDENT (*(volatile unsigned long*)(0xc5000000))
78#define USB_STATUS (*(volatile unsigned long*)(0xc50001a4)) 81#define USB_STATUS (*(volatile unsigned long*)(0xc50001a4))
diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h
index 3d205a0ea1..a34f1251c9 100644
--- a/firmware/export/pp5020.h
+++ b/firmware/export/pp5020.h
@@ -30,6 +30,15 @@
30#define PROC_ID_CPU 0x55 30#define PROC_ID_CPU 0x55
31#define PROC_ID_COP 0xaa 31#define PROC_ID_COP 0xaa
32 32
33/* Mailboxes */
34/* Each processor has two mailboxes it can write to and two which
35 it can read from. We define the first to be for sending messages
36 and the second for replying to messages */
37#define CPU_MESSAGE (*(volatile unsigned long *)(0x60001000))
38#define COP_MESSAGE (*(volatile unsigned long *)(0x60001004))
39#define CPU_REPLY (*(volatile unsigned long *)(0x60001008))
40#define COP_REPLY (*(volatile unsigned long *)(0x6000100c))
41
33/* Interrupts */ 42/* Interrupts */
34#define CPU_INT_STAT (*(volatile unsigned long*)(0x64004000)) 43#define CPU_INT_STAT (*(volatile unsigned long*)(0x64004000))
35#define COP_INT_STAT (*(volatile unsigned long*)(0x60004004)) 44#define COP_INT_STAT (*(volatile unsigned long*)(0x60004004))
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index bced98ea23..2ff4694159 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -124,13 +124,8 @@ struct core_entry {
124 124
125struct thread_entry* 125struct thread_entry*
126 create_thread(void (*function)(void), void* stack, int stack_size, 126 create_thread(void (*function)(void), void* stack, int stack_size,
127 const char *name IF_PRIO(, int priority)); 127 const char *name IF_PRIO(, int priority)
128 128 IF_COP(, unsigned int core, bool fallback));
129struct thread_entry*
130 create_thread_on_core(unsigned int core, void (*function)(void),
131 void* stack, int stack_size,
132 const char *name
133 IF_PRIO(, int priority));
134 129
135#ifdef HAVE_SCHEDULER_BOOSTCTRL 130#ifdef HAVE_SCHEDULER_BOOSTCTRL
136void trigger_cpu_boost(void); 131void trigger_cpu_boost(void);
diff --git a/firmware/kernel.c b/firmware/kernel.c
index 75c6604682..313530ffba 100644
--- a/firmware/kernel.c
+++ b/firmware/kernel.c
@@ -26,7 +26,7 @@
26#include "panic.h" 26#include "panic.h"
27 27
28#if !defined(CPU_PP) || !defined(BOOTLOADER) 28#if !defined(CPU_PP) || !defined(BOOTLOADER)
29long current_tick = 0; 29long current_tick NOCACHEDATA_ATTR = 0;
30#endif 30#endif
31 31
32static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void); 32static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
@@ -45,10 +45,13 @@ void kernel_init(void)
45 /* Init the threading API */ 45 /* Init the threading API */
46 init_threads(); 46 init_threads();
47 47
48 memset(tick_funcs, 0, sizeof(tick_funcs)); 48 if(CURRENT_CORE == CPU)
49 {
50 memset(tick_funcs, 0, sizeof(tick_funcs));
49 51
50 num_queues = 0; 52 num_queues = 0;
51 memset(all_queues, 0, sizeof(all_queues)); 53 memset(all_queues, 0, sizeof(all_queues));
54 }
52 55
53 tick_start(1000/HZ); 56 tick_start(1000/HZ);
54} 57}
@@ -496,28 +499,36 @@ void TIMER1(void)
496 int i; 499 int i;
497 500
498 TIMER1_VAL; /* Read value to ack IRQ */ 501 TIMER1_VAL; /* Read value to ack IRQ */
499 /* Run through the list of tick tasks */ 502 /* Run through the list of tick tasks (using main core) */
500 for (i = 0;i < MAX_NUM_TICK_TASKS;i++) 503 if (CURRENT_CORE == CPU)
501 { 504 {
502 if (tick_funcs[i]) 505 for (i = 0;i < MAX_NUM_TICK_TASKS;i++)
503 { 506 {
504 tick_funcs[i](); 507 if (tick_funcs[i])
508 {
509 tick_funcs[i]();
510 }
505 } 511 }
506 }
507 512
508 current_tick++; 513 current_tick++;
514 }
509} 515}
510#endif 516#endif
511 517
512void tick_start(unsigned int interval_in_ms) 518void tick_start(unsigned int interval_in_ms)
513{ 519{
514#ifndef BOOTLOADER 520#ifndef BOOTLOADER
515 TIMER1_CFG = 0x0; 521 if(CURRENT_CORE == CPU)
516 TIMER1_VAL; 522 {
517 /* enable timer */ 523 TIMER1_CFG = 0x0;
518 TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1); 524 TIMER1_VAL;
519 /* unmask interrupt source */ 525 /* enable timer */
520 CPU_INT_EN = TIMER1_MASK; 526 TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1);
527 /* unmask interrupt source */
528 CPU_INT_EN = TIMER1_MASK;
529 } else {
530 COP_INT_EN = TIMER1_MASK;
531 }
521#else 532#else
522 /* We don't enable interrupts in the bootloader */ 533 /* We don't enable interrupts in the bootloader */
523 (void)interval_in_ms; 534 (void)interval_in_ms;
@@ -645,6 +656,29 @@ void mutex_init(struct mutex *m)
645 m->thread = NULL; 656 m->thread = NULL;
646} 657}
647 658
659#ifdef CPU_PP
660/* PortalPlayer chips have 2 cores, therefore need atomic mutexes */
661
662static inline bool test_and_set(bool *x, bool v)
663{
664 asm volatile (
665 "swpb %0, %0, [%1]\n"
666 : "+r"(v)
667 : "r"(x)
668 );
669 return v;
670}
671
672void mutex_lock(struct mutex *m)
673{
674 if (test_and_set(&m->locked,true))
675 {
676 /* Wait until the lock is open... */
677 block_thread(&m->thread);
678 }
679}
680
681#else
648void mutex_lock(struct mutex *m) 682void mutex_lock(struct mutex *m)
649{ 683{
650 if (m->locked) 684 if (m->locked)
@@ -656,6 +690,7 @@ void mutex_lock(struct mutex *m)
656 /* ...and lock it */ 690 /* ...and lock it */
657 m->locked = true; 691 m->locked = true;
658} 692}
693#endif
659 694
660void mutex_unlock(struct mutex *m) 695void mutex_unlock(struct mutex *m)
661{ 696{
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 9afa8a20bd..909a21dcda 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -2905,7 +2905,8 @@ void audio_init(void)
2905 queue_init(&mpeg_queue, true); 2905 queue_init(&mpeg_queue, true);
2906#endif /* !SIMULATOR */ 2906#endif /* !SIMULATOR */
2907 create_thread(mpeg_thread, mpeg_stack, 2907 create_thread(mpeg_thread, mpeg_stack,
2908 sizeof(mpeg_stack), mpeg_thread_name IF_PRIO(, PRIORITY_SYSTEM)); 2908 sizeof(mpeg_stack), mpeg_thread_name IF_PRIO(, PRIORITY_SYSTEM)
2909 IF_COP(, CPU, false));
2909 2910
2910 memset(trackdata, sizeof(trackdata), 0); 2911 memset(trackdata, sizeof(trackdata), 0);
2911 2912
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
index 270bb354d2..3f4abc7be3 100644
--- a/firmware/pcm_record.c
+++ b/firmware/pcm_record.c
@@ -418,7 +418,8 @@ void pcm_rec_init(void)
418 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send); 418 queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send);
419 pcmrec_thread_p = 419 pcmrec_thread_p =
420 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), 420 create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
421 pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)); 421 pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
422 IF_COP(, CPU, false));
422} /* pcm_rec_init */ 423} /* pcm_rec_init */
423 424
424/** audio_* group **/ 425/** audio_* group **/
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index c2f9ca0bca..1492c80bc4 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -1245,7 +1245,8 @@ void powermgmt_init(void)
1245 /* init history to 0 */ 1245 /* init history to 0 */
1246 memset(power_history, 0x00, sizeof(power_history)); 1246 memset(power_history, 0x00, sizeof(power_history));
1247 create_thread(power_thread, power_stack, sizeof(power_stack), 1247 create_thread(power_thread, power_stack, sizeof(power_stack),
1248 power_thread_name IF_PRIO(, PRIORITY_SYSTEM)); 1248 power_thread_name IF_PRIO(, PRIORITY_SYSTEM)
1249 IF_COP(, CPU, false));
1249} 1250}
1250 1251
1251#endif /* SIMULATOR */ 1252#endif /* SIMULATOR */
diff --git a/firmware/system.c b/firmware/system.c
index a86d945093..a9c9d9e350 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -612,12 +612,22 @@ extern void ipod_mini_button_int(void);
612 612
613void irq(void) 613void irq(void)
614{ 614{
615 if (CPU_INT_STAT & TIMER1_MASK) 615 if(CURRENT_CORE == CPU)
616 TIMER1(); 616 {
617 else if (CPU_INT_STAT & TIMER2_MASK) 617 if (CPU_INT_STAT & TIMER1_MASK)
618 TIMER2(); 618 TIMER1();
619 else if (CPU_HI_INT_STAT & GPIO_MASK) 619 else if (CPU_INT_STAT & TIMER2_MASK)
620 ipod_mini_button_int(); 620 TIMER2();
621 else if (CPU_HI_INT_STAT & GPIO_MASK)
622 ipod_mini_button_int();
623 } else {
624 if (COP_INT_STAT & TIMER1_MASK)
625 TIMER1();
626 else if (COP_INT_STAT & TIMER2_MASK)
627 TIMER2();
628 else if (COP_HI_INT_STAT & GPIO_MASK)
629 ipod_mini_button_int();
630 }
621} 631}
622#elif (defined IRIVER_H10) || (defined IRIVER_H10_5GB) || defined(ELIO_TPJ1022) \ 632#elif (defined IRIVER_H10) || (defined IRIVER_H10_5GB) || defined(ELIO_TPJ1022) \
623 || (defined SANSA_E200) 633 || (defined SANSA_E200)
@@ -626,22 +636,40 @@ void irq(void)
626/* TODO: Even if it isn't in the target tree, this should be the default case */ 636/* TODO: Even if it isn't in the target tree, this should be the default case */
627void irq(void) 637void irq(void)
628{ 638{
629 if (CPU_INT_STAT & TIMER1_MASK) 639 if(CURRENT_CORE == CPU)
630 TIMER1(); 640 {
631 else if (CPU_INT_STAT & TIMER2_MASK) 641 if (CPU_INT_STAT & TIMER1_MASK)
632 TIMER2(); 642 TIMER1();
643 else if (CPU_INT_STAT & TIMER2_MASK)
644 TIMER2();
645 } else {
646 if (COP_INT_STAT & TIMER1_MASK)
647 TIMER1();
648 else if (COP_INT_STAT & TIMER2_MASK)
649 TIMER2();
650 }
633} 651}
634#else 652#else
635extern void ipod_4g_button_int(void); 653extern void ipod_4g_button_int(void);
636 654
637void irq(void) 655void irq(void)
638{ 656{
639 if (CPU_INT_STAT & TIMER1_MASK) 657 if(CURRENT_CORE == CPU)
640 TIMER1(); 658 {
641 else if (CPU_INT_STAT & TIMER2_MASK) 659 if (CPU_INT_STAT & TIMER1_MASK)
642 TIMER2(); 660 TIMER1();
643 else if (CPU_HI_INT_STAT & I2C_MASK) 661 else if (CPU_INT_STAT & TIMER2_MASK)
644 ipod_4g_button_int(); 662 TIMER2();
663 else if (CPU_HI_INT_STAT & I2C_MASK)
664 ipod_4g_button_int();
665 } else {
666 if (COP_INT_STAT & TIMER1_MASK)
667 TIMER1();
668 else if (COP_INT_STAT & TIMER2_MASK)
669 TIMER2();
670 else if (COP_HI_INT_STAT & I2C_MASK)
671 ipod_4g_button_int();
672 }
645} 673}
646#endif 674#endif
647#endif /* BOOTLOADER */ 675#endif /* BOOTLOADER */
@@ -694,43 +722,47 @@ void set_cpu_frequency(long frequency)
694{ 722{
695 unsigned long postmult; 723 unsigned long postmult;
696 724
697 if (frequency == CPUFREQ_NORMAL) 725 if (CURRENT_CORE == CPU)
698 postmult = CPUFREQ_NORMAL_MULT; 726 {
699 else if (frequency == CPUFREQ_MAX) 727 if (frequency == CPUFREQ_NORMAL)
700 postmult = CPUFREQ_MAX_MULT; 728 postmult = CPUFREQ_NORMAL_MULT;
701 else 729 else if (frequency == CPUFREQ_MAX)
702 postmult = CPUFREQ_DEFAULT_MULT; 730 postmult = CPUFREQ_MAX_MULT;
703 cpu_frequency = frequency; 731 else
732 postmult = CPUFREQ_DEFAULT_MULT;
733 cpu_frequency = frequency;
704 734
705 /* Enable PLL? */ 735 /* Enable PLL? */
706 outl(inl(0x70000020) | (1<<30), 0x70000020); 736 outl(inl(0x70000020) | (1<<30), 0x70000020);
707 737
708 /* Select 24MHz crystal as clock source? */ 738 /* Select 24MHz crystal as clock source? */
709 outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020); 739 outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020);
710 740
711 /* Clock frequency = (24/8)*postmult */ 741 /* Clock frequency = (24/8)*postmult */
712 outl(0xaa020000 | 8 | (postmult << 8), 0x60006034); 742 outl(0xaa020000 | 8 | (postmult << 8), 0x60006034);
713 743
714 /* Wait for PLL relock? */ 744 /* Wait for PLL relock? */
715 udelay(2000); 745 udelay(2000);
716 746
717 /* Select PLL as clock source? */ 747 /* Select PLL as clock source? */
718 outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020); 748 outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020);
719 749
720#if defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IRIVER_H10) || defined(IRIVER_H10_5GB) 750#if defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
721 /* We don't know why the timer interrupt gets disabled on the PP5020 751 /* We don't know why the timer interrupt gets disabled on the PP5020
722 based ipods, but without the following line, the 4Gs will freeze 752 based ipods, but without the following line, the 4Gs will freeze
723 when CPU frequency changing is enabled. 753 when CPU frequency changing is enabled.
724 754
725 Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used 755 Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used
726 elsewhere to enable interrupts) doesn't work, we need "|=". 756 elsewhere to enable interrupts) doesn't work, we need "|=".
727 757
728 It's not needed on the PP5021 and PP5022 ipods. 758 It's not needed on the PP5021 and PP5022 ipods.
729 */ 759 */
730 760
731 /* unmask interrupt source */ 761 /* unmask interrupt source */
732 CPU_INT_EN |= TIMER1_MASK; 762 CPU_INT_EN |= TIMER1_MASK;
763 COP_INT_EN |= TIMER1_MASK;
733#endif 764#endif
765 }
734} 766}
735#elif !defined(BOOTLOADER) 767#elif !defined(BOOTLOADER)
736void ipod_set_cpu_frequency(void) 768void ipod_set_cpu_frequency(void)
@@ -754,24 +786,33 @@ void ipod_set_cpu_frequency(void)
754void system_init(void) 786void system_init(void)
755{ 787{
756#ifndef BOOTLOADER 788#ifndef BOOTLOADER
757 /* Remap the flash ROM from 0x00000000 to 0x20000000. */ 789 if (CURRENT_CORE == CPU)
758 MMAP3_LOGICAL = 0x20000000 | 0x3a00; 790 {
759 MMAP3_PHYSICAL = 0x00000000 | 0x3f84; 791 /* Remap the flash ROM from 0x00000000 to 0x20000000. */
760 792 MMAP3_LOGICAL = 0x20000000 | 0x3a00;
761 /* The hw revision is written to the last 4 bytes of SDRAM by the 793 MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
762 bootloader - we save it before Rockbox overwrites it. */ 794
763 ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc))); 795 /* The hw revision is written to the last 4 bytes of SDRAM by the
764 796 bootloader - we save it before Rockbox overwrites it. */
765 /* disable all irqs */ 797 ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
766 outl(-1, 0x60001138); 798
767 outl(-1, 0x60001128); 799 /* disable all irqs */
768 outl(-1, 0x6000111c); 800 outl(-1, 0x60001138);
769 801 outl(-1, 0x60001128);
770 outl(-1, 0x60001038); 802 outl(-1, 0x6000111c);
771 outl(-1, 0x60001028); 803
772 outl(-1, 0x6000101c); 804 outl(-1, 0x60001038);
773#ifndef HAVE_ADJUSTABLE_CPU_FREQ 805 outl(-1, 0x60001028);
774 ipod_set_cpu_frequency(); 806 outl(-1, 0x6000101c);
807#if (!defined HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES == 1)
808 ipod_set_cpu_frequency();
809#endif
810 }
811#if (!defined HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
812 else
813 {
814 ipod_set_cpu_frequency();
815 }
775#endif 816#endif
776 ipod_init_cache(); 817 ipod_init_cache();
777#endif 818#endif
@@ -796,10 +837,18 @@ extern void TIMER2(void);
796 837
797void irq(void) 838void irq(void)
798{ 839{
799 if (CPU_INT_STAT & TIMER1_MASK) 840 if(CURRENT_CORE == CPU)
800 TIMER1(); 841 {
801 else if (CPU_INT_STAT & TIMER2_MASK) 842 if (CPU_INT_STAT & TIMER1_MASK)
802 TIMER2(); 843 TIMER1();
844 else if (CPU_INT_STAT & TIMER2_MASK)
845 TIMER2();
846 } else {
847 if (COP_INT_STAT & TIMER1_MASK)
848 TIMER1();
849 else if (COP_INT_STAT & TIMER2_MASK)
850 TIMER2();
851 }
803} 852}
804 853
805#endif 854#endif
@@ -848,29 +897,32 @@ void set_cpu_frequency(long frequency)
848{ 897{
849 unsigned long postmult; 898 unsigned long postmult;
850 899
851 if (frequency == CPUFREQ_NORMAL) 900 if (CURRENT_CORE == CPU)
852 postmult = CPUFREQ_NORMAL_MULT; 901 {
853 else if (frequency == CPUFREQ_MAX) 902 if (frequency == CPUFREQ_NORMAL)
854 postmult = CPUFREQ_MAX_MULT; 903 postmult = CPUFREQ_NORMAL_MULT;
855 else 904 else if (frequency == CPUFREQ_MAX)
856 postmult = CPUFREQ_DEFAULT_MULT; 905 postmult = CPUFREQ_MAX_MULT;
857 cpu_frequency = frequency; 906 else
907 postmult = CPUFREQ_DEFAULT_MULT;
908 cpu_frequency = frequency;
858 909
859 outl(0x02, 0xcf005008); 910 outl(0x02, 0xcf005008);
860 outl(0x55, 0xcf00500c); 911 outl(0x55, 0xcf00500c);
861 outl(0x6000, 0xcf005010); 912 outl(0x6000, 0xcf005010);
862 913
863 /* Clock frequency = (24/8)*postmult */ 914 /* Clock frequency = (24/8)*postmult */
864 outl(8, 0xcf005018); 915 outl(8, 0xcf005018);
865 outl(postmult, 0xcf00501c); 916 outl(postmult, 0xcf00501c);
866 917
867 outl(0xe000, 0xcf005010); 918 outl(0xe000, 0xcf005010);
868 919
869 /* Wait for PLL relock? */ 920 /* Wait for PLL relock? */
870 udelay(2000); 921 udelay(2000);
871 922
872 /* Select PLL as clock source? */ 923 /* Select PLL as clock source? */
873 outl(0xa8, 0xcf00500c); 924 outl(0xa8, 0xcf00500c);
925 }
874} 926}
875#elif !defined(BOOTLOADER) 927#elif !defined(BOOTLOADER)
876static void ipod_set_cpu_speed(void) 928static void ipod_set_cpu_speed(void)
@@ -901,17 +953,20 @@ static void ipod_set_cpu_speed(void)
901void system_init(void) 953void system_init(void)
902{ 954{
903#ifndef BOOTLOADER 955#ifndef BOOTLOADER
904 /* Remap the flash ROM from 0x00000000 to 0x20000000. */ 956 if (CURRENT_CORE == CPU)
905 MMAP3_LOGICAL = 0x20000000 | 0x3a00; 957 {
906 MMAP3_PHYSICAL = 0x00000000 | 0x3f84; 958 /* Remap the flash ROM from 0x00000000 to 0x20000000. */
907 959 MMAP3_LOGICAL = 0x20000000 | 0x3a00;
908 ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc))); 960 MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
909 outl(-1, 0xcf00101c); 961
910 outl(-1, 0xcf001028); 962 ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
911 outl(-1, 0xcf001038); 963 outl(-1, 0xcf00101c);
964 outl(-1, 0xcf001028);
965 outl(-1, 0xcf001038);
912#ifndef HAVE_ADJUSTABLE_CPU_FREQ 966#ifndef HAVE_ADJUSTABLE_CPU_FREQ
913 ipod_set_cpu_speed(); 967 ipod_set_cpu_speed();
914#endif 968#endif
969 }
915 ipod_init_cache(); 970 ipod_init_cache();
916#endif 971#endif
917} 972}
diff --git a/firmware/target/arm/crt0-pp.S b/firmware/target/arm/crt0-pp.S
index e0d1034f74..bbeace1b60 100644
--- a/firmware/target/arm/crt0-pp.S
+++ b/firmware/target/arm/crt0-pp.S
@@ -222,6 +222,19 @@ cop_init:
222 strhi r4, [r2], #4 222 strhi r4, [r2], #4
223 bhi 2b 223 bhi 2b
224 224
225 /* Set up stack for IRQ mode */
226 msr cpsr_c, #0xd2
227 ldr sp, =cop_irq_stack
228 /* Set up stack for FIQ mode */
229 msr cpsr_c, #0xd1
230 ldr sp, =fiq_stack
231
232 /* Let abort and undefined modes use IRQ stack */
233 msr cpsr_c, #0xd7
234 ldr sp, =cop_irq_stack
235 msr cpsr_c, #0xdb
236 ldr sp, =cop_irq_stack
237
225 ldr sp, =cop_stackend 238 ldr sp, =cop_stackend
226 239
227 /* Run cop_main() in apps/main.c */ 240 /* Run cop_main() in apps/main.c */
@@ -307,6 +320,10 @@ UIE:
307 .space 256*4 320 .space 256*4
308irq_stack: 321irq_stack:
309 322
323/* 256 words of COP IRQ stack */
324 .space 256*4
325cop_irq_stack:
326
310/* 256 words of FIQ stack */ 327/* 256 words of FIQ stack */
311 .space 256*4 328 .space 256*4
312fiq_stack: 329fiq_stack:
diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
index cf05397a78..73a67d1ee6 100644
--- a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
+++ b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
@@ -687,7 +687,8 @@ int ata_init(void)
687 { 687 {
688 queue_init(&sd_queue, true); 688 queue_init(&sd_queue, true);
689 create_thread(sd_thread, sd_stack, 689 create_thread(sd_thread, sd_stack,
690 sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM)); 690 sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM)
691 IF_COP(, CPU, false));
691 initialized = true; 692 initialized = true;
692 } 693 }
693 694
diff --git a/firmware/thread.c b/firmware/thread.c
index 2281f43e53..281ab0fa54 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -64,6 +64,10 @@ int *cop_stackend = stackend;
64#endif 64#endif
65#endif 65#endif
66 66
67#if (NUM_CORES > 1)
68bool IDATA_ATTR kernel_running_on_cop = false;
69#endif
70
67/* Conserve IRAM 71/* Conserve IRAM
68static void add_to_list(struct thread_entry **list, 72static void add_to_list(struct thread_entry **list,
69 struct thread_entry *thread) ICODE_ATTR; 73 struct thread_entry *thread) ICODE_ATTR;
@@ -316,10 +320,13 @@ static inline void sleep_core(void)
316#elif CONFIG_CPU == SH7034 320#elif CONFIG_CPU == SH7034
317 and_b(0x7F, &SBYCR); 321 and_b(0x7F, &SBYCR);
318 asm volatile ("sleep"); 322 asm volatile ("sleep");
319#elif CONFIG_CPU == PP5020 323#elif defined (CPU_PP)
320 /* This should sleep the CPU. It appears to wake by itself on 324 /* This should sleep the CPU. It appears to wake by itself on
321 interrupts */ 325 interrupts */
322 CPU_CTL = 0x80000000; 326 if (CURRENT_CORE == CPU)
327 CPU_CTL = PROC_SLEEP;
328 else
329 COP_CTL = PROC_SLEEP;
323#elif CONFIG_CPU == S3C2440 330#elif CONFIG_CPU == S3C2440
324 CLKCON |= (1 << 2); /* set IDLE bit */ 331 CLKCON |= (1 << 2); /* set IDLE bit */
325 for(i=0; i<10; i++); /* wait for IDLE */ 332 for(i=0; i<10; i++); /* wait for IDLE */
@@ -608,27 +615,16 @@ void wakeup_thread(struct thread_entry **list)
608} 615}
609 616
610/*--------------------------------------------------------------------------- 617/*---------------------------------------------------------------------------
611 * Create thread on the current core. 618 * Create a thread
612 * Return ID if context area could be allocated, else -1. 619 * If using a dual core architecture, specify which core to start the thread
620 * on, and whether to fall back to the other core if it can't be created
621 * Return ID if context area could be allocated, else NULL.
613 *--------------------------------------------------------------------------- 622 *---------------------------------------------------------------------------
614 */ 623 */
615struct thread_entry* 624struct thread_entry*
616 create_thread(void (*function)(void), void* stack, int stack_size, 625 create_thread(void (*function)(void), void* stack, int stack_size,
617 const char *name IF_PRIO(, int priority)) 626 const char *name IF_PRIO(, int priority)
618{ 627 IF_COP(, unsigned int core, bool fallback))
619 return create_thread_on_core(CURRENT_CORE, function, stack, stack_size,
620 name IF_PRIO(, priority));
621}
622
623/*---------------------------------------------------------------------------
624 * Create thread on a specific core.
625 * Return ID if context area could be allocated, else -1.
626 *---------------------------------------------------------------------------
627 */
628struct thread_entry*
629 create_thread_on_core(unsigned int core, void (*function)(void),
630 void* stack, int stack_size,
631 const char *name IF_PRIO(, int priority))
632{ 628{
633 unsigned int i; 629 unsigned int i;
634 unsigned int stacklen; 630 unsigned int stacklen;
@@ -637,6 +633,29 @@ struct thread_entry*
637 struct regs *regs; 633 struct regs *regs;
638 struct thread_entry *thread; 634 struct thread_entry *thread;
639 635
636/*****
637 * Ugly code alert!
638 * To prevent ifdef hell while keeping the binary size down, we define
639 * core here if it hasn't been passed as a parameter
640 *****/
641#if NUM_CORES == 1
642#define core CPU
643#endif
644
645#if NUM_CORES > 1
646/* If the kernel hasn't initialised on the COP (most likely due to an old
647 * bootloader) then refuse to start threads on the COP
648 */
649 if((core == COP) && !kernel_running_on_cop)
650 {
651 if (fallback)
652 return create_thread(function, stack, stack_size, name
653 IF_PRIO(, priority) IF_COP(, CPU, false));
654 else
655 return NULL;
656 }
657#endif
658
640 for (n = 0; n < MAXTHREADS; n++) 659 for (n = 0; n < MAXTHREADS; n++)
641 { 660 {
642 if (cores[core].threads[n].name == NULL) 661 if (cores[core].threads[n].name == NULL)
@@ -644,8 +663,15 @@ struct thread_entry*
644 } 663 }
645 664
646 if (n == MAXTHREADS) 665 if (n == MAXTHREADS)
647 return NULL; 666 {
648 667#if NUM_CORES > 1
668 if (fallback)
669 return create_thread(function, stack, stack_size, name
670 IF_PRIO(, priority) IF_COP(, 1 - core, fallback));
671 else
672#endif
673 return NULL;
674 }
649 675
650 /* Munge the stack to make it easy to spot stack overflows */ 676 /* Munge the stack to make it easy to spot stack overflows */
651 stacklen = stack_size / sizeof(int); 677 stacklen = stack_size / sizeof(int);
@@ -677,6 +703,9 @@ struct thread_entry*
677 THREAD_CPU_INIT(core, thread); 703 THREAD_CPU_INIT(core, thread);
678 704
679 return thread; 705 return thread;
706#if NUM_CORES == 1
707#undef core
708#endif
680} 709}
681 710
682#ifdef HAVE_SCHEDULER_BOOSTCTRL 711#ifdef HAVE_SCHEDULER_BOOSTCTRL
@@ -751,7 +780,8 @@ void init_threads(void)
751{ 780{
752 unsigned int core = CURRENT_CORE; 781 unsigned int core = CURRENT_CORE;
753 782
754 memset(cores, 0, sizeof cores); 783 if (core == CPU)
784 memset(cores, 0, sizeof cores);
755 cores[core].sleeping = NULL; 785 cores[core].sleeping = NULL;
756 cores[core].running = NULL; 786 cores[core].running = NULL;
757 cores[core].threads[0].name = main_thread_name; 787 cores[core].threads[0].name = main_thread_name;
@@ -779,6 +809,10 @@ void init_threads(void)
779#endif 809#endif
780 } 810 }
781 cores[core].threads[0].context.start = 0; /* thread 0 already running */ 811 cores[core].threads[0].context.start = 0; /* thread 0 already running */
812#if NUM_CORES > 1
813 if(core == COP)
814 kernel_running_on_cop = true; /* can we use context.start for this? */
815#endif
782} 816}
783 817
784int thread_stack_usage(const struct thread_entry *thread) 818int thread_stack_usage(const struct thread_entry *thread)
diff --git a/firmware/usb.c b/firmware/usb.c
index ee08b04caa..cf4641950e 100644
--- a/firmware/usb.c
+++ b/firmware/usb.c
@@ -428,7 +428,8 @@ void usb_init(void)
428#ifndef BOOTLOADER 428#ifndef BOOTLOADER
429 queue_init(&usb_queue, true); 429 queue_init(&usb_queue, true);
430 create_thread(usb_thread, usb_stack, sizeof(usb_stack), 430 create_thread(usb_thread, usb_stack, sizeof(usb_stack),
431 usb_thread_name IF_PRIO(, PRIORITY_SYSTEM)); 431 usb_thread_name IF_PRIO(, PRIORITY_SYSTEM)
432 IF_COP(, CPU, false));
432 433
433 tick_add_task(usb_tick); 434 tick_add_task(usb_tick);
434#endif 435#endif