]>
Commit | Line | Data |
---|---|---|
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), | |
5cccd37e | 92 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
93 | }; | |
94 | ||
95 | static struct irqaction stmp3xxx_timer_irq = { | |
96 | .name = "stmp3xxx_timer", | |
97 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
98 | .handler = stmp3xxx_timer_interrupt, | |
99 | .dev_id = &ckevt_timrot, | |
100 | }; | |
101 | ||
102 | ||
103 | /* | |
104 | * Set up timer interrupt, and return the current time in seconds. | |
105 | */ | |
106 | static void __init stmp3xxx_init_timer(void) | |
107 | { | |
5cccd37e | 108 | ckevt_timrot.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, |
109 | ckevt_timrot.shift); | |
110 | ckevt_timrot.min_delta_ns = clockevent_delta2ns(2, &ckevt_timrot); | |
111 | ckevt_timrot.max_delta_ns = clockevent_delta2ns(0xFFF, &ckevt_timrot); | |
112 | ckevt_timrot.cpumask = cpumask_of(0); | |
113 | ||
98f420b2 | 114 | stmp3xxx_reset_block(REGS_TIMROT_BASE, false); |
115 | ||
116 | /* clear two timers */ | |
117 | __raw_writel(0, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); | |
118 | __raw_writel(0, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); | |
119 | ||
120 | /* configure them */ | |
121 | __raw_writel( | |
122 | (8 << BP_TIMROT_TIMCTRLn_SELECT) | /* 32 kHz */ | |
123 | BM_TIMROT_TIMCTRLn_RELOAD | | |
124 | BM_TIMROT_TIMCTRLn_UPDATE | | |
125 | BM_TIMROT_TIMCTRLn_IRQ_EN, | |
126 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); | |
127 | __raw_writel( | |
128 | (8 << BP_TIMROT_TIMCTRLn_SELECT) | /* 32 kHz */ | |
129 | BM_TIMROT_TIMCTRLn_RELOAD | | |
130 | BM_TIMROT_TIMCTRLn_UPDATE | | |
131 | BM_TIMROT_TIMCTRLn_IRQ_EN, | |
132 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); | |
133 | ||
134 | __raw_writel(CLOCK_TICK_RATE / HZ - 1, | |
135 | REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); | |
136 | __raw_writel(0xFFFF, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); | |
5cccd37e | 137 | |
138 | setup_irq(IRQ_TIMER0, &stmp3xxx_timer_irq); | |
139 | ||
2218543f | 140 | clocksource_register_hz(&cksrc_stmp3xxx, CLOCK_TICK_RATE); |
5cccd37e | 141 | clockevents_register_device(&ckevt_timrot); |
142 | } | |
143 | ||
144 | #ifdef CONFIG_PM | |
145 | ||
146 | void stmp3xxx_suspend_timer(void) | |
147 | { | |
98f420b2 | 148 | stmp3xxx_clearl(BM_TIMROT_TIMCTRLn_IRQ_EN | BM_TIMROT_TIMCTRLn_IRQ, |
149 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); | |
150 | stmp3xxx_setl(BM_TIMROT_ROTCTRL_CLKGATE, | |
151 | REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); | |
5cccd37e | 152 | } |
153 | ||
154 | void stmp3xxx_resume_timer(void) | |
155 | { | |
98f420b2 | 156 | stmp3xxx_clearl(BM_TIMROT_ROTCTRL_SFTRST | BM_TIMROT_ROTCTRL_CLKGATE, |
157 | REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL); | |
158 | __raw_writel( | |
159 | 8 << BP_TIMROT_TIMCTRLn_SELECT | /* 32 kHz */ | |
160 | BM_TIMROT_TIMCTRLn_RELOAD | | |
161 | BM_TIMROT_TIMCTRLn_UPDATE | | |
162 | BM_TIMROT_TIMCTRLn_IRQ_EN, | |
163 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL0); | |
164 | __raw_writel( | |
165 | 8 << BP_TIMROT_TIMCTRLn_SELECT | /* 32 kHz */ | |
166 | BM_TIMROT_TIMCTRLn_RELOAD | | |
167 | BM_TIMROT_TIMCTRLn_UPDATE | | |
168 | BM_TIMROT_TIMCTRLn_IRQ_EN, | |
169 | REGS_TIMROT_BASE + HW_TIMROT_TIMCTRL1); | |
170 | __raw_writel(CLOCK_TICK_RATE / HZ - 1, | |
171 | REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT0); | |
172 | __raw_writel(0xFFFF, REGS_TIMROT_BASE + HW_TIMROT_TIMCOUNT1); | |
5cccd37e | 173 | } |
174 | ||
175 | #else | |
176 | ||
177 | #define stmp3xxx_suspend_timer NULL | |
178 | #define stmp3xxx_resume_timer NULL | |
179 | ||
180 | #endif /* CONFIG_PM */ | |
181 | ||
182 | struct sys_timer stmp3xxx_timer = { | |
183 | .init = stmp3xxx_init_timer, | |
184 | .suspend = stmp3xxx_suspend_timer, | |
185 | .resume = stmp3xxx_resume_timer, | |
186 | }; |