shithub: n900

ref: 1b250fe1e459965861edff5868c7f1ca2b1f1ff9
dir: /devrtc.c/

View raw version
#include "u.h"
#include "../port/lib.h"
#include "../port/error.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/i2c.h"

enum {
	Rsec		= 0x1c,
	Rmin	= 0x1d,
	Rhour	= 0x1e,
	Rday	= 0x1f,
	Rmonth	= 0x20,
	Ryear	= 0x21,
	Rweeks	= 0x22,
	Rctrl		= 0x29,
		Cget		= 1<<6,

	Qdir = 0,
	Qrtc,

	SecMin	= 60,
	SecHour	= 60*SecMin,
	SecDay	= 24*SecHour,
};

typedef struct Ctlr Ctlr;
struct Ctlr {
	I2Cdev *dev;

	int sec;
	int min;
	int hour;
	int day;
	int month;
	int year;
};

static Ctlr ctlr;
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 };
static Dirtab rtctab[] = {
	".",		{Qdir, 0, QTDIR},	0, 0555,
	"rtc",	{Qrtc, 0, QTFILE},	0, 0440,
};

#define bcddec(x) (((x) & 0xf) + ((x) >> 4) * 10)
#define bcdenc(x) (((x / 10) << 4) + (x) % 10)
#define leap(x) (((x) % 4) == 0 && ((x % 100) != 0 || (x % 400) == 0))

static u8int
csr8r(Ctlr *ctlr, u8int r)
{
	uchar buf;

	i2crecv(ctlr->dev, &buf, sizeof(buf), r);
	return buf;
}

static u8int
csr8w(Ctlr *ctlr, u8int r, u8int w)
{
	i2csend(ctlr->dev, &w, sizeof(w), r);
	return w;
}

static vlong
rtcsnarf(void)
{
	vlong s;
	int i;

	/* latch and snarf */
	csr8w(&ctlr, Rctrl, csr8r(&ctlr, Rctrl) | Cget);
	ctlr.sec = bcddec(csr8r(&ctlr, Rsec)) % 60;
	ctlr.min = bcddec(csr8r(&ctlr, Rmin)) % 60;
	ctlr.hour = bcddec(csr8r(&ctlr, Rhour)) % 24;
	ctlr.day = bcddec(csr8r(&ctlr, Rday));
	ctlr.month = bcddec(csr8r(&ctlr, Rmonth));
	ctlr.year = bcddec(csr8r(&ctlr, Ryear)) % 100;
	ctlr.year += 2000;

	/* seconds per year */
	s = 0;
	for(i = 1970; i < ctlr.year; i++) {
		if(leap(i))
			s += ldmsize[0] * SecDay;
		else
			s += dmsize[0] * SecDay;
	}

	/* seconds per month */
	for(i = 1; i < ctlr.month; i++) {
		if(leap(ctlr.year))
			s += ldmsize[i] * SecDay;
		else
			s += dmsize[i] * SecDay;
	}

	/* days, hours, minutes, seconds */
	s += (ctlr.day - 1) * SecDay;
	s += ctlr.hour * SecHour;
	s += ctlr.min * SecMin;
	s += ctlr.sec;
	return s;
}

static void
rtcreset(void)
{
	ctlr.dev = i2cdev(i2cbus("i2c1"), 0x4b);
	if(!ctlr.dev)
		return;

	ctlr.dev->subaddr = 1;
	ctlr.dev->size = 0x100;
}

static void
rtcshutdown(void)
{
}

static Chan *
rtcattach(char *spec)
{
	if(!ctlr.dev)
		error(Enonexist);

	return devattach('r', spec);
}

static Walkqid *
rtcwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, rtctab, nelem(rtctab), devgen);
}

static int
rtcstat(Chan *c, uchar *dp, int n)
{
	return devstat(c, dp, n, rtctab, nelem(rtctab), devgen);
}

static Chan *
rtcopen(Chan *c, int mode)
{
	mode = openmode(mode);
	if(c->qid.path == Qrtc) {
		if(!iseve() && mode != OREAD)
			error(Eperm);
	}

	return devopen(c, mode, rtctab, nelem(rtctab), devgen);
}

static void
rtcclose(Chan *)
{
}

static long
rtcread(Chan *c, void *a, long n, vlong off)
{
	if(c->qid.path == Qdir)
		return devdirread(c, a, n, rtctab, nelem(rtctab), devgen);
	if(c->qid.path == Qrtc)
		return readnum(off, a, n, rtcsnarf(), 12);

	error(Egreg);
	return 0;
}

static long
rtcwrite(Chan *, void *, long, vlong)
{
	error(Egreg);
	return 0;
}

Dev rtcdevtab = {
	'r',
	"rtc",

	rtcreset,
	devinit,
	rtcshutdown,
	rtcattach,
	rtcwalk,
	rtcstat,
	rtcopen,
	devcreate,
	rtcclose,
	rtcread,
	devbread,
	rtcwrite,
	devbwrite,
	devremove,
	devwstat,
};