diff --git a/apps/main.c b/apps/main.c
index fdc5c0f..50c77f7 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -271,6 +271,9 @@ #if defined(CONFIG_CHARGING) && (CONFIG_
     /* if nobody initialized ATA before, I consider this a cold start */
     bool coldstart = (PACR2 & 0x4000) != 0; /* starting from Flash */
 #endif
+#ifdef CPU_PP
+    COP_CTL = PROC_WAKE;
+#endif
     system_init();
     kernel_init();
 
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 091de05..e5d681c 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -234,14 +234,8 @@ #if (CONFIG_CPU == PP5002) || (CONFIG_CP
 #define CPU_PP
 
 /* PP family has dual cores */
-#if 0
-/* Keep it as single core until dual core support is ready */
 #define NUM_CORES 2
 #define CURRENT_CORE current_core()
-#endif
-
-#define NUM_CORES 1
-#define CURRENT_CORE 0
 #else
 #define NUM_CORES 1
 #define CURRENT_CORE 0
diff --git a/firmware/export/pp5002.h b/firmware/export/pp5002.h
index 59056e1..998ab73 100644
--- a/firmware/export/pp5002.h
+++ b/firmware/export/pp5002.h
@@ -62,6 +62,9 @@ #define DEV_EN (*(volatile unsigned long
 #define CPU_INT_STAT     (*(volatile unsigned long*)(0xcf001000))
 #define CPU_INT_EN       (*(volatile unsigned long*)(0xcf001024))
 #define CPU_INT_CLR      (*(volatile unsigned long*)(0xcf001028))
+#define COP_INT_STAT     (*(volatile unsigned long*)(0xcf001010)) /* A guess */
+#define COP_INT_EN       (*(volatile unsigned long*)(0xcf001034))
+#define COP_INT_CLR      (*(volatile unsigned long*)(0xcf001038))
 
 #define USB2D_IDENT         (*(volatile unsigned long*)(0xc5000000))
 #define USB_STATUS          (*(volatile unsigned long*)(0xc50001a4))
diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h
index be997e9..8bc9f3d 100644
--- a/firmware/export/pp5020.h
+++ b/firmware/export/pp5020.h
@@ -139,6 +139,12 @@ #define CPU_INT_EN       (*(volatile uns
 #define CPU_HI_INT_EN    (*(volatile unsigned long*)(0x60004124))
 #define CPU_INT_CLR      (*(volatile unsigned long*)(0x60004028))
 #define CPU_HI_INT_CLR   (*(volatile unsigned long*)(0x60004128))
+#define COP_INT_STAT     (*(volatile unsigned long*)(0x60004004)) /* Guessed */
+#define COP_HI_INT_STAT  (*(volatile unsigned long*)(0x60004104)) /* From IPL*/
+#define COP_INT_EN       (*(volatile unsigned long*)(0x60004034)) /* Guessed */
+#define COP_HI_INT_EN    (*(volatile unsigned long*)(0x60004134)) /* From IPL*/
+#define COP_INT_CLR      (*(volatile unsigned long*)(0x60004038)) /* Guessed */
+#define COP_HI_INT_CLR   (*(volatile unsigned long*)(0x60004138)) /* From IPL*/
        
 #define TIMER1_IRQ   0
 #define TIMER2_IRQ   1
diff --git a/firmware/kernel.c b/firmware/kernel.c
index 6415f71..395339e 100644
--- a/firmware/kernel.c
+++ b/firmware/kernel.c
@@ -45,10 +45,13 @@ void kernel_init(void)
     /* Init the threading API */
     init_threads();
     
-    memset(tick_funcs, 0, sizeof(tick_funcs));
+    if(CURRENT_CORE == CPU)
+    {
+        memset(tick_funcs, 0, sizeof(tick_funcs));
 
-    num_queues = 0;
-    memset(all_queues, 0, sizeof(all_queues));
+        num_queues = 0;
+        memset(all_queues, 0, sizeof(all_queues));
+    }
 
     tick_start(1000/HZ);
 }
@@ -366,28 +369,36 @@ void TIMER1(void)
     int i;
 
     TIMER1_VAL; /* Read value to ack IRQ */
