Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
/*
2
 *	M48T59/559 Timekeeper
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
 
11
#include	"io.h"
12
 
13
enum{
14
	STB0 = 0x74,
15
	STB1 = 0x75,
16
	Data = 0x77,
17
 
18
	NVOFF=	0,
19
	NVLEN=	0x1ff0,		/* length in bytes of NV RAM */
20
 
21
	/*
22
	 *  register offsets into time of day clock
23
	 */
24
	NVflags=		0x1ff0,
25
	NVwatchdog=	0x1ff7,
26
	NVctl=		0x1ff8,
27
	NVsec,
28
	NVmin,
29
	NVhour,	
30
	NVday,		/* (1 = Sun) */
31
	NVmday,		/* (1-31) */
32
	NVmon,		/* (1-12) */
33
	NVyear,		/* (0-99) */
34
 
35
	/* NVctl */
36
	RTwrite = (1<<7),
37
	RTread = (1<<6),
38
	RTsign = (1<<5),
39
	RTcal = 0x1f,
40
 
41
	/* NVwatchdog */
42
	WDsteer = (1<<7),		/* 0 -> intr, 1 -> reset */
43
	WDmult = (1<<2),		/* 5 bits of multiplier */
44
	WDres0 = (0<<0),		/* 1/16 sec resolution */
45
	WDres1 = (1<<0),		/* 1/4 sec resolution */
46
	WDres2 = (2<<0),		/* 1 sec resolution */
47
	WDres3 = (3<<0),		/* 4 sec resolution */
48
 
49
	Qdir = 0,
50
	Qrtc,
51
	Qnvram,
52
};
53
 
54
/*
55
 *  broken down time
56
 */
57
typedef struct
58
{
59
	int	sec;
60
	int	min;
61
	int	hour;
62
	int	mday;
63
	int	mon;
64
	int	year;
65
} Rtc;
66
 
67
QLock	rtclock;		/* mutex on nvram operations */
68
 
69
static Dirtab rtcdir[]={
70
	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
71
	"rtc",		{Qrtc, 0},	0,	0644,
72
	"nvram",	{Qnvram, 0},	0,	0600,
73
};
74
 
75
static ulong	rtc2sec(Rtc*);
76
static void	sec2rtc(ulong, Rtc*);
77
static void	setrtc(Rtc*);
78
static void	nvcksum(void);
79
static void	nvput(int, uchar);
80
static uchar	nvget(int);
81
 
82
static Chan*
83
rtcattach(char *spec)
84
{
85
	return devattach('r', spec);
86
}
87
 
88
static Walkqid*
89
rtcwalk(Chan *c, Chan *nc, char **name, int nname)
90
{
91
	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
92
}
93
 
94
static int	 
95
rtcstat(Chan *c, uchar *dp, int n)
96
{
97
	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
98
}
99
 
100
static Chan*
101
rtcopen(Chan *c, int omode)
102
{
103
	omode = openmode(omode);
104
	switch((ulong)c->qid.path){
105
	case Qrtc:
106
		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
107
			error(Eperm);
108
		break;
109
	case Qnvram:
110
		if(strcmp(up->user, eve)!=0 || !cpuserver)
111
			error(Eperm);
112
	}
113
	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
114
}
115
 
116
static void	 
117
rtcclose(Chan*)
118
{
119
}
120
 
121
static long	 
122
rtcread(Chan *c, void *buf, long n, vlong off)
123
{
124
	char *p;
125
	ulong t;
126
	int i;
127
	ulong offset = off;
128
 
129
	if(c->qid.type & QTDIR)
130
		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
131
 
132
	switch((ulong)c->qid.path){
133
	case Qrtc:
134
		qlock(&rtclock);
135
		t = rtctime();
136
		qunlock(&rtclock);
137
		n = readnum(offset, buf, n, t, 12);
138
		return n;
139
	case Qnvram:
140
		offset += NVOFF;
141
		if(offset > NVLEN)
142
			return 0;
143
		if(n > NVLEN - offset)
144
			n = NVLEN - offset;
145
		p = buf;
146
		qlock(&rtclock);
147
		for(i = 0; i < n; i++)
148
			p[i] = nvget(i+offset);
149
		qunlock(&rtclock);
150
		return n;
151
	}
152
	error(Egreg);
153
	return -1;		/* never reached */
154
}
155
 
