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