summaryrefslogtreecommitdiff
path: root/os/js/devrtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/js/devrtc.c')
-rw-r--r--os/js/devrtc.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/os/js/devrtc.c b/os/js/devrtc.c
new file mode 100644
index 00000000..25fcfa1e
--- /dev/null
+++ b/os/js/devrtc.c
@@ -0,0 +1,413 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+/*
+ * Mostek MK48T12-15 Zeropower/Timekeeper
+ * This driver is actually portable.
+ */
+typedef struct Rtc Rtc;
+struct Rtc
+{
+ int sec;
+ int min;
+ int hour;
+ int wday;
+ int mday;
+ int mon;
+ int year;
+};
+
+static uchar rtcgencksum(void);
+static void setrtc(Rtc *rtc);
+static long rtctime(void);
+static int *yrsize(int yr);
+static int *yrsize(int yr);
+static ulong rtc2sec(Rtc *rtc);
+static void sec2rtc(ulong secs, Rtc *rtc);
+
+static struct
+{
+ uchar *cksum;
+ uchar *ram;
+ RTCdev *rtc;
+}nvr;
+
+enum{
+ Qdir,
+ Qrtc,
+ Qnvram,
+};
+
+QLock rtclock; /* mutex on clock operations */
+
+static Dirtab rtcdir[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "rtc", {Qrtc, 0}, 0, 0666,
+ "nvram", {Qnvram, 0}, NVWRITE, 0666,
+};
+#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0]))
+
+static void
+rtcinit(void)
+{
+ KMap *k;
+
+ k = kmappa(NVR_CKSUM_PHYS, PTENOCACHE|PTEIO);
+ nvr.cksum = (uchar*)VA(k);
+
+ k = kmappa(NVR_PHYS, PTENOCACHE|PTEIO);
+ nvr.ram = (uchar*)VA(k);
+ nvr.rtc = (RTCdev*)(VA(k)+RTCOFF);
+
+ rtcgencksum();
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+ return devattach('r',spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, NRTC, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ omode = openmode(omode);
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)
+ error(Eperm);
+ break;
+ case Qnvram:
+ if(strcmp(up->env->user, eve)!=0)
+ error(Eperm);
+ }
+ return devopen(c, omode, rtcdir, NRTC, devgen);
+}
+
+static void
+rtccreate(Chan *c, char *name, int omode, ulong perm)
+{
+ USED(c, name, omode, perm);
+ error(Eperm);
+}
+
+static void
+rtcclose(Chan *c)
+{
+ USED(c);
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong offset)
+{
+ ulong t, ot;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, NRTC, devgen);
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ qlock(&rtclock);
+ t = rtctime();
+ do{
+ ot = t;
+ t = rtctime(); /* make sure there's no skew */
+ }while(t != ot);
+ qunlock(&rtclock);
+ n = readnum(offset, buf, n, t, 12);
+ return n;
+ case Qnvram:
+ if(offset > NVREAD)
+ return 0;
+ if(n > NVREAD - offset)
+ n = NVREAD - offset;
+ qlock(&rtclock);
+ memmove(buf, nvr.ram+offset, n);
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+/*
+ * XXX - Tad: fixme to generate the correct checksum
+ */
+static uchar
+rtcgencksum(void)
+{
+ uchar cksum;
+ int i;
+ static uchar p1cksum = 0;
+ static uchar p1cksumvalid=0;
+
+ if(!p1cksumvalid) {
+ for(i=1; i < 0x1000 ; i++)
+ p1cksum ^= nvr.cksum[i];
+ p1cksumvalid = 1;
+ }
+
+ cksum = p1cksum;
+
+ for(i=0; i < 0xfdf ; i++) {
+ cksum ^= nvr.ram[i];
+ }
+
+ return cksum;
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong offset)
+{
+ Rtc rtc;
+ ulong secs;
+ char *cp, sbuf[32];
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ /*
+ * read the time
+ */
+ if(offset != 0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ cp = sbuf;
+ while(*cp){
+ if(*cp>='0' && *cp<='9')
+ break;
+ cp++;
+ }
+ secs = strtoul(cp, 0, 0);
+ /*
+ * convert to bcd
+ */
+ sec2rtc(secs, &rtc);
+ /*
+ * write it
+ */
+ qlock(&rtclock);
+ setrtc(&rtc);
+ qunlock(&rtclock);
+ return n;
+ case Qnvram:
+ if(offset > NVWRITE)
+ return 0;
+ if(n > NVWRITE - offset)
+ n = NVWRITE - offset;
+ qlock(&rtclock);
+ memmove(nvr.ram+offset, buf, n);
+ *nvr.cksum = rtcgencksum();
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+#define bcd2dec(bcd) (((((bcd)>>4) & 0x0F) * 10) + ((bcd) & 0x0F))
+#define dec2bcd(dec) ((((dec)/10)<<4)|((dec)%10))
+
+static void
+setrtc(Rtc *rtc)
+{
+ struct RTCdev *dev;
+
+ dev = nvr.rtc;
+ dev->control |= RTCWRITE;
+ wbflush();
+ dev->year = dec2bcd(rtc->year % 100);
+ dev->mon = dec2bcd(rtc->mon);
+ dev->mday = dec2bcd(rtc->mday);
+ dev->hour = dec2bcd(rtc->hour);
+ dev->min = dec2bcd(rtc->min);
+ dev->sec = dec2bcd(rtc->sec);
+ wbflush();
+ dev->control &= ~RTCWRITE;
+ wbflush();
+}
+
+static long
+rtctime(void)
+{
+ struct RTCdev *dev;
+ Rtc rtc;
+
+ dev = nvr.rtc;
+ dev->control |= RTCREAD;
+ wbflush();
+ rtc.sec = bcd2dec(dev->sec) & 0x7F;
+ rtc.min = bcd2dec(dev->min & 0x7F);
+ rtc.hour = bcd2dec(dev->hour & 0x3F);
+ rtc.mday = bcd2dec(dev->mday & 0x3F);
+ rtc.mon = bcd2dec(dev->mon & 0x3F);
+ rtc.year = bcd2dec(dev->year);
+ dev->control &= ~RTCREAD;
+ wbflush();
+
+ if (rtc.mon < 1 || rtc.mon > 12)
+ return 0;
+ /*
+ * the world starts Jan 1 1970
+ */
+ if(rtc.year < 70)
+ rtc.year += 2000;
+ else
+ rtc.year += 1900;
+
+ return rtc2sec(&rtc);
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int yr)
+{
+ if((yr % 4) == 0)
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+ ulong secs;
+ int i;
+ int *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < rtc->year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(rtc->year);
+ for(i = 1; i < rtc->mon; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ secs += (rtc->mday-1) * SEC2DAY;
+ secs += rtc->hour * SEC2HOUR;
+ secs += rtc->min * SEC2MIN;
+ secs += rtc->sec;
+
+ return secs;
+}
+
+/*
+ * compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+ int d;
+ long hms, day;
+ int *d2m;
+
+ /*
+ * break initial number into days
+ */
+ hms = secs % SEC2DAY;
+ day = secs / SEC2DAY;
+ if(hms < 0) {
+ hms += SEC2DAY;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ rtc->sec = hms % 60;
+ d = hms / 60;
+ rtc->min = d % 60;
+ d /= 60;
+ rtc->hour = d;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d = 1970; day >= *yrsize(d); d++)
+ day -= *yrsize(d);
+ else
+ for (d = 1970; day < 0; d--)
+ day += *yrsize(d-1);
+ rtc->year = d;
+
+ /*
+ * generate month
+ */
+ d2m = yrsize(rtc->year);
+ for(d = 1; day >= d2m[d]; d++)
+ day -= d2m[d];
+ rtc->mday = day + 1;
+ rtc->mon = d;
+
+ return;
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ devreset,
+ rtcinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ rtccreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};