summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/export/timer.h7
-rw-r--r--firmware/kernel.c36
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c51
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c127
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h39
-rw-r--r--firmware/target/arm/system-arm.h44
-rw-r--r--firmware/timer.c43
8 files changed, 265 insertions, 84 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 644b7d7..7e26ca0 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -520,10 +520,12 @@ target/arm/s3c2440/gigabeat-fx/ata-meg-fx.c
target/arm/s3c2440/gigabeat-fx/backlight-meg-fx.c
target/arm/s3c2440/gigabeat-fx/button-meg-fx.c
target/arm/s3c2440/gigabeat-fx/i2c-meg-fx.c
+target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c
target/arm/s3c2440/gigabeat-fx/lcd-as-meg-fx.S
target/arm/s3c2440/gigabeat-fx/lcd-meg-fx.c
target/arm/s3c2440/gigabeat-fx/power-meg-fx.c
target/arm/s3c2440/gigabeat-fx/sc606-meg-fx.c
+target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c
target/arm/s3c2440/gigabeat-fx/usb-meg-fx.c
target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c
target/arm/s3c2440/gigabeat-fx/dma_start.c
diff --git a/firmware/export/timer.h b/firmware/export/timer.h
index 35994ce..63f0567 100644
--- a/firmware/export/timer.h
+++ b/firmware/export/timer.h
@@ -25,7 +25,6 @@
#ifndef SIMULATOR
-
#if defined(CPU_PP)
/* Portalplayer chips use a microsecond timer. */
#define TIMER_FREQ 1000000
@@ -34,6 +33,8 @@
#define TIMER_FREQ (CPU_FREQ/2)
#elif CONFIG_CPU == PNX0101
#define TIMER_FREQ 3000000
+#elif CONFIG_CPU == S3C2440
+ #include "timer-target.h"
#else
#define TIMER_FREQ CPU_FREQ
#endif
@@ -46,5 +47,9 @@ void timers_adjust_prescale(int multiplier, bool enable_irq);
#endif
void timer_unregister(void);
+/* For target-specific interface use */
+extern void (*pfn_timer)(void);
+extern void (*pfn_unregister)(void);
+
#endif /* !SIMULATOR */
#endif /* __TIMER_H__ */
diff --git a/firmware/kernel.c b/firmware/kernel.c
index 2d4ccde..b1a4e62 100644
--- a/firmware/kernel.c
+++ b/firmware/kernel.c
@@ -29,7 +29,7 @@
volatile long current_tick NOCACHEDATA_ATTR = 0;
#endif
-static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
+void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
/* This array holds all queues that are initiated. It is used for broadcast. */
static struct event_queue *all_queues[32] NOCACHEBSS_ATTR;
@@ -708,40 +708,6 @@ void tick_start(unsigned int interval_in_ms)
TIMER0.ctrl |= 0x80; /* Enable the counter */
}
-#elif CONFIG_CPU == S3C2440
-void tick_start(unsigned int interval_in_ms)
-{
- TCON &= ~(1 << 20); // stop timer 4
- // TODO: this constant depends on dividers settings inherited from
- // firmware. Set them explicitly somwhere.
- TCNTB4 = 12193 * interval_in_ms / 1000;
- TCON |= 1 << 21; // set manual bit
- TCON &= ~(1 << 21); // reset manual bit
- TCON |= 1 << 22; //interval mode
- TCON |= (1 << 20); // start timer 4
-
- INTMOD &= ~(1 << 14); // timer 4 to IRQ mode
- INTMSK &= ~(1 << 14); // timer 4 unmask interrupts
-}
-
-void TIMER4(void)
-{
- int i;
-
- SRCPND = TIMER4_MASK;
- INTPND = TIMER4_MASK;
-
- /* Run through the list of tick tasks */
- for(i = 0; i < MAX_NUM_TICK_TASKS; i++)
- {
- if(tick_funcs[i])
- {
- tick_funcs[i]();
- }
- }
-
- current_tick++;
-}
#endif
int tick_add_task(void (*f)(void))
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c
index 9df90a2..39e4efa 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c
@@ -1,13 +1,49 @@
+#include "config.h"
+#include "system.h"
#include "kernel.h"
+#include "timer.h"
#include "thread.h"
-#include <stdio.h>
-#include "lcd.h"
-
extern void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
-void timer4(void) {
- int i;
+void tick_start(unsigned int interval_in_ms)
+{
+ /*
+ * Based on default PCLK of 49.1568MHz - scaling chosen to give
+ * remainder-free result for tick interval of 10ms (100Hz)
+ * Timer input clock frequency =
+ * fPCLK / {prescaler value+1} / {divider value}
+ * TIMER_FREQ = 49156800 / 2
+ * 13300 = TIMER_FREQ / 231 / 8
+ * 49156800 = 19*(11)*(7)*7*5*5*(3)*2*2*2*2*2*2
+ * 231 = 11*7*3
+ */
+
+ /* stop timer 4 */
+ TCON &= ~(1 << 20);
+ /* Set the count for timer 4 */
+ TCNTB4 = (TIMER_FREQ / 231 / 8) * interval_in_ms / 1000;
+ /* Set the the prescaler value for timers 2,3, and 4 */
+ TCFG0 = (TCFG0 & ~0xff00) | ((231-1) << 8);
+ /* MUX4 = 1/16 */
+ TCFG1 = (TCFG1 & ~0xff0000) | 0x030000;
+ /* set manual bit */
+ TCON |= 1 << 21;
+ /* reset manual bit */
+ TCON &= ~(1 << 21);
+ /* interval mode */
+ TCON |= 1 << 22;
+ /* start timer 4 */
+ TCON |= (1 << 20);
+
+ /* timer 4 unmask interrupts */
+ INTMSK &= ~TIMER4_MASK;
+}
+
+void TIMER4(void)
+{
+ int i;
+
/* Run through the list of tick tasks */
for(i = 0; i < MAX_NUM_TICK_TASKS; i++)
{
@@ -19,7 +55,6 @@ void timer4(void) {
current_tick++;
- /* following needs to be fixed. */
- /*wake_up_thread();*/
+ SRCPND = TIMER4_MASK;
+ INTPND = TIMER4_MASK;
}
-
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c
new file mode 100644
index 0000000..4654c7c
--- /dev/null
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c
@@ -0,0 +1,127 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2007 by Michael Sevakis
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#include "config.h"
+#include "cpu.h"
+#include "system.h"
+#include "timer.h"
+#include "logf.h"
+
+/* GPB0/TOUT0 should already have been configured as output so that pin
+ should not be a functional pin and TIMER0 output unseen there */
+void TIMER0(void)
+{
+ if (pfn_timer != NULL)
+ pfn_timer();
+
+ SRCPND = TIMER0_MASK;
+ INTPND = TIMER0_MASK;
+}
+
+static void stop_timer(void)
+{
+ /* mask interrupt */
+ INTMSK |= TIMER0_MASK;
+
+ /* stop any running TIMER0 */
+ TCON &= ~(1 << 0);
+
+ /* clear pending */
+ SRCPND = TIMER0_MASK;
+ INTPND = TIMER0_MASK;
+}
+
+bool __timer_set(long cycles, bool start)
+{
+ bool retval = false;
+
+ /* Find the minimum factor that puts the counter in range 1-65535 */
+ unsigned int prescaler = (cycles + 65534) / 65535;
+
+ /* Maximum divider setting is x / 256 / 16 = x / 4096 */
+ if (prescaler <= 4096)
+ {
+ int oldlevel;
+ unsigned int divider;
+
+ if (start && pfn_unregister != NULL)
+ {
+ pfn_unregister();
+ pfn_unregister = NULL;
+ }
+
+ oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+ TCMPB0 = 0;
+ TCNTB0 = (unsigned int)cycles / prescaler;
+
+ /* Max prescale is 255+1 */
+ for (divider = 0; prescaler > 256; prescaler >>= 1, divider++);
+
+ TCFG0 = (TCFG0 & ~0xff) | (prescaler - 1);
+ TCFG1 = (TCFG1 & ~0xf) | divider;
+
+ set_irq_level(oldlevel);
+
+ retval = true;
+ }
+
+ return retval;
+}
+
+bool __timer_register(void)
+{
+ bool retval = true;
+
+ int oldstatus = set_interrupt_status(IRQ_FIQ_DISABLED, IRQ_FIQ_STATUS);
+
+ stop_timer();
+
+ /* neurosis - make sure something didn't set GPB0 to TOUT0 */
+ if ((GPBCON & 0x3) != 0x2)
+ {
+ /* manual update: on (to reset count) */
+ TCON |= (1 << 1);
+ /* dead zone: off, inverter: off, manual off */
+ TCON &= ~((1 << 4) | (1 << 2) | (1 << 1));
+ /* interval mode (auto reload): on */
+ TCON |= (1 << 3);
+ /* start timer */
+ TCON |= (1 << 0);
+ /* unmask interrupt */
+ INTMSK &= ~TIMER0_MASK;
+ }
+
+ if (!(TCON & (1 << 0)))
+ {
+ /* timer could not be started due to config error */
+ logf("Timer error: GPB0 set to TOUT0");
+ retval = false;
+ }
+
+ set_interrupt_status(oldstatus, IRQ_FIQ_STATUS);
+
+ return retval;
+}
+
+void __timer_unregister(void)
+{
+ int oldstatus = set_interrupt_status(IRQ_FIQ_DISABLED, IRQ_FIQ_STATUS);
+ stop_timer();
+ set_interrupt_status(oldstatus, IRQ_FIQ_STATUS);
+}
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h
new file mode 100644
index 0000000..e9f330c
--- /dev/null
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2007 by Michael Sevakis
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#ifndef TIMER_TARGET_H
+#define TIMER_TARGET_H
+
+/* timer is based on PCLK and minimum division is 2 */
+#define TIMER_FREQ (49156800/2)
+
+bool __timer_set(long cycles, bool set);
+bool __timer_register(void);
+void __timer_unregister(void);
+
+#define __TIMER_SET(cycles, set) \
+ __timer_set(cycles, set)
+
+#define __TIMER_REGISTER(reg_prio, unregister_callback, cycles, \
+ int_prio, timer_callback) \
+ __timer_register()
+
+#define __TIMER_UNREGISTER(...) \
+ __timer_unregister()
+
+#endif /* TIMER_TARGET_H */
diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h
index 99cab9e..37c367f 100644
--- a/firmware/target/arm/system-arm.h
+++ b/firmware/target/arm/system-arm.h
@@ -87,23 +87,6 @@ static inline uint32_t swap_odd_even32(uint32_t value)
return value;
}
-#define HIGHEST_IRQ_LEVEL (0x80)
-
-static inline int set_irq_level(int level)
-{
- unsigned long cpsr;
- int oldlevel;
- /* Read the old level and set the new one */
- asm volatile (
- "mrs %1, cpsr \n"
- "bic %0, %1, #0x80 \n"
- "orr %0, %0, %2 \n"
- "msr cpsr_c, %0 \n"
- : "=&r,r"(cpsr), "=&r,r"(oldlevel) : "r,i"(level & 0x80)
- );
- return oldlevel;
-}
-
static inline void set_fiq_handler(void(*fiq_handler)(void))
{
/* Install the FIQ handler */
@@ -133,22 +116,35 @@ static inline void disable_fiq(void)
}
/* This one returns the old status */
-#define FIQ_ENABLED 0x00
-#define FIQ_DISABLED 0x40
-static inline int set_fiq_status(int status)
+#define IRQ_ENABLED 0x00
+#define IRQ_DISABLED 0x80
+#define IRQ_STATUS 0x80
+#define FIQ_ENABLED 0x00
+#define FIQ_DISABLED 0x40
+#define FIQ_STATUS 0x40
+#define IRQ_FIQ_ENABLED 0x00
+#define IRQ_FIQ_DISABLED 0xc0
+#define IRQ_FIQ_STATUS 0xc0
+#define HIGHEST_IRQ_LEVEL IRQ_DISABLED
+
+#define set_irq_level(status) set_interrupt_status((status), IRQ_STATUS)
+#define set_fiq_status(status) set_interrupt_status((status), FIQ_STATUS)
+
+static inline int set_interrupt_status(int status, int mask)
{
unsigned long cpsr;
int oldstatus;
- /* Read the old level and set the new one */
+ /* Read the old levels and set the new ones */
asm volatile (
"mrs %1, cpsr \n"
- "bic %0, %1, #0x40 \n"
+ "bic %0, %1, %[mask] \n"
"orr %0, %0, %2 \n"
"msr cpsr_c, %0 \n"
- : "=&r,r"(cpsr), "=&r,r"(oldstatus) : "r,i"(status & 0x40)
+ : "=&r,r"(cpsr), "=&r,r"(oldstatus)
+ : "r,i"(status & mask), [mask]"i,i"(mask)
);
+
return oldstatus;
}
-
#endif /* SYSTEM_ARM_H */
diff --git a/firmware/timer.c b/firmware/timer.c
index ca23cb8..1ac16b6 100644
--- a/firmware/timer.c
+++ b/firmware/timer.c
@@ -22,10 +22,11 @@
#include "cpu.h"
#include "system.h"
#include "timer.h"
+#include "logf.h"
static int timer_prio = -1;
-static void (*pfn_timer)(void) = NULL; /* timer callback */
-static void (*pfn_unregister)(void) = NULL; /* unregister callback */
+void (*pfn_timer)(void) = NULL; /* timer callback */
+void (*pfn_unregister)(void) = NULL; /* unregister callback */
#ifdef CPU_COLDFIRE
static int base_prescale;
#elif defined CPU_PP || CONFIG_CPU == PNX0101
@@ -123,9 +124,9 @@ static bool timer_set(long cycles, bool start)
}
else
cycles_new = cycles;
-#endif
-#if CONFIG_CPU == SH7034
+ return true;
+#elif CONFIG_CPU == SH7034
if (prescale > 8)
return false;
@@ -150,6 +151,7 @@ static bool timer_set(long cycles, bool start)
TCNT4 = 0;
and_b(~0x01, &TSR4); /* clear an eventual interrupt */
+ return true;
#elif defined CPU_COLDFIRE
if (prescale > 4096/CPUFREQ_MAX_MULT)
return false;
@@ -186,6 +188,8 @@ static bool timer_set(long cycles, bool start)
if (start || (TCN1 >= TRR1))
TCN1 = 0; /* reset the timer */
TER1 = 0xff; /* clear all events */
+
+ return true;
#elif defined(CPU_PP)
if (cycles > 0x20000000 || cycles < 2)
return false;
@@ -203,11 +207,10 @@ static bool timer_set(long cycles, bool start)
else
cycles_new = cycles;
-#elif CONFIG_CPU == S3C2440 /* TODO: Implement for the Gigabeat */
- (void)start;
- (void)cycles;
-#endif /* CONFIG_CPU */
return true;
+#elif CONFIG_CPU == S3C2440
+ return __TIMER_SET(cycles, start);
+#endif /* CONFIG_CPU */
}
#ifdef CPU_COLDFIRE
@@ -236,16 +239,9 @@ bool timer_register(int reg_prio, void (*unregister_callback)(void),
if (reg_prio <= timer_prio || cycles == 0)
return false;
-#if defined(CPU_PP) || (CONFIG_CPU==PNX0101) || (CONFIG_CPU==S3C2440)
- /* TODO: Implement for PortalPlayer and iFP (if possible) */
- (void)int_prio;
-#endif
-
#if CONFIG_CPU == SH7034
if (int_prio < 1 || int_prio > 15)
return false;
-#elif defined CPU_COLDFIRE
- (void)int_prio;
#endif
if (!timer_set(cycles, true))
@@ -258,18 +254,31 @@ bool timer_register(int reg_prio, void (*unregister_callback)(void),
#if CONFIG_CPU == SH7034
IPRD = (IPRD & 0xFF0F) | int_prio << 4; /* interrupt priority */
or_b(0x10, &TSTR); /* start timer 4 */
+ return true;
#elif defined CPU_COLDFIRE
ICR2 = 0x90; /* interrupt on level 4.0 */
and_l(~(1<<10), &IMR);
TMR1 |= 1; /* start timer */
+ return true;
#elif defined(CPU_PP)
/* unmask interrupt source */
CPU_INT_EN = TIMER2_MASK;
+ return true;
#elif CONFIG_CPU == PNX0101
irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
irq_enable_int(IRQ_TIMER1);
-#endif
return true;
+#elif CONFIG_CPU == S3C2440
+ return __TIMER_REGISTER(reg_prio, unregister_callback, cycles,
+ int_prio, timer_callback);
+#endif
+ /* Cover for targets that don't use all these */
+ (void)reg_prio;
+ (void)unregister_callback;
+ (void)cycles;
+ /* TODO: Implement for PortalPlayer and iFP (if possible) */
+ (void)int_prio;
+ (void)timer_callback;
}
bool timer_set_period(long cycles)
@@ -291,6 +300,8 @@ void timer_unregister(void)
#elif CONFIG_CPU == PNX0101
TIMER1.ctrl &= ~0x80; /* disable timer 1 */
irq_disable_int(5);
+#elif CONFIG_CPU == S3C2440
+ __TIMER_UNREGISTER();
#endif
pfn_timer = NULL;
pfn_unregister = NULL;