11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Real Time Clock interface for Linux on the MVME16x 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Based on the PC driver by Paul Gortmaker. 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #define RTC_VERSION "1.00" 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds #include <linux/types.h> 101da177e4SLinus Torvalds #include <linux/errno.h> 111da177e4SLinus Torvalds #include <linux/miscdevice.h> 121da177e4SLinus Torvalds #include <linux/slab.h> 131da177e4SLinus Torvalds #include <linux/ioport.h> 141da177e4SLinus Torvalds #include <linux/fcntl.h> 151da177e4SLinus Torvalds #include <linux/init.h> 161da177e4SLinus Torvalds #include <linux/poll.h> 171da177e4SLinus Torvalds #include <linux/mc146818rtc.h> /* For struct rtc_time and ioctls, etc */ 181da177e4SLinus Torvalds #include <linux/smp_lock.h> 191da177e4SLinus Torvalds #include <asm/mvme16xhw.h> 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #include <asm/io.h> 221da177e4SLinus Torvalds #include <asm/uaccess.h> 231da177e4SLinus Torvalds #include <asm/system.h> 241da177e4SLinus Torvalds #include <asm/setup.h> 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds /* 271da177e4SLinus Torvalds * We sponge a minor off of the misc major. No need slurping 281da177e4SLinus Torvalds * up another valuable major dev number for this. If you add 291da177e4SLinus Torvalds * an ioctl, make sure you don't conflict with SPARC's RTC 301da177e4SLinus Torvalds * ioctls. 311da177e4SLinus Torvalds */ 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #define BCD2BIN(val) (((val)&15) + ((val)>>4)*10) 341da177e4SLinus Torvalds #define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds static const unsigned char days_in_mo[] = 371da177e4SLinus Torvalds {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds static atomic_t rtc_ready = ATOMIC_INIT(1); 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 421da177e4SLinus Torvalds unsigned long arg) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds volatile MK48T08ptr_t rtc = (MK48T08ptr_t)MVME_RTC_BASE; 451da177e4SLinus Torvalds unsigned long flags; 461da177e4SLinus Torvalds struct rtc_time wtime; 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds switch (cmd) { 491da177e4SLinus Torvalds case RTC_RD_TIME: /* Read the time/date from RTC */ 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds local_irq_save(flags); 521da177e4SLinus Torvalds /* Ensure clock and real-time-mode-register are accessible */ 531da177e4SLinus Torvalds rtc->ctrl = RTC_READ; 541da177e4SLinus Torvalds memset(&wtime, 0, sizeof(struct rtc_time)); 551da177e4SLinus Torvalds wtime.tm_sec = BCD2BIN(rtc->bcd_sec); 561da177e4SLinus Torvalds wtime.tm_min = BCD2BIN(rtc->bcd_min); 571da177e4SLinus Torvalds wtime.tm_hour = BCD2BIN(rtc->bcd_hr); 581da177e4SLinus Torvalds wtime.tm_mday = BCD2BIN(rtc->bcd_dom); 591da177e4SLinus Torvalds wtime.tm_mon = BCD2BIN(rtc->bcd_mth)-1; 601da177e4SLinus Torvalds wtime.tm_year = BCD2BIN(rtc->bcd_year); 611da177e4SLinus Torvalds if (wtime.tm_year < 70) 621da177e4SLinus Torvalds wtime.tm_year += 100; 631da177e4SLinus Torvalds wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1; 641da177e4SLinus Torvalds rtc->ctrl = 0; 651da177e4SLinus Torvalds local_irq_restore(flags); 661da177e4SLinus Torvalds return copy_to_user((void *)arg, &wtime, sizeof wtime) ? 671da177e4SLinus Torvalds -EFAULT : 0; 681da177e4SLinus Torvalds } 691da177e4SLinus Torvalds case RTC_SET_TIME: /* Set the RTC */ 701da177e4SLinus Torvalds { 711da177e4SLinus Torvalds struct rtc_time rtc_tm; 721da177e4SLinus Torvalds unsigned char mon, day, hrs, min, sec, leap_yr; 731da177e4SLinus Torvalds unsigned int yrs; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 761da177e4SLinus Torvalds return -EACCES; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, 791da177e4SLinus Torvalds sizeof(struct rtc_time))) 801da177e4SLinus Torvalds return -EFAULT; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds yrs = rtc_tm.tm_year; 831da177e4SLinus Torvalds if (yrs < 1900) 841da177e4SLinus Torvalds yrs += 1900; 851da177e4SLinus Torvalds mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ 861da177e4SLinus Torvalds day = rtc_tm.tm_mday; 871da177e4SLinus Torvalds hrs = rtc_tm.tm_hour; 881da177e4SLinus Torvalds min = rtc_tm.tm_min; 891da177e4SLinus Torvalds sec = rtc_tm.tm_sec; 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds if ((mon > 12) || (day == 0)) 941da177e4SLinus Torvalds return -EINVAL; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) 971da177e4SLinus Torvalds return -EINVAL; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds if ((hrs >= 24) || (min >= 60) || (sec >= 60)) 1001da177e4SLinus Torvalds return -EINVAL; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds if (yrs >= 2070) 1031da177e4SLinus Torvalds return -EINVAL; 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds local_irq_save(flags); 1061da177e4SLinus Torvalds rtc->ctrl = RTC_WRITE; 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds rtc->bcd_sec = BIN2BCD(sec); 1091da177e4SLinus Torvalds rtc->bcd_min = BIN2BCD(min); 1101da177e4SLinus Torvalds rtc->bcd_hr = BIN2BCD(hrs); 1111da177e4SLinus Torvalds rtc->bcd_dom = BIN2BCD(day); 1121da177e4SLinus Torvalds rtc->bcd_mth = BIN2BCD(mon); 1131da177e4SLinus Torvalds rtc->bcd_year = BIN2BCD(yrs%100); 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds rtc->ctrl = 0; 1161da177e4SLinus Torvalds local_irq_restore(flags); 1171da177e4SLinus Torvalds return 0; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds default: 1201da177e4SLinus Torvalds return -EINVAL; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* 1251da177e4SLinus Torvalds * We enforce only one user at a time here with the open/close. 1261da177e4SLinus Torvalds * Also clear the previous interrupt data on an open, and clean 1271da177e4SLinus Torvalds * up things on a close. 1281da177e4SLinus Torvalds */ 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds static int rtc_open(struct inode *inode, struct file *file) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds if( !atomic_dec_and_test(&rtc_ready) ) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds atomic_inc( &rtc_ready ); 1351da177e4SLinus Torvalds return -EBUSY; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds return 0; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds static int rtc_release(struct inode *inode, struct file *file) 1421da177e4SLinus Torvalds { 1431da177e4SLinus Torvalds atomic_inc( &rtc_ready ); 1441da177e4SLinus Torvalds return 0; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds /* 1481da177e4SLinus Torvalds * The various file operations we support. 1491da177e4SLinus Torvalds */ 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds static struct file_operations rtc_fops = { 1521da177e4SLinus Torvalds .ioctl = rtc_ioctl, 1531da177e4SLinus Torvalds .open = rtc_open, 1541da177e4SLinus Torvalds .release = rtc_release, 1551da177e4SLinus Torvalds }; 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds static struct miscdevice rtc_dev= 1581da177e4SLinus Torvalds { 1591da177e4SLinus Torvalds .minor = RTC_MINOR, 1601da177e4SLinus Torvalds .name = "rtc", 1611da177e4SLinus Torvalds .fops = &rtc_fops 1621da177e4SLinus Torvalds }; 1631da177e4SLinus Torvalds 164*573fc113SChristoph Hellwig static int __init rtc_MK48T08_init(void) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds if (!MACH_IS_MVME16x) 1671da177e4SLinus Torvalds return -ENODEV; 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds printk(KERN_INFO "MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION); 1701da177e4SLinus Torvalds return misc_register(&rtc_dev); 1711da177e4SLinus Torvalds } 172*573fc113SChristoph Hellwig module_init(rtc_MK48T08_init); 173