rtc.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * Real Time Clock interface for PPC64.
  3.  *
  4.  * Based on rtc.c by Paul Gortmaker
  5.  *
  6.  * This driver allows use of the real time clock
  7.  * from user space. It exports the /dev/rtc
  8.  * interface supporting various ioctl() and also the
  9.  * /proc/driver/rtc pseudo-file for status information.
  10.  *
  11.  *  Interface does not support RTC interrupts nor an alarm.
  12.  *
  13.  * This program is free software; you can redistribute it and/or
  14.  * modify it under the terms of the GNU General Public License
  15.  * as published by the Free Software Foundation; either version
  16.  * 2 of the License, or (at your option) any later version.
  17.  *
  18.  *      1.0 Mike Corrigan:    IBM iSeries rtc support
  19.  *      1.1 Dave Engebretsen: IBM pSeries rtc support
  20.  */
  21. #define RTC_VERSION "1.1"
  22. #include <linux/module.h>
  23. #include <linux/kernel.h>
  24. #include <linux/types.h>
  25. #include <linux/miscdevice.h>
  26. #include <linux/ioport.h>
  27. #include <linux/fcntl.h>
  28. #include <linux/mc146818rtc.h>
  29. #include <linux/init.h>
  30. #include <linux/poll.h>
  31. #include <linux/proc_fs.h>
  32. #include <linux/spinlock.h>
  33. #include <asm/io.h>
  34. #include <asm/uaccess.h>
  35. #include <asm/system.h>
  36. #include <asm/time.h>
  37. #include <asm/iSeries/LparData.h>
  38. #include <asm/iSeries/mf.h>
  39. #include <asm/machdep.h>
  40. #include <asm/iSeries/ItSpCommArea.h>
  41. extern int piranha_simulator;
  42. /*
  43.  * We sponge a minor off of the misc major. No need slurping
  44.  * up another valuable major dev number for this. If you add
  45.  * an ioctl, make sure you don't conflict with SPARC's RTC
  46.  * ioctls.
  47.  */
  48. static loff_t rtc_llseek(struct file *file, loff_t offset, int origin);
  49. static ssize_t rtc_read(struct file *file, char *buf,
  50. size_t count, loff_t *ppos);
  51. static int rtc_ioctl(struct inode *inode, struct file *file,
  52.      unsigned int cmd, unsigned long arg);
  53. static int rtc_read_proc(char *page, char **start, off_t off,
  54.                          int count, int *eof, void *data);
  55. /*
  56.  * If this driver ever becomes modularised, it will be really nice
  57.  * to make the epoch retain its value across module reload...
  58.  */
  59. static unsigned long epoch = 1900; /* year corresponding to 0x00 */
  60. static const unsigned char days_in_mo[] = 
  61. {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  62. /*
  63.  * Now all the various file operations that we export.
  64.  */
  65. static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
  66. {
  67. return -ESPIPE;
  68. }
  69. static ssize_t rtc_read(struct file *file, char *buf,
  70. size_t count, loff_t *ppos)
  71. {
  72. return -EIO;
  73. }
  74. static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  75.      unsigned long arg)
  76. {
  77. struct rtc_time wtime; 
  78. switch (cmd) {
  79. case RTC_RD_TIME: /* Read the time/date from RTC */
  80. {
  81. ppc_md.get_rtc_time(&wtime);
  82. break;
  83. }
  84. case RTC_SET_TIME: /* Set the RTC */
  85. {
  86. struct rtc_time rtc_tm;
  87. unsigned char mon, day, hrs, min, sec, leap_yr;
  88. unsigned int yrs;
  89. if (!capable(CAP_SYS_TIME))
  90. return -EACCES;
  91. if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
  92.    sizeof(struct rtc_time)))
  93. return -EFAULT;
  94. yrs = rtc_tm.tm_year;
  95. mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
  96. day = rtc_tm.tm_mday;
  97. hrs = rtc_tm.tm_hour;
  98. min = rtc_tm.tm_min;
  99. sec = rtc_tm.tm_sec;
  100. if (yrs < 70)
  101. return -EINVAL;
  102. leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
  103. if ((mon > 12) || (day == 0))
  104. return -EINVAL;
  105. if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
  106. return -EINVAL;
  107. if ((hrs >= 24) || (min >= 60) || (sec >= 60))
  108. return -EINVAL;
  109. if ( yrs > 169 )
  110. return -EINVAL;
  111. ppc_md.set_rtc_time(&rtc_tm);
  112. return 0;
  113. }
  114. case RTC_EPOCH_READ: /* Read the epoch. */
  115. {
  116. return put_user (epoch, (unsigned long *)arg);
  117. }
  118. case RTC_EPOCH_SET: /* Set the epoch. */
  119. {
  120. /* 
  121.  * There were no RTC clocks before 1900.
  122.  */
  123. if (arg < 1900)
  124. return -EINVAL;
  125. if (!capable(CAP_SYS_TIME))
  126. return -EACCES;
  127. epoch = arg;
  128. return 0;
  129. }
  130. default:
  131. return -EINVAL;
  132. }
  133. return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
  134. }
  135. static int rtc_open(struct inode *inode, struct file *file)
  136. {
  137. return 0;
  138. }
  139. static int rtc_release(struct inode *inode, struct file *file)
  140. {
  141. return 0;
  142. }
  143. /*
  144.  * The various file operations we support.
  145.  */
  146. static struct file_operations rtc_fops = {
  147. owner: THIS_MODULE,
  148. llseek: rtc_llseek,
  149. read: rtc_read,
  150. ioctl: rtc_ioctl,
  151. open: rtc_open,
  152. release: rtc_release,
  153. };
  154. static struct miscdevice rtc_dev=
  155. {
  156. RTC_MINOR,
  157. "rtc",
  158. &rtc_fops
  159. };
  160. static int __init rtc_init(void)
  161. {
  162. misc_register(&rtc_dev);
  163. create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
  164. printk(KERN_INFO "i/pSeries Real Time Clock Driver v" RTC_VERSION "n");
  165. return 0;
  166. }
  167. static void __exit rtc_exit (void)
  168. {
  169. remove_proc_entry ("driver/rtc", NULL);
  170. misc_deregister(&rtc_dev);
  171. }
  172. module_init(rtc_init);
  173. module_exit(rtc_exit);
  174. EXPORT_NO_SYMBOLS;
  175. /*
  176.  * Info exported via "/proc/driver/rtc".
  177.  */
  178. static int rtc_proc_output (char *buf)
  179. {
  180. char *p;
  181. struct rtc_time tm;
  182. p = buf;
  183. ppc_md.get_rtc_time(&tm);
  184. /*
  185.  * There is no way to tell if the luser has the RTC set for local
  186.  * time or for Universal Standard Time (GMT). Probably local though.
  187.  */
  188. p += sprintf(p,
  189.      "rtc_timet: %02d:%02d:%02dn"
  190.      "rtc_datet: %04d-%02d-%02dn"
  191.        "rtc_epocht: %04lun",
  192.      tm.tm_hour, tm.tm_min, tm.tm_sec,
  193.      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
  194. p += sprintf(p,
  195.      "DST_enablet: non"
  196.      "BCDtt: yesn"
  197.      "24hrtt: yesn" );
  198. return  p - buf;
  199. }
  200. static int rtc_read_proc(char *page, char **start, off_t off,
  201.                          int count, int *eof, void *data)
  202. {
  203.         int len = rtc_proc_output (page);
  204.         if (len <= off+count) *eof = 1;
  205.         *start = page + off;
  206.         len -= off;
  207.         if (len>count) len = count;
  208.         if (len<0) len = 0;
  209.         return len;
  210. }
  211. /*
  212.  * Get the RTC from the virtual service processor
  213.  * This requires flowing LpEvents to the primary partition
  214.  */
  215. void iSeries_get_rtc_time(struct rtc_time *rtc_tm)
  216. {
  217. if (piranha_simulator)
  218. return;
  219. mf_getRtc(rtc_tm);
  220. rtc_tm->tm_mon--;
  221. }
  222. void pSeries_get_rtc_time(struct rtc_time *rtc_tm)
  223. {
  224.         unsigned long ret[8];
  225.         int error;
  226. int count;
  227. /*
  228.  * error -2 is clock busy, we keep retrying a few times to see
  229.  * if it will come good  -- paulus
  230.  */
  231. count = 0;
  232. do {
  233. error = rtas_call(rtas_token("get-time-of-day"), 0, 8, (void *)&ret);
  234. } while (error == -2 && ++count < 1000);
  235.         if (error != 0) {
  236.                 printk(KERN_WARNING "error: reading the clock failed (%d)n",
  237.        error);
  238. return;
  239.         }
  240. rtc_tm->tm_sec = ret[5];
  241. rtc_tm->tm_min = ret[4];
  242. rtc_tm->tm_hour = ret[3];
  243. rtc_tm->tm_mday = ret[2];
  244. rtc_tm->tm_mon = ret[1] - 1;
  245. rtc_tm->tm_year = ret[0] - 1900;
  246. }
  247. int pSeries_set_rtc_time(struct rtc_time *tm)
  248. {
  249.         int error;
  250. int count;
  251. /*
  252.  * error -2 is clock busy, we keep retrying a few times to see
  253.  * if it will come good  -- paulus
  254.  */
  255. count = 0;
  256. do {
  257.         error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL,
  258.   tm->tm_year + 1900, tm->tm_mon + 1, 
  259.   tm->tm_mday, tm->tm_hour, tm->tm_min, 
  260.   tm->tm_sec, 0);
  261. } while (error == -2 && ++count < 1000);
  262.         if (error != 0)
  263.                 printk(KERN_WARNING "error: setting the clock failed (%d)n",
  264.        error); 
  265.         return 0;
  266. }
  267. /*
  268.  * Set the RTC in the virtual service processor
  269.  * This requires flowing LpEvents to the primary partition
  270.  */
  271. int iSeries_set_rtc_time(struct rtc_time *tm)
  272. {
  273. mf_setRtc(tm);
  274. return 0;
  275. }
  276. void iSeries_get_boot_time(struct rtc_time *tm)
  277. {
  278. unsigned long time;
  279. static unsigned long lastsec = 1;
  280. u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart));
  281. u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1);
  282. int year = 1970;
  283. int year1 = ( dataWord1 >> 24 ) & 0x000000FF;
  284. int year2 = ( dataWord1 >> 16 ) & 0x000000FF;
  285. int sec = ( dataWord1 >> 8 ) & 0x000000FF;
  286. int min = dataWord1 & 0x000000FF;
  287. int hour = ( dataWord2 >> 24 ) & 0x000000FF;
  288. int day = ( dataWord2 >> 8 ) & 0x000000FF;
  289. int mon = dataWord2 & 0x000000FF;
  290. if ( piranha_simulator )
  291. return;
  292. BCD_TO_BIN(sec);
  293. BCD_TO_BIN(min);
  294. BCD_TO_BIN(hour);
  295. BCD_TO_BIN(day);
  296. BCD_TO_BIN(mon);
  297. BCD_TO_BIN(year1);
  298. BCD_TO_BIN(year2);
  299. year = year1 * 100 + year2;
  300. time = mktime(year, mon, day, hour, min, sec);
  301. time += ( jiffies / HZ );
  302. /* Now THIS is a nasty hack!
  303. * It ensures that the first two calls get different answers.  
  304. * That way the loop in init_time (time.c) will not think
  305. * the clock is stuck.
  306. */
  307. if ( lastsec ) {
  308. time -= lastsec;
  309. --lastsec;
  310. }
  311. to_tm(time, tm); 
  312. tm->tm_year -= 1900;
  313. tm->tm_mon  -= 1;
  314. }