aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin
diff options
context:
space:
mode:
authorVitja Makarov <vitja.makarov@gmail.com>2008-02-28 23:24:23 -0500
committerBryan Wu <cooloney@kernel.org>2008-02-28 23:24:23 -0500
commit8b5f79f9d7ee4f4edb0212886771c977476eb811 (patch)
tree5c9928710ad8c2556b64cee56fea768ce5ac4ba7 /arch/blackfin
parent3dc5063786b273f1aee545844f6bd4e9651ebffe (diff)
[Blackfin] arch: initial generic time and clock sources
This patch enables Hight-Res Timers and tickless kernel Signed-off-by: Vitja Makarov <vitja.makarov@gmail.com> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org>
Diffstat (limited to 'arch/blackfin')
-rw-r--r--arch/blackfin/Kconfig28
-rw-r--r--arch/blackfin/kernel/Makefile8
-rw-r--r--arch/blackfin/kernel/process.c43
-rw-r--r--arch/blackfin/kernel/time-ts.c194
4 files changed, 253 insertions, 20 deletions
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index 2dd1f300a5cf..a3cf9d0a528b 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -47,10 +47,6 @@ config GENERIC_IRQ_PROBE
47 bool 47 bool
48 default y 48 default y
49 49
50config GENERIC_TIME
51 bool
52 default n
53
54config GENERIC_GPIO 50config GENERIC_GPIO
55 bool 51 bool
56 default y 52 default y
@@ -415,6 +411,30 @@ comment "Kernel Timer/Scheduler"
415 411
416source kernel/Kconfig.hz 412source kernel/Kconfig.hz
417 413
414config GENERIC_TIME
415 bool "Generic time"
416 default y
417
418config GENERIC_CLOCKEVENTS
419 bool "Generic clock events"
420 depends on GENERIC_TIME
421 default y
422
423config CYCLES_CLOCKSOURCE
424 bool "Use 'CYCLES' as a clocksource (EXPERIMENTAL)"
425 depends on EXPERIMENTAL
426 depends on GENERIC_CLOCKEVENTS
427 depends on !BFIN_SCRATCH_REG_CYCLES
428 default n
429 help
430 If you say Y here, you will enable support for using the 'cycles'
431 registers as a clock source. Doing so means you will be unable to
432 safely write to the 'cycles' register during runtime. You will
433 still be able to read it (such as for performance monitoring), but
434 writing the registers will most likely crash the kernel.
435
436source kernel/time/Kconfig
437
418comment "Memory Setup" 438comment "Memory Setup"
419 439
420config MEM_SIZE 440config MEM_SIZE
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile
index 318b9b692a48..6140cd69c782 100644
--- a/arch/blackfin/kernel/Makefile
+++ b/arch/blackfin/kernel/Makefile
@@ -6,9 +6,15 @@ extra-y := init_task.o vmlinux.lds
6 6
7obj-y := \ 7obj-y := \
8 entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ 8 entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \
9 sys_bfin.o time.o traps.o irqchip.o dma-mapping.o flat.o \ 9 sys_bfin.o traps.o irqchip.o dma-mapping.o flat.o \
10 fixed_code.o reboot.o bfin_gpio.o 10 fixed_code.o reboot.o bfin_gpio.o
11 11
12ifeq ($(CONFIG_GENERIC_CLOCKEVENTS),y)
13 obj-y += time-ts.o
14else
15 obj-y += time.o
16endif
17
12obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o 18obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o
13obj-$(CONFIG_MODULES) += module.o 19obj-$(CONFIG_MODULES) += module.o
14obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o 20obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
index 6b8459c66163..6dedb2da8b67 100644
--- a/arch/blackfin/kernel/process.c
+++ b/arch/blackfin/kernel/process.c
@@ -32,6 +32,8 @@
32#include <linux/unistd.h> 32#include <linux/unistd.h>
33#include <linux/user.h> 33#include <linux/user.h>
34#include <linux/uaccess.h> 34#include <linux/uaccess.h>
35#include <linux/sched.h>
36#include <linux/tick.h>
35#include <linux/fs.h> 37#include <linux/fs.h>
36#include <linux/err.h> 38#include <linux/err.h>
37 39
@@ -69,33 +71,44 @@ EXPORT_SYMBOL(pm_power_off);
69 * The idle loop on BFIN 71 * The idle loop on BFIN
70 */ 72 */
71#ifdef CONFIG_IDLE_L1 73#ifdef CONFIG_IDLE_L1
72void default_idle(void)__attribute__((l1_text)); 74static void default_idle(void)__attribute__((l1_text));
73void cpu_idle(void)__attribute__((l1_text)); 75void cpu_idle(void)__attribute__((l1_text));
74#endif 76#endif
75 77
76void default_idle(void) 78/*
79 * This is our default idle handler. We need to disable
80 * interrupts here to ensure we don't miss a wakeup call.
81 */
82static void default_idle(void)
77{ 83{
78 while (!need_resched()) { 84 local_irq_disable();
79 local_irq_disable(); 85 if (!need_resched())
80 if (likely(!need_resched())) 86 idle_with_irq_disabled();
81 idle_with_irq_disabled();
82 local_irq_enable();
83 }
84}
85 87
86void (*idle)(void) = default_idle; 88 local_irq_enable();
89}
87 90
88/* 91/*
89 * The idle thread. There's no useful work to be 92 * The idle thread. We try to conserve power, while trying to keep
90 * done, so just try to conserve power and have a 93 * overall latency low. The architecture specific idle is passed
91 * low exit latency (ie sit in a loop waiting for 94 * a value to indicate the level of "idleness" of the system.
92 * somebody to say that they'd like to reschedule)
93 */ 95 */
94void cpu_idle(void) 96void cpu_idle(void)
95{ 97{
96 /* endless idle loop with no priority at all */ 98 /* endless idle loop with no priority at all */
97 while (1) { 99 while (1) {
98 idle(); 100 void (*idle)(void) = pm_idle;
101
102#ifdef CONFIG_HOTPLUG_CPU
103 if (cpu_is_offline(smp_processor_id()))
104 cpu_die();
105#endif
106 if (!idle)
107 idle = default_idle;
108 tick_nohz_stop_sched_tick();
109 while (!need_resched())
110 idle();
111 tick_nohz_restart_sched_tick();
99 preempt_enable_no_resched(); 112 preempt_enable_no_resched();
100 schedule(); 113 schedule();
101 preempt_disable(); 114 preempt_disable();
diff --git a/arch/blackfin/kernel/time-ts.c b/arch/blackfin/kernel/time-ts.c
new file mode 100644
index 000000000000..3aad6d710726
--- /dev/null
+++ b/arch/blackfin/kernel/time-ts.c
@@ -0,0 +1,194 @@
1/*
2 * linux/arch/kernel/time-ts.c
3 *
4 * Based on arm clockevents implementation and old bfin time tick.
5 *
6 * Copyright(C) 2008, GeoTechnologies, Vitja Makarov
7 *
8 * This code is licenced under the GPL version 2. For details see
9 * kernel-base/COPYING.
10 */
11#include <linux/module.h>
12#include <linux/profile.h>
13#include <linux/interrupt.h>
14#include <linux/time.h>
15#include <linux/irq.h>
16#include <linux/clocksource.h>
17#include <linux/clockchips.h>
18
19#include <asm/blackfin.h>
20
21#ifdef CONFIG_CYCLES_CLOCKSOURCE
22
23static unsigned long cyc2ns_scale;
24#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
25
26static inline void set_cyc2ns_scale(unsigned long cpu_khz)
27{
28 cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR) / cpu_khz;
29}
30
31static inline unsigned long long cycles_2_ns(cycle_t cyc)
32{
33 return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
34}
35
36static cycle_t read_cycles(void)
37{
38 unsigned long tmp, tmp2;
39 asm("%0 = cycles; %1 = cycles2;" : "=d"(tmp), "=d"(tmp2));
40 return tmp | ((cycle_t)tmp2 << 32);
41}
42
43unsigned long long sched_clock(void)
44{
45 return cycles_2_ns(read_cycles());
46}
47
48static struct clocksource clocksource_bfin = {
49 .name = "bfin_cycles",
50 .rating = 350,
51 .read = read_cycles,
52 .mask = CLOCKSOURCE_MASK(64),
53 .shift = 22,
54 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
55};
56
57static int __init bfin_clocksource_init(void)
58{
59 set_cyc2ns_scale(get_cclk() / 1000);
60
61 clocksource_bfin.mult = clocksource_hz2mult(get_cclk(), clocksource_bfin.shift);
62
63 if (clocksource_register(&clocksource_bfin))
64 panic("failed to register clocksource");
65
66 return 0;
67}
68
69#else
70# define bfin_clocksource_init()
71#endif
72
73static int bfin_timer_set_next_event(unsigned long cycles,
74 struct clock_event_device *evt)
75{
76 bfin_write_TCOUNT(cycles);
77 CSYNC();
78 return 0;
79}
80
81static void bfin_timer_set_mode(enum clock_event_mode mode,
82 struct clock_event_device *evt)
83{
84 switch (mode) {
85 case CLOCK_EVT_MODE_PERIODIC: {
86 unsigned long tcount = ((get_cclk() / (HZ * 1)) - 1);
87 bfin_write_TCNTL(TMPWR);
88 CSYNC();
89 bfin_write_TPERIOD(tcount);
90 bfin_write_TCOUNT(tcount);
91 bfin_write_TCNTL(TMPWR | TMREN | TAUTORLD);
92 CSYNC();
93 break;
94 }
95 case CLOCK_EVT_MODE_ONESHOT:
96 bfin_write_TCOUNT(0);
97 bfin_write_TCNTL(TMPWR | TMREN);
98 CSYNC();
99 break;
100 case CLOCK_EVT_MODE_UNUSED:
101 case CLOCK_EVT_MODE_SHUTDOWN:
102 bfin_write_TCNTL(0);
103 CSYNC();
104 break;
105 case CLOCK_EVT_MODE_RESUME:
106 break;
107 }
108}
109
110static void __init bfin_timer_init(void)
111{
112 /* power up the timer, but don't enable it just yet */
113 bfin_write_TCNTL(TMPWR);
114 CSYNC();
115
116 /*
117 * the TSCALE prescaler counter.
118 */
119 bfin_write_TSCALE(0);
120 bfin_write_TPERIOD(0);
121 bfin_write_TCOUNT(0);
122
123 /* now enable the timer */
124 CSYNC();
125}
126
127/*
128 * timer_interrupt() needs to keep up the real-time clock,
129 * as well as call the "do_timer()" routine every clocktick
130 */
131#ifdef CONFIG_CORE_TIMER_IRQ_L1
132__attribute__((l1_text))
133#endif
134irqreturn_t timer_interrupt(int irq, void *dev_id);
135
136static struct clock_event_device clockevent_bfin = {
137 .name = "bfin_core_timer",
138 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
139 .shift = 32,
140 .cpumask = CPU_MASK_CPU0,
141 .set_next_event = bfin_timer_set_next_event,
142 .set_mode = bfin_timer_set_mode,
143};
144
145static struct irqaction bfin_timer_irq = {
146 .name = "Blackfin Core Timer",
147 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
148 .handler = timer_interrupt,
149 .dev_id = &clockevent_bfin,
150};
151
152irqreturn_t timer_interrupt(int irq, void *dev_id)
153{
154 struct clock_event_device *evt = dev_id;
155 evt->event_handler(evt);
156 return IRQ_HANDLED;
157}
158
159static int __init bfin_clockevent_init(void)
160{
161 setup_irq(IRQ_CORETMR, &bfin_timer_irq);
162 bfin_timer_init();
163
164 clockevent_bfin.mult = div_sc(get_cclk(), NSEC_PER_SEC, clockevent_bfin.shift);
165 clockevent_bfin.max_delta_ns = clockevent_delta2ns(-1, &clockevent_bfin);
166 clockevent_bfin.min_delta_ns = clockevent_delta2ns(100, &clockevent_bfin);
167 clockevents_register_device(&clockevent_bfin);
168
169 return 0;
170}
171
172void __init time_init(void)
173{
174 time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */
175
176#ifdef CONFIG_RTC_DRV_BFIN
177 /* [#2663] hack to filter junk RTC values that would cause
178 * userspace to have to deal with time values greater than
179 * 2^31 seconds (which uClibc cannot cope with yet)
180 */
181 if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) {
182 printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n");
183 bfin_write_RTC_STAT(0);
184 }
185#endif
186
187 /* Initialize xtime. From now on, xtime is updated with timer interrupts */
188 xtime.tv_sec = secs_since_1970;
189 xtime.tv_nsec = 0;
190 set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
191
192 bfin_clocksource_init();
193 bfin_clockevent_init();
194}