File: /usr/src/linux/arch/m68k/mvme16x/rtc.c

1     /*
2      *	Real Time Clock interface for Linux on the MVME16x
3      *
4      * Based on the PC driver by Paul Gortmaker.
5      */
6     
7     #define RTC_VERSION		"1.00"
8     
9     #include <linux/types.h>
10     #include <linux/errno.h>
11     #include <linux/miscdevice.h>
12     #include <linux/slab.h>
13     #include <linux/ioport.h>
14     #include <linux/fcntl.h>
15     #include <linux/init.h>
16     #include <linux/poll.h>
17     #include <linux/mc146818rtc.h>	/* For struct rtc_time and ioctls, etc */
18     #include <linux/smp_lock.h>
19     #include <asm/mvme16xhw.h>
20     
21     #include <asm/io.h>
22     #include <asm/uaccess.h>
23     #include <asm/system.h>
24     #include <asm/setup.h>
25     
26     /*
27      *	We sponge a minor off of the misc major. No need slurping
28      *	up another valuable major dev number for this. If you add
29      *	an ioctl, make sure you don't conflict with SPARC's RTC
30      *	ioctls.
31      */
32     
33     #define BCD2BIN(val) (((val)&15) + ((val)>>4)*10)
34     #define BIN2BCD(val) ((((val)/10)<<4) + (val)%10)
35     
36     static unsigned char days_in_mo[] =
37     {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
38     
39     static char rtc_status = 0;
40     
41     static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
42     		     unsigned long arg)
43     {
44     	volatile MK48T08ptr_t rtc = (MK48T08ptr_t)MVME_RTC_BASE;
45     	unsigned long flags;
46     	struct rtc_time wtime; 
47     
48     	switch (cmd) {
49     	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
50     	{
51     		save_flags(flags);
52     		cli();
53     		/* Ensure clock and real-time-mode-register are accessible */
54     		rtc->ctrl = RTC_READ;
55     		wtime.tm_sec =  BCD2BIN(rtc->bcd_sec);
56     		wtime.tm_min =  BCD2BIN(rtc->bcd_min);
57     		wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
58     		wtime.tm_mday =  BCD2BIN(rtc->bcd_dom);
59     		wtime.tm_mon =  BCD2BIN(rtc->bcd_mth)-1;
60     		wtime.tm_year = BCD2BIN(rtc->bcd_year);
61     		if (wtime.tm_year < 70)
62     			wtime.tm_year += 100;
63     		wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
64     		rtc->ctrl = 0;
65     		restore_flags(flags);
66     		return copy_to_user((void *)arg, &wtime, sizeof wtime) ?
67     								-EFAULT : 0;
68     	}
69     	case RTC_SET_TIME:	/* Set the RTC */
70     	{
71     		struct rtc_time rtc_tm;
72     		unsigned char mon, day, hrs, min, sec, leap_yr;
73     		unsigned int yrs;
74     
75     		if (!capable(CAP_SYS_ADMIN))
76     			return -EACCES;
77     
78     		if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
79     				   sizeof(struct rtc_time)))
80     			return -EFAULT;
81     
82     		yrs = rtc_tm.tm_year;
83     		if (yrs < 1900)
84     			yrs += 1900;
85     		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
86     		day = rtc_tm.tm_mday;
87     		hrs = rtc_tm.tm_hour;
88     		min = rtc_tm.tm_min;
89     		sec = rtc_tm.tm_sec;
90     
91     		leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
92     
93     		if ((mon > 12) || (day == 0))
94     			return -EINVAL;
95     
96     		if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
97     			return -EINVAL;
98     
99     		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
100     			return -EINVAL;
101     
102     		if (yrs >= 2070)
103     			return -EINVAL;
104     		
105     		save_flags(flags);
106     		cli();
107     		rtc->ctrl     = RTC_WRITE;
108     
109     		rtc->bcd_sec  = BIN2BCD(sec);
110     		rtc->bcd_min  = BIN2BCD(min);
111     		rtc->bcd_hr   = BIN2BCD(hrs);
112     		rtc->bcd_dom  = BIN2BCD(day);
113     		rtc->bcd_mth  = BIN2BCD(mon);
114     		rtc->bcd_year = BIN2BCD(yrs%100);
115     
116     		rtc->ctrl     = 0;
117     		restore_flags(flags);
118     		return 0;
119     	}
120     	default:
121     		return -EINVAL;
122     	}
123     }
124     
125     /*
126      *	We enforce only one user at a time here with the open/close.
127      *	Also clear the previous interrupt data on an open, and clean
128      *	up things on a close.
129      */
130     
131     static int rtc_open(struct inode *inode, struct file *file)
132     {
133     	if(rtc_status)
134     		return -EBUSY;
135     
136     	rtc_status = 1;
137     	return 0;
138     }
139     
140     static int rtc_release(struct inode *inode, struct file *file)
141     {
142     	lock_kernel();
143     	rtc_status = 0;
144     	unlock_kernel();
145     	return 0;
146     }
147     
148     /*
149      *	The various file operations we support.
150      */
151     
152     static struct file_operations rtc_fops = {
153     	ioctl:		rtc_ioctl,
154     	open:		rtc_open,
155     	release:	rtc_release,
156     };
157     
158     static struct miscdevice rtc_dev=
159     {
160     	RTC_MINOR,
161     	"rtc",
162     	&rtc_fops
163     };
164     
165     int __init rtc_MK48T08_init(void)
166     {
167     	if (!MACH_IS_MVME16x)
168     		return -ENODEV;
169     
170     	printk(KERN_INFO "MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION);
171     	misc_register(&rtc_dev);
172     	return 0;
173     }
174     
175