ref: 606901dc5da9cb09acb5593c5cf74ce1b52ca6e2
dir: /os/pxa/devrtc.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
/*
* SA11x0 real time clock
* TO DO: alarms, wakeup, allow trim setting(?)
*/
enum{
Qdir,
Qrtc,
Qrtctrim,
};
static Dirtab rtcdir[]={
".", {Qdir,0,QTDIR}, 0, 0555,
"rtc", {Qrtc}, NUMSIZE, 0664,
"rtctrim", {Qrtctrim}, 0, 0664,
};
#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0]))
extern ulong boottime;
enum {
RTSR_al= 1<<0, /* RTC alarm detected */
RTSR_hz= 1<<1, /* 1-Hz rising-edge detected */
RTSR_ale= 1<<2, /* RTC alarm interrupt enabled */
RTSR_hze= 1<<3, /* 1-Hz interrupt enable */
};
static void
rtcreset(void)
{
RTCreg *r;
r = RTCREG;
if((r->rttr & 0xFFFF) == 0){ /* reset state */
r->rttr = 32768-1;
r->rcnr = boottime; /* typically zero */
}
r->rtar = ~0;
r->rtsr = RTSR_al | RTSR_hz;
}
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)
{
return devopen(c, omode, rtcdir, NRTC, 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, NRTC, devgen);
switch((ulong)c->qid.path){
case Qrtc:
return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE);
case Qrtctrim:
return readnum(off, buf, n, RTCREG->rttr, NUMSIZE);
}
error(Egreg);
return 0; /* not reached */
}
static long
rtcwrite(Chan *c, void *buf, long n, vlong off)
{
ulong offset = off;
ulong secs;
char *cp, sbuf[32];
switch((ulong)c->qid.path){
case Qrtc:
/*
* write 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);
RTCREG->rcnr = secs;
return n;
case Qrtctrim:
if(offset != 0 || n >= sizeof(sbuf)-1)
error(Ebadarg);
memmove(sbuf, buf, n);
sbuf[n] = '\0';
RTCREG->rttr = strtoul(sbuf, 0, 0);
return n;
}
error(Egreg);
return 0; /* not reached */
}
static void
rtcpower(int on)
{
if(on)
boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks);
else
RTCREG->rcnr = seconds();
}
long
rtctime(void)
{
return RTCREG->rcnr;
}
Dev rtcdevtab = {
'r',
"rtc",
rtcreset,
devinit,
devshutdown,
rtcattach,
rtcwalk,
rtcstat,
rtcopen,
devcreate,
rtcclose,
rtcread,
devbread,
rtcwrite,
devbwrite,
devremove,
devwstat,
rtcpower,
};