-    /* Run through the list of tick tasks */
-    for (i = 0;i < MAX_NUM_TICK_TASKS;i++)
+    /* Run through the list of tick tasks (using main core) */
+    if (CURRENT_CORE == CPU)
     {
-        if (tick_funcs[i])
+        for (i = 0;i < MAX_NUM_TICK_TASKS;i++)
         {
-            tick_funcs[i]();
+            if (tick_funcs[i])
+            {
+                tick_funcs[i]();
+            }
         }
-    }
 
-    current_tick++;
+        current_tick++;
+    }
 }
 #endif
 
 void tick_start(unsigned int interval_in_ms)
 {
 #ifndef BOOTLOADER
-    TIMER1_CFG = 0x0;
-    TIMER1_VAL;
-    /* enable timer */
-    TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1);
-    /* unmask interrupt source */
-    CPU_INT_EN = TIMER1_MASK;
+    if(CURRENT_CORE == CPU)
+    {
+        TIMER1_CFG = 0x0;
+        TIMER1_VAL;
+        /* enable timer */
+        TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1);
+        /* unmask interrupt source */
+        CPU_INT_EN = TIMER1_MASK;
+    } else {
+        COP_INT_EN = TIMER1_MASK;
+    }
 #else
     /* We don't enable interrupts in the bootloader */
     (void)interval_in_ms;
@@ -508,6 +519,29 @@ void mutex_init(struct mutex *m)
     m->thread = NULL;
 }
 
+#ifdef CPU_PP
+/* PortalPlayer chips have 2 cores, therefore need atomic mutexes */
+
+static inline bool test_and_set(bool *x, bool v)
+{
+    asm volatile (
+        "swpb %0, %0, [%1]\n"
+        : "+r"(v)
+        : "r"(x)
+    );
+    return v;
+}
+
+void mutex_lock(struct mutex *m)
+{
+    if (test_and_set(&m->locked,true))
+    {
+        /* Wait until the lock is open... */
+        block_thread(&m->thread, 0);
+    }
+}
+
+#else
 void mutex_lock(struct mutex *m)
 {
     if (m->locked)
@@ -519,6 +553,7 @@ void mutex_lock(struct mutex *m)
     /* ...and lock it */
     m->locked = true;
 }
+#endif
 
 void mutex_unlock(struct mutex *m)
 {
diff --git a/firmware/system.c b/firmware/system.c
index 6ba5dc6..0197a5e 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -1223,34 +1223,62 @@ extern void ipod_mini_button_int(void);
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
-    else if (CPU_HI_INT_STAT & GPIO_MASK)
-        ipod_mini_button_int();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (CPU_HI_INT_STAT & GPIO_MASK)
+            ipod_mini_button_int();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (COP_HI_INT_STAT & GPIO_MASK)
+            ipod_mini_button_int();
+    }
 }
 #elif (defined IRIVER_H10) || (defined IRIVER_H10_5GB) || defined(ELIO_TPJ1022)
 /* TODO: this should really be in the target tree, but moving it there caused
    crt0.S not to find it while linking */
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    }
 }
 #else
 extern void ipod_4g_button_int(void);
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
-    else if (CPU_HI_INT_STAT & I2C_MASK)
-        ipod_4g_button_int();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (CPU_HI_INT_STAT & I2C_MASK)
+            ipod_4g_button_int();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (COP_HI_INT_STAT & I2C_MASK)
+            ipod_4g_button_int();
+    }
 }
 #endif
 #endif /* BOOTLOADER */
@@ -1303,43 +1331,47 @@ void set_cpu_frequency(long frequency)
 {
     unsigned long postmult;
 
-    if (frequency == CPUFREQ_NORMAL)
-        postmult = CPUFREQ_NORMAL_MULT;
-    else if (frequency == CPUFREQ_MAX)
-        postmult = CPUFREQ_MAX_MULT;
-    else
-        postmult = CPUFREQ_DEFAULT_MULT;
-    cpu_frequency = frequency;
+    if (CURRENT_CORE == CPU)
+    {
+        if (frequency == CPUFREQ_NORMAL)
+            postmult = CPUFREQ_NORMAL_MULT;
+        else if (frequency == CPUFREQ_MAX)
+            postmult = CPUFREQ_MAX_MULT;
+        else
+            postmult = CPUFREQ_DEFAULT_MULT;
+        cpu_frequency = frequency;
 
-    /* Enable PLL? */
-    outl(inl(0x70000020) | (1<<30), 0x70000020);
+        /* Enable PLL? */
+        outl(inl(0x70000020) | (1<<30), 0x70000020);
 
-    /* Select 24MHz crystal as clock source? */
-    outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020);
+        /* Select 24MHz crystal as clock source? */
+        outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020);
 
