xref: /inferno-os/os/pxa/devrtc.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
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