156
static long	 
157
rtcwrite(Chan *c, void *buf, long n, vlong off)
158
{
159
	Rtc rtc;
160
	ulong secs;
161
	char *cp, *ep;
162
	int i;
163
	ulong offset = off;
164
 
165
	switch((ulong)c->qid.path){
166
	case Qrtc:
167
		if(offset!=0)
168
			error(Ebadarg);
169
		/*
170
		 *  read the time
171
		 */
172
		cp = ep = buf;
173
		ep += n;
174
		while(cp < ep){
175
			if(*cp>='0' && *cp<='9')
176
				break;
177
			cp++;
178
		}
179
		secs = strtoul(cp, 0, 0);
180
		/*
181
		 *  convert to bcd
182
		 */
183
		sec2rtc(secs, &rtc);
184
		/*
185
		 * write it
186
		 */
187
		qlock(&rtclock);
188
		setrtc(&rtc);
189
		qunlock(&rtclock);
190
		return n;
191
	case Qnvram:
192
		offset += NVOFF;
193
		if(offset > NVLEN)
194
			return 0;
195
		if(n > NVLEN - offset)
196
			n = NVLEN - offset;
197
		qlock(&rtclock);
198
		for(i = 0; i < n; i++)
199
			nvput(i+offset, ((uchar*)buf)[i]);
200
		nvcksum();
201
		qunlock(&rtclock);
202
		return n;
203
	}
204
	error(Egreg);
205
	return -1;		/* never reached */
206
}
207
 
208
long
209
rtcbwrite(Chan *c, Block *bp, ulong offset)
210
{
211
	return devbwrite(c, bp, offset);
212
}
213
 
214
Dev rtcdevtab = {
215
	'r',
216
	"rtc",
217
 
218
	devreset,
219
	devinit,
220
	devshutdown,
221
	rtcattach,
222
	rtcwalk,
223
	rtcstat,
224
	rtcopen,
225
	devcreate,
226
	rtcclose,
227
	rtcread,
228
	devbread,
229
	rtcwrite,
230
	devbwrite,
231
	devremove,
232
	devwstat,
233
};
234
 
235
static void
236
nvput(int offset, uchar val)
237
{
238
	outb(STB0, offset);
239
	outb(STB1, offset>>8);
240
	outb(Data, val);
241
}
242
 
243
static uchar
244
nvget(int offset)
245
{
246
	outb(STB0, offset);
247
	outb(STB1, offset>>8);
248
	return inb(Data);
249
}
250
 
251
static void
252
nvcksum(void)
253
{
254
}
255
 
256
void
257
watchreset(void)
258
{
259
	splhi();
260
	nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0);
261
	for(;;);
262
}
263
 
264
static int
265
getbcd(int bcd)
266
{
267
	return (bcd&0x0f) + 10 * (bcd>>4);
268
}
269
 
270
static int
271
putbcd(int val)
272
{
273
	return (val % 10) | (((val/10) % 10) << 4);
274
}
275
 
276
long	 
277
rtctime(void)
278
{
279
	int ctl;
280
	Rtc rtc;
281
 
282
	/*
283
	 *  convert from BCD
284
	 */
285
	ctl = nvget(NVctl);
286
	ctl &= RTsign|RTcal;
287
	nvput(NVctl, ctl|RTread);
288
 
289
	rtc.sec = getbcd(nvget(NVsec) & 0x7f);
290
	rtc.min = getbcd(nvget(NVmin));
291
	rtc.hour = getbcd(nvget(NVhour));
292
	rtc.mday = getbcd(nvget(NVmday));
293
	rtc.mon = getbcd(nvget(NVmon));
294
	rtc.year = getbcd(nvget(NVyear));
295
	if(rtc.year < 70)
296
		rtc.year += 2000;
297
	else
298
		rtc.year += 1900;
299
 
300
	nvput(NVctl, ctl);
301
 
302
	return rtc2sec(&rtc);
303
}
304
 
