Patchwork [U-Boot] Update s3c24x0 timer implementation

login
register
mail settings
Submitter Mark Norman
Date Oct. 24, 2011, 11:27 a.m.
Message ID <CAJtrzLNz3TBBRHoNtHkuGci4YJZ9PLrJfvGaR_XdVbrdcprxbQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/121328/
State Superseded
Headers show

Comments

Mark Norman - Oct. 24, 2011, 11:27 a.m.
Dear Wolfgang Denk,

Thank you for your response.

>>  Since the .rel.text section is required by the relocation code, I
>> assume that .bss global variables cannot be used until after
>> relocation?
>
> Why do you have to make such assumptions?  That's documented
> behaviour.  Didn't you RTFM?
>

I thought I had done a thorough search previously but after receiving
your response I managed to find some of the details outlined in the
README file.

I have attached an updated patch below which hopefully addresses the
other issues you highlighted.

Kind 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@gmail.com>
---
 arch/arm/cpu/arm920t/s3c24x0/timer.c |  155 +++++++++++++--------------------
 arch/arm/include/asm/global_data.h   |    4 +
 2 files changed, 65 insertions(+), 94 deletions(-)
Marek Vasut - Oct. 24, 2011, 1:58 p.m.
> Dear Wolfgang Denk,
> 
> Thank you for your response.
> 
> >>  Since the .rel.text section is required by the relocation code, I
> >> assume that .bss global variables cannot be used until after
> >> relocation?
> > 
> > Why do you have to make such assumptions?  That's documented
> > behaviour.  Didn't you RTFM?
> 
> I thought I had done a thorough search previously but after receiving
> your response I managed to find some of the details outlined in the
> README file.
> 
> I have attached an updated patch below which hopefully addresses the
> other issues you highlighted.

Hehe, you should RTFM ;-) http://www.denx.de/wiki/U-Boot/Patches

Generally, use git send-email to send the patch.

> 
> Kind 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@gmail.com>
> ---
>  arch/arm/cpu/arm920t/s3c24x0/timer.c |  155
> +++++++++++++-------------------- arch/arm/include/asm/global_data.h   |  
>  4 +
>  2 files changed, 65 insertions(+), 94 deletions(-)
> 
> diff --git a/arch/arm/cpu/arm920t/s3c24x0/timer.c
> b/arch/arm/cpu/arm920t/s3c24x0/timer.c
> index 9571870..5695c62 100644
> --- a/arch/arm/cpu/arm920t/s3c24x0/timer.c
> +++ b/arch/arm/cpu/arm920t/s3c24x0/timer.c
> @@ -35,142 +35,109 @@
>  #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)
>  {
> +	/*
> +	 * PWM Timer 4 is used 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.
> +	 */
> +	const ulong prescaler = 250;
>  	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 */
> +	/* Set prescaler for Timer 4 */
> +	writel((prescaler-1) << 8, &timers->tcfg0);

Can we get rid of the magic numbers please?

> +
> +	/* Calculate timer freq, approx 100kHz @ PCLK=50MHz. */
> +	gd->timer_rate_hz = get_PCLK() / (2 * prescaler);

Can get_PCLK() be renamed to be lower-case only?

> +
> +	/* 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;

Magic values :-(

>  	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);

return 0;

if you're cleaning this crap up, do it all at once ;-)

>  }
> 
>  /*
> - * 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*/;
>  }
> 
>  /*
> diff --git a/arch/arm/include/asm/global_data.h
> b/arch/arm/include/asm/global_data.h
> index fac98d5..b836915 100644
> --- a/arch/arm/include/asm/global_data.h
> +++ b/arch/arm/include/asm/global_data.h
> @@ -67,6 +67,10 @@ typedef	struct	global_data {
>  #ifdef CONFIG_IXP425
>  	unsigned long	timestamp;
>  #endif
> +#ifdef CONFIG_S3C24X0
> +	unsigned long	lastdec;
> +	unsigned long	timestamp;
> +#endif

I wonder about this change ...

>  	unsigned long	relocaddr;	/* Start address of U-Boot in RAM */
>  	phys_size_t	ram_size;	/* RAM size */
>  	unsigned long	mon_len;	/* monitor len */

Cheers
Wolfgang Denk - Oct. 24, 2011, 7:34 p.m.
Dear Mark Norman,