-    /* Clock frequency = (24/8)*postmult */
-    outl(0xaa020000 | 8 | (postmult << 8), 0x60006034);
+        /* Clock frequency = (24/8)*postmult */
+        outl(0xaa020000 | 8 | (postmult << 8), 0x60006034);
 
-    /* Wait for PLL relock? */
-    udelay(2000);
+        /* Wait for PLL relock? */
+        udelay(2000);
 
-    /* Select PLL as clock source? */
-    outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020);
+        /* Select PLL as clock source? */
+        outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020);
 
 #if defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
-    /* We don't know why the timer interrupt gets disabled on the PP5020
-       based ipods, but without the following line, the 4Gs will freeze
-       when CPU frequency changing is enabled.
+        /* We don't know why the timer interrupt gets disabled on the PP5020
+           based ipods, but without the following line, the 4Gs will freeze
+           when CPU frequency changing is enabled.
 
-       Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used
-       elsewhere to enable interrupts) doesn't work, we need "|=".
+           Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used
+           elsewhere to enable interrupts) doesn't work, we need "|=".
 
-       It's not needed on the PP5021 and PP5022 ipods.
-    */
+           It's not needed on the PP5021 and PP5022 ipods.
+        */
 
-    /* unmask interrupt source */
-    CPU_INT_EN |= TIMER1_MASK;
+        /* unmask interrupt source */
+        CPU_INT_EN |= TIMER1_MASK;
+        COP_INT_EN |= TIMER1_MASK;
 #endif
+    }
 }
 #elif !defined(BOOTLOADER)
 void ipod_set_cpu_frequency(void)
@@ -1363,21 +1395,24 @@ #endif
 void system_init(void)
 {
 #ifndef BOOTLOADER
-    /* The hw revision is written to the last 4 bytes of SDRAM by the
-       bootloader - we save it before Rockbox overwrites it. */
-    ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
-
-    /* disable all irqs */
-    outl(-1, 0x60001138);
-    outl(-1, 0x60001128);
-    outl(-1, 0x6000111c);
-
-    outl(-1, 0x60001038);
-    outl(-1, 0x60001028);
-    outl(-1, 0x6000101c);
+    if (CURRENT_CORE == CPU)
+    {
+        /* The hw revision is written to the last 4 bytes of SDRAM by the
+           bootloader - we save it before Rockbox overwrites it. */
+        ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
+
+        /* disable all irqs */
+        outl(-1, 0x60001138);
+        outl(-1, 0x60001128);
+        outl(-1, 0x6000111c);
+
+        outl(-1, 0x60001038);
+        outl(-1, 0x60001028);
+        outl(-1, 0x6000101c);
 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
-    ipod_set_cpu_frequency();
+        ipod_set_cpu_frequency();
 #endif
+    }
     ipod_init_cache();
 #endif
 }
@@ -1401,10 +1436,18 @@ extern void TIMER2(void);
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    }
 }
 
 #endif
