blob: 0c00c41f96fc1e9a77d6d178d610b8fd962bf581 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * Real Time Clock driver for WL-HDD
3 *
4 * Copyright (C) 2007 Andreas Engel
5 *
6 * Hacked together mostly by copying the relevant code parts from:
7 * drivers/i2c/i2c-bcm5365.c
8 * drivers/i2c/i2c-algo-bit.c
9 * drivers/char/rtc.c
10 *
11 * Note 1:
12 * This module uses the standard char device (10,135), while the Asus module
13 * rtcdrv.o uses (12,0). So, both can coexist which might be handy during
14 * development (but see the comment in rtc_open()).
15 *
16 * Note 2:
17 * You might need to set the clock once after loading the driver the first
18 * time because the driver switches the chip into 24h mode if it is running
19 * in 12h mode.
20 *
21 * Usage:
22 * For compatibility reasons with the original asus driver, the time can be
23 * read and set via the /dev/rtc device entry. The only accepted data format
24 * is "YYYY:MM:DD:W:HH:MM:SS\n". See OpenWrt wiki for a script which handles
25 * this format.
26 *
27 * In addition, this driver supports the standard ioctl() calls for setting
28 * and reading the hardware clock, so the ordinary hwclock utility can also
29 * be used.
30 *
31 * This program is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU General Public License
33 * as published by the Free Software Foundation; either version
34 * 2 of the License, or (at your option) any later version.
35 *
36 * TODO:
37 * - add a /proc/driver/rtc interface?
38 * - make the battery failure bit available through the /proc interface?
39 *
40 * $Id: rtc.c 7 2007-05-25 19:37:01Z ae $
41 */
42
43#include <linux/module.h>
44#include <linux/kmod.h>
45#include <linux/kernel.h>
46#include <linux/types.h>
47#include <linux/miscdevice.h>
48#include <linux/ioport.h>
49#include <linux/fcntl.h>
50#include <linux/mc146818rtc.h>
51#include <linux/init.h>
52#include <linux/spinlock.h>
53#include <linux/rtc.h>
54#include <linux/delay.h>
55#include <linux/version.h>
56#include <linux/gpio.h>
57#include <linux/uaccess.h>
58
59#include <asm/current.h>
60
61#include <bcm47xx.h>
62#include <linux/bcm47xx_nvram.h>
63
64#define RTC_IS_OPEN 0x01 /* Means /dev/rtc is in use. */
65
66/* Can be changed via a module parameter. */
67static int rtc_debug = 0;
68
69static unsigned long rtc_status = 0; /* Bitmapped status byte. */
70
71/* These settings are platform dependents. */
72unsigned int sda_index = 0;
73unsigned int scl_index = 0;
74
75#define I2C_READ_MASK 1
76#define I2C_WRITE_MASK 0
77
78#define I2C_ACK 1
79#define I2C_NAK 0
80
81#define RTC_EPOCH 1900
82#define RTC_I2C_ADDRESS (0x32 << 1)
83#define RTC_24HOUR_MODE_MASK 0x20
84#define RTC_PM_MASK 0x20
85#define RTC_VDET_MASK 0x40
86#define RTC_Y2K_MASK 0x80
87
88/*
89 * Delay in microseconds for generating the pulses on the I2C bus. We use
90 * a rather conservative setting here. See datasheet of the RTC chip.
91 */
92#define ADAP_DELAY 50
93
94/* Avoid spurious compiler warnings. */
95#define UNUSED __attribute__((unused))
96
97MODULE_AUTHOR("Andreas Engel");
98MODULE_LICENSE("GPL");
99
100/* Test stolen from switch-adm.c. */
101module_param(rtc_debug, int, 0);
102
103static inline void sdalo(void)
104{
105 gpio_direction_output(sda_index, 1);
106 udelay(ADAP_DELAY);
107}
108
109static inline void sdahi(void)
110{
111 gpio_direction_input(sda_index);
112 udelay(ADAP_DELAY);
113}
114
115static inline void scllo(void)
116{
117 gpio_direction_output(scl_index, 1);
118 udelay(ADAP_DELAY);
119}
120
121static inline int getscl(void)
122{
123 return (gpio_get_value(scl_index));
124}
125
126static inline int getsda(void)
127{
128 return (gpio_get_value(sda_index));
129}
130
131/*
132 * We shouldn't simply set the SCL pin to high. Like SDA, the SCL line is
133 * bidirectional too. According to the I2C spec, the slave is allowed to
134 * pull down the SCL line to slow down the clock, so we need to check this.
135 * Generally, we'd need a timeout here, but in our case, we just check the
136 * line, assuming the RTC chip behaves well.
137 */
138static int sclhi(void)
139{
140 gpio_direction_input(scl_index);
141 udelay(ADAP_DELAY);
142 if (!getscl()) {
143 printk(KERN_ERR "SCL pin should be low\n");
144 return -ETIMEDOUT;
145 }
146 return 0;
147}
148
149static void i2c_start(void)
150{
151 sdalo();
152 scllo();
153}
154
155static void i2c_stop(void)
156{
157 sdalo();
158 sclhi();
159 sdahi();
160}
161
162static int i2c_outb(int c)
163{
164 int i;
165 int ack;
166
167 /* assert: scl is low */
168 for (i = 7; i >= 0; i--) {
169 if (c & ( 1 << i )) {
170 sdahi();
171 } else {
172 sdalo();
173 }
174 if (sclhi() < 0) { /* timed out */
175 sdahi(); /* we don't want to block the net */
176 return -ETIMEDOUT;
177 };
178 scllo();
179 }
180 sdahi();
181 if (sclhi() < 0) {
182 return -ETIMEDOUT;
183 };
184 /* read ack: SDA should be pulled down by slave */
185 ack = getsda() == 0; /* ack: sda is pulled low ->success. */
186 scllo();
187
188 if (rtc_debug)
189 printk(KERN_DEBUG "i2c_outb(0x%02x) -> %s\n",
190 c, ack ? "ACK": "NAK");
191
192 return ack; /* return 1 if device acked */
193 /* assert: scl is low (sda undef) */
194}
195
196static int i2c_inb(int ack)
197{
198 int i;
199 unsigned int indata = 0;
200
201 /* assert: scl is low */
202
203 sdahi();
204 for (i = 0; i < 8; i++) {
205 if (sclhi() < 0) {
206 return -ETIMEDOUT;
207 };
208 indata *= 2;
209 if (getsda())
210 indata |= 0x01;
211 scllo();
212 }
213 if (ack) {
214 sdalo();
215 } else {
216 sdahi();
217 }
218
219 if (sclhi() < 0) {
220 sdahi();
221 return -ETIMEDOUT;
222 }
223 scllo();
224 sdahi();
225
226 if (rtc_debug)
227 printk(KERN_DEBUG "i2c_inb() -> 0x%02x\n", indata);
228
229 /* assert: scl is low */
230 return indata & 0xff;
231}
232
233static void i2c_init(void)
234{
235 /* no gpio_control for EXTIF */
236 // ssb_gpio_control(&ssb, sda_mask | scl_mask, 0);
237
238 gpio_set_value(sda_index, 0);
239 gpio_set_value(scl_index, 0);
240 sdahi();
241 sclhi();
242}
243
244static int rtc_open(UNUSED struct inode *inode, UNUSED struct file *filp)
245{
246 spin_lock_irq(&rtc_lock);
247
248 if (rtc_status & RTC_IS_OPEN) {
249 spin_unlock_irq(&rtc_lock);
250 return -EBUSY;
251 }
252
253 rtc_status |= RTC_IS_OPEN;
254
255 /*
256 * The following call is only necessary if we use both this driver and
257 * the proprietary one from asus at the same time (which, b.t.w. only
258 * makes sense during development). Otherwise, each access via the asus
259 * driver will make access via this driver impossible.
260 */
261 i2c_init();
262
263 spin_unlock_irq(&rtc_lock);
264
265 return 0;
266}
267
268static int rtc_release(UNUSED struct inode *inode, UNUSED struct file *filp)
269{
270 /* No need for locking here. */
271 rtc_status &= ~RTC_IS_OPEN;
272 return 0;
273}
274
275static int from_bcd(int bcdnum)
276{
277 int fac, num = 0;
278
279 for (fac = 1; bcdnum; fac *= 10) {
280 num += (bcdnum % 16) * fac;
281 bcdnum /= 16;
282 }
283
284 return num;
285}
286
287static int to_bcd(int decnum)
288{
289 int fac, num = 0;
290
291 for (fac = 1; decnum; fac *= 16) {
292 num += (decnum % 10) * fac;
293 decnum /= 10;
294 }
295
296 return num;
297}
298
299static void get_rtc_time(struct rtc_time *rtc_tm)
300{
301 int cr2;
302
303 /*
304 * Read date and time from the RTC. We use read method (3).
305 */
306
307 spin_lock_irq(&rtc_lock);
308 i2c_start();
309 i2c_outb(RTC_I2C_ADDRESS | I2C_READ_MASK);
310 cr2 = i2c_inb(I2C_ACK);
311 rtc_tm->tm_sec = i2c_inb(I2C_ACK);
312 rtc_tm->tm_min = i2c_inb(I2C_ACK);
313 rtc_tm->tm_hour = i2c_inb(I2C_ACK);
314 rtc_tm->tm_wday = i2c_inb(I2C_ACK);
315 rtc_tm->tm_mday = i2c_inb(I2C_ACK);
316 rtc_tm->tm_mon = i2c_inb(I2C_ACK);
317 rtc_tm->tm_year = i2c_inb(I2C_NAK);
318 i2c_stop();
319 spin_unlock_irq(&rtc_lock);
320
321 if (cr2 & RTC_VDET_MASK) {
322 printk(KERN_WARNING "***RTC BATTERY FAILURE***\n");
323 }
324
325 /* Handle century bit */
326 if (rtc_tm->tm_mon & RTC_Y2K_MASK) {
327 rtc_tm->tm_mon &= ~RTC_Y2K_MASK;
328 rtc_tm->tm_year += 0x100;
329 }
330
331 rtc_tm->tm_sec = from_bcd(rtc_tm->tm_sec);
332 rtc_tm->tm_min = from_bcd(rtc_tm->tm_min);
333 rtc_tm->tm_hour = from_bcd(rtc_tm->tm_hour);
334 rtc_tm->tm_mday = from_bcd(rtc_tm->tm_mday);
335 rtc_tm->tm_mon = from_bcd(rtc_tm->tm_mon) - 1;
336 rtc_tm->tm_year = from_bcd(rtc_tm->tm_year);
337
338 rtc_tm->tm_isdst = -1; /* DST not known */
339}
340
341static void set_rtc_time(struct rtc_time *rtc_tm)
342{
343 rtc_tm->tm_sec = to_bcd(rtc_tm->tm_sec);
344 rtc_tm->tm_min = to_bcd(rtc_tm->tm_min);
345 rtc_tm->tm_hour = to_bcd(rtc_tm->tm_hour);
346 rtc_tm->tm_mday = to_bcd(rtc_tm->tm_mday);
347 rtc_tm->tm_mon = to_bcd(rtc_tm->tm_mon + 1);
348 rtc_tm->tm_year = to_bcd(rtc_tm->tm_year);
349
350 if (rtc_tm->tm_year >= 0x100) {
351 rtc_tm->tm_year -= 0x100;
352 rtc_tm->tm_mon |= RTC_Y2K_MASK;
353 }
354
355 spin_lock_irq(&rtc_lock);
356 i2c_start();
357 i2c_outb(RTC_I2C_ADDRESS | I2C_WRITE_MASK);
358 i2c_outb(0x00); /* set starting register to 0 (=seconds) */
359 i2c_outb(rtc_tm->tm_sec);
360 i2c_outb(rtc_tm->tm_min);
361 i2c_outb(rtc_tm->tm_hour);
362 i2c_outb(rtc_tm->tm_wday);
363 i2c_outb(rtc_tm->tm_mday);
364 i2c_outb(rtc_tm->tm_mon);
365 i2c_outb(rtc_tm->tm_year);
366 i2c_stop();
367 spin_unlock_irq(&rtc_lock);
368}
369
370static ssize_t rtc_write(UNUSED struct file *filp, const char *buf,
371 size_t count, loff_t *ppos)
372{
373 struct rtc_time rtc_tm;
374 char buffer[23];
375 char *p;
376
377 if (!capable(CAP_SYS_TIME))
378 return -EACCES;
379
380 if (ppos != &filp->f_pos)
381 return -ESPIPE;
382
383 /*
384 * For simplicity, the only acceptable format is:
385 * YYYY:MM:DD:W:HH:MM:SS\n
386 */
387
388 if (count != 22)
389 goto err_out;
390
391 if (copy_from_user(buffer, buf, count))
392 return -EFAULT;
393
394 buffer[sizeof(buffer)-1] = '\0';
395
396 p = &buffer[0];
397
398 rtc_tm.tm_year = simple_strtoul(p, &p, 10);
399 if (*p++ != ':') goto err_out;
400
401 rtc_tm.tm_mon = simple_strtoul(p, &p, 10) - 1;
402 if (*p++ != ':') goto err_out;
403
404 rtc_tm.tm_mday = simple_strtoul(p, &p, 10);
405 if (*p++ != ':') goto err_out;
406
407 rtc_tm.tm_wday = simple_strtoul(p, &p, 10);
408 if (*p++ != ':') goto err_out;
409
410 rtc_tm.tm_hour = simple_strtoul(p, &p, 10);
411 if (*p++ != ':') goto err_out;
412
413 rtc_tm.tm_min = simple_strtoul(p, &p, 10);
414 if (*p++ != ':') goto err_out;
415
416 rtc_tm.tm_sec = simple_strtoul(p, &p, 10);
417 if (*p != '\n') goto err_out;
418
419 rtc_tm.tm_year -= RTC_EPOCH;
420
421 set_rtc_time(&rtc_tm);
422
423 *ppos += count;
424
425 return count;
426
427 err_out:
428 printk(KERN_ERR "invalid format: use YYYY:MM:DD:W:HH:MM:SS\\n\n");
429 return -EINVAL;
430}
431
432
433static ssize_t rtc_read(UNUSED struct file *filp, char *buf, size_t count,
434 loff_t *ppos)
435{
436 char wbuf[23];
437 struct rtc_time tm;
438 ssize_t len;
439
440 if (count == 0 || *ppos != 0)
441 return 0;
442
443 get_rtc_time(&tm);
444
445 len = sprintf(wbuf, "%04d:%02d:%02d:%d:%02d:%02d:%02d\n",
446 tm.tm_year + RTC_EPOCH,
447 tm.tm_mon + 1,
448 tm.tm_mday,
449 tm.tm_wday,
450 tm.tm_hour,
451 tm.tm_min,
452 tm.tm_sec);
453
454 if (len > (ssize_t)count)
455 len = count;
456
457 if (copy_to_user(buf, wbuf, len))
458 return -EFAULT;
459
460 *ppos += len;
461
462 return len;
463}
464
465static int rtc_do_ioctl(unsigned int cmd, unsigned long arg)
466{
467 struct rtc_time rtc_tm;
468
469 switch (cmd) {
470 case RTC_RD_TIME:
471 memset(&rtc_tm, 0, sizeof(struct rtc_time));
472 get_rtc_time(&rtc_tm);
473 if (copy_to_user((void *)arg, &rtc_tm, sizeof(rtc_tm)))
474 return -EFAULT;
475 break;
476
477 case RTC_SET_TIME:
478 if (!capable(CAP_SYS_TIME))
479 return -EACCES;
480
481 if (copy_from_user(&rtc_tm, (struct rtc_time *)arg,
482 sizeof(struct rtc_time)))
483 return -EFAULT;
484
485 set_rtc_time(&rtc_tm);
486 break;
487
488 default:
489 return -ENOTTY;
490 }
491
492 return 0;
493}
494
495static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
496{
497 long ret;
498 ret = rtc_do_ioctl(cmd, arg);
499 return ret;
500}
501
502static const struct file_operations rtc_fops = {
503 .owner = THIS_MODULE,
504 .llseek = no_llseek,
505 .read = rtc_read,
506 .write = rtc_write,
507 .unlocked_ioctl = rtc_ioctl,
508 .open = rtc_open,
509 .release = rtc_release,
510};
511
512static struct miscdevice rtc_dev = {
513 .minor = RTC_MINOR,
514 .name = "rtc",
515 .fops = &rtc_fops,
516};
517
518/* Savagely ripped from diag.c. */
519static inline int startswith (char *source, char *cmp)
520{
521 return !strncmp(source, cmp, strlen(cmp));
522}
523
524static void platform_detect(void)
525{
526 char buf[20];
527 int et0phyaddr, et1phyaddr;
528
529 /* Based on "model_no". */
530 if (bcm47xx_nvram_getenv("model_no", buf, sizeof(buf)) >= 0) {
531 if (startswith(buf, "WL700")) { /* WL700* */
532 sda_index = 2;
533 scl_index = 5;
534 return;
535 }
536 }
537
538 if (bcm47xx_nvram_getenv("et0phyaddr", buf, sizeof(buf)) >= 0 )
539 et0phyaddr = simple_strtoul(buf, NULL, 0);
540 if (bcm47xx_nvram_getenv("et1phyaddr", buf, sizeof(buf)) >= 0 )
541 et1phyaddr = simple_strtoul(buf, NULL, 0);
542
543 if (bcm47xx_nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0) {
544 /* Either WL-300g or WL-HDD, do more extensive checks */
545 if (startswith(buf, "WL300-") && et0phyaddr == 0 && et1phyaddr == 1) {
546 sda_index = 4;
547 scl_index = 5;
548 return;
549 }
550 }
551 /* not found */
552}
553
554static int __init rtc_init(void)
555{
556 int cr1;
557
558 platform_detect();
559
560 if (sda_index == scl_index) {
561 printk(KERN_ERR "RTC-RV5C386A: unrecognized platform!\n");
562 return -ENODEV;
563 }
564
565 i2c_init();
566
567 /*
568 * Switch RTC to 24h mode
569 */
570 spin_lock_irq(&rtc_lock);
571 i2c_start();
572 i2c_outb(RTC_I2C_ADDRESS | I2C_WRITE_MASK);
573 i2c_outb(0xE4); /* start at address 0xE, transmission mode 4 */
574 cr1 = i2c_inb(I2C_NAK);
575 i2c_stop();
576 spin_unlock_irq(&rtc_lock);
577 if ((cr1 & RTC_24HOUR_MODE_MASK) == 0) {
578 /* RTC is running in 12h mode */
579 printk(KERN_INFO "rtc.o: switching to 24h mode\n");
580 spin_lock_irq(&rtc_lock);
581 i2c_start();
582 i2c_outb(RTC_I2C_ADDRESS | I2C_WRITE_MASK);
583 i2c_outb(0xE0);
584 i2c_outb(cr1 | RTC_24HOUR_MODE_MASK);
585 i2c_stop();
586 spin_unlock_irq(&rtc_lock);
587 }
588
589 misc_register(&rtc_dev);
590
591 printk(KERN_INFO "RV5C386A Real Time Clock Driver loaded\n");
592
593 return 0;
594}
595
596static void __exit rtc_exit (void)
597{
598 misc_deregister(&rtc_dev);
599 printk(KERN_INFO "Successfully removed RTC RV5C386A driver\n");
600}
601
602module_init(rtc_init);
603module_exit(rtc_exit);
604
605/*
606 * Local Variables:
607 * indent-tabs-mode:t
608 * c-basic-offset:8
609 * End:
610 */