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