diff options
-rw-r--r-- | arch/arm/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-orion/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-orion/common.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-orion/time.c | 181 |
4 files changed, 189 insertions, 1 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 71e905ebcc05..20084f273d8e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -338,6 +338,8 @@ config ARCH_ORION | |||
338 | depends on MMU | 338 | depends on MMU |
339 | select PCI | 339 | select PCI |
340 | select GENERIC_GPIO | 340 | select GENERIC_GPIO |
341 | select GENERIC_TIME | ||
342 | select GENERIC_CLOCKEVENTS | ||
341 | help | 343 | help |
342 | Support for Marvell Orion System on Chip family. | 344 | Support for Marvell Orion System on Chip family. |
343 | 345 | ||
diff --git a/arch/arm/mach-orion/Makefile b/arch/arm/mach-orion/Makefile index 57d459a2090e..3b2a3eef39dd 100644 --- a/arch/arm/mach-orion/Makefile +++ b/arch/arm/mach-orion/Makefile | |||
@@ -1 +1 @@ | |||
obj-y += common.o addr-map.o pci.o gpio.o irq.o | obj-y += common.o addr-map.o pci.o gpio.o irq.o time.o | ||
diff --git a/arch/arm/mach-orion/common.h b/arch/arm/mach-orion/common.h index 155371e1d672..2b452facadd1 100644 --- a/arch/arm/mach-orion/common.h +++ b/arch/arm/mach-orion/common.h | |||
@@ -62,4 +62,9 @@ int orion_pci_hw_wr_conf(u32 bus, u32 dev, u32 func, u32 where, u32 size, u32 va | |||
62 | void __init orion_gpio_set_valid_pins(u32 pins); | 62 | void __init orion_gpio_set_valid_pins(u32 pins); |
63 | void gpio_display(void); /* debug */ | 63 | void gpio_display(void); /* debug */ |
64 | 64 | ||
65 | /* | ||
66 | * Orion system timer (clocksource + clockevnt, /mach-orion/time.c) | ||
67 | */ | ||
68 | extern struct sys_timer orion_timer; | ||
69 | |||
65 | #endif /* __ARCH_ORION_COMMON_H__ */ | 70 | #endif /* __ARCH_ORION_COMMON_H__ */ |
diff --git a/arch/arm/mach-orion/time.c b/arch/arm/mach-orion/time.c new file mode 100644 index 000000000000..bd4262da4f40 --- /dev/null +++ b/arch/arm/mach-orion/time.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-orion/time.c | ||
3 | * | ||
4 | * Core time functions for Marvell Orion System On Chip | ||
5 | * | ||
6 | * Maintainer: Tzachi Perelstein <tzachi@marvell.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/clockchips.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <asm/mach/time.h> | ||
18 | #include <asm/arch/orion.h> | ||
19 | #include "common.h" | ||
20 | |||
21 | /* | ||
22 | * Timer0: clock_event_device, Tick. | ||
23 | * Timer1: clocksource, Free running. | ||
24 | * WatchDog: Not used. | ||
25 | * | ||
26 | * Timers are counting down. | ||
27 | */ | ||
28 | #define CLOCKEVENT 0 | ||
29 | #define CLOCKSOURCE 1 | ||
30 | |||
31 | /* | ||
32 | * Timers bits | ||
33 | */ | ||
34 | #define BRIDGE_INT_TIMER(x) (1 << ((x) + 1)) | ||
35 | #define TIMER_EN(x) (1 << ((x) * 2)) | ||
36 | #define TIMER_RELOAD_EN(x) (1 << (((x) * 2) + 1)) | ||
37 | #define BRIDGE_INT_TIMER_WD (1 << 3) | ||
38 | #define TIMER_WD_EN (1 << 4) | ||
39 | #define TIMER_WD_RELOAD_EN (1 << 5) | ||
40 | |||
41 | static cycle_t orion_clksrc_read(void) | ||
42 | { | ||
43 | return (0xffffffff - orion_read(TIMER_VAL(CLOCKSOURCE))); | ||
44 | } | ||
45 | |||
46 | static struct clocksource orion_clksrc = { | ||
47 | .name = "orion_clocksource", | ||
48 | .shift = 20, | ||
49 | .rating = 300, | ||
50 | .read = orion_clksrc_read, | ||
51 | .mask = CLOCKSOURCE_MASK(32), | ||
52 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
53 | }; | ||
54 | |||
55 | static int | ||
56 | orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev) | ||
57 | { | ||
58 | unsigned long flags; | ||
59 | |||
60 | if (delta == 0) | ||
61 | return -ETIME; | ||
62 | |||
63 | local_irq_save(flags); | ||
64 | |||
65 | /* | ||
66 | * Clear and enable timer interrupt bit | ||
67 | */ | ||
68 | orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
69 | orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
70 | |||
71 | /* | ||
72 | * Setup new timer value | ||
73 | */ | ||
74 | orion_write(TIMER_VAL(CLOCKEVENT), delta); | ||
75 | |||
76 | /* | ||
77 | * Disable auto reload and kickoff the timer | ||
78 | */ | ||
79 | orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT)); | ||
80 | orion_setbits(TIMER_CTRL, TIMER_EN(CLOCKEVENT)); | ||
81 | |||
82 | local_irq_restore(flags); | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static void | ||
88 | orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) | ||
89 | { | ||
90 | unsigned long flags; | ||
91 | |||
92 | local_irq_save(flags); | ||
93 | |||
94 | if (mode == CLOCK_EVT_MODE_PERIODIC) { | ||
95 | /* | ||
96 | * Setup latch cycles in timer and enable reload interrupt. | ||
97 | */ | ||
98 | orion_write(TIMER_VAL_RELOAD(CLOCKEVENT), LATCH); | ||
99 | orion_write(TIMER_VAL(CLOCKEVENT), LATCH); | ||
100 | orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
101 | orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | | ||
102 | TIMER_EN(CLOCKEVENT)); | ||
103 | } else { | ||
104 | /* | ||
105 | * Disable timer and interrupt | ||
106 | */ | ||
107 | orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
108 | orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
109 | orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) | | ||
110 | TIMER_EN(CLOCKEVENT)); | ||
111 | } | ||
112 | |||
113 | local_irq_restore(flags); | ||
114 | } | ||
115 | |||
116 | static struct clock_event_device orion_clkevt = { | ||
117 | .name = "orion_tick", | ||
118 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
119 | .shift = 32, | ||
120 | .rating = 300, | ||
121 | .cpumask = CPU_MASK_CPU0, | ||
122 | .set_next_event = orion_clkevt_next_event, | ||
123 | .set_mode = orion_clkevt_mode, | ||
124 | }; | ||
125 | |||
126 | static irqreturn_t orion_timer_interrupt(int irq, void *dev_id) | ||
127 | { | ||
128 | /* | ||
129 | * Clear cause bit and do event | ||
130 | */ | ||
131 | orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT)); | ||
132 | orion_clkevt.event_handler(&orion_clkevt); | ||
133 | return IRQ_HANDLED; | ||
134 | } | ||
135 | |||
136 | static struct irqaction orion_timer_irq = { | ||
137 | .name = "orion_tick", | ||
138 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
139 | .handler = orion_timer_interrupt | ||
140 | }; | ||
141 | |||
142 | static void orion_timer_init(void) | ||
143 | { | ||
144 | /* | ||
145 | * Setup clocksource free running timer (no interrupt on reload) | ||
146 | */ | ||
147 | orion_write(TIMER_VAL(CLOCKSOURCE), 0xffffffff); | ||
148 | orion_write(TIMER_VAL_RELOAD(CLOCKSOURCE), 0xffffffff); | ||
149 | orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKSOURCE)); | ||
150 | orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKSOURCE) | | ||
151 | TIMER_EN(CLOCKSOURCE)); | ||
152 | |||
153 | /* | ||
154 | * Register clocksource | ||
155 | */ | ||
156 | orion_clksrc.mult = | ||
157 | clocksource_hz2mult(CLOCK_TICK_RATE, orion_clksrc.shift); | ||
158 | |||
159 | clocksource_register(&orion_clksrc); | ||
160 | |||
161 | /* | ||
162 | * Connect and enable tick handler | ||
163 | */ | ||
164 | setup_irq(IRQ_ORION_BRIDGE, &orion_timer_irq); | ||
165 | |||
166 | /* | ||
167 | * Register clockevent | ||
168 | */ | ||
169 | orion_clkevt.mult = | ||
170 | div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, orion_clkevt.shift); | ||
171 | orion_clkevt.max_delta_ns = | ||
172 | clockevent_delta2ns(0xfffffffe, &orion_clkevt); | ||
173 | orion_clkevt.min_delta_ns = | ||
174 | clockevent_delta2ns(1, &orion_clkevt); | ||
175 | |||
176 | clockevents_register_device(&orion_clkevt); | ||
177 | } | ||
178 | |||
179 | struct sys_timer orion_timer = { | ||
180 | .init = orion_timer_init, | ||
181 | }; | ||