@@ -1453,29 +1496,32 @@ void set_cpu_frequency(long frequency)
 {
     unsigned long postmult;
 
-    if (frequency == CPUFREQ_NORMAL)
-        postmult = CPUFREQ_NORMAL_MULT;
-    else if (frequency == CPUFREQ_MAX)
-        postmult = CPUFREQ_MAX_MULT;
-    else
-        postmult = CPUFREQ_DEFAULT_MULT;
-    cpu_frequency = frequency;
+    if (CURRENT_CORE == CPU)
+    {
+        if (frequency == CPUFREQ_NORMAL)
+            postmult = CPUFREQ_NORMAL_MULT;
+        else if (frequency == CPUFREQ_MAX)
+            postmult = CPUFREQ_MAX_MULT;
+        else
+            postmult = CPUFREQ_DEFAULT_MULT;
+        cpu_frequency = frequency;
 
-    outl(0x02, 0xcf005008);
-    outl(0x55, 0xcf00500c);
-    outl(0x6000, 0xcf005010);
+        outl(0x02, 0xcf005008);
+        outl(0x55, 0xcf00500c);
+        outl(0x6000, 0xcf005010);
 
-    /* Clock frequency = (24/8)*postmult */
-    outl(8, 0xcf005018);
-    outl(postmult, 0xcf00501c);
+        /* Clock frequency = (24/8)*postmult */
+        outl(8, 0xcf005018);
+        outl(postmult, 0xcf00501c);
 
-    outl(0xe000, 0xcf005010);
+        outl(0xe000, 0xcf005010);
 
-    /* Wait for PLL relock? */
-    udelay(2000);
+        /* Wait for PLL relock? */
+        udelay(2000);
 
-    /* Select PLL as clock source? */
-    outl(0xa8, 0xcf00500c);
+        /* Select PLL as clock source? */
+        outl(0xa8, 0xcf00500c);
+    }
 }
 #elif !defined(BOOTLOADER)
 static void ipod_set_cpu_speed(void)
@@ -1506,13 +1552,16 @@ #endif
 void system_init(void)
 {
 #ifndef BOOTLOADER
-    ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
-    outl(-1, 0xcf00101c);
-    outl(-1, 0xcf001028);
-    outl(-1, 0xcf001038);
+    if (CURRENT_CORE == CPU)
+    {
+        ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
+        outl(-1, 0xcf00101c);
+        outl(-1, 0xcf001028);
+        outl(-1, 0xcf001038);
 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
-    ipod_set_cpu_speed();
+        ipod_set_cpu_speed();
 #endif
+    }
     ipod_init_cache();
 #endif
 }
diff --git a/firmware/target/arm/crt0-pp.S b/firmware/target/arm/crt0-pp.S
index 3bf2805..7badc69 100644
--- a/firmware/target/arm/crt0-pp.S
+++ b/firmware/target/arm/crt0-pp.S
@@ -285,6 +285,19 @@ cop_init:
     strhi  r4, [r2], #4
     bhi    2b
 
+    /* Set up stack for IRQ mode */
+    msr    cpsr_c, #0xd2
+    ldr    sp, =cop_irq_stack
+    /* Set up stack for FIQ mode */
+    msr    cpsr_c, #0xd1
+    ldr    sp, =fiq_stack
+
+    /* Let abort and undefined modes use IRQ stack */
+    msr    cpsr_c, #0xd7
+    ldr    sp, =cop_irq_stack
+    msr    cpsr_c, #0xdb
+    ldr    sp, =cop_irq_stack
+
     ldr    sp, =cop_stackend
     bl     cop_main
     
@@ -368,6 +381,10 @@ #endif
     .space 256*4
 irq_stack:
 
+/* 256 words of COP IRQ stack */
+    .space 256*4
+cop_irq_stack:
+
 /* 256 words of FIQ stack */
     .space 256*4
 fiq_stack:
diff --git a/firmware/thread.c b/firmware/thread.c
index 1662740..4eada84 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -338,10 +338,13 @@ #ifdef CPU_COLDFIRE
 #elif CONFIG_CPU == SH7034
         and_b(0x7F, &SBYCR);
         asm volatile ("sleep");
-#elif CONFIG_CPU == PP5020
+#elif defined (CPU_PP)
         /* This should sleep the CPU. It appears to wake by itself on
            interrupts */
-        CPU_CTL = 0x80000000;
+        if (CURRENT_CORE == CPU)
+            CPU_CTL = PROC_SLEEP;
+        else
+            COP_CTL = PROC_SLEEP;
 #elif CONFIG_CPU == TCC730
 	    /* Sleep mode is triggered by the SYS instr on CalmRisc16.
          * Unfortunately, the manual doesn't specify which arg to use.
@@ -690,7 +693,8 @@ void init_threads(void)
 {
     unsigned int core = CURRENT_CORE;
 
-    memset(cores, 0, sizeof cores);
+    if (core == CPU)
+        memset(cores, 0, sizeof cores);
     cores[core].sleeping = NULL;
     cores[core].running = NULL;
     cores[core].threads[0].name = main_thread_name;
