• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(struct clocksource * cs)41 static cycle_t goldfish_timer_read(struct clocksource *cs)
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 
88 /*
89  * TODO: the mechanism for overriding the default sched_clock() has
90  * changed since 2.6, but I'm not sure how the new mechanism works, so
91  * I'm just reverting to the default to avoid a link-time multiple
92  * definition error.
93 unsigned long long sched_clock(void)
94 {
95 	if(goldfish_timer_ready)
96 		return ktime_to_ns(ktime_get());
97 	else
98 		return 0;
99 }
100  */
101 
102 static struct clock_event_device goldfish_clockevent = {
103 	.name           = "goldfish_timer",
104 	.features       = CLOCK_EVT_FEAT_ONESHOT,
105 	.max_delta_ns   = ULONG_MAX,
106 	.min_delta_ns   = 1,
107 	.mult           = 1,
108 	.shift          = 0,
109 	.rating         = 200,
110 	.set_next_event = goldfish_timer_set_next_event,
111 	.set_mode       = goldfish_timer_set_mode,
112 };
113 
114 static struct clocksource goldfish_clocksource = {
115 	.name           = "goldfish_timer",
116 	.rating         = 200,
117 	.read           = goldfish_timer_read,
118 	.mult           = 1,
119 	.mask           = CLOCKSOURCE_MASK(64),
120 	.shift          = 0,
121 	.flags          = CLOCK_SOURCE_IS_CONTINUOUS,
122 };
123 
124 static struct irqaction goldfish_timer_irq = {
125 	.name		= "Goldfish Timer Tick",
126 	.flags		= IRQF_DISABLED | IRQF_TIMER,
127 	.handler	= goldfish_timer_interrupt,
128 	.dev_id		= &goldfish_clockevent,
129 };
130 
goldfish_timer_init(void)131 static void __init goldfish_timer_init(void)
132 {
133 	int res;
134 
135 	res = clocksource_register(&goldfish_clocksource);
136 	if (res)
137 		printk(KERN_ERR "goldfish_timer_init: "
138 		       "clocksource_register failed\n");
139 
140 	res = setup_irq(IRQ_TIMER, &goldfish_timer_irq);
141 	if (res)
142 		printk(KERN_ERR "goldfish_timer_init: setup_irq failed\n");
143 
144 	goldfish_clockevent.cpumask = cpumask_of(0);
145 	clockevents_register_device(&goldfish_clockevent);
146 
147 	goldfish_timer_ready = 1;
148 }
149 
150 struct sys_timer goldfish_timer = {
151 	.init		= goldfish_timer_init,
152 };
153 
154