hp_sdc_rtc.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * HP i8042 SDC + MSM-58321 BBRTC driver.
  3. *
  4. * Copyright (c) 2001 Brian S. Julin
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions, and the following disclaimer,
  12. * without modification.
  13. * 2. The name of the author may not be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * Alternatively, this software may be distributed under the terms of the
  17. * GNU General Public License ("GPL").
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  20. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  23. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  25. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  28. *
  29. * References:
  30. * System Device Controller Microprocessor Firmware Theory of Operation
  31. * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2
  32. * efirtc.c by Stephane Eranian/Hewlett Packard
  33. *
  34. */
  35. #include <linux/hp_sdc.h>
  36. #include <linux/errno.h>
  37. #include <linux/types.h>
  38. #include <linux/init.h>
  39. #include <linux/module.h>
  40. #include <linux/time.h>
  41. #include <linux/miscdevice.h>
  42. #include <linux/proc_fs.h>
  43. #include <linux/seq_file.h>
  44. #include <linux/poll.h>
  45. #include <linux/rtc.h>
  46. #include <linux/mutex.h>
  47. #include <linux/semaphore.h>
  48. MODULE_AUTHOR("Brian S. Julin <[email protected]>");
  49. MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
  50. MODULE_LICENSE("Dual BSD/GPL");
  51. #define RTC_VERSION "1.10d"
  52. static unsigned long epoch = 2000;
  53. static struct semaphore i8042tregs;
  54. static void hp_sdc_rtc_isr (int irq, void *dev_id,
  55. uint8_t status, uint8_t data)
  56. {
  57. return;
  58. }
  59. static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
  60. {
  61. struct semaphore tsem;
  62. hp_sdc_transaction t;
  63. uint8_t tseq[91];
  64. int i;
  65. i = 0;
  66. while (i < 91) {
  67. tseq[i++] = HP_SDC_ACT_DATAREG |
  68. HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
  69. tseq[i++] = 0x01; /* write i8042[0x70] */
  70. tseq[i] = i / 7; /* BBRTC reg address */
  71. i++;
  72. tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */
  73. tseq[i++] = 2; /* expect 1 stat/dat pair back. */
  74. i++; i++; /* buffer for stat/dat pair */
  75. }
  76. tseq[84] |= HP_SDC_ACT_SEMAPHORE;
  77. t.endidx = 91;
  78. t.seq = tseq;
  79. t.act.semaphore = &tsem;
  80. sema_init(&tsem, 0);
  81. if (hp_sdc_enqueue_transaction(&t)) return -1;
  82. /* Put ourselves to sleep for results. */
  83. if (WARN_ON(down_interruptible(&tsem)))
  84. return -1;
  85. /* Check for nonpresence of BBRTC */
  86. if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
  87. tseq[55] | tseq[62] | tseq[34] | tseq[41] |
  88. tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f))
  89. return -1;
  90. memset(rtctm, 0, sizeof(struct rtc_time));
  91. rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
  92. rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
  93. rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
  94. rtctm->tm_wday = (tseq[48] & 0x0f);
  95. rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
  96. rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
  97. rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10;
  98. return 0;
  99. }
  100. static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
  101. {
  102. struct rtc_time tm, tm_last;
  103. int i = 0;
  104. /* MSM-58321 has no read latch, so must read twice and compare. */
  105. if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
  106. if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
  107. while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
  108. if (i++ > 4) return -1;
  109. memcpy(&tm_last, &tm, sizeof(struct rtc_time));
  110. if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
  111. }
  112. memcpy(rtctm, &tm, sizeof(struct rtc_time));
  113. return 0;
  114. }
  115. static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
  116. {
  117. hp_sdc_transaction t;
  118. uint8_t tseq[26] = {
  119. HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
  120. 0,
  121. HP_SDC_CMD_READ_T1, 2, 0, 0,
  122. HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
  123. HP_SDC_CMD_READ_T2, 2, 0, 0,
  124. HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
  125. HP_SDC_CMD_READ_T3, 2, 0, 0,
  126. HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
  127. HP_SDC_CMD_READ_T4, 2, 0, 0,
  128. HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
  129. HP_SDC_CMD_READ_T5, 2, 0, 0
  130. };
  131. t.endidx = numreg * 5;
  132. tseq[1] = loadcmd;
  133. tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
  134. t.seq = tseq;
  135. t.act.semaphore = &i8042tregs;
  136. /* Sleep if output regs in use. */
  137. if (WARN_ON(down_interruptible(&i8042tregs)))
  138. return -1;
  139. if (hp_sdc_enqueue_transaction(&t)) {
  140. up(&i8042tregs);
  141. return -1;
  142. }
  143. /* Sleep until results come back. */
  144. if (WARN_ON(down_interruptible(&i8042tregs)))
  145. return -1;
  146. up(&i8042tregs);
  147. return (tseq[5] |
  148. ((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) |
  149. ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
  150. }
  151. /* Read the i8042 real-time clock */
  152. static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
  153. int64_t raw;
  154. uint32_t tenms;
  155. unsigned int days;
  156. raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
  157. if (raw < 0) return -1;
  158. tenms = (uint32_t)raw & 0xffffff;
  159. days = (unsigned int)(raw >> 24) & 0xffff;
  160. res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
  161. res->tv_sec = (tenms / 100) + (time64_t)days * 86400;
  162. return 0;
  163. }
  164. /* Read the i8042 fast handshake timer */
  165. static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
  166. int64_t raw;
  167. unsigned int tenms;
  168. raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
  169. if (raw < 0) return -1;
  170. tenms = (unsigned int)raw & 0xffff;
  171. res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
  172. res->tv_sec = (time64_t)(tenms / 100);
  173. return 0;
  174. }
  175. /* Read the i8042 match timer (a.k.a. alarm) */
  176. static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
  177. int64_t raw;
  178. uint32_t tenms;
  179. raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
  180. if (raw < 0) return -1;
  181. tenms = (uint32_t)raw & 0xffffff;
  182. res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
  183. res->tv_sec = (time64_t)(tenms / 100);
  184. return 0;
  185. }
  186. /* Read the i8042 delay timer */
  187. static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
  188. int64_t raw;
  189. uint32_t tenms;
  190. raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
  191. if (raw < 0) return -1;
  192. tenms = (uint32_t)raw & 0xffffff;
  193. res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
  194. res->tv_sec = (time64_t)(tenms / 100);
  195. return 0;
  196. }
  197. /* Read the i8042 cycle timer (a.k.a. periodic) */
  198. static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
  199. int64_t raw;
  200. uint32_t tenms;
  201. raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
  202. if (raw < 0) return -1;
  203. tenms = (uint32_t)raw & 0xffffff;
  204. res->tv_nsec = (long)(tenms % 100) * 10000 * 1000;
  205. res->tv_sec = (time64_t)(tenms / 100);
  206. return 0;
  207. }
  208. static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
  209. {
  210. #define YN(bit) ("no")
  211. #define NY(bit) ("yes")
  212. struct rtc_time tm;
  213. struct timespec64 tv;
  214. memset(&tm, 0, sizeof(struct rtc_time));
  215. if (hp_sdc_rtc_read_bbrtc(&tm)) {
  216. seq_puts(m, "BBRTC\t\t: READ FAILED!\n");
  217. } else {
  218. seq_printf(m,
  219. "rtc_time\t: %ptRt\n"
  220. "rtc_date\t: %ptRd\n"
  221. "rtc_epoch\t: %04lu\n",
  222. &tm, &tm, epoch);
  223. }
  224. if (hp_sdc_rtc_read_rt(&tv)) {
  225. seq_puts(m, "i8042 rtc\t: READ FAILED!\n");
  226. } else {
  227. seq_printf(m, "i8042 rtc\t: %lld.%02ld seconds\n",
  228. (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
  229. }
  230. if (hp_sdc_rtc_read_fhs(&tv)) {
  231. seq_puts(m, "handshake\t: READ FAILED!\n");
  232. } else {
  233. seq_printf(m, "handshake\t: %lld.%02ld seconds\n",
  234. (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
  235. }
  236. if (hp_sdc_rtc_read_mt(&tv)) {
  237. seq_puts(m, "alarm\t\t: READ FAILED!\n");
  238. } else {
  239. seq_printf(m, "alarm\t\t: %lld.%02ld seconds\n",
  240. (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
  241. }
  242. if (hp_sdc_rtc_read_dt(&tv)) {
  243. seq_puts(m, "delay\t\t: READ FAILED!\n");
  244. } else {
  245. seq_printf(m, "delay\t\t: %lld.%02ld seconds\n",
  246. (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
  247. }
  248. if (hp_sdc_rtc_read_ct(&tv)) {
  249. seq_puts(m, "periodic\t: READ FAILED!\n");
  250. } else {
  251. seq_printf(m, "periodic\t: %lld.%02ld seconds\n",
  252. (s64)tv.tv_sec, (long)tv.tv_nsec/1000000L);
  253. }
  254. seq_printf(m,
  255. "DST_enable\t: %s\n"
  256. "BCD\t\t: %s\n"
  257. "24hr\t\t: %s\n"
  258. "square_wave\t: %s\n"
  259. "alarm_IRQ\t: %s\n"
  260. "update_IRQ\t: %s\n"
  261. "periodic_IRQ\t: %s\n"
  262. "periodic_freq\t: %ld\n"
  263. "batt_status\t: %s\n",
  264. YN(RTC_DST_EN),
  265. NY(RTC_DM_BINARY),
  266. YN(RTC_24H),
  267. YN(RTC_SQWE),
  268. YN(RTC_AIE),
  269. YN(RTC_UIE),
  270. YN(RTC_PIE),
  271. 1UL,
  272. 1 ? "okay" : "dead");
  273. return 0;
  274. #undef YN
  275. #undef NY
  276. }
  277. static int __init hp_sdc_rtc_init(void)
  278. {
  279. int ret;
  280. #ifdef __mc68000__
  281. if (!MACH_IS_HP300)
  282. return -ENODEV;
  283. #endif
  284. sema_init(&i8042tregs, 1);
  285. if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
  286. return ret;
  287. proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show);
  288. printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
  289. "(RTC v " RTC_VERSION ")\n");
  290. return 0;
  291. }
  292. static void __exit hp_sdc_rtc_exit(void)
  293. {
  294. remove_proc_entry ("driver/rtc", NULL);
  295. hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
  296. printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
  297. }
  298. module_init(hp_sdc_rtc_init);
  299. module_exit(hp_sdc_rtc_exit);