[U-Boot] [PATCH] Update s3c24x0 timer implementation
Mark Norman
mpnorman at gmail.com
Sun Oct 23 13:52:40 CEST 2011
I have been doing some work to get U-Boot running on a samsung S3C2440
based SBC (QQ2440). I experienced several issues getting the board
running including "raise: Signal # 8 caught" errors being printed to
the console. After a bit of debugging with a JTAG debugger I found
the problems were due to arch/arm/cpu/arm920t/s3c24x0/timer.c using
several global variables prior to relocation to RAM. I noticed that
the global variable .bss section is shared with the .rel.text section.
Since the .rel.text section is required by the relocation code, I
assume that .bss global variables cannot be used until after
relocation?
After studying several other timer.c files I developed the following
patch which uses the global data struct to store the global variables.
I also restructured some of the code based on structure of the other
timer.c files. I have confirmed it works correctly on the SBC I have.
Regards
Mark Norman
The s3c24x0 timer has been updated to use the global_data struct.
Restructured code based on other timer.c files.
Updated comments and several parameters.
Signed-off-by: Mark Norman <mpnorman at gmail.com>
---
arch/arm/cpu/arm920t/s3c24x0/timer.c | 158 +++++++++++++--------------------
arch/arm/include/asm/global_data.h | 12 +--
2 files changed, 66 insertions(+), 104 deletions(-)
diff --git a/arch/arm/cpu/arm920t/s3c24x0/timer.c
b/arch/arm/cpu/arm920t/s3c24x0/timer.c
index 9571870..1552345 100644
--- a/arch/arm/cpu/arm920t/s3c24x0/timer.c
+++ b/arch/arm/cpu/arm920t/s3c24x0/timer.c
@@ -35,146 +35,112 @@
#include <asm/io.h>
#include <asm/arch/s3c24x0_cpu.h>
-int timer_load_val = 0;
-static ulong timer_clk;
+DECLARE_GLOBAL_DATA_PTR;
-/* macro to read the 16 bit timer */
-static inline ulong READ_TIMER(void)
+/* Read the 16 bit timer */
+static inline ulong read_timer(void)
{
struct s3c24x0_timers *timers = s3c24x0_get_base_timers();
-
return readl(&timers->tcnto4) & 0xffff;
}
-static ulong timestamp;
-static ulong lastdec;
-
int timer_init(void)
{
struct s3c24x0_timers *timers = s3c24x0_get_base_timers();
ulong tmr;
- /* use PWM Timer 4 because it has no output */
- /* prescaler for Timer 4 is 16 */
- writel(0x0f00, &timers->tcfg0);
- if (timer_load_val == 0) {
- /*
- * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
- * (default) and prescaler = 16. Should be 10390
- * @33.25MHz and 15625 @ 50 MHz
- */
- timer_load_val = get_PCLK() / (2 * 16 * 100);
- timer_clk = get_PCLK() / (2 * 16);
- }
- /* load value for 10 ms timeout */
- lastdec = timer_load_val;
- writel(timer_load_val, &timers->tcntb4);
- /* auto load, manual update of timer 4 */
+ /* Use PWM Timer 4 because it has no output.
+ * Prescaler is hard fixed at 250, divider at 2.
+ * This generates a Timer clock frequency of 100kHz (@PCLK=50MHz) and
+ * therefore 10us timer ticks.
+ */
+
+ /* Prescaler for Timer 4 is 250 */
+ const ulong prescaler = 250;
+ writel((prescaler-1) << 8, &timers->tcfg0);
+
+ /* Calculate timer freq, approx 100kHz @ PCLK=50MHz. */
+ gd->timer_rate_hz = get_PCLK() / (2 * prescaler);
+
+ /* Set timer for 0.5s timeout (50000 ticks @ 10us ticks). */
+ gd->timer_reset_value = 50000;
+ writel(gd->timer_reset_value, &timers->tcntb4);
+ gd->lastdec = gd->timer_reset_value;
+
+ /* Load the initial timer 4 count value using the manual update bit. */
tmr = (readl(&timers->tcon) & ~0x0700000) | 0x0600000;
writel(tmr, &timers->tcon);
- /* auto load, start timer 4 */
+
+ /* Configure timer 4 for auto reload and start it. */
tmr = (tmr & ~0x0700000) | 0x0500000;
writel(tmr, &timers->tcon);
- timestamp = 0;
+
+ gd->timestamp = 0;
return (0);
}
/*
- * timer without interrupts
+ * Get the number of ticks (in CONFIG_SYS_HZ resolution)
*/
-ulong get_timer(ulong base)
+unsigned long long get_ticks(void)
{
- return get_timer_masked() - base;
+ return get_timer(0);
}
-void __udelay (unsigned long usec)
+unsigned long get_timer_raw(void)
{
- ulong tmo;
- ulong start = get_ticks();
+ ulong now = read_timer();
- tmo = usec / 1000;
- tmo *= (timer_load_val * 100);
- tmo /= 1000;
+ if (gd->lastdec >= now) {
+ /* normal mode */
+ gd->timestamp += gd->lastdec - now;
+ } else {
+ /* we have an overflow ... */
+ gd->timestamp += gd->lastdec + gd->timer_reset_value - now;
+ }
+ gd->lastdec = now;
- while ((ulong) (get_ticks() - start) < tmo)
- /*NOP*/;
+ return gd->timestamp;
}
-ulong get_timer_masked(void)
+/*
+ * This function is derived from PowerPC code (timebase clock frequency).
+ * On ARM it returns the number of timer ticks per second.
+ */
+ulong get_tbclk(void)
{
- ulong tmr = get_ticks();
-
- return tmr / (timer_clk / CONFIG_SYS_HZ);
+ return CONFIG_SYS_HZ;
}
-void udelay_masked(unsigned long usec)
+ulong get_timer_masked(void)
{
- ulong tmo;
- ulong endtime;
- signed long diff;
-
- if (usec >= 1000) {
- tmo = usec / 1000;
- tmo *= (timer_load_val * 100);
- tmo /= 1000;
- } else {
- tmo = usec * (timer_load_val * 100);
- tmo /= (1000 * 1000);
- }
+ unsigned long tmr = get_timer_raw();
- endtime = get_ticks() + tmo;
+ return (tmr * CONFIG_SYS_HZ) / gd->timer_rate_hz;
+}
- do {
- ulong now = get_ticks();
- diff = endtime - now;
- } while (diff >= 0);
+ulong get_timer(ulong base)
+{
+ return get_timer_masked() - base;
}
-/*
- * This function is derived from PowerPC code (read timebase as long long).
- * On ARM it just returns the timer value.
- */
-unsigned long long get_ticks(void)
+void __udelay(unsigned long usec)
{
- ulong now = READ_TIMER();
+ unsigned long tmp;
+ unsigned long tmo;
- if (lastdec >= now) {
- /* normal mode */
- timestamp += lastdec - now;
- } else {
- /* we have an overflow ... */
- timestamp += lastdec + timer_load_val - now;
- }
- lastdec = now;
+ /* convert usec to ticks. */
+ tmo = ((gd->timer_rate_hz / 1000) * usec) / 1000;
- return timestamp;
-}
+ tmp = get_timer_raw() + tmo; /* get current timestamp */
-/*
- * This function is derived from PowerPC code (timebase clock frequency).
- * On ARM it returns the number of timer ticks per second.
- */
-ulong get_tbclk(void)
-{
- ulong tbclk;
-
-#if defined(CONFIG_SMDK2400)
- tbclk = timer_load_val * 100;
-#elif defined(CONFIG_SBC2410X) || \
- defined(CONFIG_SMDK2410) || \
- defined(CONFIG_S3C2440) || \
- defined(CONFIG_VCMA9)
- tbclk = CONFIG_SYS_HZ;
-#else
-# error "tbclk not configured"
-#endif
-
- return tbclk;
+ while (get_timer_raw() < tmp) /* loop till event */
+ /*NOP*/;
}
/*
- * reset the cpu by setting up the watchdog timer and let him time out
+ * Reset the cpu by setting up the watchdog timer and let him time out
*/
void reset_cpu(ulong ignored)
{
diff --git a/arch/arm/include/asm/global_data.h
b/arch/arm/include/asm/global_data.h
index fac98d5..a20a9f7 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -38,9 +38,6 @@ typedef struct global_data {
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
-#ifdef CONFIG_PRE_CONSOLE_BUFFER
- unsigned long precon_buf_idx; /* Pre-Console buffer index */
-#endif
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
@@ -67,6 +64,10 @@ typedef struct global_data {
#ifdef CONFIG_IXP425
unsigned long timestamp;
#endif
+#ifdef CONFIG_S3C24X0
+ unsigned long lastdec;
+ unsigned long timestamp;
+#endif
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long mon_len; /* monitor len */
@@ -78,11 +79,6 @@ typedef struct global_data {
#endif
void **jt; /* jump table */
char env_buf[32]; /* buffer for getenv() before reloc. */
-#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER)
- unsigned long post_log_word; /* Record POST activities */
- unsigned long post_log_res; /* success of POST test */
- unsigned long post_init_f_time; /* When post_init_f started */
-#endif
} gd_t;
/*
--
1.7.1
More information about the U-Boot
mailing list