1 /*
2  * linux/drivers/char/ds1742.c
3  *
4  * Dallas DS1742 Real Time Clock driver
5  *
6  * Copyright (C) 2003 TimeSys Corp.
7  *                    S. James Hill (James.Hill@timesys.com)
8  *                                  (sjhill@realitydiluted.com)
9  *
10  * Copyright (C) 2001 MontaVista Software Inc.
11  *                    ahennessy@mvista.com
12  *
13  * Copyright (C) 2000-2001 Toshiba Corporation
14  *
15  *  This program is free software; you can redistribute it and/or modify it
16  *  under the terms of the GNU General Public License as published by the
17  *  Free Software Foundation; either version 2 of the License, or (at your
18  *  option) any later version.
19  *
20  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
29  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  *  You should have received a copy of the GNU General Public License along
32  *  with this program; if not, write to the Free Software Foundation, Inc.,
33  *  675 Mass Ave, Cambridge, MA 02139, USA.
34  */
35 #include <linux/init.h>
36 #include <linux/kernel.h>
37 #include <linux/fcntl.h>
38 #include <linux/miscdevice.h>
39 #include <linux/module.h>
40 #include <linux/proc_fs.h>
41 #include <linux/spinlock.h>
42 #include <linux/types.h>
43 #include <asm/debug.h>
44 #include <asm/mc146818rtc.h>
45 #include <asm/time.h>
46 #include <asm/uaccess.h>
47 
48 #define DS1742_VERSION          "2.0"
49 
50 /*
51  * Registers
52  */
53 #define RTC_CONTROL		(rtc_base + 0x7f8)
54 #define RTC_CENTURY		(rtc_base + 0x7f8)
55 #define RTC_SECONDS		(rtc_base + 0x7f9)
56 #define RTC_MINUTES		(rtc_base + 0x7fa)
57 #define RTC_HOURS		(rtc_base + 0x7fb)
58 #define RTC_DAY			(rtc_base + 0x7fc)
59 #define RTC_DATE		(rtc_base + 0x7fd)
60 #define RTC_MONTH		(rtc_base + 0x7fe)
61 #define RTC_YEAR		(rtc_base + 0x7ff)
62 
63 #define RTC_CENTURY_MASK	0x3f
64 #define RTC_SECONDS_MASK	0x7f
65 #define RTC_DAY_MASK		0x07
66 
67 /*
68  * Bits in the Control/Century register
69  */
70 #define RTC_WRITE		0x80
71 #define RTC_READ		0x40
72 
73 /*
74  * Bits in the Seconds register
75  */
76 #define RTC_STOP		0x80
77 
78 /*
79  * Bits in the Day register
80  */
81 #define RTC_BATT_FLAG		0x80
82 #define RTC_FREQ_TEST		0x40
83 
84 /*
85  * Conversion between binary and BCD
86  */
87 #define BCD_TO_BIN(val) 	(((val)&15) + ((val)>>4)*10)
88 #define BIN_TO_BCD(val) 	((((val)/10)<<4) + (val)%10)
89 
90 /*
91  * CMOS Year Epoch
92  */
93 #define	EPOCH			2000
94 
95 /*
96  * The entry /dev/rtc is being used
97  */
98 #define RTC_IS_OPEN		0x1
99 
100 static unsigned long rtc_base = 0;
101 static unsigned long rtc_status = 0;
102 static spinlock_t rtc_lock;
103 
104 extern void to_tm(unsigned long tim, struct rtc_time * tm);
105 
rtc_ds1742_get_time(void)106 static unsigned long rtc_ds1742_get_time(void)
107 {
108 	unsigned int year, month, day, hour, minute, second;
109 	unsigned int century;
110 
111 	CMOS_WRITE(RTC_READ, RTC_CONTROL);
112 	second = BCD_TO_BIN(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK);
113 	minute = BCD_TO_BIN(CMOS_READ(RTC_MINUTES));
114 	hour = BCD_TO_BIN(CMOS_READ(RTC_HOURS));
115 	day = BCD_TO_BIN(CMOS_READ(RTC_DATE));
116 	month = BCD_TO_BIN(CMOS_READ(RTC_MONTH));
117 	year = BCD_TO_BIN(CMOS_READ(RTC_YEAR));
118 	century = BCD_TO_BIN(CMOS_READ(RTC_CENTURY) & RTC_CENTURY_MASK);
119 	CMOS_WRITE(0, RTC_CONTROL);
120 
121 	year += century * 100;
122 
123 	return mktime(year, month, day, hour, minute, second);
124 }
125 
rtc_ds1742_set_time(unsigned long t)126 static int rtc_ds1742_set_time(unsigned long t)
127 {
128 	struct rtc_time tm;
129 	u8 year, month, day, hour, minute, second;
130 	u8 cmos_year, cmos_month, cmos_day, cmos_hour, cmos_minute, cmos_second;
131 	int cmos_century;
132 
133 	CMOS_WRITE(RTC_READ, RTC_CONTROL);
134 	cmos_second = (u8)(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK);
135 	cmos_minute = (u8)CMOS_READ(RTC_MINUTES);
136 	cmos_hour = (u8)CMOS_READ(RTC_HOURS);
137 	cmos_day = (u8)CMOS_READ(RTC_DATE);
138 	cmos_month = (u8)CMOS_READ(RTC_MONTH);
139 	cmos_year = (u8)CMOS_READ(RTC_YEAR);
140 	cmos_century = CMOS_READ(RTC_CENTURY) & RTC_CENTURY_MASK;
141 
142 	CMOS_WRITE(RTC_WRITE, RTC_CONTROL);
143 
144 	/* convert */
145 	to_tm(t, &tm);
146 
147 	/* check each field one by one */
148 	year = BIN_TO_BCD(tm.tm_year - EPOCH);
149 	if (year != cmos_year) {
150 		CMOS_WRITE(year,RTC_YEAR);
151 	}
152 
153 	month = BIN_TO_BCD(tm.tm_mon + 1);
154 	if (month != (cmos_month & 0x1f)) {
155 		CMOS_WRITE((month & 0x1f) | (cmos_month & ~0x1f),RTC_MONTH);
156 	}
157 
158 	day = BIN_TO_BCD(tm.tm_mday);
159 	if (day != cmos_day)
160 		CMOS_WRITE(day, RTC_DATE);
161 
162 	if (cmos_hour & 0x40) {
163 		/* 12 hour format */
164 		hour = 0x40;
165 		if (tm.tm_hour > 12) {
166 			hour |= 0x20 | (BIN_TO_BCD(hour-12) & 0x1f);
167 		} else {
168 			hour |= BIN_TO_BCD(tm.tm_hour);
169 		}
170 	} else {
171 		/* 24 hour format */
172 		hour = BIN_TO_BCD(tm.tm_hour) & 0x3f;
173 	}
174 	if (hour != cmos_hour) CMOS_WRITE(hour, RTC_HOURS);
175 
176 	minute = BIN_TO_BCD(tm.tm_min);
177 	if (minute !=  cmos_minute) {
178 		CMOS_WRITE(minute, RTC_MINUTES);
179 	}
180 
181 	second = BIN_TO_BCD(tm.tm_sec);
182 	if (second !=  cmos_second) {
183 		CMOS_WRITE(second & RTC_SECONDS_MASK,RTC_SECONDS);
184 	}
185 
186 	/* RTC_CENTURY and RTC_CONTROL share same address... */
187 	CMOS_WRITE(cmos_century, RTC_CONTROL);
188 
189 	return 0;
190 }
191 
rtc_ds1742_init(unsigned long base)192 void __init rtc_ds1742_init(unsigned long base)
193 {
194 	u8  cmos_second;
195 
196 	/* remember the base */
197 	rtc_base = base;
198 	db_assert((rtc_base & 0xe0000000) == KSEG1);
199 
200 	/* set the function pointers */
201 	rtc_get_time = rtc_ds1742_get_time;
202 	rtc_set_time = rtc_ds1742_set_time;
203 
204 	/* clear oscillator stop bit */
205 	CMOS_WRITE(RTC_READ, RTC_CONTROL);
206 	cmos_second = (u8)(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK);
207 	CMOS_WRITE(RTC_WRITE, RTC_CONTROL);
208 	CMOS_WRITE(cmos_second, RTC_SECONDS); /* clear msb */
209 	CMOS_WRITE(0, RTC_CONTROL);
210 }
211 
get_ds1742_status(char * buf)212 static int get_ds1742_status(char *buf)
213 {
214 	char *p;
215 	struct rtc_time tm;
216 	unsigned long curr_time;
217 
218 	curr_time = rtc_ds1742_get_time();
219 	to_tm(curr_time, &tm);
220 
221 	p = buf;
222 
223 	/*
224 	 * There is no way to tell if the luser has the RTC set for local
225 	 * time or for Universal Standard Time (GMT). Probably local though.
226 	 */
227 	p += sprintf(p,
228 		     "rtc_time\t: %02d:%02d:%02d\n"
229 		     "rtc_date\t: %04d-%02d-%02d\n"
230 		     "rtc_epoch\t: %04lu\n",
231 		     tm.tm_hour, tm.tm_min, tm.tm_sec,
232 		     tm.tm_year, tm.tm_mon + 1, tm.tm_mday, 0L);
233 
234 	return p - buf;
235 }
236 
ds1742_read_proc(char * page,char ** start,off_t off,int count,int * eof,void * data)237 static int ds1742_read_proc(char *page, char **start, off_t off, int count,
238 				int *eof, void *data)
239 {
240 	int len = get_ds1742_status(page);
241 	if (len <= off + count)
242 		*eof = 1;
243 	*start = page + off;
244 	len -= off;
245 	if (len > count)
246 		len = count;
247 	if (len < 0)
248 		len = 0;
249 	return len;
250 }
251 
rtc_ds1742_wait(void)252 void rtc_ds1742_wait(void)
253 {
254 	while (CMOS_READ(RTC_SECONDS) & 1);
255 	while (!(CMOS_READ(RTC_SECONDS) & 1));
256 }
257 
ds1742_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)258 static int ds1742_ioctl(struct inode *inode, struct file *file,
259 				unsigned int cmd, unsigned long arg)
260 {
261 	struct rtc_time rtc_tm;
262 	ulong curr_time;
263 
264 	switch (cmd) {
265 	case RTC_RD_TIME:	/* Read the time/date from RTC  */
266 		curr_time = rtc_ds1742_get_time();
267 		to_tm(curr_time, &rtc_tm);
268 		rtc_tm.tm_year -= 1900;
269 		return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ?
270 			-EFAULT : 0;
271 	case RTC_SET_TIME:	/* Set the RTC */
272 		if (!capable(CAP_SYS_TIME))
273 			return -EACCES;
274 
275 		if (copy_from_user(&rtc_tm,
276 				   (struct rtc_time *) arg,
277 		                   sizeof(struct rtc_time)))
278 			return -EFAULT;
279 
280 		curr_time = mktime(rtc_tm.tm_year + 1900,
281 				   rtc_tm.tm_mon + 1, /* tm_mon starts from 0 */
282 				   rtc_tm.tm_mday,
283 				   rtc_tm.tm_hour,
284 				   rtc_tm.tm_min,
285 				   rtc_tm.tm_sec);
286 		return rtc_ds1742_set_time(curr_time);
287 	default:
288 		return -EINVAL;
289 	}
290 }
291 
ds1742_open(struct inode * inode,struct file * file)292 static int ds1742_open(struct inode *inode, struct file *file)
293 {
294 	spin_lock_irq(&rtc_lock);
295 
296 	if (rtc_status & RTC_IS_OPEN) {
297 		spin_unlock_irq(&rtc_lock);
298 		return -EBUSY;
299 	}
300 
301 	rtc_status |= RTC_IS_OPEN;
302 
303 	spin_unlock_irq(&rtc_lock);
304 	return 0;
305 }
306 
ds1742_release(struct inode * inode,struct file * file)307 static int ds1742_release(struct inode *inode, struct file *file)
308 {
309 	spin_lock_irq(&rtc_lock);
310 	rtc_status &= ~RTC_IS_OPEN;
311 	spin_unlock_irq(&rtc_lock);
312 	return 0;
313 }
314 
315 static struct file_operations ds1742_fops = {
316 	owner:THIS_MODULE,
317 	llseek:no_llseek,
318 	ioctl:ds1742_ioctl,
319 	open:ds1742_open,
320 	release:ds1742_release,
321 };
322 
323 static struct miscdevice ds1742_dev = {
324 	RTC_MINOR,
325 	"rtc",
326 	&ds1742_fops
327 };
328 
ds1742_init(void)329 static int __init ds1742_init(void)
330 {
331 	printk(KERN_INFO "DS1742 Real Time Clock Driver v%s\n", DS1742_VERSION);
332 	misc_register(&ds1742_dev);
333 	create_proc_read_entry("driver/rtc", 0, 0, ds1742_read_proc, NULL);
334 	return 0;
335 }
336 
ds1742_exit(void)337 static void __exit ds1742_exit(void)
338 {
339 	remove_proc_entry("driver/rtc", NULL);
340 	misc_deregister(&ds1742_dev);
341 }
342 
343 module_init(ds1742_init);
344 module_exit(ds1742_exit);
345 EXPORT_NO_SYMBOLS;
346 
347 MODULE_AUTHOR("Steven J. Hill");
348 MODULE_LICENSE("GPL");
349