diff options
Diffstat (limited to 'arch/arm/mach-w90x900/time.c')
-rw-r--r-- | arch/arm/mach-w90x900/time.c | 151 |
1 files changed, 127 insertions, 24 deletions
diff --git a/arch/arm/mach-w90x900/time.c b/arch/arm/mach-w90x900/time.c index bcc838f6b393..5e06770e6014 100644 --- a/arch/arm/mach-w90x900/time.c +++ b/arch/arm/mach-w90x900/time.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Based on linux/arch/arm/plat-s3c24xx/time.c by Ben Dooks | 4 | * Based on linux/arch/arm/plat-s3c24xx/time.c by Ben Dooks |
5 | * | 5 | * |
6 | * Copyright (c) 2008 Nuvoton technology corporation | 6 | * Copyright (c) 2009 Nuvoton technology corporation |
7 | * All rights reserved. | 7 | * All rights reserved. |
8 | * | 8 | * |
9 | * Wan ZongShun <mcuos.com@gmail.com> | 9 | * Wan ZongShun <mcuos.com@gmail.com> |
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
24 | #include <linux/io.h> | 24 | #include <linux/io.h> |
25 | #include <linux/leds.h> | 25 | #include <linux/leds.h> |
26 | #include <linux/clocksource.h> | ||
27 | #include <linux/clockchips.h> | ||
26 | 28 | ||
27 | #include <asm/mach-types.h> | 29 | #include <asm/mach-types.h> |
28 | #include <asm/mach/irq.h> | 30 | #include <asm/mach/irq.h> |
@@ -31,49 +33,150 @@ | |||
31 | #include <mach/map.h> | 33 | #include <mach/map.h> |
32 | #include <mach/regs-timer.h> | 34 | #include <mach/regs-timer.h> |
33 | 35 | ||
34 | static unsigned long w90x900_gettimeoffset(void) | 36 | #define RESETINT 0x1f |
37 | #define PERIOD (0x01 << 27) | ||
38 | #define ONESHOT (0x00 << 27) | ||
39 | #define COUNTEN (0x01 << 30) | ||
40 | #define INTEN (0x01 << 29) | ||
41 | |||
42 | #define TICKS_PER_SEC 100 | ||
43 | #define PRESCALE 0x63 /* Divider = prescale + 1 */ | ||
44 | |||
45 | unsigned int timer0_load; | ||
46 | |||
47 | static void w90p910_clockevent_setmode(enum clock_event_mode mode, | ||
48 | struct clock_event_device *clk) | ||
35 | { | 49 | { |
50 | unsigned int val; | ||
51 | |||
52 | val = __raw_readl(REG_TCSR0); | ||
53 | val &= ~(0x03 << 27); | ||
54 | |||
55 | switch (mode) { | ||
56 | case CLOCK_EVT_MODE_PERIODIC: | ||
57 | __raw_writel(timer0_load, REG_TICR0); | ||
58 | val |= (PERIOD | COUNTEN | INTEN | PRESCALE); | ||
59 | break; | ||
60 | |||
61 | case CLOCK_EVT_MODE_ONESHOT: | ||
62 | val |= (ONESHOT | COUNTEN | INTEN | PRESCALE); | ||
63 | break; | ||
64 | |||
65 | case CLOCK_EVT_MODE_UNUSED: | ||
66 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
67 | case CLOCK_EVT_MODE_RESUME: | ||
68 | break; | ||
69 | } | ||
70 | |||
71 | __raw_writel(val, REG_TCSR0); | ||
72 | } | ||
73 | |||
74 | static int w90p910_clockevent_setnextevent(unsigned long evt, | ||
75 | struct clock_event_device *clk) | ||
76 | { | ||
77 | unsigned int val; | ||
78 | |||
79 | __raw_writel(evt, REG_TICR0); | ||
80 | |||
81 | val = __raw_readl(REG_TCSR0); | ||
82 | val |= (COUNTEN | INTEN | PRESCALE); | ||
83 | __raw_writel(val, REG_TCSR0); | ||
84 | |||
36 | return 0; | 85 | return 0; |
37 | } | 86 | } |
38 | 87 | ||
88 | static struct clock_event_device w90p910_clockevent_device = { | ||
89 | .name = "w90p910-timer0", | ||
90 | .shift = 32, | ||
91 | .features = CLOCK_EVT_MODE_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
92 | .set_mode = w90p910_clockevent_setmode, | ||
93 | .set_next_event = w90p910_clockevent_setnextevent, | ||
94 | .rating = 300, | ||
95 | }; | ||
96 | |||
39 | /*IRQ handler for the timer*/ | 97 | /*IRQ handler for the timer*/ |
40 | 98 | ||
41 | static irqreturn_t | 99 | static irqreturn_t w90p910_timer0_interrupt(int irq, void *dev_id) |
42 | w90x900_timer_interrupt(int irq, void *dev_id) | ||
43 | { | 100 | { |
44 | timer_tick(); | 101 | struct clock_event_device *evt = &w90p910_clockevent_device; |
102 | |||
45 | __raw_writel(0x01, REG_TISR); /* clear TIF0 */ | 103 | __raw_writel(0x01, REG_TISR); /* clear TIF0 */ |
104 | |||
105 | evt->event_handler(evt); | ||
46 | return IRQ_HANDLED; | 106 | return IRQ_HANDLED; |
47 | } | 107 | } |
48 | 108 | ||
49 | static struct irqaction w90x900_timer_irq = { | 109 | static struct irqaction w90p910_timer0_irq = { |
50 | .name = "w90x900 Timer Tick", | 110 | .name = "w90p910-timer0", |
51 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 111 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
52 | .handler = w90x900_timer_interrupt, | 112 | .handler = w90p910_timer0_interrupt, |
53 | }; | 113 | }; |
54 | 114 | ||
55 | /*Set up timer reg.*/ | 115 | static void __init w90p910_clockevents_init(unsigned int rate) |
116 | { | ||
117 | w90p910_clockevent_device.mult = div_sc(rate, NSEC_PER_SEC, | ||
118 | w90p910_clockevent_device.shift); | ||
119 | w90p910_clockevent_device.max_delta_ns = clockevent_delta2ns(0xffffffff, | ||
120 | &w90p910_clockevent_device); | ||
121 | w90p910_clockevent_device.min_delta_ns = clockevent_delta2ns(0xf, | ||
122 | &w90p910_clockevent_device); | ||
123 | w90p910_clockevent_device.cpumask = cpumask_of(0); | ||
56 | 124 | ||
57 | static void w90x900_timer_setup(void) | 125 | clockevents_register_device(&w90p910_clockevent_device); |
126 | } | ||
127 | |||
128 | static cycle_t w90p910_get_cycles(struct clocksource *cs) | ||
58 | { | 129 | { |
59 | __raw_writel(0, REG_TCSR0); | 130 | return ~__raw_readl(REG_TDR1); |
60 | __raw_writel(0, REG_TCSR1); | ||
61 | __raw_writel(0, REG_TCSR2); | ||
62 | __raw_writel(0, REG_TCSR3); | ||
63 | __raw_writel(0, REG_TCSR4); | ||
64 | __raw_writel(0x1F, REG_TISR); | ||
65 | __raw_writel(15000000/(100 * 100), REG_TICR0); | ||
66 | __raw_writel(0x68000063, REG_TCSR0); | ||
67 | } | 131 | } |
68 | 132 | ||
69 | static void __init w90x900_timer_init(void) | 133 | static struct clocksource clocksource_w90p910 = { |
134 | .name = "w90p910-timer1", | ||
135 | .rating = 200, | ||
136 | .read = w90p910_get_cycles, | ||
137 | .mask = CLOCKSOURCE_MASK(32), | ||
138 | .shift = 20, | ||
139 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
140 | }; | ||
141 | |||
142 | static void __init w90p910_clocksource_init(unsigned int rate) | ||
70 | { | 143 | { |
71 | w90x900_timer_setup(); | 144 | unsigned int val; |
72 | setup_irq(IRQ_TIMER0, &w90x900_timer_irq); | 145 | |
146 | __raw_writel(0xffffffff, REG_TICR1); | ||
147 | |||
148 | val = __raw_readl(REG_TCSR1); | ||
149 | val |= (COUNTEN | PERIOD); | ||
150 | __raw_writel(val, REG_TCSR1); | ||
151 | |||
152 | clocksource_w90p910.mult = | ||
153 | clocksource_khz2mult((rate / 1000), clocksource_w90p910.shift); | ||
154 | clocksource_register(&clocksource_w90p910); | ||
155 | } | ||
156 | |||
157 | static void __init w90p910_timer_init(void) | ||
158 | { | ||
159 | struct clk *ck_ext = clk_get(NULL, "ext"); | ||
160 | unsigned int rate; | ||
161 | |||
162 | BUG_ON(IS_ERR(ck_ext)); | ||
163 | |||
164 | rate = clk_get_rate(ck_ext); | ||
165 | clk_put(ck_ext); | ||
166 | rate = rate / (PRESCALE + 0x01); | ||
167 | |||
168 | /* set a known state */ | ||
169 | __raw_writel(0x00, REG_TCSR0); | ||
170 | __raw_writel(0x00, REG_TCSR1); | ||
171 | __raw_writel(RESETINT, REG_TISR); | ||
172 | timer0_load = (rate / TICKS_PER_SEC); | ||
173 | |||
174 | setup_irq(IRQ_TIMER0, &w90p910_timer0_irq); | ||
175 | |||
176 | w90p910_clocksource_init(rate); | ||
177 | w90p910_clockevents_init(rate); | ||
73 | } | 178 | } |
74 | 179 | ||
75 | struct sys_timer w90x900_timer = { | 180 | struct sys_timer w90x900_timer = { |
76 | .init = w90x900_timer_init, | 181 | .init = w90p910_timer_init, |
77 | .offset = w90x900_gettimeoffset, | ||
78 | .resume = w90x900_timer_setup | ||
79 | }; | 182 | }; |