1/* $Id: rtc.c,v 1.1.1.1 2007/08/03 18:52:55 Exp $ 2 * 3 * Linux/SPARC Real Time Clock Driver 4 * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) 5 * 6 * This is a little driver that lets a user-level program access 7 * the SPARC Mostek real time clock chip. It is no use unless you 8 * use the modified clock utility. 9 * 10 * Get the modified clock utility from: 11 * ftp://vger.kernel.org/pub/linux/Sparc/userland/clock.c 12 */ 13 14#include <linux/module.h> 15#include <linux/types.h> 16#include <linux/errno.h> 17#include <linux/miscdevice.h> 18#include <linux/slab.h> 19#include <linux/fcntl.h> 20#include <linux/poll.h> 21#include <linux/init.h> 22#include <asm/io.h> 23#include <asm/mostek.h> 24#include <asm/system.h> 25#include <asm/uaccess.h> 26#include <asm/rtc.h> 27 28static int rtc_busy = 0; 29 30/* This is the structure layout used by drivers/char/rtc.c, we 31 * support that driver's ioctls so that things are less messy in 32 * userspace. 33 */ 34struct rtc_time_generic { 35 int tm_sec; 36 int tm_min; 37 int tm_hour; 38 int tm_mday; 39 int tm_mon; 40 int tm_year; 41 int tm_wday; 42 int tm_yday; 43 int tm_isdst; 44}; 45#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */ 46#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */ 47#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ 48#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */ 49#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */ 50#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */ 51#define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */ 52#define RTC_WIE_OFF _IO('p', 0x10) /* ... off */ 53#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time_generic) /* Read RTC time */ 54#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time_generic) /* Set RTC time */ 55#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */ 56#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */ 57#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */ 58#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */ 59#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ 60#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ 61#define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/ 62#define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/ 63#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ 64#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ 65 66/* Retrieve the current date and time from the real time clock. */ 67static void get_rtc_time(struct rtc_time *t) 68{ 69 void __iomem *regs = mstk48t02_regs; 70 u8 tmp; 71 72 spin_lock_irq(&mostek_lock); 73 74 tmp = mostek_read(regs + MOSTEK_CREG); 75 tmp |= MSTK_CREG_READ; 76 mostek_write(regs + MOSTEK_CREG, tmp); 77 78 t->sec = MSTK_REG_SEC(regs); 79 t->min = MSTK_REG_MIN(regs); 80 t->hour = MSTK_REG_HOUR(regs); 81 t->dow = MSTK_REG_DOW(regs); 82 t->dom = MSTK_REG_DOM(regs); 83 t->month = MSTK_REG_MONTH(regs); 84 t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); 85 86 tmp = mostek_read(regs + MOSTEK_CREG); 87 tmp &= ~MSTK_CREG_READ; 88 mostek_write(regs + MOSTEK_CREG, tmp); 89 90 spin_unlock_irq(&mostek_lock); 91} 92 93/* Set the current date and time inthe real time clock. */ 94void set_rtc_time(struct rtc_time *t) 95{ 96 void __iomem *regs = mstk48t02_regs; 97 u8 tmp; 98 99 spin_lock_irq(&mostek_lock); 100 101 tmp = mostek_read(regs + MOSTEK_CREG); 102 tmp |= MSTK_CREG_WRITE; 103 mostek_write(regs + MOSTEK_CREG, tmp); 104 105 MSTK_SET_REG_SEC(regs,t->sec); 106 MSTK_SET_REG_MIN(regs,t->min); 107 MSTK_SET_REG_HOUR(regs,t->hour); 108 MSTK_SET_REG_DOW(regs,t->dow); 109 MSTK_SET_REG_DOM(regs,t->dom); 110 MSTK_SET_REG_MONTH(regs,t->month); 111 MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO); 112 113 tmp = mostek_read(regs + MOSTEK_CREG); 114 tmp &= ~MSTK_CREG_WRITE; 115 mostek_write(regs + MOSTEK_CREG, tmp); 116 117 spin_unlock_irq(&mostek_lock); 118} 119 120static int put_rtc_time_generic(void __user *argp, struct rtc_time *tm) 121{ 122 struct rtc_time_generic __user *utm = argp; 123 124 if (__put_user(tm->sec, &utm->tm_sec) || 125 __put_user(tm->min, &utm->tm_min) || 126 __put_user(tm->hour, &utm->tm_hour) || 127 __put_user(tm->dom, &utm->tm_mday) || 128 __put_user(tm->month, &utm->tm_mon) || 129 __put_user(tm->year, &utm->tm_year) || 130 __put_user(tm->dow, &utm->tm_wday) || 131 __put_user(0, &utm->tm_yday) || 132 __put_user(0, &utm->tm_isdst)) 133 return -EFAULT; 134 135 return 0; 136} 137 138static int get_rtc_time_generic(struct rtc_time *tm, void __user *argp) 139{ 140 struct rtc_time_generic __user *utm = argp; 141 142 if (__get_user(tm->sec, &utm->tm_sec) || 143 __get_user(tm->min, &utm->tm_min) || 144 __get_user(tm->hour, &utm->tm_hour) || 145 __get_user(tm->dom, &utm->tm_mday) || 146 __get_user(tm->month, &utm->tm_mon) || 147 __get_user(tm->year, &utm->tm_year) || 148 __get_user(tm->dow, &utm->tm_wday)) 149 return -EFAULT; 150 151 return 0; 152} 153 154static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 155 unsigned long arg) 156{ 157 struct rtc_time rtc_tm; 158 void __user *argp = (void __user *)arg; 159 160 switch (cmd) { 161 /* No interrupt support, return an error 162 * compatible with drivers/char/rtc.c 163 */ 164 case RTC_AIE_OFF: 165 case RTC_AIE_ON: 166 case RTC_PIE_OFF: 167 case RTC_PIE_ON: 168 case RTC_UIE_OFF: 169 case RTC_UIE_ON: 170 case RTC_IRQP_READ: 171 case RTC_IRQP_SET: 172 case RTC_EPOCH_SET: 173 case RTC_EPOCH_READ: 174 return -EINVAL; 175 176 case RTCGET: 177 case RTC_RD_TIME: 178 memset(&rtc_tm, 0, sizeof(struct rtc_time)); 179 get_rtc_time(&rtc_tm); 180 181 if (cmd == RTCGET) { 182 if (copy_to_user(argp, &rtc_tm, 183 sizeof(struct rtc_time))) 184 return -EFAULT; 185 } else if (put_rtc_time_generic(argp, &rtc_tm)) 186 return -EFAULT; 187 188 return 0; 189 190 191 case RTCSET: 192 case RTC_SET_TIME: 193 if (!capable(CAP_SYS_TIME)) 194 return -EPERM; 195 196 if (cmd == RTCSET) { 197 if (copy_from_user(&rtc_tm, argp, 198 sizeof(struct rtc_time))) 199 return -EFAULT; 200 } else if (get_rtc_time_generic(&rtc_tm, argp)) 201 return -EFAULT; 202 203 set_rtc_time(&rtc_tm); 204 205 return 0; 206 207 default: 208 return -EINVAL; 209 } 210} 211 212static int rtc_open(struct inode *inode, struct file *file) 213{ 214 int ret; 215 216 spin_lock_irq(&mostek_lock); 217 if (rtc_busy) { 218 ret = -EBUSY; 219 } else { 220 rtc_busy = 1; 221 ret = 0; 222 } 223 spin_unlock_irq(&mostek_lock); 224 225 return ret; 226} 227 228static int rtc_release(struct inode *inode, struct file *file) 229{ 230 rtc_busy = 0; 231 232 return 0; 233} 234 235static const struct file_operations rtc_fops = { 236 .owner = THIS_MODULE, 237 .llseek = no_llseek, 238 .ioctl = rtc_ioctl, 239 .open = rtc_open, 240 .release = rtc_release, 241}; 242 243static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; 244 245static int __init rtc_sun_init(void) 246{ 247 int error; 248 249 /* It is possible we are being driven by some other RTC chip 250 * and thus another RTC driver is handling things. 251 */ 252 if (!mstk48t02_regs) 253 return -ENODEV; 254 255 error = misc_register(&rtc_dev); 256 if (error) { 257 printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n"); 258 return error; 259 } 260 printk("rtc_sun_init: Registered Mostek RTC driver.\n"); 261 262 return 0; 263} 264 265static void __exit rtc_sun_cleanup(void) 266{ 267 misc_deregister(&rtc_dev); 268} 269 270module_init(rtc_sun_init); 271module_exit(rtc_sun_cleanup); 272MODULE_LICENSE("GPL"); 273