rtc-sun4v.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems.
  2. *
  3. * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
  4. */
  5. #include <linux/kernel.h>
  6. #include <linux/module.h>
  7. #include <linux/delay.h>
  8. #include <linux/init.h>
  9. #include <linux/time.h>
  10. #include <linux/rtc.h>
  11. #include <linux/platform_device.h>
  12. #include <asm/hypervisor.h>
  13. MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
  14. MODULE_DESCRIPTION("SUN4V RTC driver");
  15. MODULE_LICENSE("GPL");
  16. struct sun4v_rtc {
  17. struct rtc_device *rtc;
  18. spinlock_t lock;
  19. };
  20. static unsigned long hypervisor_get_time(void)
  21. {
  22. unsigned long ret, time;
  23. int retries = 10000;
  24. retry:
  25. ret = sun4v_tod_get(&time);
  26. if (ret == HV_EOK)
  27. return time;
  28. if (ret == HV_EWOULDBLOCK) {
  29. if (--retries > 0) {
  30. udelay(100);
  31. goto retry;
  32. }
  33. printk(KERN_WARNING "SUN4V: tod_get() timed out.\n");
  34. return 0;
  35. }
  36. printk(KERN_WARNING "SUN4V: tod_get() not supported.\n");
  37. return 0;
  38. }
  39. static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
  40. {
  41. struct sun4v_rtc *p = dev_get_drvdata(dev);
  42. unsigned long flags, secs;
  43. spin_lock_irqsave(&p->lock, flags);
  44. secs = hypervisor_get_time();
  45. spin_unlock_irqrestore(&p->lock, flags);
  46. rtc_time_to_tm(secs, tm);
  47. return 0;
  48. }
  49. static int hypervisor_set_time(unsigned long secs)
  50. {
  51. unsigned long ret;
  52. int retries = 10000;
  53. retry:
  54. ret = sun4v_tod_set(secs);
  55. if (ret == HV_EOK)
  56. return 0;
  57. if (ret == HV_EWOULDBLOCK) {
  58. if (--retries > 0) {
  59. udelay(100);
  60. goto retry;
  61. }
  62. printk(KERN_WARNING "SUN4V: tod_set() timed out.\n");
  63. return -EAGAIN;
  64. }
  65. printk(KERN_WARNING "SUN4V: tod_set() not supported.\n");
  66. return -EOPNOTSUPP;
  67. }
  68. static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
  69. {
  70. struct sun4v_rtc *p = dev_get_drvdata(dev);
  71. unsigned long flags, secs;
  72. int err;
  73. err = rtc_tm_to_time(tm, &secs);
  74. if (err)
  75. return err;
  76. spin_lock_irqsave(&p->lock, flags);
  77. err = hypervisor_set_time(secs);
  78. spin_unlock_irqrestore(&p->lock, flags);
  79. return err;
  80. }
  81. static const struct rtc_class_ops sun4v_rtc_ops = {
  82. .read_time = sun4v_read_time,
  83. .set_time = sun4v_set_time,
  84. };
  85. static int __devinit sun4v_rtc_probe(struct platform_device *pdev)
  86. {
  87. struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL);
  88. if (!p)
  89. return -ENOMEM;
  90. spin_lock_init(&p->lock);
  91. p->rtc = rtc_device_register("sun4v", &pdev->dev,
  92. &sun4v_rtc_ops, THIS_MODULE);
  93. if (IS_ERR(p->rtc)) {
  94. int err = PTR_ERR(p->rtc);
  95. kfree(p);
  96. return err;
  97. }
  98. platform_set_drvdata(pdev, p);
  99. return 0;
  100. }
  101. static int __devexit sun4v_rtc_remove(struct platform_device *pdev)
  102. {
  103. struct sun4v_rtc *p = platform_get_drvdata(pdev);
  104. rtc_device_unregister(p->rtc);
  105. kfree(p);
  106. return 0;
  107. }
  108. static struct platform_driver sun4v_rtc_driver = {
  109. .driver = {
  110. .name = "rtc-sun4v",
  111. .owner = THIS_MODULE,
  112. },
  113. .probe = sun4v_rtc_probe,
  114. .remove = __devexit_p(sun4v_rtc_remove),
  115. };
  116. static int __init sun4v_rtc_init(void)
  117. {
  118. return platform_driver_register(&sun4v_rtc_driver);
  119. }
  120. static void __exit sun4v_rtc_exit(void)
  121. {
  122. platform_driver_unregister(&sun4v_rtc_driver);
  123. }
  124. module_init(sun4v_rtc_init);
  125. module_exit(sun4v_rtc_exit);