diff options
Diffstat (limited to 'arch/arm/mach-ns9xxx/time.c')
-rw-r--r-- | arch/arm/mach-ns9xxx/time.c | 172 |
1 files changed, 134 insertions, 38 deletions
diff --git a/arch/arm/mach-ns9xxx/time.c b/arch/arm/mach-ns9xxx/time.c index b97d0c54a388..c3dd1f4acb99 100644 --- a/arch/arm/mach-ns9xxx/time.c +++ b/arch/arm/mach-ns9xxx/time.c | |||
@@ -11,78 +11,174 @@ | |||
11 | #include <linux/jiffies.h> | 11 | #include <linux/jiffies.h> |
12 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
13 | #include <linux/irq.h> | 13 | #include <linux/irq.h> |
14 | #include <linux/stringify.h> | ||
15 | #include <linux/clocksource.h> | ||
16 | #include <linux/clockchips.h> | ||
17 | |||
14 | #include <asm/arch-ns9xxx/regs-sys.h> | 18 | #include <asm/arch-ns9xxx/regs-sys.h> |
15 | #include <asm/arch-ns9xxx/clock.h> | 19 | #include <asm/arch-ns9xxx/clock.h> |
16 | #include <asm/arch-ns9xxx/irqs.h> | 20 | #include <asm/arch-ns9xxx/irqs.h> |
17 | #include <asm/arch/system.h> | 21 | #include <asm/arch/system.h> |
18 | #include "generic.h" | 22 | #include "generic.h" |
19 | 23 | ||
20 | #define TIMERCLOCKSELECT 64 | 24 | #define TIMER_CLOCKSOURCE 0 |
25 | #define TIMER_CLOCKEVENT 1 | ||
26 | static u32 latch; | ||
27 | |||
28 | static cycle_t ns9xxx_clocksource_read(void) | ||
29 | { | ||
30 | return __raw_readl(SYS_TR(TIMER_CLOCKSOURCE)); | ||
31 | } | ||
21 | 32 | ||
22 | static u32 usecs_per_tick; | 33 | static struct clocksource ns9xxx_clocksource = { |
34 | .name = "ns9xxx-timer" __stringify(TIMER_CLOCKSOURCE), | ||
35 | .rating = 300, | ||
36 | .read = ns9xxx_clocksource_read, | ||
37 | .mask = CLOCKSOURCE_MASK(32), | ||
38 | .shift = 20, | ||
39 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
40 | }; | ||
23 | 41 | ||
24 | static irqreturn_t | 42 | static void ns9xxx_clockevent_setmode(enum clock_event_mode mode, |
25 | ns9xxx_timer_interrupt(int irq, void *dev_id) | 43 | struct clock_event_device *clk) |
26 | { | 44 | { |
27 | write_seqlock(&xtime_lock); | 45 | u32 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); |
28 | timer_tick(); | 46 | |
29 | write_sequnlock(&xtime_lock); | 47 | switch(mode) { |
48 | case CLOCK_EVT_MODE_PERIODIC: | ||
49 | __raw_writel(latch, SYS_TRC(TIMER_CLOCKEVENT)); | ||
50 | REGSET(tc, SYS_TCx, REN, EN); | ||
51 | REGSET(tc, SYS_TCx, INTS, EN); | ||
52 | REGSET(tc, SYS_TCx, TEN, EN); | ||
53 | break; | ||
54 | |||
55 | case CLOCK_EVT_MODE_ONESHOT: | ||
56 | REGSET(tc, SYS_TCx, REN, DIS); | ||
57 | REGSET(tc, SYS_TCx, INTS, EN); | ||
58 | |||
59 | /* fall through */ | ||
60 | |||
61 | case CLOCK_EVT_MODE_UNUSED: | ||
62 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
63 | case CLOCK_EVT_MODE_RESUME: | ||
64 | default: | ||
65 | REGSET(tc, SYS_TCx, TEN, DIS); | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); | ||
70 | } | ||
30 | 71 | ||
31 | return IRQ_HANDLED; | 72 | static int ns9xxx_clockevent_setnextevent(unsigned long evt, |
73 | struct clock_event_device *clk) | ||
74 | { | ||
75 | u32 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); | ||
76 | |||
77 | if (REGGET(tc, SYS_TCx, TEN)) { | ||
78 | REGSET(tc, SYS_TCx, TEN, DIS); | ||
79 | __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); | ||
80 | } | ||
81 | |||
82 | REGSET(tc, SYS_TCx, TEN, EN); | ||
83 | |||
84 | __raw_writel(evt, SYS_TRC(TIMER_CLOCKEVENT)); | ||
85 | |||
86 | __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); | ||
87 | |||
88 | return 0; | ||
32 | } | 89 | } |
33 | 90 | ||
34 | static unsigned long ns9xxx_timer_gettimeoffset(void) | 91 | static struct clock_event_device ns9xxx_clockevent_device = { |
92 | .name = "ns9xxx-timer" __stringify(TIMER_CLOCKEVENT), | ||
93 | .shift = 20, | ||
94 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
95 | .set_mode = ns9xxx_clockevent_setmode, | ||
96 | .set_next_event = ns9xxx_clockevent_setnextevent, | ||
97 | }; | ||
98 | |||
99 | static irqreturn_t ns9xxx_clockevent_handler(int irq, void *dev_id) | ||
35 | { | 100 | { |
36 | /* return the microseconds which have passed since the last interrupt | 101 | int timerno = irq - IRQ_TIMER0; |
37 | * was _serviced_. That is, if an interrupt is pending or the counter | 102 | u32 tc; |
38 | * reloads, return one period more. */ | ||
39 | 103 | ||
40 | u32 counter1 = SYS_TR(0); | 104 | struct clock_event_device *evt = &ns9xxx_clockevent_device; |
41 | int pending = SYS_ISR & (1 << IRQ_TIMER0); | ||
42 | u32 counter2 = SYS_TR(0); | ||
43 | u32 elapsed; | ||
44 | 105 | ||
45 | if (pending || counter2 > counter1) | 106 | /* clear irq */ |
46 | elapsed = 2 * SYS_TRC(0) - counter2; | 107 | tc = __raw_readl(SYS_TC(timerno)); |
47 | else | 108 | if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) { |
48 | elapsed = SYS_TRC(0) - counter1; | 109 | REGSET(tc, SYS_TCx, TEN, DIS); |
110 | __raw_writel(tc, SYS_TC(timerno)); | ||
111 | } | ||
112 | REGSET(tc, SYS_TCx, INTC, SET); | ||
113 | __raw_writel(tc, SYS_TC(timerno)); | ||
114 | REGSET(tc, SYS_TCx, INTC, UNSET); | ||
115 | __raw_writel(tc, SYS_TC(timerno)); | ||
49 | 116 | ||
50 | return (elapsed * usecs_per_tick) >> 16; | 117 | evt->event_handler(evt); |
51 | 118 | ||
119 | return IRQ_HANDLED; | ||
52 | } | 120 | } |
53 | 121 | ||
54 | static struct irqaction ns9xxx_timer_irq = { | 122 | static struct irqaction ns9xxx_clockevent_action = { |
55 | .name = "NS9xxx Timer Tick", | 123 | .name = "ns9xxx-timer" __stringify(TIMER_CLOCKEVENT), |
56 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 124 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
57 | .handler = ns9xxx_timer_interrupt, | 125 | .handler = ns9xxx_clockevent_handler, |
58 | }; | 126 | }; |
59 | 127 | ||
60 | static void __init ns9xxx_timer_init(void) | 128 | static void __init ns9xxx_timer_init(void) |
61 | { | 129 | { |
62 | int tc; | 130 | int tc; |
63 | 131 | ||
64 | usecs_per_tick = | 132 | tc = __raw_readl(SYS_TC(TIMER_CLOCKSOURCE)); |
65 | SH_DIV(1000000 * TIMERCLOCKSELECT, ns9xxx_cpuclock(), 16); | 133 | if (REGGET(tc, SYS_TCx, TEN)) { |
134 | REGSET(tc, SYS_TCx, TEN, DIS); | ||
135 | __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE)); | ||
136 | } | ||
66 | 137 | ||
67 | /* disable timer */ | 138 | __raw_writel(0, SYS_TRC(TIMER_CLOCKSOURCE)); |
68 | if ((tc = SYS_TC(0)) & SYS_TCx_TEN) | ||
69 | SYS_TC(0) = tc & ~SYS_TCx_TEN; | ||
70 | |||
71 | SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0); | ||
72 | 139 | ||
73 | REGSET(tc, SYS_TCx, TEN, EN); | 140 | REGSET(tc, SYS_TCx, TEN, EN); |
74 | REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */ | ||
75 | REGSET(tc, SYS_TCx, INTS, EN); | ||
76 | REGSET(tc, SYS_TCx, UDS, DOWN); | ||
77 | REGSET(tc, SYS_TCx, TDBG, STOP); | 141 | REGSET(tc, SYS_TCx, TDBG, STOP); |
142 | REGSET(tc, SYS_TCx, TLCS, CPU); | ||
143 | REGSET(tc, SYS_TCx, TM, IEE); | ||
144 | REGSET(tc, SYS_TCx, INTS, DIS); | ||
145 | REGSET(tc, SYS_TCx, UDS, UP); | ||
78 | REGSET(tc, SYS_TCx, TSZ, 32); | 146 | REGSET(tc, SYS_TCx, TSZ, 32); |
79 | REGSET(tc, SYS_TCx, REN, EN); | 147 | REGSET(tc, SYS_TCx, REN, EN); |
80 | SYS_TC(0) = tc; | ||
81 | 148 | ||
82 | setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq); | 149 | __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE)); |
150 | |||
151 | ns9xxx_clocksource.mult = clocksource_hz2mult(ns9xxx_cpuclock(), | ||
152 | ns9xxx_clocksource.shift); | ||
153 | |||
154 | clocksource_register(&ns9xxx_clocksource); | ||
155 | |||
156 | latch = SH_DIV(ns9xxx_cpuclock(), HZ, 0); | ||
157 | |||
158 | tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); | ||
159 | REGSET(tc, SYS_TCx, TEN, DIS); | ||
160 | REGSET(tc, SYS_TCx, TDBG, STOP); | ||
161 | REGSET(tc, SYS_TCx, TLCS, CPU); | ||
162 | REGSET(tc, SYS_TCx, TM, IEE); | ||
163 | REGSET(tc, SYS_TCx, INTS, DIS); | ||
164 | REGSET(tc, SYS_TCx, UDS, DOWN); | ||
165 | REGSET(tc, SYS_TCx, TSZ, 32); | ||
166 | REGSET(tc, SYS_TCx, REN, EN); | ||
167 | __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); | ||
168 | |||
169 | ns9xxx_clockevent_device.mult = div_sc(ns9xxx_cpuclock(), | ||
170 | NSEC_PER_SEC, ns9xxx_clockevent_device.shift); | ||
171 | ns9xxx_clockevent_device.max_delta_ns = | ||
172 | clockevent_delta2ns(-1, &ns9xxx_clockevent_device); | ||
173 | ns9xxx_clockevent_device.min_delta_ns = | ||
174 | clockevent_delta2ns(1, &ns9xxx_clockevent_device); | ||
175 | |||
176 | ns9xxx_clockevent_device.cpumask = cpumask_of_cpu(0); | ||
177 | clockevents_register_device(&ns9xxx_clockevent_device); | ||
178 | |||
179 | setup_irq(IRQ_TIMER0 + TIMER_CLOCKEVENT, &ns9xxx_clockevent_action); | ||
83 | } | 180 | } |
84 | 181 | ||
85 | struct sys_timer ns9xxx_timer = { | 182 | struct sys_timer ns9xxx_timer = { |
86 | .init = ns9xxx_timer_init, | 183 | .init = ns9xxx_timer_init, |
87 | .offset = ns9xxx_timer_gettimeoffset, | ||
88 | }; | 184 | }; |