diff --git a/firmware/SOURCES b/firmware/SOURCES index 644b7d7148..7e26ca0bc4 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 35994ce5f2..63f0567165 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 2d4ccde267..b1a4e62a81 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 9df90a2344..39e4efab49 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 -#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 0000000000..4654c7c845 --- /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 0000000000..e9f330cf8e --- /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 99cab9e1a3..37c367fdc4 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 ca23cb890c..1ac16b697a 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;