1 /* arch/arm/mach-goldfish/timer.c
2 **
3 ** Copyright (C) 2007 Google, Inc.
4 **
5 ** This software is licensed under the terms of the GNU General Public
6 ** License version 2, as published by the Free Software Foundation, and
7 ** may be copied, distributed, and modified under those terms.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ** GNU General Public License for more details.
13 **
14 */
15
16 #include <linux/clockchips.h>
17 #include <linux/interrupt.h>
18 #include <linux/irq.h>
19
20 #include <mach/timer.h>
21 #include <mach/hardware.h>
22 #include <asm/io.h>
23 #include <asm/mach/time.h>
24
25 #include <linux/platform_device.h>
26
27 static DEFINE_SPINLOCK(goldfish_timer_lock);
28 static int goldfish_timer_ready;
29
goldfish_timer_interrupt(int irq,void * dev_id)30 static irqreturn_t goldfish_timer_interrupt(int irq, void *dev_id)
31 {
32 uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);
33 struct clock_event_device *evt = dev_id;
34
35 writel(1, timer_base + TIMER_CLEAR_INTERRUPT);
36 if (evt->event_handler)
37 evt->event_handler(evt);
38 return IRQ_HANDLED;
39 }
40
goldfish_timer_read(void)41 static cycle_t goldfish_timer_read(void)
42 {
43 uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);
44 unsigned long irqflags;
45 cycle_t rv;
46
47 spin_lock_irqsave(&goldfish_timer_lock, irqflags);
48 rv = readl(timer_base + TIMER_TIME_LOW);
49 rv |= (int64_t)readl(timer_base + TIMER_TIME_HIGH) << 32;
50 spin_unlock_irqrestore(&goldfish_timer_lock, irqflags);
51 return rv;
52 }
53
goldfish_timer_set_next_event(unsigned long cycles,struct clock_event_device * evt)54 static int goldfish_timer_set_next_event(unsigned long cycles,
55 struct clock_event_device *evt)
56 {
57 uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);
58 unsigned long irqflags;
59 uint64_t alarm;
60
61 spin_lock_irqsave(&goldfish_timer_lock, irqflags);
62 alarm = readl(timer_base + TIMER_TIME_LOW);
63 alarm |= (int64_t)readl(timer_base + TIMER_TIME_HIGH) << 32;
64 alarm += cycles;
65 writel(alarm >> 32, timer_base + TIMER_ALARM_HIGH);
66 writel(alarm, timer_base + TIMER_ALARM_LOW);
67 spin_unlock_irqrestore(&goldfish_timer_lock, irqflags);
68 return 0;
69 }
70
goldfish_timer_set_mode(enum clock_event_mode mode,struct clock_event_device * evt)71 static void goldfish_timer_set_mode(enum clock_event_mode mode,
72 struct clock_event_device *evt)
73 {
74 uint32_t timer_base = IO_ADDRESS(GOLDFISH_TIMER_BASE);
75 switch (mode) {
76 case CLOCK_EVT_MODE_RESUME:
77 case CLOCK_EVT_MODE_PERIODIC:
78 break;
79 case CLOCK_EVT_MODE_ONESHOT:
80 break;
81 case CLOCK_EVT_MODE_UNUSED:
82 case CLOCK_EVT_MODE_SHUTDOWN:
83 writel(1, timer_base + TIMER_CLEAR_ALARM);
84 break;
85 }
86 }
87
sched_clock(void)88 unsigned long long sched_clock(void)
89 {
90 if(goldfish_timer_ready)
91 return ktime_to_ns(ktime_get());
92 else
93 return 0;
94 }
95
96 static struct clock_event_device goldfish_clockevent = {
97 .name = "goldfish_timer",
98 .features = CLOCK_EVT_FEAT_ONESHOT,
99 .max_delta_ns = ULONG_MAX,
100 .min_delta_ns = 1,
101 .mult = 1,
102 .shift = 0,
103 .rating = 200,
104 .set_next_event = goldfish_timer_set_next_event,
105 .set_mode = goldfish_timer_set_mode,
106 };
107
108 static struct clocksource goldfish_clocksource = {
109 .name = "goldfish_timer",
110 .rating = 200,
111 .read = goldfish_timer_read,
112 .mult = 1,
113 .mask = CLOCKSOURCE_MASK(64),
114 .shift = 0,
115 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
116 };
117
118 static struct irqaction goldfish_timer_irq = {
119 .name = "Goldfish Timer Tick",
120 .flags = IRQF_DISABLED | IRQF_TIMER,
121 .handler = goldfish_timer_interrupt,
122 .dev_id = &goldfish_clockevent,
123 };
124
goldfish_timer_init(void)125 static void __init goldfish_timer_init(void)
126 {
127 int res;
128
129 res = clocksource_register(&goldfish_clocksource);
130 if (res)
131 printk(KERN_ERR "goldfish_timer_init: "
132 "clocksource_register failed\n");
133
134 res = setup_irq(IRQ_TIMER, &goldfish_timer_irq);
135 if (res)
136 printk(KERN_ERR "goldfish_timer_init: setup_irq failed\n");
137
138 goldfish_clockevent.cpumask = cpumask_of(0);
139 clockevents_register_device(&goldfish_clockevent);
140
141 goldfish_timer_ready = 1;
142 }
143
144 struct sys_timer goldfish_timer = {
145 .init = goldfish_timer_init,
146 };
147
148