]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - arch/sh/kernel/timers/timer-cmt.c
9eab395cd3415332c3499a425019a85d2774f4ca
[mirror_ubuntu-bionic-kernel.git] / arch / sh / kernel / timers / timer-cmt.c
1 /*
2 * arch/sh/kernel/timers/timer-cmt.c - CMT Timer Support
3 *
4 * Copyright (C) 2005 Yoshinori Sato
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive
8 * for more details.
9 */
10
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/interrupt.h>
14 #include <linux/spinlock.h>
15 #include <linux/seqlock.h>
16 #include <asm/timer.h>
17 #include <asm/rtc.h>
18 #include <asm/io.h>
19 #include <asm/irq.h>
20 #include <asm/clock.h>
21
22 #if defined(CONFIG_CPU_SUBTYPE_SH7619)
23 #define CMT_CMSTR 0xf84a0070
24 #define CMT_CMCSR_0 0xf84a0072
25 #define CMT_CMCNT_0 0xf84a0074
26 #define CMT_CMCOR_0 0xf84a0076
27 #define CMT_CMCSR_1 0xf84a0078
28 #define CMT_CMCNT_1 0xf84a007a
29 #define CMT_CMCOR_1 0xf84a007c
30
31 #define STBCR3 0xf80a0000
32 #define cmt_clock_enable() do { ctrl_outb(ctrl_inb(STBCR3) & ~0x10, STBCR3); } while(0)
33 #define CMT_CMCSR_INIT 0x0040
34 #define CMT_CMCSR_CALIB 0x0000
35 #elif defined(CONFIG_CPU_SUBTYPE_SH7206)
36 #define CMT_CMSTR 0xfffec000
37 #define CMT_CMCSR_0 0xfffec002
38 #define CMT_CMCNT_0 0xfffec004
39 #define CMT_CMCOR_0 0xfffec006
40
41 #define STBCR4 0xfffe040c
42 #define cmt_clock_enable() do { ctrl_outb(ctrl_inb(STBCR4) & ~0x04, STBCR4); } while(0)
43 #define CMT_CMCSR_INIT 0x0040
44 #define CMT_CMCSR_CALIB 0x0000
45 #else
46 #error "Unknown CPU SUBTYPE"
47 #endif
48
49 static DEFINE_SPINLOCK(cmt0_lock);
50
51 static unsigned long cmt_timer_get_offset(void)
52 {
53 int count;
54 unsigned long flags;
55
56 static unsigned short count_p = 0xffff; /* for the first call after boot */
57 static unsigned long jiffies_p = 0;
58
59 /*
60 * cache volatile jiffies temporarily; we have IRQs turned off.
61 */
62 unsigned long jiffies_t;
63
64 spin_lock_irqsave(&cmt0_lock, flags);
65 /* timer count may underflow right here */
66 count = ctrl_inw(CMT_CMCOR_0);
67 count -= ctrl_inw(CMT_CMCNT_0);
68
69 jiffies_t = jiffies;
70
71 /*
72 * avoiding timer inconsistencies (they are rare, but they happen)...
73 * there is one kind of problem that must be avoided here:
74 * 1. the timer counter underflows
75 */
76
77 if (jiffies_t == jiffies_p) {
78 if (count > count_p) {
79 /* the nutcase */
80 if (ctrl_inw(CMT_CMCSR_0) & 0x80) { /* Check CMF bit */
81 count -= LATCH;
82 } else {
83 printk("%s (): hardware timer problem?\n",
84 __FUNCTION__);
85 }
86 }
87 } else
88 jiffies_p = jiffies_t;
89
90 count_p = count;
91 spin_unlock_irqrestore(&cmt0_lock, flags);
92
93 count = ((LATCH-1) - count) * TICK_SIZE;
94 count = (count + LATCH/2) / LATCH;
95
96 return count;
97 }
98
99 static irqreturn_t cmt_timer_interrupt(int irq, void *dev_id,
100 struct pt_regs *regs)
101 {
102 unsigned long timer_status;
103
104 /* Clear CMF bit */
105 timer_status = ctrl_inw(CMT_CMCSR_0);
106 timer_status &= ~0x80;
107 ctrl_outw(timer_status, CMT_CMCSR_0);
108
109 /*
110 * Here we are in the timer irq handler. We just have irqs locally
111 * disabled but we don't know if the timer_bh is running on the other
112 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
113 * the irq version of write_lock because as just said we have irq
114 * locally disabled. -arca
115 */
116 write_seqlock(&xtime_lock);
117 handle_timer_tick(regs);
118 write_sequnlock(&xtime_lock);
119
120 return IRQ_HANDLED;
121 }
122
123 static struct irqaction cmt_irq = {
124 .name = "timer",
125 .handler = cmt_timer_interrupt,
126 .flags = SA_INTERRUPT,
127 .mask = CPU_MASK_NONE,
128 };
129
130 /*
131 * Hah! We'll see if this works (switching from usecs to nsecs).
132 */
133 static unsigned long cmt_timer_get_frequency(void)
134 {
135 u32 freq;
136 struct timespec ts1, ts2;
137 unsigned long diff_nsec;
138 unsigned long factor;
139
140 /* Setup the timer: We don't want to generate interrupts, just
141 * have it count down at its natural rate.
142 */
143
144 ctrl_outw(ctrl_inw(CMT_CMSTR) & ~0x01, CMT_CMSTR);
145 ctrl_outw(CMT_CMCSR_CALIB, CMT_CMCSR_0);
146 ctrl_outw(0xffff, CMT_CMCOR_0);
147 ctrl_outw(0xffff, CMT_CMCNT_0);
148
149 rtc_sh_get_time(&ts2);
150
151 do {
152 rtc_sh_get_time(&ts1);
153 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
154
155 /* actually start the timer */
156 ctrl_outw(ctrl_inw(CMT_CMSTR) | 0x01, CMT_CMSTR);
157
158 do {
159 rtc_sh_get_time(&ts2);
160 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
161
162 freq = 0xffff - ctrl_inw(CMT_CMCNT_0);
163 if (ts2.tv_nsec < ts1.tv_nsec) {
164 ts2.tv_nsec += 1000000000;
165 ts2.tv_sec--;
166 }
167
168 diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
169
170 /* this should work well if the RTC has a precision of n Hz, where
171 * n is an integer. I don't think we have to worry about the other
172 * cases. */
173 factor = (1000000000 + diff_nsec/2) / diff_nsec;
174
175 if (factor * diff_nsec > 1100000000 ||
176 factor * diff_nsec < 900000000)
177 panic("weird RTC (diff_nsec %ld)", diff_nsec);
178
179 return freq * factor;
180 }
181
182 static void cmt_clk_init(struct clk *clk)
183 {
184 u8 divisor = CMT_CMCSR_INIT & 0x3;
185 ctrl_inw(CMT_CMCSR_0);
186 ctrl_outw(CMT_CMCSR_INIT, CMT_CMCSR_0);
187 clk->parent = clk_get("module_clk");
188 clk->rate = clk->parent->rate / (8 << (divisor << 1));
189 }
190
191 static void cmt_clk_recalc(struct clk *clk)
192 {
193 u8 divisor = ctrl_inw(CMT_CMCSR_0) & 0x3;
194 clk->rate = clk->parent->rate / (8 << (divisor << 1));
195 }
196
197 static struct clk_ops cmt_clk_ops = {
198 .init = cmt_clk_init,
199 .recalc = cmt_clk_recalc,
200 };
201
202 static struct clk cmt0_clk = {
203 .name = "cmt0_clk",
204 .ops = &cmt_clk_ops,
205 };
206
207 static int cmt_timer_start(void)
208 {
209 ctrl_outw(ctrl_inw(CMT_CMSTR) | 0x01, CMT_CMSTR);
210 return 0;
211 }
212
213 static int cmt_timer_stop(void)
214 {
215 ctrl_outw(ctrl_inw(CMT_CMSTR) & ~0x01, CMT_CMSTR);
216 return 0;
217 }
218
219 static int cmt_timer_init(void)
220 {
221 unsigned long interval;
222
223 cmt_clock_enable();
224
225 setup_irq(TIMER_IRQ, &cmt_irq);
226
227 cmt0_clk.parent = clk_get("module_clk");
228
229 cmt_timer_stop();
230
231 interval = cmt0_clk.parent->rate / 8 / HZ;
232 printk(KERN_INFO "Interval = %ld\n", interval);
233
234 ctrl_outw(interval, CMT_CMCOR_0);
235
236 clk_register(&cmt0_clk);
237 clk_enable(&cmt0_clk);
238
239 cmt_timer_start();
240
241 return 0;
242 }
243
244 struct sys_timer_ops cmt_timer_ops = {
245 .init = cmt_timer_init,
246 .start = cmt_timer_start,
247 .stop = cmt_timer_stop,
248 .get_frequency = cmt_timer_get_frequency,
249 .get_offset = cmt_timer_get_offset,
250 };
251
252 struct sys_timer cmt_timer = {
253 .name = "cmt",
254 .ops = &cmt_timer_ops,
255 };
256