Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
/*
9
 *  real time clock and non-volatile ram
10
 */
11
 
12
enum {
13
	Paddr=		0x70,	/* address port */
14
	Pdata=		0x71,	/* data port */
15
 
16
	Seconds=	0x00,
17
	Minutes=	0x02,
18
	Hours=		0x04, 
19
	Mday=		0x07,
20
	Month=		0x08,
21
	Year=		0x09,
22
	Status=		0x0A,
23
 
24
	Nvoff=		128,	/* where usable nvram lives */
25
	Nvsize=		256,
26
 
27
	Nbcd=		6,
28
};
29
 
30
typedef struct Rtc	Rtc;
31
struct Rtc
32
{
33
	int	sec;
34
	int	min;
35
	int	hour;
36
	int	mday;
37
	int	mon;
38
	int	year;
39
};
40
 
41
 
42
enum{
43
	Qdir = 0,
44
	Qrtc,
45
	Qnvram,
46
};
47
 
48
Dirtab rtcdir[]={
49
	".",	{Qdir, 0, QTDIR},	0,	0555,
50
	"nvram",	{Qnvram, 0},	Nvsize,	0664,
51
	"rtc",		{Qrtc, 0},	0,	0664,
52
};
53
 
54
static ulong rtc2sec(Rtc*);
55
static void sec2rtc(ulong, Rtc*);
56
 
57
void
58
rtcinit(void)
59
{
60
	if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0)
61
		panic("rtcinit: ioalloc failed");
62
}
63
 
64
static Chan*
65
rtcattach(char* spec)
66
{
67
	return devattach('r', spec);
68
}
69
 
70
static Walkqid*	 
71
rtcwalk(Chan* c, Chan *nc, char** name, int nname)
72
{
73
	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
74
}
75
 
76
static int	 
77
rtcstat(Chan* c, uchar* dp, int n)
78
{
79
	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
80
}
81
 
82
static Chan*
83
rtcopen(Chan* c, int omode)
84
{
85
	omode = openmode(omode);
86
	switch((ulong)c->qid.path){
87
	case Qrtc:
88
		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
89
			error(Eperm);
90
		break;
91
	case Qnvram:
92
		if(strcmp(up->user, eve)!=0)
93
			error(Eperm);
94
	}
95
	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
96
}
97
 
98
static void	 
99
rtcclose(Chan*)
100
{
101
}
102
 
103
#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4))
104
 
105
static long	 
106
_rtctime(void)
107
{
108
	uchar bcdclock[Nbcd];
109
	Rtc rtc;
110
	int i;
111
 
112
	/* don't do the read until the clock is no longer busy */
113
	for(i = 0; i < 10000; i++){
114
		outb(Paddr, Status);
115
		if(inb(Pdata) & 0x80)
116
			continue;
117
 
118
		/* read clock values */
119
		outb(Paddr, Seconds);	bcdclock[0] = inb(Pdata);
120
		outb(Paddr, Minutes);	bcdclock[1] = inb(Pdata);
121
		outb(Paddr, Hours);	bcdclock[2] = inb(Pdata);
122
		outb(Paddr, Mday);	bcdclock[3] = inb(Pdata);
123
		outb(Paddr, Month);	bcdclock[4] = inb(Pdata);
124
		outb(Paddr, Year);	bcdclock[5] = inb(Pdata);
125
 
126
		outb(Paddr, Status);
127
		if((inb(Pdata) & 0x80) == 0)
128
			break;
129
	}
130
 
131
	/*
132
	 *  convert from BCD
133
	 */
134
	rtc.sec = GETBCD(0);
135
	rtc.min = GETBCD(1);
136
	rtc.hour = GETBCD(2);
137
	rtc.mday = GETBCD(3);
138
	rtc.mon = GETBCD(4);
139
	rtc.year = GETBCD(5);
140
 
141
	/*
142
	 *  the world starts jan 1 1970
143
	 */
144
	if(rtc.year < 70)
145
		rtc.year += 2000;
146
	else
147
		rtc.year += 1900;
148
	return rtc2sec(&rtc);
149
}
150
 
151
static Lock nvrtlock;
152
 
