diff options
author | David S. Miller <davem@davemloft.net> | 2008-09-03 18:52:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-03 18:52:38 -0400 |
commit | c4cbe6f96ebf8eb03884c31504d36dccd2ef1062 (patch) | |
tree | 37dc6a68db054a3bb0b8e1bed83e54cc67b190d1 /arch/sparc/kernel/time.c | |
parent | 64151ad5b3a03e236390d6d5160805ee4f4e7c67 (diff) |
sparc32: use RTC subsystem
Use rtc subsystem for sparc32 architecture.
Actually, only one driver is needed: m48t59
as it supports the most common clocks on sparc32
machines: m48t08 and m48t02.
[ Add proper RTC layer calls to set_rtc_mmss() -DaveM ]
Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/time.c')
-rw-r--r-- | arch/sparc/kernel/time.c | 217 |
1 files changed, 60 insertions, 157 deletions
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index f0a2874b04e9..698c45059fa5 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/mm.h> | 23 | #include <linux/mm.h> |
24 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
25 | #include <linux/time.h> | 25 | #include <linux/time.h> |
26 | #include <linux/rtc.h> | ||
27 | #include <linux/rtc/m48t59.h> | ||
26 | #include <linux/timex.h> | 28 | #include <linux/timex.h> |
27 | #include <linux/init.h> | 29 | #include <linux/init.h> |
28 | #include <linux/pci.h> | 30 | #include <linux/pci.h> |
@@ -30,10 +32,10 @@ | |||
30 | #include <linux/profile.h> | 32 | #include <linux/profile.h> |
31 | #include <linux/of.h> | 33 | #include <linux/of.h> |
32 | #include <linux/of_device.h> | 34 | #include <linux/of_device.h> |
35 | #include <linux/platform_device.h> | ||
33 | 36 | ||
34 | #include <asm/oplib.h> | 37 | #include <asm/oplib.h> |
35 | #include <asm/timer.h> | 38 | #include <asm/timer.h> |
36 | #include <asm/mostek.h> | ||
37 | #include <asm/system.h> | 39 | #include <asm/system.h> |
38 | #include <asm/irq.h> | 40 | #include <asm/irq.h> |
39 | #include <asm/io.h> | 41 | #include <asm/io.h> |
@@ -46,10 +48,6 @@ | |||
46 | #include "irq.h" | 48 | #include "irq.h" |
47 | 49 | ||
48 | DEFINE_SPINLOCK(rtc_lock); | 50 | DEFINE_SPINLOCK(rtc_lock); |
49 | static enum sparc_clock_type sp_clock_typ; | ||
50 | DEFINE_SPINLOCK(mostek_lock); | ||
51 | void __iomem *mstk48t02_regs = NULL; | ||
52 | static struct mostek48t08 __iomem *mstk48t08_regs = NULL; | ||
53 | static int set_rtc_mmss(unsigned long); | 51 | static int set_rtc_mmss(unsigned long); |
54 | static int sbus_do_settimeofday(struct timespec *tv); | 52 | static int sbus_do_settimeofday(struct timespec *tv); |
55 | 53 | ||
@@ -118,107 +116,55 @@ static irqreturn_t timer_interrupt(int dummy, void *dev_id) | |||
118 | return IRQ_HANDLED; | 116 | return IRQ_HANDLED; |
119 | } | 117 | } |
120 | 118 | ||
121 | /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ | 119 | static unsigned char mostek_read_byte(struct device *dev, u32 ofs) |
122 | static void __devinit kick_start_clock(void) | ||
123 | { | 120 | { |
124 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | 121 | struct platform_device *pdev = to_platform_device(dev); |
125 | unsigned char sec; | 122 | struct m48t59_plat_data *pdata = pdev->dev.platform_data; |
126 | int i, count; | 123 | void __iomem *regs = pdata->ioaddr; |
127 | 124 | unsigned char val = readb(regs + ofs); | |
128 | prom_printf("CLOCK: Clock was stopped. Kick start "); | 125 | |
129 | 126 | /* the year 0 is 1968 */ | |
130 | spin_lock_irq(&mostek_lock); | 127 | if (ofs == pdata->offset + M48T59_YEAR) { |
131 | 128 | val += 0x68; | |
132 | /* Turn on the kick start bit to start the oscillator. */ | 129 | if ((val & 0xf) > 9) |
133 | regs->creg |= MSTK_CREG_WRITE; | 130 | val += 6; |
134 | regs->sec &= ~MSTK_STOP; | ||
135 | regs->hour |= MSTK_KICK_START; | ||
136 | regs->creg &= ~MSTK_CREG_WRITE; | ||
137 | |||
138 | spin_unlock_irq(&mostek_lock); | ||
139 | |||
140 | /* Delay to allow the clock oscillator to start. */ | ||
141 | sec = MSTK_REG_SEC(regs); | ||
142 | for (i = 0; i < 3; i++) { | ||
143 | while (sec == MSTK_REG_SEC(regs)) | ||
144 | for (count = 0; count < 100000; count++) | ||
145 | /* nothing */ ; | ||
146 | prom_printf("."); | ||
147 | sec = regs->sec; | ||
148 | } | 131 | } |
149 | prom_printf("\n"); | 132 | return val; |
150 | |||
151 | spin_lock_irq(&mostek_lock); | ||
152 | |||
153 | /* Turn off kick start and set a "valid" time and date. */ | ||
154 | regs->creg |= MSTK_CREG_WRITE; | ||
155 | regs->hour &= ~MSTK_KICK_START; | ||
156 | MSTK_SET_REG_SEC(regs,0); | ||
157 | MSTK_SET_REG_MIN(regs,0); | ||
158 | MSTK_SET_REG_HOUR(regs,0); | ||
159 | MSTK_SET_REG_DOW(regs,5); | ||
160 | MSTK_SET_REG_DOM(regs,1); | ||
161 | MSTK_SET_REG_MONTH(regs,8); | ||
162 | MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); | ||
163 | regs->creg &= ~MSTK_CREG_WRITE; | ||
164 | |||
165 | spin_unlock_irq(&mostek_lock); | ||
166 | |||
167 | /* Ensure the kick start bit is off. If it isn't, turn it off. */ | ||
168 | while (regs->hour & MSTK_KICK_START) { | ||
169 | prom_printf("CLOCK: Kick start still on!\n"); | ||
170 | |||
171 | spin_lock_irq(&mostek_lock); | ||
172 | regs->creg |= MSTK_CREG_WRITE; | ||
173 | regs->hour &= ~MSTK_KICK_START; | ||
174 | regs->creg &= ~MSTK_CREG_WRITE; | ||
175 | spin_unlock_irq(&mostek_lock); | ||
176 | } | ||
177 | |||
178 | prom_printf("CLOCK: Kick start procedure successful.\n"); | ||
179 | } | 133 | } |
180 | 134 | ||
181 | /* Return nonzero if the clock chip battery is low. */ | 135 | static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) |
182 | static inline int has_low_battery(void) | ||
183 | { | 136 | { |
184 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | 137 | struct platform_device *pdev = to_platform_device(dev); |
185 | unsigned char data1, data2; | 138 | struct m48t59_plat_data *pdata = pdev->dev.platform_data; |
186 | 139 | void __iomem *regs = pdata->ioaddr; | |
187 | spin_lock_irq(&mostek_lock); | 140 | |
188 | data1 = regs->eeprom[0]; /* Read some data. */ | 141 | if (ofs == pdata->offset + M48T59_YEAR) { |
189 | regs->eeprom[0] = ~data1; /* Write back the complement. */ | 142 | if (val < 0x68) |
190 | data2 = regs->eeprom[0]; /* Read back the complement. */ | 143 | val += 0x32; |
191 | regs->eeprom[0] = data1; /* Restore the original value. */ | 144 | else |
192 | spin_unlock_irq(&mostek_lock); | 145 | val -= 0x68; |
193 | 146 | if ((val & 0xf) > 9) | |
194 | return (data1 == data2); /* Was the write blocked? */ | 147 | val += 6; |
148 | if ((val & 0xf0) > 0x9A) | ||
149 | val += 0x60; | ||
150 | } | ||
151 | writeb(val, regs + ofs); | ||
195 | } | 152 | } |
196 | 153 | ||
197 | static void __devinit mostek_set_system_time(void) | 154 | static struct m48t59_plat_data m48t59_data = { |
198 | { | 155 | .read_byte = mostek_read_byte, |
199 | unsigned int year, mon, day, hour, min, sec; | 156 | .write_byte = mostek_write_byte, |
200 | struct mostek48t02 *mregs; | 157 | }; |
201 | 158 | ||
202 | mregs = (struct mostek48t02 *)mstk48t02_regs; | 159 | /* resource is set at runtime */ |
203 | if(!mregs) { | 160 | static struct platform_device m48t59_rtc = { |
204 | prom_printf("Something wrong, clock regs not mapped yet.\n"); | 161 | .name = "rtc-m48t59", |
205 | prom_halt(); | 162 | .id = 0, |
206 | } | 163 | .num_resources = 1, |
207 | spin_lock_irq(&mostek_lock); | 164 | .dev = { |
208 | mregs->creg |= MSTK_CREG_READ; | 165 | .platform_data = &m48t59_data, |
209 | sec = MSTK_REG_SEC(mregs); | 166 | }, |
210 | min = MSTK_REG_MIN(mregs); | 167 | }; |
211 | hour = MSTK_REG_HOUR(mregs); | ||
212 | day = MSTK_REG_DOM(mregs); | ||
213 | mon = MSTK_REG_MONTH(mregs); | ||
214 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); | ||
215 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | ||
216 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | ||
217 | set_normalized_timespec(&wall_to_monotonic, | ||
218 | -xtime.tv_sec, -xtime.tv_nsec); | ||
219 | mregs->creg &= ~MSTK_CREG_READ; | ||
220 | spin_unlock_irq(&mostek_lock); | ||
221 | } | ||
222 | 168 | ||
223 | static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) | 169 | static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) |
224 | { | 170 | { |
@@ -228,33 +174,21 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id | |||
228 | if (!model) | 174 | if (!model) |
229 | return -ENODEV; | 175 | return -ENODEV; |
230 | 176 | ||
177 | m48t59_rtc.resource = &op->resource[0]; | ||
231 | if (!strcmp(model, "mk48t02")) { | 178 | if (!strcmp(model, "mk48t02")) { |
232 | sp_clock_typ = MSTK48T02; | ||
233 | |||
234 | /* Map the clock register io area read-only */ | 179 | /* Map the clock register io area read-only */ |
235 | mstk48t02_regs = of_ioremap(&op->resource[0], 0, | 180 | m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, |
236 | sizeof(struct mostek48t02), | 181 | 2048, "rtc-m48t59"); |
237 | "mk48t02"); | 182 | m48t59_data.type = M48T59RTC_TYPE_M48T02; |
238 | mstk48t08_regs = NULL; /* To catch weirdness */ | ||
239 | } else if (!strcmp(model, "mk48t08")) { | 183 | } else if (!strcmp(model, "mk48t08")) { |
240 | sp_clock_typ = MSTK48T08; | 184 | m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, |
241 | mstk48t08_regs = of_ioremap(&op->resource[0], 0, | 185 | 8192, "rtc-m48t59"); |
242 | sizeof(struct mostek48t08), | 186 | m48t59_data.type = M48T59RTC_TYPE_M48T08; |
243 | "mk48t08"); | ||
244 | |||
245 | mstk48t02_regs = &mstk48t08_regs->regs; | ||
246 | } else | 187 | } else |
247 | return -ENODEV; | 188 | return -ENODEV; |
248 | 189 | ||
249 | /* Report a low battery voltage condition. */ | 190 | if (platform_device_register(&m48t59_rtc) < 0) |
250 | if (has_low_battery()) | 191 | printk(KERN_ERR "Registering RTC device failed\n"); |
251 | printk(KERN_CRIT "NVRAM: Low battery voltage!\n"); | ||
252 | |||
253 | /* Kick start the clock if it is completely stopped. */ | ||
254 | if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) | ||
255 | kick_start_clock(); | ||
256 | |||
257 | mostek_set_system_time(); | ||
258 | 192 | ||
259 | return 0; | 193 | return 0; |
260 | } | 194 | } |
@@ -270,7 +204,7 @@ static struct of_platform_driver clock_driver = { | |||
270 | .match_table = clock_match, | 204 | .match_table = clock_match, |
271 | .probe = clock_probe, | 205 | .probe = clock_probe, |
272 | .driver = { | 206 | .driver = { |
273 | .name = "clock", | 207 | .name = "rtc", |
274 | }, | 208 | }, |
275 | }; | 209 | }; |
276 | 210 | ||
@@ -400,43 +334,12 @@ static int sbus_do_settimeofday(struct timespec *tv) | |||
400 | return 0; | 334 | return 0; |
401 | } | 335 | } |
402 | 336 | ||
403 | /* | 337 | static int set_rtc_mmss(unsigned long secs) |
404 | * BUG: This routine does not handle hour overflow properly; it just | ||
405 | * sets the minutes. Usually you won't notice until after reboot! | ||
406 | */ | ||
407 | static int set_rtc_mmss(unsigned long nowtime) | ||
408 | { | 338 | { |
409 | int real_seconds, real_minutes, mostek_minutes; | 339 | struct rtc_device *rtc = rtc_class_open("rtc0"); |
410 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | ||
411 | unsigned long flags; | ||
412 | 340 | ||
413 | spin_lock_irqsave(&mostek_lock, flags); | 341 | if (rtc) |
414 | /* Read the current RTC minutes. */ | 342 | return rtc_set_mmss(rtc, secs); |
415 | regs->creg |= MSTK_CREG_READ; | ||
416 | mostek_minutes = MSTK_REG_MIN(regs); | ||
417 | regs->creg &= ~MSTK_CREG_READ; | ||
418 | 343 | ||
419 | /* | 344 | return -1; |
420 | * since we're only adjusting minutes and seconds, | ||
421 | * don't interfere with hour overflow. This avoids | ||
422 | * messing with unknown time zones but requires your | ||
423 | * RTC not to be off by more than 15 minutes | ||
424 | */ | ||
425 | real_seconds = nowtime % 60; | ||
426 | real_minutes = nowtime / 60; | ||
427 | if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) | ||
428 | real_minutes += 30; /* correct for half hour time zone */ | ||
429 | real_minutes %= 60; | ||
430 | |||
431 | if (abs(real_minutes - mostek_minutes) < 30) { | ||
432 | regs->creg |= MSTK_CREG_WRITE; | ||
433 | MSTK_SET_REG_SEC(regs,real_seconds); | ||
434 | MSTK_SET_REG_MIN(regs,real_minutes); | ||
435 | regs->creg &= ~MSTK_CREG_WRITE; | ||
436 | spin_unlock_irqrestore(&mostek_lock, flags); | ||
437 | return 0; | ||
438 | } else { | ||
439 | spin_unlock_irqrestore(&mostek_lock, flags); | ||
440 | return -1; | ||
441 | } | ||
442 | } | 345 | } |