diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /arch/arm/common/timer-sp.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'arch/arm/common/timer-sp.c')
-rw-r--r-- | arch/arm/common/timer-sp.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c new file mode 100644 index 000000000000..41df47875122 --- /dev/null +++ b/arch/arm/common/timer-sp.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/common/timer-sp.c | ||
3 | * | ||
4 | * Copyright (C) 1999 - 2003 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/clocksource.h> | ||
23 | #include <linux/clockchips.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/io.h> | ||
28 | |||
29 | #include <asm/hardware/arm_timer.h> | ||
30 | |||
31 | static long __init sp804_get_clock_rate(const char *name) | ||
32 | { | ||
33 | struct clk *clk; | ||
34 | long rate; | ||
35 | int err; | ||
36 | |||
37 | clk = clk_get_sys("sp804", name); | ||
38 | if (IS_ERR(clk)) { | ||
39 | pr_err("sp804: %s clock not found: %d\n", name, | ||
40 | (int)PTR_ERR(clk)); | ||
41 | return PTR_ERR(clk); | ||
42 | } | ||
43 | |||
44 | err = clk_enable(clk); | ||
45 | if (err) { | ||
46 | pr_err("sp804: %s clock failed to enable: %d\n", name, err); | ||
47 | clk_put(clk); | ||
48 | return err; | ||
49 | } | ||
50 | |||
51 | rate = clk_get_rate(clk); | ||
52 | if (rate < 0) { | ||
53 | pr_err("sp804: %s clock failed to get rate: %ld\n", name, rate); | ||
54 | clk_disable(clk); | ||
55 | clk_put(clk); | ||
56 | } | ||
57 | |||
58 | return rate; | ||
59 | } | ||
60 | |||
61 | void __init sp804_clocksource_init(void __iomem *base, const char *name) | ||
62 | { | ||
63 | long rate = sp804_get_clock_rate(name); | ||
64 | |||
65 | if (rate < 0) | ||
66 | return; | ||
67 | |||
68 | /* setup timer 0 as free-running clocksource */ | ||
69 | writel(0, base + TIMER_CTRL); | ||
70 | writel(0xffffffff, base + TIMER_LOAD); | ||
71 | writel(0xffffffff, base + TIMER_VALUE); | ||
72 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, | ||
73 | base + TIMER_CTRL); | ||
74 | |||
75 | clocksource_mmio_init(base + TIMER_VALUE, name, | ||
76 | rate, 200, 32, clocksource_mmio_readl_down); | ||
77 | } | ||
78 | |||
79 | |||
80 | static void __iomem *clkevt_base; | ||
81 | static unsigned long clkevt_reload; | ||
82 | |||
83 | /* | ||
84 | * IRQ handler for the timer | ||
85 | */ | ||
86 | static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) | ||
87 | { | ||
88 | struct clock_event_device *evt = dev_id; | ||
89 | |||
90 | /* clear the interrupt */ | ||
91 | writel(1, clkevt_base + TIMER_INTCLR); | ||
92 | |||
93 | evt->event_handler(evt); | ||
94 | |||
95 | return IRQ_HANDLED; | ||
96 | } | ||
97 | |||
98 | static void sp804_set_mode(enum clock_event_mode mode, | ||
99 | struct clock_event_device *evt) | ||
100 | { | ||
101 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; | ||
102 | |||
103 | writel(ctrl, clkevt_base + TIMER_CTRL); | ||
104 | |||
105 | switch (mode) { | ||
106 | case CLOCK_EVT_MODE_PERIODIC: | ||
107 | writel(clkevt_reload, clkevt_base + TIMER_LOAD); | ||
108 | ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; | ||
109 | break; | ||
110 | |||
111 | case CLOCK_EVT_MODE_ONESHOT: | ||
112 | /* period set, and timer enabled in 'next_event' hook */ | ||
113 | ctrl |= TIMER_CTRL_ONESHOT; | ||
114 | break; | ||
115 | |||
116 | case CLOCK_EVT_MODE_UNUSED: | ||
117 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
118 | default: | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | writel(ctrl, clkevt_base + TIMER_CTRL); | ||
123 | } | ||
124 | |||
125 | static int sp804_set_next_event(unsigned long next, | ||
126 | struct clock_event_device *evt) | ||
127 | { | ||
128 | unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); | ||
129 | |||
130 | writel(next, clkevt_base + TIMER_LOAD); | ||
131 | writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static struct clock_event_device sp804_clockevent = { | ||
137 | .shift = 32, | ||
138 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
139 | .set_mode = sp804_set_mode, | ||
140 | .set_next_event = sp804_set_next_event, | ||
141 | .rating = 300, | ||
142 | .cpumask = cpu_all_mask, | ||
143 | }; | ||
144 | |||
145 | static struct irqaction sp804_timer_irq = { | ||
146 | .name = "timer", | ||
147 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
148 | .handler = sp804_timer_interrupt, | ||
149 | .dev_id = &sp804_clockevent, | ||
150 | }; | ||
151 | |||
152 | void __init sp804_clockevents_init(void __iomem *base, unsigned int irq, | ||
153 | const char *name) | ||
154 | { | ||
155 | struct clock_event_device *evt = &sp804_clockevent; | ||
156 | long rate = sp804_get_clock_rate(name); | ||
157 | |||
158 | if (rate < 0) | ||
159 | return; | ||
160 | |||
161 | clkevt_base = base; | ||
162 | clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ); | ||
163 | |||
164 | evt->name = name; | ||
165 | evt->irq = irq; | ||
166 | evt->mult = div_sc(rate, NSEC_PER_SEC, evt->shift); | ||
167 | evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); | ||
168 | evt->min_delta_ns = clockevent_delta2ns(0xf, evt); | ||
169 | |||
170 | setup_irq(irq, &sp804_timer_irq); | ||
171 | clockevents_register_device(evt); | ||
172 | } | ||