153
long
154
rtctime(void)
155
{
156
	int i;
157
	long t, ot;
158
 
159
	ilock(&nvrtlock);
160
 
161
	/* loop till we get two reads in a row the same */
162
	t = _rtctime();
163
	for(i = 0; i < 100; i++){
164
		ot = t;
165
		t = _rtctime();
166
		if(ot == t)
167
			break;
168
	}
169
	if(i == 100) print("we are boofheads\n");
170
 
171
	iunlock(&nvrtlock);
172
 
173
	return t;
174
}
175
 
176
static long	 
177
rtcread(Chan* c, void* buf, long n, vlong off)
178
{
179
	ulong t;
180
	char *a, *start;
181
	ulong offset = off;
182
 
183
	if(c->qid.type & QTDIR)
184
		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
185
 
186
	switch((ulong)c->qid.path){
187
	case Qrtc:
188
		t = rtctime();
189
		n = readnum(offset, buf, n, t, 12);
190
		return n;
191
	case Qnvram:
192
		if(n == 0)
193
			return 0;
194
		if(n > Nvsize)
195
			n = Nvsize;
196
		a = start = smalloc(n);
197
 
198
		ilock(&nvrtlock);
199
		for(t = offset; t < offset + n; t++){
200
			if(t >= Nvsize)
201
				break;
202
			outb(Paddr, Nvoff+t);
203
			*a++ = inb(Pdata);
204
		}
205
		iunlock(&nvrtlock);
206
 
207
		if(waserror()){
208
			free(start);
209
			nexterror();
210
		}
211
		memmove(buf, start, t - offset);
212
		poperror();
213
 
214
		free(start);
215
		return t - offset;
216
	}
217
	error(Ebadarg);
218
	return 0;
219
}
220
 
221
#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
222
 
223
static long	 
224
rtcwrite(Chan* c, void* buf, long n, vlong off)
225
{
226
	int t;
227
	char *a, *start;
228
	Rtc rtc;
229
	ulong secs;
230
	uchar bcdclock[Nbcd];
231
	char *cp, *ep;
232
	ulong offset = off;
233
 
234
	if(offset!=0)
235
		error(Ebadarg);
236
 
237
 
238
	switch((ulong)c->qid.path){
239
	case Qrtc:
240
		/*
241
		 *  read the time
242
		 */
243
		cp = ep = buf;
244
		ep += n;
245
		while(cp < ep){
246
			if(*cp>='0' && *cp<='9')
247
				break;
248
			cp++;
249
		}
250
		secs = strtoul(cp, 0, 0);
251
 
252
		/*
253
		 *  convert to bcd
254
		 */
255
		sec2rtc(secs, &rtc);
256
		PUTBCD(rtc.sec, 0);
257
		PUTBCD(rtc.min, 1);
258
		PUTBCD(rtc.hour, 2);
259
		PUTBCD(rtc.mday, 3);
260
		PUTBCD(rtc.mon, 4);
261
		PUTBCD(rtc.year, 5);
262
 
263
		/*
264
		 *  write the clock
265
		 */
266
		ilock(&nvrtlock);
267
		outb(Paddr, Seconds);	outb(Pdata, bcdclock[0]);
268
		outb(Paddr, Minutes);	outb(Pdata, bcdclock[1]);
269
		outb(Paddr, Hours);	outb(Pdata, bcdclock[2]);
270
		outb(Paddr, Mday);	outb(Pdata, bcdclock[3]);
271
		outb(Paddr, Month);	outb(Pdata, bcdclock[4]);
272
		outb(Paddr, Year);	outb(Pdata, bcdclock[5]);
273
		iunlock(&nvrtlock);
274
		return n;
275
	case Qnvram:
276
		if(n == 0)
277
			return 0;
278
		if(n > Nvsize)
279
			n = Nvsize;
280
 
281
		start = a = smalloc(n);
282
		if(waserror()){
283
			free(start);
284
			nexterror();
285
		}
286
		memmove(a, buf, n);
287
		poperror();
288
 
289
		ilock(&nvrtlock);
290
		for(t = offset; t < offset + n; t++){
291
			if(t >= Nvsize)
292
				break;
293
			outb(Paddr, Nvoff+t);
294
			outb(Pdata, *a++);
295
		}
296
		iunlock(&nvrtlock);
297
 
298
		free(start);
299
		return t - offset;
300
	}
301
	error(Ebadarg);
302
	return 0;
303
}
304
 
