rtc.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:3k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support
  3.  *
  4.  *  Copyright (C) 2000  Philipp Rumpf <prumpf@tux.org>
  5.  *  Copyright (C) 1999  Tetsuya Okada & Niibe Yutaka
  6.  */
  7. #include <linux/init.h>
  8. #include <linux/kernel.h>
  9. #include <linux/sched.h>
  10. #include <linux/time.h>
  11. #include <asm/io.h>
  12. #include <asm/rtc.h>
  13. #ifndef BCD_TO_BIN
  14. #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
  15. #endif
  16. #ifndef BIN_TO_BCD
  17. #define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
  18. #endif
  19. void sh_rtc_gettimeofday(struct timeval *tv)
  20. {
  21. unsigned int sec128, sec, min, hr, wk, day, mon, yr, yr100;
  22.  again:
  23. do {
  24. ctrl_outb(0, RCR1);  /* Clear CF-bit */
  25. sec128 = ctrl_inb(R64CNT);
  26. sec = ctrl_inb(RSECCNT);
  27. min = ctrl_inb(RMINCNT);
  28. hr  = ctrl_inb(RHRCNT);
  29. wk  = ctrl_inb(RWKCNT);
  30. day = ctrl_inb(RDAYCNT);
  31. mon = ctrl_inb(RMONCNT);
  32. #if defined(__SH4__)
  33. yr  = ctrl_inw(RYRCNT);
  34. yr100 = (yr >> 8);
  35. yr &= 0xff;
  36. #else
  37. yr  = ctrl_inb(RYRCNT);
  38. yr100 = (yr == 0x99) ? 0x19 : 0x20;
  39. #endif
  40. } while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
  41. #if RTC_BIT_INVERTED != 0
  42. /* Work around to avoid reading incorrect value. */
  43. if (sec128 == RTC_BIT_INVERTED) {
  44. schedule_timeout(1);
  45. goto again;
  46. }
  47. #endif
  48. BCD_TO_BIN(yr100);
  49. BCD_TO_BIN(yr);
  50. BCD_TO_BIN(mon);
  51. BCD_TO_BIN(day);
  52. BCD_TO_BIN(hr);
  53. BCD_TO_BIN(min);
  54. BCD_TO_BIN(sec);
  55. if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
  56.     hr > 23 || min > 59 || sec > 59) {
  57. printk(KERN_ERR
  58.        "SH RTC: invalid value, resetting to 1 Jan 2000n");
  59. ctrl_outb(RCR2_RESET, RCR2);  /* Reset & Stop */
  60. ctrl_outb(0, RSECCNT);
  61. ctrl_outb(0, RMINCNT);
  62. ctrl_outb(0, RHRCNT);
  63. ctrl_outb(6, RWKCNT);
  64. ctrl_outb(1, RDAYCNT);
  65. ctrl_outb(1, RMONCNT);
  66. #if defined(__SH4__)
  67. ctrl_outw(0x2000, RYRCNT);
  68. #else
  69. ctrl_outb(0, RYRCNT);
  70. #endif
  71. ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start */
  72. goto again;
  73. }
  74. #if RTC_BIT_INVERTED != 0
  75. if ((sec128 & RTC_BIT_INVERTED))
  76. sec--;
  77. #endif
  78. tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
  79. tv->tv_usec = (sec128 * 1000000) / 128;
  80. }
  81. int sh_rtc_settimeofday(const struct timeval *tv)
  82. {
  83. unsigned long nowtime = tv->tv_sec;
  84. int retval = 0;
  85. int real_seconds, real_minutes, cmos_minutes;
  86. ctrl_outb(RCR2_RESET, RCR2);  /* Reset pre-scaler & stop RTC */
  87. cmos_minutes = ctrl_inb(RMINCNT);
  88. BCD_TO_BIN(cmos_minutes);
  89. /*
  90.  * since we're only adjusting minutes and seconds,
  91.  * don't interfere with hour overflow. This avoids
  92.  * messing with unknown time zones but requires your
  93.  * RTC not to be off by more than 15 minutes
  94.  */
  95. real_seconds = nowtime % 60;
  96. real_minutes = nowtime / 60;
  97. if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
  98. real_minutes += 30; /* correct for half hour time zone */
  99. real_minutes %= 60;
  100. if (abs(real_minutes - cmos_minutes) < 30) {
  101. BIN_TO_BCD(real_seconds);
  102. BIN_TO_BCD(real_minutes);
  103. ctrl_outb(real_seconds, RSECCNT);
  104. ctrl_outb(real_minutes, RMINCNT);
  105. } else {
  106. printk(KERN_WARNING
  107.        "set_rtc_time: can't update from %d to %dn",
  108.        cmos_minutes, real_minutes);
  109. retval = -1;
  110. }
  111. ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start RTC */
  112. return retval;
  113. }