Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * devrtc - real-time clock, for kirkwood
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
 
12
typedef	struct	RtcReg	RtcReg;
13
typedef	struct	Rtc	Rtc;
14
 
15
struct RtcReg
16
{
17
	ulong	time;
18
	ulong	date;
19
	ulong	alarmtm;
20
	ulong	alarmdt;
21
	ulong	intrmask;
22
	ulong	intrcause;
23
};
24
 
25
struct Rtc
26
{
27
	int	sec;
28
	int	min;
29
	int	hour;
30
	int	wday;
31
	int	mday;
32
	int	mon;
33
	int	year;
34
};
35
 
36
enum {
37
	Qdir,
38
	Qrtc,
39
};
40
 
41
static Dirtab rtcdir[] = {
42
	".",	{Qdir, 0, QTDIR},	0,		0555,
43
	"rtc",	{Qrtc},			NUMSIZE,	0664,
44
};
45
static	RtcReg	*rtcreg;		/* filled in by attach */
46
static	Lock	rtclock;
47
 
48
#define SEC2MIN	60
49
#define SEC2HOUR (60*SEC2MIN)
50
#define SEC2DAY (24L*SEC2HOUR)
51
 
52
/*
53
 * days per month plus days/year
54
 */
55
static	int	dmsize[] =
56
{
57
	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
58
};
59
static	int	ldmsize[] =
60
{
61
	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
62
};
63
 
64
/*
65
 *  return the days/month for the given year
66
 */
67
static int *
68
yrsize(int yr)
69
{
70
	if((yr % 4) == 0)
71
		return ldmsize;
72
	else
73
		return dmsize;
74
}
75
 
76
/*
77
 *  compute seconds since Jan 1 1970
78
 */
79
static ulong
80
rtc2sec(Rtc *rtc)
81
{
82
	ulong secs;
83
	int i;
84
	int *d2m;
85
 
86
	/*
87
	 *  seconds per year
88
	 */
89
	secs = 0;
90
	for(i = 1970; i < rtc->year; i++){
91
		d2m = yrsize(i);
92
		secs += d2m[0] * SEC2DAY;
93
	}
94
 
95
	/*
96
	 *  seconds per month
97
	 */
98
	d2m = yrsize(rtc->year);
99
	for(i = 1; i < rtc->mon; i++)
100
		secs += d2m[i] * SEC2DAY;
101
 
102
	secs += (rtc->mday-1) * SEC2DAY;
103
	secs += rtc->hour * SEC2HOUR;
104
	secs += rtc->min * SEC2MIN;
105
	secs += rtc->sec;
106
 
107
	return secs;
108
}
109
 
110
/*
111
 *  compute rtc from seconds since Jan 1 1970
112
 */
113
static void
114
sec2rtc(ulong secs, Rtc *rtc)
115
{
116
	int d;
117
	long hms, day;
118
	int *d2m;
119
 
120
	/*
121
	 * break initial number into days
122
	 */
123
	hms = secs % SEC2DAY;
124
	day = secs / SEC2DAY;
125
	if(hms < 0) {
126
		hms += SEC2DAY;
127
		day -= 1;
128
	}
129
 
130
	/*
131
	 * 19700101 was thursday
132
	 */
133
	rtc->wday = (day + 7340036L) % 7;
134
 
135
	/*
136
	 * generate hours:minutes:seconds
137
	 */
138
	rtc->sec = hms % 60;
139
	d = hms / 60;
140
	rtc->min = d % 60;
141
	d /= 60;
142
	rtc->hour = d;
143
 
144
	/*
145
	 * year number
146
	 */
147
	if(day >= 0)
148
		for(d = 1970; day >= *yrsize(d); d++)
149
			day -= *yrsize(d);
150
	else
151
		for (d = 1970; day < 0; d--)
152
			day += *yrsize(d-1);
153
	rtc->year = d;
154
 
155
	/*
156
	 * generate month
157
	 */
158
	d2m = yrsize(rtc->year);
159
	for(d = 1; day >= d2m[d]; d++)
160
		day -= d2m[d];
161
	rtc->mday = day + 1;
162
	rtc->mon = d;
163
}
164
 
165
enum {
166
	Rtcsec	= 0x00007f,
167
	Rtcmin	= 0x007f00,
168
	Rtcms	= 8,
169
	Rtchr12	= 0x1f0000,
170
	Rtchr24	= 0x3f0000,
171
	Rtchrs	= 16,
172
 
173
	Rdmday	= 0x00003f,
174
	Rdmon	= 0x001f00,
175
	Rdms	= 8,
176
	Rdyear	= 0x7f0000,
177
	Rdys	= 16,
178
 
179
	Rtcpm	= 1<<21,		/* pm bit */
180
	Rtc12	= 1<<22,		/* 12 hr clock */
181
};
182
 
