1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "io.h"
9
10 /*
11 * SA11x0 real time clock
12 * TO DO: alarms, wakeup, allow trim setting(?)
13 */
14
15 enum{
16 Qdir,
17 Qrtc,
18 Qrtctrim,
19 };
20
21 static Dirtab rtcdir[]={
22 ".", {Qdir,0,QTDIR}, 0, 0555,
23 "rtc", {Qrtc}, NUMSIZE, 0664,
24 "rtctrim", {Qrtctrim}, 0, 0664,
25 };
26 #define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0]))
27
28 extern ulong boottime;
29
30 enum {
31 RTSR_al= 1<<0, /* RTC alarm detected */
32 RTSR_hz= 1<<1, /* 1-Hz rising-edge detected */
33 RTSR_ale= 1<<2, /* RTC alarm interrupt enabled */
34 RTSR_hze= 1<<3, /* 1-Hz interrupt enable */
35 };
36
37 static void
rtcreset(void)38 rtcreset(void)
39 {
40 RtcReg *r;
41
42 r = RTCREG;
43 if((r->rttr & 0xFFFF) == 0){ /* reset state */
44 r->rttr = 32768-1;
45 r->rcnr = boottime; /* typically zero */
46 }
47 r->rtar = ~0;
48 r->rtsr = RTSR_al | RTSR_hz;
49 }
50
51 static Chan*
rtcattach(char * spec)52 rtcattach(char *spec)
53 {
54 return devattach('r', spec);
55 }
56
57 static Walkqid*
rtcwalk(Chan * c,Chan * nc,char ** name,int nname)58 rtcwalk(Chan *c, Chan *nc, char **name, int nname)
59 {
60 return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
61 }
62
63 static int
rtcstat(Chan * c,uchar * dp,int n)64 rtcstat(Chan *c, uchar *dp, int n)
65 {
66 return devstat(c, dp, n, rtcdir, NRTC, devgen);
67 }
68
69 static Chan*
rtcopen(Chan * c,int omode)70 rtcopen(Chan *c, int omode)
71 {
72 return devopen(c, omode, rtcdir, NRTC, devgen);
73 }
74
75 static void
rtcclose(Chan *)76 rtcclose(Chan*)
77 {
78 }
79
80 static long
rtcread(Chan * c,void * buf,long n,vlong off)81 rtcread(Chan *c, void *buf, long n, vlong off)
82 {
83 if(c->qid.type & QTDIR)
84 return devdirread(c, buf, n, rtcdir, NRTC, devgen);
85
86 switch((ulong)c->qid.path){
87 case Qrtc:
88 return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE);
89 case Qrtctrim:
90 return readnum(off, buf, n, RTCREG->rttr, NUMSIZE);
91 }
92 error(Egreg);
93 return 0; /* not reached */
94 }
95
96 static long
rtcwrite(Chan * c,void * buf,long n,vlong off)97 rtcwrite(Chan *c, void *buf, long n, vlong off)
98 {
99 ulong offset = off;
100 ulong secs;
101 char *cp, sbuf[32];
102
103 switch((ulong)c->qid.path){
104 case Qrtc:
105 /*
106 * write the time
107 */
108 if(offset != 0 || n >= sizeof(sbuf)-1)
109 error(Ebadarg);
110 memmove(sbuf, buf, n);
111 sbuf[n] = '\0';
112 cp = sbuf;
113 while(*cp){
114 if(*cp>='0' && *cp<='9')
115 break;
116 cp++;
117 }
118 secs = strtoul(cp, 0, 0);
119 RTCREG->rcnr = secs;
120 return n;
121
122 case Qrtctrim:
123 if(offset != 0 || n >= sizeof(sbuf)-1)
124 error(Ebadarg);
125 memmove(sbuf, buf, n);
126 sbuf[n] = '\0';
127 RTCREG->rttr = strtoul(sbuf, 0, 0);
128 return n;
129 }
130 error(Egreg);
131 return 0; /* not reached */
132 }
133
134 static void
rtcpower(int on)135 rtcpower(int on)
136 {
137 if(on)
138 boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks);
139 else
140 RTCREG->rcnr = seconds();
141 }
142
143 long
rtctime(void)144 rtctime(void)
145 {
146 return RTCREG->rcnr;
147 }
148
149 Dev rtcdevtab = {
150 'r',
151 "rtc",
152
153 rtcreset,
154 devinit,
155 devshutdown,
156 rtcattach,
157 rtcwalk,
158 rtcstat,
159 rtcopen,
160 devcreate,
161 rtcclose,
162 rtcread,
163 devbread,
164 rtcwrite,
165 devbwrite,
166 devremove,
167 devwstat,
168 rtcpower,
169 };
170