OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [ds1742.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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
 
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
 
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
 
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
 
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
 
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
 
252
void rtc_ds1742_wait(void)
253
{
254
        while (CMOS_READ(RTC_SECONDS) & 1);
255
        while (!(CMOS_READ(RTC_SECONDS) & 1));
256
}
257
 
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
 
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
 
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
 
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
 
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");

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.