summaryrefslogtreecommitdiff
path: root/sys/src/9/kw/devrtc.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/kw/devrtc.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/kw/devrtc.c')
-rwxr-xr-xsys/src/9/kw/devrtc.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/sys/src/9/kw/devrtc.c b/sys/src/9/kw/devrtc.c
new file mode 100755
index 000000000..a47fc3033
--- /dev/null
+++ b/sys/src/9/kw/devrtc.c
@@ -0,0 +1,359 @@
+/*
+ * devrtc - real-time clock, for kirkwood
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+typedef struct RtcReg RtcReg;
+typedef struct Rtc Rtc;
+
+struct RtcReg
+{
+ ulong time;
+ ulong date;
+ ulong alarmtm;
+ ulong alarmdt;
+ ulong intrmask;
+ ulong intrcause;
+};
+
+struct Rtc
+{
+ int sec;
+ int min;
+ int hour;
+ int wday;
+ int mday;
+ int mon;
+ int year;
+};
+
+enum {
+ Qdir,
+ Qrtc,
+};
+
+static Dirtab rtcdir[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "rtc", {Qrtc}, NUMSIZE, 0664,
+};
+static RtcReg *rtcreg; /* filled in by attach */
+static Lock rtclock;
+
+#define SEC2MIN 60
+#define SEC2HOUR (60*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;
+
+ /*
+ * seconds per year
+ */
+ secs = 0;
+ 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;
+ }
+
+ /*
+ * 19700101 was thursday
+ */
+ rtc->wday = (day + 7340036L) % 7;
+
+ /*
+ * 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;
+}
+
+enum {
+ Rtcsec = 0x00007f,
+ Rtcmin = 0x007f00,
+ Rtcms = 8,
+ Rtchr12 = 0x1f0000,
+ Rtchr24 = 0x3f0000,
+ Rtchrs = 16,
+
+ Rdmday = 0x00003f,
+ Rdmon = 0x001f00,
+ Rdms = 8,
+ Rdyear = 0x7f0000,
+ Rdys = 16,
+
+ Rtcpm = 1<<21, /* pm bit */
+ Rtc12 = 1<<22, /* 12 hr clock */
+};
+
+static ulong
+bcd2dec(ulong bcd)
+{
+ ulong d, m, i;
+
+ d = 0;
+ m = 1;
+ for(i = 0; i < 2 * sizeof d; i++){
+ d += ((bcd >> (4*i)) & 0xf) * m;
+ m *= 10;
+ }
+ return d;
+}
+
+static ulong
+dec2bcd(ulong d)
+{
+ ulong bcd, i;
+
+ bcd = 0;
+ for(i = 0; d != 0; i++){
+ bcd |= (d%10) << (4*i);
+ d /= 10;
+ }
+ return bcd;
+}
+
+static long
+_rtctime(void)
+{
+ ulong t, d;
+ Rtc rtc;
+
+ t = rtcreg->time;
+ d = rtcreg->date;
+
+ rtc.sec = bcd2dec(t & Rtcsec);
+ rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
+
+ if(t & Rtc12){
+ rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
+ if(t & Rtcpm)
+ rtc.hour += 12;
+ }else
+ rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs); /* 0—23 */
+
+ rtc.mday = bcd2dec(d & Rdmday); /* 1—31 */
+ rtc.mon = bcd2dec((d & Rdmon) >> Rdms); /* 1—12 */
+ rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000; /* year%100 */
+
+// print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
+// rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
+ return rtc2sec(&rtc);
+}
+
+long
+rtctime(void)
+{
+ int i;
+ long t, ot;
+
+ ilock(&rtclock);
+
+ /* loop until we get two reads in a row the same */
+ t = _rtctime();
+ ot = ~t;
+ for(i = 0; i < 100 && ot != t; i++){
+ ot = t;
+ t = _rtctime();
+ }
+ if(ot != t)
+ print("rtctime: we are boofheads\n");
+
+ iunlock(&rtclock);
+ return t;
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+ ilock(&rtclock);
+ rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
+ dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
+ rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
+ dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
+ iunlock(&rtclock);
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+ rtcreg = (RtcReg*)soc.rtc;
+ return devattach(L'r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+ switch((ulong)c->qid.path){
+ default:
+ error(Egreg);
+ case Qrtc:
+ return readnum(off, buf, n, rtctime(), NUMSIZE);
+ }
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ ulong offset = off;
+ char *cp, sbuf[32];
+ Rtc rtc;
+
+ switch((ulong)c->qid.path){
+ default:
+ error(Egreg);
+ case Qrtc:
+ if(offset != 0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ for(cp = sbuf; *cp != '\0'; cp++)
+ if(*cp >= '0' && *cp <= '9')
+ break;
+ sec2rtc(strtoul(cp, 0, 0), &rtc);
+ setrtc(&rtc);
+ return n;
+ }
+}
+
+Dev rtcdevtab = {
+ L'r',
+ "rtc",
+
+ devreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+ devpower,
+};