xref: /inferno-os/os/pxa/devrtc.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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
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*
52 rtcattach(char *spec)
53 {
54 	return devattach('r', spec);
55 }
56 
57 static Walkqid*
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
64 rtcstat(Chan *c, uchar *dp, int n)
65 {
66 	return devstat(c, dp, n, rtcdir, NRTC, devgen);
67 }
68 
69 static Chan*
70 rtcopen(Chan *c, int omode)
71 {
72 	return devopen(c, omode, rtcdir, NRTC, devgen);
73 }
74 
75 static void
76 rtcclose(Chan*)
77 {
78 }
79 
80 static long
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
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
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
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