In message <CAJtrzLNz3TBBRHoNtHkuGci4YJZ9PLrJfvGaR_XdVbrdcprxbQ@mail.gmail.com> you wrote:
> Dear Wolfgang Denk,
> 
> Thank you for your response.
> 
> >> =A0Since the .rel.text section is required by the relocation code, I
> >> assume that .bss global variables cannot be used until after
> >> relocation?
> >
> > Why do you have to make such assumptions? =A0That's documented
> > behaviour. =A0Didn't you RTFM?
> >
> 
> I thought I had done a thorough search previously but after receiving
> your response I managed to find some of the details outlined in the
> README file.
> 
> I have attached an updated patch below which hopefully addresses the
> other issues you highlighted.

It seems you read my message only halfway through.

> Kind 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@gmail.com>
> ---

I wrote before:

Then please submit a proper patch - these introductory comments don't
belong into the commit message and should be moved into the comment
section (below the "---" line).


Best regards,

Wolfgang Denk
Mark Norman - Oct. 31, 2011, 11:27 a.m.
Hi Marek Vasut,

>>
>> I have attached an updated patch below which hopefully addresses the
>> other issues you highlighted.
>
> Hehe, you should RTFM ;-) http://www.denx.de/wiki/U-Boot/Patches
>
> Generally, use git send-email to send the patch.
>

Thanks for your feedback.  I have submitted an updated patch using git
send-email.  I think I got the formatting right this time. :)


>>
>> 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@gmail.com>
>> ---
>>  arch/arm/cpu/arm920t/s3c24x0/timer.c |  155
>> +++++++++++++-------------------- arch/arm/include/asm/global_data.h   |
>>  4 +
>>  2 files changed, 65 insertions(+), 94 deletions(-)
>>
>> diff --git a/arch/arm/cpu/arm920t/s3c24x0/timer.c
>> b/arch/arm/cpu/arm920t/s3c24x0/timer.c
>> index 9571870..5695c62 100644
>> --- a/arch/arm/cpu/arm920t/s3c24x0/timer.c
>> +++ b/arch/arm/cpu/arm920t/s3c24x0/timer.c
>> @@ -35,142 +35,109 @@
>>  #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)
>>  {
>> +     /*
>> +      * PWM Timer 4 is used 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.
>> +      */
>> +     const ulong prescaler = 250;
>>       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 */
>> +     /* Set prescaler for Timer 4 */
>> +     writel((prescaler-1) << 8, &timers->tcfg0);
>
> Can we get rid of the magic numbers please?

I have declared some bitfields to remove the magic numbers.

>
>> +
>> +     /* Calculate timer freq, approx 100kHz @ PCLK=50MHz. */
>> +     gd->timer_rate_hz = get_PCLK() / (2 * prescaler);
>
> Can get_PCLK() be renamed to be lower-case only?

I haven't addressed this in my latest patch as it would be quite a
large change that I though might be worthy of its own patch (there are
several related functions get_FCLK, get_HCLK, etc. which could be
updated at the same time).


>> diff --git a/arch/arm/include/asm/global_data.h
>> b/arch/arm/include/asm/global_data.h
>> index fac98d5..b836915 100644
>> --- a/arch/arm/include/asm/global_data.h
>> +++ b/arch/arm/include/asm/global_data.h
>> @@ -67,6 +67,10 @@ typedef    struct  global_data {
>>  #ifdef CONFIG_IXP425
>>       unsigned long   timestamp;
>>  #endif
>> +#ifdef CONFIG_S3C24X0
>> +     unsigned long   lastdec;
>> +     unsigned long   timestamp;
>> +#endif
>
> I wonder about this change ...
>

The latest patch no longer creates new variables in the global_data
struct and instead reuses some of the existing ones.

Any more feedback would be welcomed.

Cheers

Mark Norman

Patch

diff --git a/arch/arm/cpu/arm920t/s3c24x0/timer.c
b/arch/arm/cpu/arm920t/s3c24x0/timer.c
index 9571870..5695c62 100644
--- a/arch/arm/cpu/arm920t/s3c24x0/timer.c
+++ b/arch/arm/cpu/arm920t/s3c24x0/timer.c
@@ -35,142 +35,109 @@ 
 #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)
 {
+	/*
+	 * PWM Timer 4 is used 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.
+	 */
+	const ulong prescaler = 250;
 	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 */
+	/* Set prescaler for Timer 4 */
+	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*/;
 }

 /*
diff --git a/arch/arm/include/asm/global_data.h
b/arch/arm/include/asm/global_data.h
index fac98d5..b836915 100644
--- a/arch/arm/include/asm/global_data.h
+++ b/arch/arm/include/asm/global_data.h
@@ -67,6 +67,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 */