5cccd37e |
1 | /* |
2 | * System timer for Freescale STMP37XX/STMP378X |
3 | * |
4 | * Embedded Alley Solutions, Inc <source@embeddedalley.com> |
5 | * |
6 | * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. |
7 | * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. |
8 | */ |
9 | |
10 | /* |
11 | * The code contained herein is licensed under the GNU General Public |
12 | * License. You may obtain a copy of the GNU General Public License |
13 | * Version 2 or later at the following locations: |
14 | * |
15 | * http://www.opensource.org/licenses/gpl-license.html |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | */ |
18 | #include <linux/kernel.h> |
19 | #include <linux/init.h> |
20 | #include <linux/spinlock.h> |
21 | #include <linux/clocksource.h> |
22 | #include <linux/clockchips.h> |
23 | #include <linux/io.h> |
24 | #include <linux/irq.h> |
25 | #include <linux/interrupt.h> |
26 | |
27 | #include <asm/mach/time.h> |
28 | #include <mach/stmp3xxx.h> |
98f420b2 |
29 | #include <mach/platform.h> |
5cccd37e |
30 | #include <mach/regs-timrot.h> |
31 | |
32 | static irqreturn_t |
33 | stmp3xxx_timer_interrupt(int irq, void *dev_id) |
34 | { |
35 | struct clock_event_device *c = dev_id; |
36 | |
98f420b2 |
37 | /* timer 0 */ |
38 | if (__raw_readl(REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0) & |
39 | BM_TIMROT_TIMCTRLn_IRQ) { |
40 | stmp3xxx_clearl(BM_TIMROT_TIMCTRLn_IRQ, |
41 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); |
5cccd37e |
42 | c->event_handler(c); |
98f420b2 |
43 | } |
44 | |
45 | /* timer 1 */ |
46 | else if (__raw_readl(REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1) |
47 | & BM_TIMROT_TIMCTRLn_IRQ) { |
48 | stmp3xxx_clearl(BM_TIMROT_TIMCTRLn_IRQ, |
49 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); |
50 | stmp3xxx_clearl(BM_TIMROT_TIMCTRLn_IRQ_EN, |
51 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); |
52 | __raw_writel(0xFFFF, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); |
5cccd37e |
53 | } |
54 | |
55 | return IRQ_HANDLED; |
56 | } |
57 | |
b4380b8e |
58 | static cycle_t stmp3xxx_clock_read(struct clocksource *cs) |
5cccd37e |
59 | { |
98f420b2 |
60 | return ~((__raw_readl(REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1) |
61 | & 0xFFFF0000) >> 16); |
5cccd37e |
62 | } |
63 | |
64 | static int |
65 | stmp3xxx_timrot_set_next_event(unsigned long delta, |
66 | struct clock_event_device *dev) |
67 | { |
98f420b2 |
68 | /* reload the timer */ |
69 | __raw_writel(delta, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); |
5cccd37e |
70 | return 0; |
71 | } |
72 | |
73 | static void |
74 | stmp3xxx_timrot_set_mode(enum clock_event_mode mode, |
75 | struct clock_event_device *dev) |
76 | { |
77 | } |
78 | |
79 | static struct clock_event_device ckevt_timrot = { |
80 | .name = "timrot", |
81 | .features = CLOCK_EVT_FEAT_ONESHOT, |
82 | .shift = 32, |
83 | .set_next_event = stmp3xxx_timrot_set_next_event, |
84 | .set_mode = stmp3xxx_timrot_set_mode, |
85 | }; |
86 | |
87 | static struct clocksource cksrc_stmp3xxx = { |
88 | .name = "cksrc_stmp3xxx", |
89 | .rating = 250, |
90 | .read = stmp3xxx_clock_read, |
91 | .mask = CLOCKSOURCE_MASK(16), |
92 | .shift = 10, |
93 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
94 | }; |
95 | |
96 | static struct irqaction stmp3xxx_timer_irq = { |
97 | .name = "stmp3xxx_timer", |
98 | .flags = IRQF_DISABLED | IRQF_TIMER, |
99 | .handler = stmp3xxx_timer_interrupt, |
100 | .dev_id = &ckevt_timrot, |
101 | }; |
102 | |
103 | |
104 | /* |
105 | * Set up timer interrupt, and return the current time in seconds. |
106 | */ |
107 | static void __init stmp3xxx_init_timer(void) |
108 | { |
109 | cksrc_stmp3xxx.mult = clocksource_hz2mult(CLOCK_TICK_RATE, |
110 | cksrc_stmp3xxx.shift); |
111 | ckevt_timrot.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, |
112 | ckevt_timrot.shift); |
113 | ckevt_timrot.min_delta_ns = clockevent_delta2ns(2, &ckevt_timrot); |
114 | ckevt_timrot.max_delta_ns = clockevent_delta2ns(0xFFF, &ckevt_timrot); |
115 | ckevt_timrot.cpumask = cpumask_of(0); |
116 | |
98f420b2 |
117 | stmp3xxx_reset_block(REGS_TIMROT_BASE, false); |
118 | |
119 | /* clear two timers */ |
120 | __raw_writel(0, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); |
121 | __raw_writel(0, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); |
122 | |
123 | /* configure them */ |
124 | __raw_writel( |
125 | (8 << BP_TIMROT_TIMCTRLn_SELECT) | /* 32 kHz */ |
126 | BM_TIMROT_TIMCTRLn_RELOAD | |
127 | BM_TIMROT_TIMCTRLn_UPDATE | |
128 | BM_TIMROT_TIMCTRLn_IRQ_EN, |
129 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); |
130 | __raw_writel( |
131 | (8 << BP_TIMROT_TIMCTRLn_SELECT) | /* 32 kHz */ |
132 | BM_TIMROT_TIMCTRLn_RELOAD | |
133 | BM_TIMROT_TIMCTRLn_UPDATE | |
134 | BM_TIMROT_TIMCTRLn_IRQ_EN, |
135 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); |
136 | |
137 | __raw_writel(CLOCK_TICK_RATE / HZ - 1, |
138 | REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); |
139 | __raw_writel(0xFFFF, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); |
5cccd37e |
140 | |
141 | setup_irq(IRQ_TIMER0, &stmp3xxx_timer_irq); |
142 | |
143 | clocksource_register(&cksrc_stmp3xxx); |
144 | clockevents_register_device(&ckevt_timrot); |
145 | } |
146 | |
147 | #ifdef CONFIG_PM |
148 | |
149 | void stmp3xxx_suspend_timer(void) |
150 | { |
98f420b2 |
151 | stmp3xxx_clearl(BM_TIMROT_TIMCTRLn_IRQ_EN | BM_TIMROT_TIMCTRLn_IRQ, |
152 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); |
153 | stmp3xxx_setl(BM_TIMROT_ROTCTRL_CLKGATE, |
154 | REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); |
5cccd37e |
155 | } |
156 | |
157 | void stmp3xxx_resume_timer(void) |
158 | { |
98f420b2 |
159 | stmp3xxx_clearl(BM_TIMROT_ROTCTRL_SFTRST | BM_TIMROT_ROTCTRL_CLKGATE, |
160 | REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); |
161 | __raw_writel( |
162 | 8 << BP_TIMROT_TIMCTRLn_SELECT | /* 32 kHz */ |
163 | BM_TIMROT_TIMCTRLn_RELOAD | |
164 | BM_TIMROT_TIMCTRLn_UPDATE | |
165 | BM_TIMROT_TIMCTRLn_IRQ_EN, |
166 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); |
167 | __raw_writel( |
168 | 8 << BP_TIMROT_TIMCTRLn_SELECT | /* 32 kHz */ |
169 | BM_TIMROT_TIMCTRLn_RELOAD | |
170 | BM_TIMROT_TIMCTRLn_UPDATE | |
171 | BM_TIMROT_TIMCTRLn_IRQ_EN, |
172 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); |
173 | __raw_writel(CLOCK_TICK_RATE / HZ - 1, |
174 | REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); |
175 | __raw_writel(0xFFFF, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); |
5cccd37e |
176 | } |
177 | |
178 | #else |
179 | |
180 | #define stmp3xxx_suspend_timer NULL |
181 | #define stmp3xxx_resume_timer NULL |
182 | |
183 | #endif /* CONFIG_PM */ |
184 | |
185 | struct sys_timer stmp3xxx_timer = { |
186 | .init = stmp3xxx_init_timer, |
187 | .suspend = stmp3xxx_suspend_timer, |
188 | .resume = stmp3xxx_resume_timer, |
189 | }; |