diff options
Diffstat (limited to 'drivers/rtc/rtc-bfin.c')
-rw-r--r-- | drivers/rtc/rtc-bfin.c | 153 |
1 files changed, 77 insertions, 76 deletions
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 8624f55d0560..34439ce3967e 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Blackfin On-Chip Real Time Clock Driver | 2 | * Blackfin On-Chip Real Time Clock Driver |
3 | * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789] | 3 | * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789] |
4 | * | 4 | * |
5 | * Copyright 2004-2007 Analog Devices Inc. | 5 | * Copyright 2004-2008 Analog Devices Inc. |
6 | * | 6 | * |
7 | * Enter bugs at http://blackfin.uclinux.org/ | 7 | * Enter bugs at http://blackfin.uclinux.org/ |
8 | * | 8 | * |
@@ -32,6 +32,15 @@ | |||
32 | * writes to clear status registers complete immediately. | 32 | * writes to clear status registers complete immediately. |
33 | */ | 33 | */ |
34 | 34 | ||
35 | /* It may seem odd that there is no SWCNT code in here (which would be exposed | ||
36 | * via the periodic interrupt event, or PIE). Since the Blackfin RTC peripheral | ||
37 | * runs in units of seconds (N/HZ) but the Linux framework runs in units of HZ | ||
38 | * (2^N HZ), there is no point in keeping code that only provides 1 HZ PIEs. | ||
39 | * The same exact behavior can be accomplished by using the update interrupt | ||
40 | * event (UIE). Maybe down the line the RTC peripheral will suck less in which | ||
41 | * case we can re-introduce PIE support. | ||
42 | */ | ||
43 | |||
35 | #include <linux/bcd.h> | 44 | #include <linux/bcd.h> |
36 | #include <linux/completion.h> | 45 | #include <linux/completion.h> |
37 | #include <linux/delay.h> | 46 | #include <linux/delay.h> |
@@ -144,14 +153,13 @@ static void bfin_rtc_sync_pending(struct device *dev) | |||
144 | * Initialize the RTC. Enable pre-scaler to scale RTC clock | 153 | * Initialize the RTC. Enable pre-scaler to scale RTC clock |
145 | * to 1Hz and clear interrupt/status registers. | 154 | * to 1Hz and clear interrupt/status registers. |
146 | */ | 155 | */ |
147 | static void bfin_rtc_reset(struct device *dev) | 156 | static void bfin_rtc_reset(struct device *dev, u16 rtc_ictl) |
148 | { | 157 | { |
149 | struct bfin_rtc *rtc = dev_get_drvdata(dev); | 158 | struct bfin_rtc *rtc = dev_get_drvdata(dev); |
150 | dev_dbg_stamp(dev); | 159 | dev_dbg_stamp(dev); |
151 | bfin_rtc_sync_pending(dev); | 160 | bfin_rtc_sync_pending(dev); |
152 | bfin_write_RTC_PREN(0x1); | 161 | bfin_write_RTC_PREN(0x1); |
153 | bfin_write_RTC_ICTL(RTC_ISTAT_WRITE_COMPLETE); | 162 | bfin_write_RTC_ICTL(rtc_ictl); |
154 | bfin_write_RTC_SWCNT(0); | ||
155 | bfin_write_RTC_ALARM(0); | 163 | bfin_write_RTC_ALARM(0); |
156 | bfin_write_RTC_ISTAT(0xFFFF); | 164 | bfin_write_RTC_ISTAT(0xFFFF); |
157 | rtc->rtc_wrote_regs = 0; | 165 | rtc->rtc_wrote_regs = 0; |
@@ -194,14 +202,6 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) | |||
194 | } | 202 | } |
195 | } | 203 | } |
196 | 204 | ||
197 | if (rtc_ictl & RTC_ISTAT_STOPWATCH) { | ||
198 | if (rtc_istat & RTC_ISTAT_STOPWATCH) { | ||
199 | bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); | ||
200 | events |= RTC_PF | RTC_IRQF; | ||
201 | bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | if (rtc_ictl & RTC_ISTAT_SEC) { | 205 | if (rtc_ictl & RTC_ISTAT_SEC) { |
206 | if (rtc_istat & RTC_ISTAT_SEC) { | 206 | if (rtc_istat & RTC_ISTAT_SEC) { |
207 | bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); | 207 | bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); |
@@ -218,32 +218,12 @@ static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) | |||
218 | return IRQ_NONE; | 218 | return IRQ_NONE; |
219 | } | 219 | } |
220 | 220 | ||
221 | static int bfin_rtc_open(struct device *dev) | 221 | static void bfin_rtc_int_set(u16 rtc_int) |
222 | { | ||
223 | int ret; | ||
224 | |||
225 | dev_dbg_stamp(dev); | ||
226 | |||
227 | ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, to_platform_device(dev)->name, dev); | ||
228 | if (!ret) | ||
229 | bfin_rtc_reset(dev); | ||
230 | |||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static void bfin_rtc_release(struct device *dev) | ||
235 | { | ||
236 | dev_dbg_stamp(dev); | ||
237 | bfin_rtc_reset(dev); | ||
238 | free_irq(IRQ_RTC, dev); | ||
239 | } | ||
240 | |||
241 | static void bfin_rtc_int_set(struct bfin_rtc *rtc, u16 rtc_int) | ||
242 | { | 222 | { |
243 | bfin_write_RTC_ISTAT(rtc_int); | 223 | bfin_write_RTC_ISTAT(rtc_int); |
244 | bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int); | 224 | bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int); |
245 | } | 225 | } |
246 | static void bfin_rtc_int_clear(struct bfin_rtc *rtc, u16 rtc_int) | 226 | static void bfin_rtc_int_clear(u16 rtc_int) |
247 | { | 227 | { |
248 | bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int); | 228 | bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int); |
249 | } | 229 | } |
@@ -252,7 +232,7 @@ static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc) | |||
252 | /* Blackfin has different bits for whether the alarm is | 232 | /* Blackfin has different bits for whether the alarm is |
253 | * more than 24 hours away. | 233 | * more than 24 hours away. |
254 | */ | 234 | */ |
255 | bfin_rtc_int_set(rtc, (rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY)); | 235 | bfin_rtc_int_set(rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY); |
256 | } | 236 | } |
257 | static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | 237 | static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) |
258 | { | 238 | { |
@@ -264,23 +244,13 @@ static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long ar | |||
264 | bfin_rtc_sync_pending(dev); | 244 | bfin_rtc_sync_pending(dev); |
265 | 245 | ||
266 | switch (cmd) { | 246 | switch (cmd) { |
267 | case RTC_PIE_ON: | ||
268 | dev_dbg_stamp(dev); | ||
269 | bfin_rtc_int_set(rtc, RTC_ISTAT_STOPWATCH); | ||
270 | bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); | ||
271 | break; | ||
272 | case RTC_PIE_OFF: | ||
273 | dev_dbg_stamp(dev); | ||
274 | bfin_rtc_int_clear(rtc, ~RTC_ISTAT_STOPWATCH); | ||
275 | break; | ||
276 | |||
277 | case RTC_UIE_ON: | 247 | case RTC_UIE_ON: |
278 | dev_dbg_stamp(dev); | 248 | dev_dbg_stamp(dev); |
279 | bfin_rtc_int_set(rtc, RTC_ISTAT_SEC); | 249 | bfin_rtc_int_set(RTC_ISTAT_SEC); |
280 | break; | 250 | break; |
281 | case RTC_UIE_OFF: | 251 | case RTC_UIE_OFF: |
282 | dev_dbg_stamp(dev); | 252 | dev_dbg_stamp(dev); |
283 | bfin_rtc_int_clear(rtc, ~RTC_ISTAT_SEC); | 253 | bfin_rtc_int_clear(~RTC_ISTAT_SEC); |
284 | break; | 254 | break; |
285 | 255 | ||
286 | case RTC_AIE_ON: | 256 | case RTC_AIE_ON: |
@@ -289,7 +259,7 @@ static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long ar | |||
289 | break; | 259 | break; |
290 | case RTC_AIE_OFF: | 260 | case RTC_AIE_OFF: |
291 | dev_dbg_stamp(dev); | 261 | dev_dbg_stamp(dev); |
292 | bfin_rtc_int_clear(rtc, ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); | 262 | bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); |
293 | break; | 263 | break; |
294 | 264 | ||
295 | default: | 265 | default: |
@@ -371,64 +341,64 @@ static int bfin_rtc_proc(struct device *dev, struct seq_file *seq) | |||
371 | seq_printf(seq, | 341 | seq_printf(seq, |
372 | "alarm_IRQ\t: %s\n" | 342 | "alarm_IRQ\t: %s\n" |
373 | "wkalarm_IRQ\t: %s\n" | 343 | "wkalarm_IRQ\t: %s\n" |
374 | "seconds_IRQ\t: %s\n" | 344 | "seconds_IRQ\t: %s\n", |
375 | "periodic_IRQ\t: %s\n", | ||
376 | yesno(ictl & RTC_ISTAT_ALARM), | 345 | yesno(ictl & RTC_ISTAT_ALARM), |
377 | yesno(ictl & RTC_ISTAT_ALARM_DAY), | 346 | yesno(ictl & RTC_ISTAT_ALARM_DAY), |
378 | yesno(ictl & RTC_ISTAT_SEC), | 347 | yesno(ictl & RTC_ISTAT_SEC)); |
379 | yesno(ictl & RTC_ISTAT_STOPWATCH)); | ||
380 | return 0; | 348 | return 0; |
381 | #undef yesno | 349 | #undef yesno |
382 | } | 350 | } |
383 | 351 | ||
384 | /** | ||
385 | * bfin_irq_set_freq - make sure hardware supports requested freq | ||
386 | * @dev: pointer to RTC device structure | ||
387 | * @freq: requested frequency rate | ||
388 | * | ||
389 | * The Blackfin RTC can only generate periodic events at 1 per | ||
390 | * second (1 Hz), so reject any attempt at changing it. | ||
391 | */ | ||
392 | static int bfin_irq_set_freq(struct device *dev, int freq) | ||
393 | { | ||
394 | dev_dbg_stamp(dev); | ||
395 | return -ENOTTY; | ||
396 | } | ||
397 | |||
398 | static struct rtc_class_ops bfin_rtc_ops = { | 352 | static struct rtc_class_ops bfin_rtc_ops = { |
399 | .open = bfin_rtc_open, | ||
400 | .release = bfin_rtc_release, | ||
401 | .ioctl = bfin_rtc_ioctl, | 353 | .ioctl = bfin_rtc_ioctl, |
402 | .read_time = bfin_rtc_read_time, | 354 | .read_time = bfin_rtc_read_time, |
403 | .set_time = bfin_rtc_set_time, | 355 | .set_time = bfin_rtc_set_time, |
404 | .read_alarm = bfin_rtc_read_alarm, | 356 | .read_alarm = bfin_rtc_read_alarm, |
405 | .set_alarm = bfin_rtc_set_alarm, | 357 | .set_alarm = bfin_rtc_set_alarm, |
406 | .proc = bfin_rtc_proc, | 358 | .proc = bfin_rtc_proc, |
407 | .irq_set_freq = bfin_irq_set_freq, | ||
408 | }; | 359 | }; |
409 | 360 | ||
410 | static int __devinit bfin_rtc_probe(struct platform_device *pdev) | 361 | static int __devinit bfin_rtc_probe(struct platform_device *pdev) |
411 | { | 362 | { |
412 | struct bfin_rtc *rtc; | 363 | struct bfin_rtc *rtc; |
364 | struct device *dev = &pdev->dev; | ||
413 | int ret = 0; | 365 | int ret = 0; |
366 | unsigned long timeout; | ||
414 | 367 | ||
415 | dev_dbg_stamp(&pdev->dev); | 368 | dev_dbg_stamp(dev); |
416 | 369 | ||
370 | /* Allocate memory for our RTC struct */ | ||
417 | rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); | 371 | rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); |
418 | if (unlikely(!rtc)) | 372 | if (unlikely(!rtc)) |
419 | return -ENOMEM; | 373 | return -ENOMEM; |
374 | platform_set_drvdata(pdev, rtc); | ||
375 | device_init_wakeup(dev, 1); | ||
420 | 376 | ||
421 | rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE); | 377 | /* Grab the IRQ and init the hardware */ |
422 | if (IS_ERR(rtc)) { | 378 | ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, pdev->name, dev); |
423 | ret = PTR_ERR(rtc->rtc_dev); | 379 | if (unlikely(ret)) |
424 | goto err; | 380 | goto err; |
425 | } | 381 | /* sometimes the bootloader touched things, but the write complete was not |
426 | rtc->rtc_dev->irq_freq = 1; | 382 | * enabled, so let's just do a quick timeout here since the IRQ will not fire ... |
383 | */ | ||
384 | timeout = jiffies + HZ; | ||
385 | while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING) | ||
386 | if (time_after(jiffies, timeout)) | ||
387 | break; | ||
388 | bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE); | ||
389 | bfin_write_RTC_SWCNT(0); | ||
427 | 390 | ||
428 | platform_set_drvdata(pdev, rtc); | 391 | /* Register our RTC with the RTC framework */ |
392 | rtc->rtc_dev = rtc_device_register(pdev->name, dev, &bfin_rtc_ops, THIS_MODULE); | ||
393 | if (unlikely(IS_ERR(rtc))) { | ||
394 | ret = PTR_ERR(rtc->rtc_dev); | ||
395 | goto err_irq; | ||
396 | } | ||
429 | 397 | ||
430 | return 0; | 398 | return 0; |
431 | 399 | ||
400 | err_irq: | ||
401 | free_irq(IRQ_RTC, dev); | ||
432 | err: | 402 | err: |
433 | kfree(rtc); | 403 | kfree(rtc); |
434 | return ret; | 404 | return ret; |
@@ -437,7 +407,10 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) | |||
437 | static int __devexit bfin_rtc_remove(struct platform_device *pdev) | 407 | static int __devexit bfin_rtc_remove(struct platform_device *pdev) |
438 | { | 408 | { |
439 | struct bfin_rtc *rtc = platform_get_drvdata(pdev); | 409 | struct bfin_rtc *rtc = platform_get_drvdata(pdev); |
410 | struct device *dev = &pdev->dev; | ||
440 | 411 | ||
412 | bfin_rtc_reset(dev, 0); | ||
413 | free_irq(IRQ_RTC, dev); | ||
441 | rtc_device_unregister(rtc->rtc_dev); | 414 | rtc_device_unregister(rtc->rtc_dev); |
442 | platform_set_drvdata(pdev, NULL); | 415 | platform_set_drvdata(pdev, NULL); |
443 | kfree(rtc); | 416 | kfree(rtc); |
@@ -445,6 +418,32 @@ static int __devexit bfin_rtc_remove(struct platform_device *pdev) | |||
445 | return 0; | 418 | return 0; |
446 | } | 419 | } |
447 | 420 | ||
421 | #ifdef CONFIG_PM | ||
422 | static int bfin_rtc_suspend(struct platform_device *pdev, pm_message_t state) | ||
423 | { | ||
424 | if (device_may_wakeup(&pdev->dev)) { | ||
425 | enable_irq_wake(IRQ_RTC); | ||
426 | bfin_rtc_sync_pending(&pdev->dev); | ||
427 | } else | ||
428 | bfin_rtc_int_clear(-1); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int bfin_rtc_resume(struct platform_device *pdev) | ||
434 | { | ||
435 | if (device_may_wakeup(&pdev->dev)) | ||
436 | disable_irq_wake(IRQ_RTC); | ||
437 | else | ||
438 | bfin_write_RTC_ISTAT(-1); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | #else | ||
443 | # define bfin_rtc_suspend NULL | ||
444 | # define bfin_rtc_resume NULL | ||
445 | #endif | ||
446 | |||
448 | static struct platform_driver bfin_rtc_driver = { | 447 | static struct platform_driver bfin_rtc_driver = { |
449 | .driver = { | 448 | .driver = { |
450 | .name = "rtc-bfin", | 449 | .name = "rtc-bfin", |
@@ -452,6 +451,8 @@ static struct platform_driver bfin_rtc_driver = { | |||
452 | }, | 451 | }, |
453 | .probe = bfin_rtc_probe, | 452 | .probe = bfin_rtc_probe, |
454 | .remove = __devexit_p(bfin_rtc_remove), | 453 | .remove = __devexit_p(bfin_rtc_remove), |
454 | .suspend = bfin_rtc_suspend, | ||
455 | .resume = bfin_rtc_resume, | ||
455 | }; | 456 | }; |
456 | 457 | ||
457 | static int __init bfin_rtc_init(void) | 458 | static int __init bfin_rtc_init(void) |