rtc.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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 timespec *ts)
  20. {
  21. unsigned int sec128, sec, sec2, min, hr, wk, day, mon, yr, yr100, cf_bit;
  22. unsigned long flags;
  23. again:
  24. do {
  25. local_irq_save(flags);
  26. ctrl_outb(0, RCR1); /* Clear CF-bit */
  27. sec128 = ctrl_inb(R64CNT);
  28. sec = ctrl_inb(RSECCNT);
  29. min = ctrl_inb(RMINCNT);
  30. hr = ctrl_inb(RHRCNT);
  31. wk = ctrl_inb(RWKCNT);
  32. day = ctrl_inb(RDAYCNT);
  33. mon = ctrl_inb(RMONCNT);
  34. #if defined(CONFIG_CPU_SH4)
  35. yr = ctrl_inw(RYRCNT);
  36. yr100 = (yr >> 8);
  37. yr &= 0xff;
  38. #else
  39. yr = ctrl_inb(RYRCNT);
  40. yr100 = (yr == 0x99) ? 0x19 : 0x20;
  41. #endif
  42. sec2 = ctrl_inb(R64CNT);
  43. cf_bit = ctrl_inb(RCR1) & RCR1_CF;
  44. local_irq_restore(flags);
  45. } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
  46. BCD_TO_BIN(yr100);
  47. BCD_TO_BIN(yr);
  48. BCD_TO_BIN(mon);
  49. BCD_TO_BIN(day);
  50. BCD_TO_BIN(hr);
  51. BCD_TO_BIN(min);
  52. BCD_TO_BIN(sec);
  53. if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
  54. hr > 23 || min > 59 || sec > 59) {
  55. printk(KERN_ERR
  56. "SH RTC: invalid value, resetting to 1 Jan 2000\n");
  57. local_irq_save(flags);
  58. ctrl_outb(RCR2_RESET, RCR2); /* Reset & Stop */
  59. ctrl_outb(0, RSECCNT);
  60. ctrl_outb(0, RMINCNT);
  61. ctrl_outb(0, RHRCNT);
  62. ctrl_outb(6, RWKCNT);
  63. ctrl_outb(1, RDAYCNT);
  64. ctrl_outb(1, RMONCNT);
  65. #if defined(CONFIG_CPU_SH4)
  66. ctrl_outw(0x2000, RYRCNT);
  67. #else
  68. ctrl_outb(0, RYRCNT);
  69. #endif
  70. ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start */
  71. goto again;
  72. }
  73. #if RTC_BIT_INVERTED != 0
  74. if ((sec128 & RTC_BIT_INVERTED))
  75. sec--;
  76. #endif
  77. ts->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
  78. ts->tv_nsec = ((sec128 * 1000000) / 128) * 1000;
  79. }
  80. /*
  81. * Changed to only care about tv_sec, and not the full timespec struct
  82. * (i.e. tv_nsec). It can easily be switched to timespec for future cpus
  83. * that support setting usec or nsec RTC values.
  84. */
  85. int sh_rtc_settimeofday(const time_t secs)
  86. {
  87. int retval = 0;
  88. int real_seconds, real_minutes, cmos_minutes;
  89. unsigned long flags;
  90. local_irq_save(flags);
  91. ctrl_outb(RCR2_RESET, RCR2); /* Reset pre-scaler & stop RTC */
  92. cmos_minutes = ctrl_inb(RMINCNT);
  93. BCD_TO_BIN(cmos_minutes);
  94. /*
  95. * since we're only adjusting minutes and seconds,
  96. * don't interfere with hour overflow. This avoids
  97. * messing with unknown time zones but requires your
  98. * RTC not to be off by more than 15 minutes
  99. */
  100. real_seconds = secs % 60;
  101. real_minutes = secs / 60;
  102. if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
  103. real_minutes += 30; /* correct for half hour time zone */
  104. real_minutes %= 60;
  105. if (abs(real_minutes - cmos_minutes) < 30) {
  106. BIN_TO_BCD(real_seconds);
  107. BIN_TO_BCD(real_minutes);
  108. ctrl_outb(real_seconds, RSECCNT);
  109. ctrl_outb(real_minutes, RMINCNT);
  110. } else {
  111. printk(KERN_WARNING
  112. "set_rtc_time: can't update from %d to %d\n",
  113. cmos_minutes, real_minutes);
  114. retval = -1;
  115. }
  116. ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start RTC */
  117. local_irq_restore(flags);
  118. return retval;
  119. }