305
static void
306
setrtc(Rtc *rtc)
307
{
308
	int ctl;
309
 
310
	ctl = nvget(NVctl);
311
	ctl &= RTsign|RTcal;
312
	nvput(NVctl, ctl|RTwrite);
313
 
314
	nvput(NVsec, putbcd(rtc->sec));
315
	nvput(NVmin, putbcd(rtc->min));
316
	nvput(NVhour, putbcd(rtc->hour));
317
	nvput(NVmday, putbcd(rtc->mday));
318
	nvput(NVmon, putbcd(rtc->mon));
319
	nvput(NVyear, putbcd(rtc->year % 100));
320
 
321
	nvput(NVctl, ctl);
322
}
323
 
324
#define SEC2MIN 60L
325
#define SEC2HOUR (60L*SEC2MIN)
326
#define SEC2DAY (24L*SEC2HOUR)
327
 
328
/*
329
 *  days per month plus days/year
330
 */
331
static	int	dmsize[] =
332
{
333
	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
334
};
335
static	int	ldmsize[] =
336
{
337
	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
338
};
339
 
340
/*
341
 *  return the days/month for the given year
342
 */
343
static int *
344
yrsize(int y)
345
{
346
 
347
	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
348
		return ldmsize;
349
	else
350
		return dmsize;
351
}
352
 
353
/*
354
 *  compute seconds since Jan 1 1970
355
 */
356
static ulong
357
rtc2sec(Rtc *rtc)
358
{
359
	ulong secs;
360
	int i;
361
	int *d2m;
362
 
363
	secs = 0;
364
 
365
	/*
366
	 *  seconds per year
367
	 */
368
	for(i = 1970; i < rtc->year; i++){
369
		d2m = yrsize(i);
370
		secs += d2m[0] * SEC2DAY;
371
	}
372
 
373
	/*
374
	 *  seconds per month
375
	 */
376
	d2m = yrsize(rtc->year);
377
	for(i = 1; i < rtc->mon; i++)
378
		secs += d2m[i] * SEC2DAY;
379
 
380
	secs += (rtc->mday-1) * SEC2DAY;
381
	secs += rtc->hour * SEC2HOUR;
382
	secs += rtc->min * SEC2MIN;
383
	secs += rtc->sec;
384
 
385
	return secs;
386
}
387
 
388
/*
389
 *  compute rtc from seconds since Jan 1 1970
390
 */
391
static void
392
sec2rtc(ulong secs, Rtc *rtc)
393
{
394
	int d;
395
	long hms, day;
396
	int *d2m;
397
 
398
	/*
399
	 * break initial number into days
400
	 */
401
	hms = secs % SEC2DAY;
402
	day = secs / SEC2DAY;
403
	if(hms < 0) {
404
		hms += SEC2DAY;
405
		day -= 1;
406
	}
407
 
408
	/*
409
	 * generate hours:minutes:seconds
410
	 */
411
	rtc->sec = hms % 60;
412
	d = hms / 60;
413
	rtc->min = d % 60;
414
	d /= 60;
415
	rtc->hour = d;
416
 
417
	/*
418
	 * year number
419
	 */
420
	if(day >= 0)
421
		for(d = 1970; day >= *yrsize(d); d++)
422
			day -= *yrsize(d);
423
	else
424
		for (d = 1970; day < 0; d--)
425
			day += *yrsize(d-1);
426
	rtc->year = d;
427
 
428
	/*
429
	 * generate month
430
	 */
431
	d2m = yrsize(rtc->year);
432
	for(d = 1; day >= d2m[d]; d++)
433
		day -= d2m[d];
434
	rtc->mday = day + 1;
435
	rtc->mon = d;
436
 
437
	return;
438
}