183
static ulong
184
bcd2dec(ulong bcd)
185
{
186
	ulong d, m, i;
187
 
188
	d = 0;
189
	m = 1;
190
	for(i = 0; i < 2 * sizeof d; i++){
191
		d += ((bcd >> (4*i)) & 0xf) * m;
192
		m *= 10;
193
	}
194
	return d;
195
}
196
 
197
static ulong
198
dec2bcd(ulong d)
199
{
200
	ulong bcd, i;
201
 
202
	bcd = 0;
203
	for(i = 0; d != 0; i++){
204
		bcd |= (d%10) << (4*i);
205
		d /= 10;
206
	}
207
	return bcd;
208
}
209
 
210
static long
211
_rtctime(void)
212
{
213
	ulong t, d;
214
	Rtc rtc;
215
 
216
	t = rtcreg->time;
217
	d = rtcreg->date;
218
 
219
	rtc.sec = bcd2dec(t & Rtcsec);
220
	rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
221
 
222
	if(t & Rtc12){
223
		rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
224
		if(t & Rtcpm)
225
			rtc.hour += 12;
226
	}else
227
		rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs);	/* 0—23 */
228
 
229
	rtc.mday = bcd2dec(d & Rdmday);				/* 1—31 */
230
	rtc.mon = bcd2dec((d & Rdmon) >> Rdms);			/* 1—12 */
231
	rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000;	/* year%100 */
232
 
233
//	print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
234
//		rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
235
	return rtc2sec(&rtc);
236
}
237
 
238
long
239
rtctime(void)
240
{
241
	int i;
242
	long t, ot;
243
 
244
	ilock(&rtclock);
245
 
246
	/* loop until we get two reads in a row the same */
247
	t = _rtctime();
248
	ot = ~t;
249
	for(i = 0; i < 100 && ot != t; i++){
250
		ot = t;
251
		t = _rtctime();
252
	}
253
	if(ot != t)
254
		print("rtctime: we are boofheads\n");
255
 
256
	iunlock(&rtclock);
257
	return t;
258
}
259
 
260
static void
261
setrtc(Rtc *rtc)
262
{
263
	ilock(&rtclock);
264
	rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
265
		dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
266
	rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
267
		dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
268
	iunlock(&rtclock);
269
}
270
 
271
static Chan*
272
rtcattach(char *spec)
273
{
274
	rtcreg = (RtcReg*)soc.rtc;
275
	return devattach(L'r', spec);
276
}
277
 
278
static Walkqid*
279
rtcwalk(Chan *c, Chan *nc, char **name, int nname)
280
{
281
	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
282
}
283
 
284
static int
285
rtcstat(Chan *c, uchar *dp, int n)
286
{
287
	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
288
}
289
 
290
static Chan*
291
rtcopen(Chan *c, int omode)
292
{
293
	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
294
}
295
 
296
static void
297
rtcclose(Chan*)
298
{
299
}
300
 
301
static long
302
rtcread(Chan *c, void *buf, long n, vlong off)
303
{
304
	if(c->qid.type & QTDIR)
305
		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
306
 
307
	switch((ulong)c->qid.path){
308
	default:
309
		error(Egreg);
310
	case Qrtc:
311
		return readnum(off, buf, n, rtctime(), NUMSIZE);
312
	}
313
}
314
 
315
static long
316
rtcwrite(Chan *c, void *buf, long n, vlong off)
317
{
318
	ulong offset = off;
319
	char *cp, sbuf[32];
320
	Rtc rtc;
321
 
322
	switch((ulong)c->qid.path){
323
	default:
324
		error(Egreg);
325
	case Qrtc:
326
		if(offset != 0 || n >= sizeof(sbuf)-1)
327
			error(Ebadarg);
328
		memmove(sbuf, buf, n);
329
		sbuf[n] = '\0';
330
		for(cp = sbuf; *cp != '\0'; cp++)
331
			if(*cp >= '0' && *cp <= '9')
332
				break;
333
		sec2rtc(strtoul(cp, 0, 0), &rtc);
334
		setrtc(&rtc);
335
		return n;
336
	}
337
}
338
 
339
Dev rtcdevtab = {
340
	L'r',
341
	"rtc",
342
 
343
	devreset,
344
	devinit,
345
	devshutdown,
346
	rtcattach,
347
	rtcwalk,
348
	rtcstat,
349
	rtcopen,
350
	devcreate,
351
	rtcclose,
352
	rtcread,
353
	devbread,
354
	rtcwrite,
355
	devbwrite,
356
	devremove,
357
	devwstat,
358
	devpower,
359
};