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