devrtc.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * M48T59/559 Timekeeper
  3. */
  4. #include "u.h"
  5. #include "../port/lib.h"
  6. #include "mem.h"
  7. #include "dat.h"
  8. #include "fns.h"
  9. #include "../port/error.h"
  10. #include "io.h"
  11. enum{
  12. STB0 = 0x74,
  13. STB1 = 0x75,
  14. Data = 0x77,
  15. NVOFF= 0,
  16. NVLEN= 0x1ff0, /* length in bytes of NV RAM */
  17. /*
  18. * register offsets into time of day clock
  19. */
  20. NVflags= 0x1ff0,
  21. NVwatchdog= 0x1ff7,
  22. NVctl= 0x1ff8,
  23. NVsec,
  24. NVmin,
  25. NVhour,
  26. NVday, /* (1 = Sun) */
  27. NVmday, /* (1-31) */
  28. NVmon, /* (1-12) */
  29. NVyear, /* (0-99) */
  30. /* NVctl */
  31. RTwrite = (1<<7),
  32. RTread = (1<<6),
  33. RTsign = (1<<5),
  34. RTcal = 0x1f,
  35. /* NVwatchdog */
  36. WDsteer = (1<<7), /* 0 -> intr, 1 -> reset */
  37. WDmult = (1<<2), /* 5 bits of multiplier */
  38. WDres0 = (0<<0), /* 1/16 sec resolution */
  39. WDres1 = (1<<0), /* 1/4 sec resolution */
  40. WDres2 = (2<<0), /* 1 sec resolution */
  41. WDres3 = (3<<0), /* 4 sec resolution */
  42. Qdir = 0,
  43. Qrtc,
  44. Qnvram,
  45. };
  46. /*
  47. * broken down time
  48. */
  49. typedef struct
  50. {
  51. int sec;
  52. int min;
  53. int hour;
  54. int mday;
  55. int mon;
  56. int year;
  57. } Rtc;
  58. QLock rtclock; /* mutex on nvram operations */
  59. static Dirtab rtcdir[]={
  60. ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
  61. "rtc", {Qrtc, 0}, 0, 0644,
  62. "nvram", {Qnvram, 0}, 0, 0600,
  63. };
  64. static ulong rtc2sec(Rtc*);
  65. static void sec2rtc(ulong, Rtc*);
  66. static void setrtc(Rtc*);
  67. static void nvcksum(void);
  68. static void nvput(int, uchar);
  69. static uchar nvget(int);
  70. static Chan*
  71. rtcattach(char *spec)
  72. {
  73. return devattach('r', spec);
  74. }
  75. static Walkqid*
  76. rtcwalk(Chan *c, Chan *nc, char **name, int nname)
  77. {
  78. return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
  79. }
  80. static int
  81. rtcstat(Chan *c, uchar *dp, int n)
  82. {
  83. return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
  84. }
  85. static Chan*
  86. rtcopen(Chan *c, int omode)
  87. {
  88. omode = openmode(omode);
  89. switch((ulong)c->qid.path){
  90. case Qrtc:
  91. if(strcmp(up->user, eve)!=0 && omode!=OREAD)
  92. error(Eperm);
  93. break;
  94. case Qnvram:
  95. if(strcmp(up->user, eve)!=0 || !cpuserver)
  96. error(Eperm);
  97. }
  98. return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
  99. }
  100. static void
  101. rtcclose(Chan*)
  102. {
  103. }
  104. static long
  105. rtcread(Chan *c, void *buf, long n, vlong off)
  106. {
  107. char *p;
  108. ulong t;
  109. int i;
  110. ulong offset = off;
  111. if(c->qid.type & QTDIR)
  112. return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
  113. switch((ulong)c->qid.path){
  114. case Qrtc:
  115. qlock(&rtclock);
  116. t = rtctime();
  117. qunlock(&rtclock);
  118. n = readnum(offset, buf, n, t, 12);
  119. return n;
  120. case Qnvram:
  121. offset += NVOFF;
  122. if(offset > NVLEN)
  123. return 0;
  124. if(n > NVLEN - offset)
  125. n = NVLEN - offset;
  126. p = buf;
  127. qlock(&rtclock);
  128. for(i = 0; i < n; i++)
  129. p[i] = nvget(i+offset);
  130. qunlock(&rtclock);
  131. return n;
  132. }
  133. error(Egreg);
  134. return -1; /* never reached */
  135. }
  136. static long
  137. rtcwrite(Chan *c, void *buf, long n, vlong off)
  138. {
  139. Rtc rtc;
  140. ulong secs;
  141. char *cp, *ep;
  142. int i;
  143. ulong offset = off;
  144. switch((ulong)c->qid.path){
  145. case Qrtc:
  146. if(offset!=0)
  147. error(Ebadarg);
  148. /*
  149. * read the time
  150. */
  151. cp = ep = buf;
  152. ep += n;
  153. while(cp < ep){
  154. if(*cp>='0' && *cp<='9')
  155. break;
  156. cp++;
  157. }
  158. secs = strtoul(cp, 0, 0);
  159. /*
  160. * convert to bcd
  161. */
  162. sec2rtc(secs, &rtc);
  163. /*
  164. * write it
  165. */
  166. qlock(&rtclock);
  167. setrtc(&rtc);
  168. qunlock(&rtclock);
  169. return n;
  170. case Qnvram:
  171. offset += NVOFF;
  172. if(offset > NVLEN)
  173. return 0;
  174. if(n > NVLEN - offset)
  175. n = NVLEN - offset;
  176. qlock(&rtclock);
  177. for(i = 0; i < n; i++)
  178. nvput(i+offset, ((uchar*)buf)[i]);
  179. nvcksum();
  180. qunlock(&rtclock);
  181. return n;
  182. }
  183. error(Egreg);
  184. return -1; /* never reached */
  185. }
  186. long
  187. rtcbwrite(Chan *c, Block *bp, ulong offset)
  188. {
  189. return devbwrite(c, bp, offset);
  190. }
  191. Dev rtcdevtab = {
  192. 'r',
  193. "rtc",
  194. devreset,
  195. devinit,
  196. devshutdown,
  197. rtcattach,
  198. rtcwalk,
  199. rtcstat,
  200. rtcopen,
  201. devcreate,
  202. rtcclose,
  203. rtcread,
  204. devbread,
  205. rtcwrite,
  206. devbwrite,
  207. devremove,
  208. devwstat,
  209. };
  210. static void
  211. nvput(int offset, uchar val)
  212. {
  213. outb(STB0, offset);
  214. outb(STB1, offset>>8);
  215. outb(Data, val);
  216. }
  217. static uchar
  218. nvget(int offset)
  219. {
  220. outb(STB0, offset);
  221. outb(STB1, offset>>8);
  222. return inb(Data);
  223. }
  224. static void
  225. nvcksum(void)
  226. {
  227. }
  228. void
  229. watchreset(void)
  230. {
  231. splhi();
  232. nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0);
  233. for(;;);
  234. }
  235. static int
  236. getbcd(int bcd)
  237. {
  238. return (bcd&0x0f) + 10 * (bcd>>4);
  239. }
  240. static int
  241. putbcd(int val)
  242. {
  243. return (val % 10) | (((val/10) % 10) << 4);
  244. }
  245. long
  246. rtctime(void)
  247. {
  248. int ctl;
  249. Rtc rtc;
  250. /*
  251. * convert from BCD
  252. */
  253. ctl = nvget(NVctl);
  254. ctl &= RTsign|RTcal;
  255. nvput(NVctl, ctl|RTread);
  256. rtc.sec = getbcd(nvget(NVsec) & 0x7f);
  257. rtc.min = getbcd(nvget(NVmin));
  258. rtc.hour = getbcd(nvget(NVhour));
  259. rtc.mday = getbcd(nvget(NVmday));
  260. rtc.mon = getbcd(nvget(NVmon));
  261. rtc.year = getbcd(nvget(NVyear));
  262. if(rtc.year < 70)
  263. rtc.year += 2000;
  264. else
  265. rtc.year += 1900;
  266. nvput(NVctl, ctl);
  267. return rtc2sec(&rtc);
  268. }
  269. static void
  270. setrtc(Rtc *rtc)
  271. {
  272. int ctl;
  273. ctl = nvget(NVctl);
  274. ctl &= RTsign|RTcal;
  275. nvput(NVctl, ctl|RTwrite);
  276. nvput(NVsec, putbcd(rtc->sec));
  277. nvput(NVmin, putbcd(rtc->min));
  278. nvput(NVhour, putbcd(rtc->hour));
  279. nvput(NVmday, putbcd(rtc->mday));
  280. nvput(NVmon, putbcd(rtc->mon));
  281. nvput(NVyear, putbcd(rtc->year % 100));
  282. nvput(NVctl, ctl);
  283. }
  284. #define SEC2MIN 60L
  285. #define SEC2HOUR (60L*SEC2MIN)
  286. #define SEC2DAY (24L*SEC2HOUR)
  287. /*
  288. * days per month plus days/year
  289. */
  290. static int dmsize[] =
  291. {
  292. 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  293. };
  294. static int ldmsize[] =
  295. {
  296. 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  297. };
  298. /*
  299. * return the days/month for the given year
  300. */
  301. static int *
  302. yrsize(int y)
  303. {
  304. if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
  305. return ldmsize;
  306. else
  307. return dmsize;
  308. }
  309. /*
  310. * compute seconds since Jan 1 1970
  311. */
  312. static ulong
  313. rtc2sec(Rtc *rtc)
  314. {
  315. ulong secs;
  316. int i;
  317. int *d2m;
  318. secs = 0;
  319. /*
  320. * seconds per year
  321. */
  322. for(i = 1970; i < rtc->year; i++){
  323. d2m = yrsize(i);
  324. secs += d2m[0] * SEC2DAY;
  325. }
  326. /*
  327. * seconds per month
  328. */
  329. d2m = yrsize(rtc->year);
  330. for(i = 1; i < rtc->mon; i++)
  331. secs += d2m[i] * SEC2DAY;
  332. secs += (rtc->mday-1) * SEC2DAY;
  333. secs += rtc->hour * SEC2HOUR;
  334. secs += rtc->min * SEC2MIN;
  335. secs += rtc->sec;
  336. return secs;
  337. }
  338. /*
  339. * compute rtc from seconds since Jan 1 1970
  340. */
  341. static void
  342. sec2rtc(ulong secs, Rtc *rtc)
  343. {
  344. int d;
  345. long hms, day;
  346. int *d2m;
  347. /*
  348. * break initial number into days
  349. */
  350. hms = secs % SEC2DAY;
  351. day = secs / SEC2DAY;
  352. if(hms < 0) {
  353. hms += SEC2DAY;
  354. day -= 1;
  355. }
  356. /*
  357. * generate hours:minutes:seconds
  358. */
  359. rtc->sec = hms % 60;
  360. d = hms / 60;
  361. rtc->min = d % 60;
  362. d /= 60;
  363. rtc->hour = d;
  364. /*
  365. * year number
  366. */
  367. if(day >= 0)
  368. for(d = 1970; day >= *yrsize(d); d++)
  369. day -= *yrsize(d);
  370. else
  371. for (d = 1970; day < 0; d--)
  372. day += *yrsize(d-1);
  373. rtc->year = d;
  374. /*
  375. * generate month
  376. */
  377. d2m = yrsize(rtc->year);
  378. for(d = 1; day >= d2m[d]; d++)
  379. day -= d2m[d];
  380. rtc->mday = day + 1;
  381. rtc->mon = d;
  382. return;
  383. }