diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/rtc/rtc-spear.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'drivers/rtc/rtc-spear.c')
-rw-r--r-- | drivers/rtc/rtc-spear.c | 225 |
1 files changed, 121 insertions, 104 deletions
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index c2121b5a01f..893bac2bb21 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/irq.h> | 17 | #include <linux/irq.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
21 | #include <linux/rtc.h> | 20 | #include <linux/rtc.h> |
22 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
@@ -78,11 +77,9 @@ | |||
78 | #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) | 77 | #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) |
79 | 78 | ||
80 | struct spear_rtc_config { | 79 | struct spear_rtc_config { |
81 | struct rtc_device *rtc; | ||
82 | struct clk *clk; | 80 | struct clk *clk; |
83 | spinlock_t lock; | 81 | spinlock_t lock; |
84 | void __iomem *ioaddr; | 82 | void __iomem *ioaddr; |
85 | unsigned int irq_wake; | ||
86 | }; | 83 | }; |
87 | 84 | ||
88 | static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) | 85 | static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) |
@@ -152,7 +149,8 @@ static void rtc_wait_not_busy(struct spear_rtc_config *config) | |||
152 | 149 | ||
153 | static irqreturn_t spear_rtc_irq(int irq, void *dev_id) | 150 | static irqreturn_t spear_rtc_irq(int irq, void *dev_id) |
154 | { | 151 | { |
155 | struct spear_rtc_config *config = dev_id; | 152 | struct rtc_device *rtc = (struct rtc_device *)dev_id; |
153 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
156 | unsigned long flags, events = 0; | 154 | unsigned long flags, events = 0; |
157 | unsigned int irq_data; | 155 | unsigned int irq_data; |
158 | 156 | ||
@@ -163,7 +161,7 @@ static irqreturn_t spear_rtc_irq(int irq, void *dev_id) | |||
163 | if ((irq_data & RTC_INT_MASK)) { | 161 | if ((irq_data & RTC_INT_MASK)) { |
164 | spear_rtc_clear_interrupt(config); | 162 | spear_rtc_clear_interrupt(config); |
165 | events = RTC_IRQF | RTC_AF; | 163 | events = RTC_IRQF | RTC_AF; |
166 | rtc_update_irq(config->rtc, 1, events); | 164 | rtc_update_irq(rtc, 1, events); |
167 | return IRQ_HANDLED; | 165 | return IRQ_HANDLED; |
168 | } else | 166 | } else |
169 | return IRQ_NONE; | 167 | return IRQ_NONE; |
@@ -205,7 +203,9 @@ static void bcd2tm(struct rtc_time *tm) | |||
205 | */ | 203 | */ |
206 | static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) | 204 | static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) |
207 | { | 205 | { |
208 | struct spear_rtc_config *config = dev_get_drvdata(dev); | 206 | struct platform_device *pdev = to_platform_device(dev); |
207 | struct rtc_device *rtc = platform_get_drvdata(pdev); | ||
208 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
209 | unsigned int time, date; | 209 | unsigned int time, date; |
210 | 210 | ||
211 | /* we don't report wday/yday/isdst ... */ | 211 | /* we don't report wday/yday/isdst ... */ |
@@ -234,8 +234,10 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
234 | */ | 234 | */ |
235 | static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) | 235 | static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) |
236 | { | 236 | { |
237 | struct spear_rtc_config *config = dev_get_drvdata(dev); | 237 | struct platform_device *pdev = to_platform_device(dev); |
238 | unsigned int time, date; | 238 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
239 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
240 | unsigned int time, date, err = 0; | ||
239 | 241 | ||
240 | if (tm2bcd(tm) < 0) | 242 | if (tm2bcd(tm) < 0) |
241 | return -EINVAL; | 243 | return -EINVAL; |
@@ -247,8 +249,11 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
247 | (tm->tm_year << YEAR_SHIFT); | 249 | (tm->tm_year << YEAR_SHIFT); |
248 | writel(time, config->ioaddr + TIME_REG); | 250 | writel(time, config->ioaddr + TIME_REG); |
249 | writel(date, config->ioaddr + DATE_REG); | 251 | writel(date, config->ioaddr + DATE_REG); |
252 | err = is_write_complete(config); | ||
253 | if (err < 0) | ||
254 | return err; | ||
250 | 255 | ||
251 | return is_write_complete(config); | 256 | return 0; |
252 | } | 257 | } |
253 | 258 | ||
254 | /* | 259 | /* |
@@ -261,7 +266,9 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) | |||
261 | */ | 266 | */ |
262 | static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | 267 | static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
263 | { | 268 | { |
264 | struct spear_rtc_config *config = dev_get_drvdata(dev); | 269 | struct platform_device *pdev = to_platform_device(dev); |
270 | struct rtc_device *rtc = platform_get_drvdata(pdev); | ||
271 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
265 | unsigned int time, date; | 272 | unsigned int time, date; |
266 | 273 | ||
267 | rtc_wait_not_busy(config); | 274 | rtc_wait_not_busy(config); |
@@ -291,9 +298,10 @@ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | |||
291 | */ | 298 | */ |
292 | static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | 299 | static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
293 | { | 300 | { |
294 | struct spear_rtc_config *config = dev_get_drvdata(dev); | 301 | struct platform_device *pdev = to_platform_device(dev); |
295 | unsigned int time, date; | 302 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
296 | int err; | 303 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); |
304 | unsigned int time, date, err = 0; | ||
297 | 305 | ||
298 | if (tm2bcd(&alm->time) < 0) | 306 | if (tm2bcd(&alm->time) < 0) |
299 | return -EINVAL; | 307 | return -EINVAL; |
@@ -318,44 +326,19 @@ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | |||
318 | 326 | ||
319 | return 0; | 327 | return 0; |
320 | } | 328 | } |
321 | |||
322 | static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
323 | { | ||
324 | struct spear_rtc_config *config = dev_get_drvdata(dev); | ||
325 | int ret = 0; | ||
326 | |||
327 | spear_rtc_clear_interrupt(config); | ||
328 | |||
329 | switch (enabled) { | ||
330 | case 0: | ||
331 | /* alarm off */ | ||
332 | spear_rtc_disable_interrupt(config); | ||
333 | break; | ||
334 | case 1: | ||
335 | /* alarm on */ | ||
336 | spear_rtc_enable_interrupt(config); | ||
337 | break; | ||
338 | default: | ||
339 | ret = -EINVAL; | ||
340 | break; | ||
341 | } | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | static struct rtc_class_ops spear_rtc_ops = { | 329 | static struct rtc_class_ops spear_rtc_ops = { |
347 | .read_time = spear_rtc_read_time, | 330 | .read_time = spear_rtc_read_time, |
348 | .set_time = spear_rtc_set_time, | 331 | .set_time = spear_rtc_set_time, |
349 | .read_alarm = spear_rtc_read_alarm, | 332 | .read_alarm = spear_rtc_read_alarm, |
350 | .set_alarm = spear_rtc_set_alarm, | 333 | .set_alarm = spear_rtc_set_alarm, |
351 | .alarm_irq_enable = spear_alarm_irq_enable, | ||
352 | }; | 334 | }; |
353 | 335 | ||
354 | static int spear_rtc_probe(struct platform_device *pdev) | 336 | static int __devinit spear_rtc_probe(struct platform_device *pdev) |
355 | { | 337 | { |
356 | struct resource *res; | 338 | struct resource *res; |
339 | struct rtc_device *rtc; | ||
357 | struct spear_rtc_config *config; | 340 | struct spear_rtc_config *config; |
358 | int status = 0; | 341 | unsigned int status = 0; |
359 | int irq; | 342 | int irq; |
360 | 343 | ||
361 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 344 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
@@ -363,76 +346,110 @@ static int spear_rtc_probe(struct platform_device *pdev) | |||
363 | dev_err(&pdev->dev, "no resource defined\n"); | 346 | dev_err(&pdev->dev, "no resource defined\n"); |
364 | return -EBUSY; | 347 | return -EBUSY; |
365 | } | 348 | } |
349 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
350 | dev_err(&pdev->dev, "rtc region already claimed\n"); | ||
351 | return -EBUSY; | ||
352 | } | ||
366 | 353 | ||
367 | config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); | 354 | config = kzalloc(sizeof(*config), GFP_KERNEL); |
368 | if (!config) { | 355 | if (!config) { |
369 | dev_err(&pdev->dev, "out of memory\n"); | 356 | dev_err(&pdev->dev, "out of memory\n"); |
370 | return -ENOMEM; | 357 | status = -ENOMEM; |
358 | goto err_release_region; | ||
371 | } | 359 | } |
372 | 360 | ||
373 | /* alarm irqs */ | 361 | config->clk = clk_get(&pdev->dev, NULL); |
374 | irq = platform_get_irq(pdev, 0); | 362 | if (IS_ERR(config->clk)) { |
375 | if (irq < 0) { | 363 | status = PTR_ERR(config->clk); |
376 | dev_err(&pdev->dev, "no update irq?\n"); | 364 | goto err_kfree; |
377 | return irq; | ||
378 | } | 365 | } |
379 | 366 | ||
380 | status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, | 367 | status = clk_enable(config->clk); |
381 | config); | 368 | if (status < 0) |
382 | if (status) { | 369 | goto err_clk_put; |
383 | dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n", | ||
384 | irq); | ||
385 | return status; | ||
386 | } | ||
387 | 370 | ||
388 | config->ioaddr = devm_request_and_ioremap(&pdev->dev, res); | 371 | config->ioaddr = ioremap(res->start, resource_size(res)); |
389 | if (!config->ioaddr) { | 372 | if (!config->ioaddr) { |
390 | dev_err(&pdev->dev, "request-ioremap fail\n"); | 373 | dev_err(&pdev->dev, "ioremap fail\n"); |
391 | return -ENOMEM; | 374 | status = -ENOMEM; |
375 | goto err_disable_clock; | ||
392 | } | 376 | } |
393 | 377 | ||
394 | config->clk = devm_clk_get(&pdev->dev, NULL); | ||
395 | if (IS_ERR(config->clk)) | ||
396 | return PTR_ERR(config->clk); | ||
397 | |||
398 | status = clk_prepare_enable(config->clk); | ||
399 | if (status < 0) | ||
400 | return status; | ||
401 | |||
402 | spin_lock_init(&config->lock); | 378 | spin_lock_init(&config->lock); |
403 | platform_set_drvdata(pdev, config); | ||
404 | 379 | ||
405 | config->rtc = rtc_device_register(pdev->name, &pdev->dev, | 380 | rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops, |
406 | &spear_rtc_ops, THIS_MODULE); | 381 | THIS_MODULE); |
407 | if (IS_ERR(config->rtc)) { | 382 | if (IS_ERR(rtc)) { |
408 | dev_err(&pdev->dev, "can't register RTC device, err %ld\n", | 383 | dev_err(&pdev->dev, "can't register RTC device, err %ld\n", |
409 | PTR_ERR(config->rtc)); | 384 | PTR_ERR(rtc)); |
410 | status = PTR_ERR(config->rtc); | 385 | status = PTR_ERR(rtc); |
411 | goto err_disable_clock; | 386 | goto err_iounmap; |
412 | } | 387 | } |
413 | 388 | ||
414 | config->rtc->uie_unsupported = 1; | 389 | platform_set_drvdata(pdev, rtc); |
390 | dev_set_drvdata(&rtc->dev, config); | ||
391 | |||
392 | /* alarm irqs */ | ||
393 | irq = platform_get_irq(pdev, 0); | ||
394 | if (irq < 0) { | ||
395 | dev_err(&pdev->dev, "no update irq?\n"); | ||
396 | status = irq; | ||
397 | goto err_clear_platdata; | ||
398 | } | ||
399 | |||
400 | status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc); | ||
401 | if (status) { | ||
402 | dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ | ||
403 | claimed\n", irq); | ||
404 | goto err_clear_platdata; | ||
405 | } | ||
415 | 406 | ||
416 | if (!device_can_wakeup(&pdev->dev)) | 407 | if (!device_can_wakeup(&pdev->dev)) |
417 | device_init_wakeup(&pdev->dev, 1); | 408 | device_init_wakeup(&pdev->dev, 1); |
418 | 409 | ||
419 | return 0; | 410 | return 0; |
420 | 411 | ||
421 | err_disable_clock: | 412 | err_clear_platdata: |
422 | platform_set_drvdata(pdev, NULL); | 413 | platform_set_drvdata(pdev, NULL); |
423 | clk_disable_unprepare(config->clk); | 414 | dev_set_drvdata(&rtc->dev, NULL); |
415 | rtc_device_unregister(rtc); | ||
416 | err_iounmap: | ||
417 | iounmap(config->ioaddr); | ||
418 | err_disable_clock: | ||
419 | clk_disable(config->clk); | ||
420 | err_clk_put: | ||
421 | clk_put(config->clk); | ||
422 | err_kfree: | ||
423 | kfree(config); | ||
424 | err_release_region: | ||
425 | release_mem_region(res->start, resource_size(res)); | ||
424 | 426 | ||
425 | return status; | 427 | return status; |
426 | } | 428 | } |
427 | 429 | ||
428 | static int spear_rtc_remove(struct platform_device *pdev) | 430 | static int __devexit spear_rtc_remove(struct platform_device *pdev) |
429 | { | 431 | { |
430 | struct spear_rtc_config *config = platform_get_drvdata(pdev); | 432 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
433 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
434 | int irq; | ||
435 | struct resource *res; | ||
431 | 436 | ||
432 | rtc_device_unregister(config->rtc); | 437 | /* leave rtc running, but disable irqs */ |
433 | spear_rtc_disable_interrupt(config); | 438 | spear_rtc_disable_interrupt(config); |
434 | clk_disable_unprepare(config->clk); | ||
435 | device_init_wakeup(&pdev->dev, 0); | 439 | device_init_wakeup(&pdev->dev, 0); |
440 | irq = platform_get_irq(pdev, 0); | ||
441 | if (irq) | ||
442 | free_irq(irq, pdev); | ||
443 | clk_disable(config->clk); | ||
444 | clk_put(config->clk); | ||
445 | iounmap(config->ioaddr); | ||
446 | kfree(config); | ||
447 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
448 | if (res) | ||
449 | release_mem_region(res->start, resource_size(res)); | ||
450 | platform_set_drvdata(pdev, NULL); | ||
451 | dev_set_drvdata(&rtc->dev, NULL); | ||
452 | rtc_device_unregister(rtc); | ||
436 | 453 | ||
437 | return 0; | 454 | return 0; |
438 | } | 455 | } |
@@ -441,14 +458,14 @@ static int spear_rtc_remove(struct platform_device *pdev) | |||
441 | 458 | ||
442 | static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 459 | static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) |
443 | { | 460 | { |
444 | struct spear_rtc_config *config = platform_get_drvdata(pdev); | 461 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
462 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
445 | int irq; | 463 | int irq; |
446 | 464 | ||
447 | irq = platform_get_irq(pdev, 0); | 465 | irq = platform_get_irq(pdev, 0); |
448 | if (device_may_wakeup(&pdev->dev)) { | 466 | if (device_may_wakeup(&pdev->dev)) |
449 | if (!enable_irq_wake(irq)) | 467 | enable_irq_wake(irq); |
450 | config->irq_wake = 1; | 468 | else { |
451 | } else { | ||
452 | spear_rtc_disable_interrupt(config); | 469 | spear_rtc_disable_interrupt(config); |
453 | clk_disable(config->clk); | 470 | clk_disable(config->clk); |
454 | } | 471 | } |
@@ -458,17 +475,15 @@ static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) | |||
458 | 475 | ||
459 | static int spear_rtc_resume(struct platform_device *pdev) | 476 | static int spear_rtc_resume(struct platform_device *pdev) |
460 | { | 477 | { |
461 | struct spear_rtc_config *config = platform_get_drvdata(pdev); | 478 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
479 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
462 | int irq; | 480 | int irq; |
463 | 481 | ||
464 | irq = platform_get_irq(pdev, 0); | 482 | irq = platform_get_irq(pdev, 0); |
465 | 483 | ||
466 | if (device_may_wakeup(&pdev->dev)) { | 484 | if (device_may_wakeup(&pdev->dev)) |
467 | if (config->irq_wake) { | 485 | disable_irq_wake(irq); |
468 | disable_irq_wake(irq); | 486 | else { |
469 | config->irq_wake = 0; | ||
470 | } | ||
471 | } else { | ||
472 | clk_enable(config->clk); | 487 | clk_enable(config->clk); |
473 | spear_rtc_enable_interrupt(config); | 488 | spear_rtc_enable_interrupt(config); |
474 | } | 489 | } |
@@ -483,33 +498,35 @@ static int spear_rtc_resume(struct platform_device *pdev) | |||
483 | 498 | ||
484 | static void spear_rtc_shutdown(struct platform_device *pdev) | 499 | static void spear_rtc_shutdown(struct platform_device *pdev) |
485 | { | 500 | { |
486 | struct spear_rtc_config *config = platform_get_drvdata(pdev); | 501 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
502 | struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); | ||
487 | 503 | ||
488 | spear_rtc_disable_interrupt(config); | 504 | spear_rtc_disable_interrupt(config); |
489 | clk_disable(config->clk); | 505 | clk_disable(config->clk); |
490 | } | 506 | } |
491 | 507 | ||
492 | #ifdef CONFIG_OF | ||
493 | static const struct of_device_id spear_rtc_id_table[] = { | ||
494 | { .compatible = "st,spear600-rtc" }, | ||
495 | {} | ||
496 | }; | ||
497 | MODULE_DEVICE_TABLE(of, spear_rtc_id_table); | ||
498 | #endif | ||
499 | |||
500 | static struct platform_driver spear_rtc_driver = { | 508 | static struct platform_driver spear_rtc_driver = { |
501 | .probe = spear_rtc_probe, | 509 | .probe = spear_rtc_probe, |
502 | .remove = spear_rtc_remove, | 510 | .remove = __devexit_p(spear_rtc_remove), |
503 | .suspend = spear_rtc_suspend, | 511 | .suspend = spear_rtc_suspend, |
504 | .resume = spear_rtc_resume, | 512 | .resume = spear_rtc_resume, |
505 | .shutdown = spear_rtc_shutdown, | 513 | .shutdown = spear_rtc_shutdown, |
506 | .driver = { | 514 | .driver = { |
507 | .name = "rtc-spear", | 515 | .name = "rtc-spear", |
508 | .of_match_table = of_match_ptr(spear_rtc_id_table), | ||
509 | }, | 516 | }, |
510 | }; | 517 | }; |
511 | 518 | ||
512 | module_platform_driver(spear_rtc_driver); | 519 | static int __init rtc_init(void) |
520 | { | ||
521 | return platform_driver_register(&spear_rtc_driver); | ||
522 | } | ||
523 | module_init(rtc_init); | ||
524 | |||
525 | static void __exit rtc_exit(void) | ||
526 | { | ||
527 | platform_driver_unregister(&spear_rtc_driver); | ||
528 | } | ||
529 | module_exit(rtc_exit); | ||
513 | 530 | ||
514 | MODULE_ALIAS("platform:rtc-spear"); | 531 | MODULE_ALIAS("platform:rtc-spear"); |
515 | MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); | 532 | MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); |