305
Dev rtcdevtab = {
306
	'r',
307
	"rtc",
308
 
309
	devreset,
310
	rtcinit,
311
	devshutdown,
312
	rtcattach,
313
	rtcwalk,
314
	rtcstat,
315
	rtcopen,
316
	devcreate,
317
	rtcclose,
318
	rtcread,
319
	devbread,
320
	rtcwrite,
321
	devbwrite,
322
	devremove,
323
	devwstat,
324
};
325
 
326
#define SEC2MIN 60L
327
#define SEC2HOUR (60L*SEC2MIN)
328
#define SEC2DAY (24L*SEC2HOUR)
329
 
330
/*
331
 *  days per month plus days/year
332
 */
333
static	int	dmsize[] =
334
{
335
	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
336
};
337
static	int	ldmsize[] =
338
{
339
	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
340
};
341
 
342
/*
343
 *  return the days/month for the given year
344
 */
345
static int*
346
yrsize(int y)
347
{
348
	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
349
		return ldmsize;
350
	else
351
		return dmsize;
352
}
353
 
354
/*
355
 *  compute seconds since Jan 1 1970
356
 */
357
static ulong
358
rtc2sec(Rtc *rtc)
359
{
360
	ulong secs;
361
	int i;
362
	int *d2m;
363
 
364
	secs = 0;
365
 
366
	/*
367
	 *  seconds per year
368
	 */
369
	for(i = 1970; i < rtc->year; i++){
370
		d2m = yrsize(i);
371
		secs += d2m[0] * SEC2DAY;
372
	}
373
 
374
	/*
375
	 *  seconds per month
376
	 */
377
	d2m = yrsize(rtc->year);
378
	for(i = 1; i < rtc->mon; i++)
379
		secs += d2m[i] * SEC2DAY;
380
 
381
	secs += (rtc->mday-1) * SEC2DAY;
382
	secs += rtc->hour * SEC2HOUR;
383
	secs += rtc->min * SEC2MIN;
384
	secs += rtc->sec;
385
 
386
	return secs;
387
}
388
 
389
/*
390
 *  compute rtc from seconds since Jan 1 1970
391
 */
392
static void
393
sec2rtc(ulong secs, Rtc *rtc)
394
{
395
	int d;
396
	long hms, day;
397
	int *d2m;
398
 
399
	/*
400
	 * break initial number into days
401
	 */
402
	hms = secs % SEC2DAY;
403
	day = secs / SEC2DAY;
404
	if(hms < 0) {
405
		hms += SEC2DAY;
406
		day -= 1;
407
	}
408
 
409
	/*
410
	 * generate hours:minutes:seconds
411
	 */
412
	rtc->sec = hms % 60;
413
	d = hms / 60;
414
	rtc->min = d % 60;
415
	d /= 60;
416
	rtc->hour = d;
417
 
418
	/*
419
	 * year number
420
	 */
421
	if(day >= 0)
422
		for(d = 1970; day >= *yrsize(d); d++)
423
			day -= *yrsize(d);
424
	else
425
		for (d = 1970; day < 0; d--)
426
			day += *yrsize(d-1);
427
	rtc->year = d;
428
 
429
	/*
430
	 * generate month
431
	 */
432
	d2m = yrsize(rtc->year);
433
	for(d = 1; day >= d2m[d]; d++)
434
		day -= d2m[d];
435
	rtc->mday = day + 1;
436
	rtc->mon = d;
437
 
438
	return;
439
}
440
 
441
uchar
442
nvramread(int addr)
443
{
444
	uchar data;
445
 
446
	ilock(&nvrtlock);
447
	outb(Paddr, addr);
448
	data = inb(Pdata);
449
	iunlock(&nvrtlock);
450
 
451
	return data;
452
}
453
 
454
void
455
nvramwrite(int addr, uchar data)
456
{
457
	ilock(&nvrtlock);
458
	outb(Paddr, addr);
459
	outb(Pdata, data);
460
	iunlock(&nvrtlock);
461
}