Commit 385c98fc authored by Linus Walleij's avatar Linus Walleij Committed by Daniel Lezcano

clocksource/drivers/fttmr010: Implement delay timer

This timer is often used on the ARM architecture, so as with so
many siblings, we can implement delay timers, removing the need
for the system to calibrate jiffys at boot, and potentially
handling CPU frequency scaling on targets.

We cannot just protect the Kconfig with a "depends on ARM" because
it is already known that different architectures are using Faraday
IP blocks, so it is better to make things open-ended and use

Result on boot dmesg:

Switching to timer-based delay loop, resolution 40n
Calibrating delay loop (skipped), value calculated using
  timer frequency.. 50.00 BogoMIPS (lpj=250000)

This is accurately the timer frequency, 250MHz on the APB
bus.

Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Jonas Jensen <jonas.jensen@gmail.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Tested-by: default avatarJonas Jensen <jonas.jensen@gmail.com>
Tested-by: default avatarAndrew Jeffery <andrew@aj.id.au>
Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parent dc11bae7
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/delay.h>
/* /*
* Register definitions for the timers * Register definitions for the timers
...@@ -81,9 +82,15 @@ struct fttmr010 { ...@@ -81,9 +82,15 @@ struct fttmr010 {
bool count_down; bool count_down;
u32 t1_enable_val; u32 t1_enable_val;
struct clock_event_device clkevt; struct clock_event_device clkevt;
#ifdef CONFIG_ARM
struct delay_timer delay_timer;
#endif
}; };
/* A local singleton used by sched_clock, which is stateless */ /*
* A local singleton used by sched_clock and delay timer reads, which are
* fast and stateless
*/
static struct fttmr010 *local_fttmr; static struct fttmr010 *local_fttmr;
static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt) static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt)
...@@ -101,6 +108,20 @@ static u64 notrace fttmr010_read_sched_clock_down(void) ...@@ -101,6 +108,20 @@ static u64 notrace fttmr010_read_sched_clock_down(void)
return ~readl(local_fttmr->base + TIMER2_COUNT); return ~readl(local_fttmr->base + TIMER2_COUNT);
} }
#ifdef CONFIG_ARM
static unsigned long fttmr010_read_current_timer_up(void)
{
return readl(local_fttmr->base + TIMER2_COUNT);
}
static unsigned long fttmr010_read_current_timer_down(void)
{
return ~readl(local_fttmr->base + TIMER2_COUNT);
}
#endif
static int fttmr010_timer_set_next_event(unsigned long cycles, static int fttmr010_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt) struct clock_event_device *evt)
{ {
...@@ -347,6 +368,18 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) ...@@ -347,6 +368,18 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
fttmr010->tick_rate, fttmr010->tick_rate,
1, 0xffffffff); 1, 0xffffffff);
#ifdef CONFIG_ARM
/* Also use this timer for delays */
if (fttmr010->count_down)
fttmr010->delay_timer.read_current_timer =
fttmr010_read_current_timer_down;
else
fttmr010->delay_timer.read_current_timer =
fttmr010_read_current_timer_up;
fttmr010->delay_timer.freq = fttmr010->tick_rate;
register_current_timer_delay(&fttmr010->delay_timer);
#endif
return 0; return 0;
out_unmap: out_unmap:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment