imx8: add simple i2c rtc driver for NXP PCF8523
datasheet: http://nxp.com/docs/en/data-sheet/PCF8523.pdf
This commit is contained in:
parent
dd8a2d3964
commit
951ab17262
2 changed files with 350 additions and 0 deletions
349
sys/src/9/imx8/devrtc.c
Normal file
349
sys/src/9/imx8/devrtc.c
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
/*
|
||||||
|
* NXP PCF8523 real time clock
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "u.h"
|
||||||
|
#include "../port/lib.h"
|
||||||
|
#include "mem.h"
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
#include "../port/error.h"
|
||||||
|
#include "../port/i2c.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Seconds= 0x03,
|
||||||
|
Minutes= 0x04,
|
||||||
|
Hours= 0x05,
|
||||||
|
Mday= 0x06,
|
||||||
|
Wday= 0x07,
|
||||||
|
Month= 0x08,
|
||||||
|
Year= 0x09,
|
||||||
|
Nbcd= 1+Year-Seconds,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Rtc Rtc;
|
||||||
|
struct Rtc
|
||||||
|
{
|
||||||
|
int sec;
|
||||||
|
int min;
|
||||||
|
int hour;
|
||||||
|
int mday;
|
||||||
|
int wday;
|
||||||
|
int mon;
|
||||||
|
int year;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
Qdir = 0,
|
||||||
|
Qrtc,
|
||||||
|
};
|
||||||
|
|
||||||
|
Dirtab rtcdir[]={
|
||||||
|
".", {Qdir, 0, QTDIR}, 0, 0555,
|
||||||
|
"rtc", {Qrtc, 0}, 0, 0664,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ulong rtc2sec(Rtc*);
|
||||||
|
static void sec2rtc(ulong, Rtc*);
|
||||||
|
static I2Cdev *i2c;
|
||||||
|
|
||||||
|
static Chan*
|
||||||
|
rtcattach(char* spec)
|
||||||
|
{
|
||||||
|
i2c = i2cdev(i2cbus("i2c3"), 0x68);
|
||||||
|
if(i2c == nil)
|
||||||
|
error(Enonexist);
|
||||||
|
|
||||||
|
i2c->subaddr = 1;
|
||||||
|
i2c->size = 0x14;
|
||||||
|
|
||||||
|
return devattach('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)
|
||||||
|
{
|
||||||
|
omode = openmode(omode);
|
||||||
|
switch((ulong)c->qid.path){
|
||||||
|
case Qrtc:
|
||||||
|
if(strcmp(up->user, eve)!=0 && omode!=OREAD)
|
||||||
|
error(Eperm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rtcclose(Chan*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GETBCD(o) (((bcdclock[o]&0xf)%10) + 10*((bcdclock[o]>>4)%10))
|
||||||
|
|
||||||
|
static long
|
||||||
|
_rtctime(void)
|
||||||
|
{
|
||||||
|
uchar bcdclock[Nbcd];
|
||||||
|
Rtc rtc;
|
||||||
|
|
||||||
|
/* read clock values */
|
||||||
|
i2crecv(i2c, bcdclock, Nbcd, Seconds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* convert from BCD
|
||||||
|
*/
|
||||||
|
rtc.sec = GETBCD(Seconds-Seconds) % 60;
|
||||||
|
rtc.min = GETBCD(Minutes-Seconds) % 60;
|
||||||
|
rtc.hour = GETBCD(Hours-Seconds) % 24;
|
||||||
|
rtc.mday = GETBCD(Mday-Seconds);
|
||||||
|
rtc.wday = GETBCD(Wday-Seconds) % 7;
|
||||||
|
rtc.mon = GETBCD(Month-Seconds);
|
||||||
|
rtc.year = GETBCD(Year-Seconds) % 100;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the world starts jan 1 1970
|
||||||
|
*/
|
||||||
|
if(rtc.year < 70)
|
||||||
|
rtc.year += 2000;
|
||||||
|
else
|
||||||
|
rtc.year += 1900;
|
||||||
|
return rtc2sec(&rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
rtctime(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
long t, ot;
|
||||||
|
|
||||||
|
/* loop till we get two reads in a row the same */
|
||||||
|
t = _rtctime();
|
||||||
|
for(i = 0; i < 100; i++){
|
||||||
|
ot = t;
|
||||||
|
t = _rtctime();
|
||||||
|
if(ot == t)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(i == 100) print("we are boofheads\n");
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
rtcread(Chan* c, void* buf, long n, vlong off)
|
||||||
|
{
|
||||||
|
ulong offset = off;
|
||||||
|
|
||||||
|
if(c->qid.type & QTDIR)
|
||||||
|
return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
|
||||||
|
|
||||||
|
switch((ulong)c->qid.path){
|
||||||
|
case Qrtc:
|
||||||
|
return readnum(offset, buf, n, rtctime(), 12);
|
||||||
|
}
|
||||||
|
error(Ebadarg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
|
||||||
|
|
||||||
|
static long
|
||||||
|
rtcwrite(Chan* c, void* buf, long n, vlong off)
|
||||||
|
{
|
||||||
|
Rtc rtc;
|
||||||
|
ulong secs;
|
||||||
|
char *cp, sbuf[32];
|
||||||
|
uchar bcdclock[Nbcd];
|
||||||
|
ulong offset = off;
|
||||||
|
|
||||||
|
if(offset!=0)
|
||||||
|
error(Ebadarg);
|
||||||
|
|
||||||
|
|
||||||
|
switch((ulong)c->qid.path){
|
||||||
|
case Qrtc:
|
||||||
|
if(n >= sizeof(sbuf))
|
||||||
|
error(Ebadarg);
|
||||||
|
strncpy(sbuf, buf, n);
|
||||||
|
sbuf[n] = '\0'
|
||||||
|
for(cp = sbuf; *cp != '\0'; cp++)
|
||||||
|
if(*cp >= '0' && *cp <= '9')
|
||||||
|
break;
|
||||||
|
secs = strtoul(cp, 0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* convert to bcd
|
||||||
|
*/
|
||||||
|
sec2rtc(secs, &rtc);
|
||||||
|
|
||||||
|
PUTBCD(rtc.sec, Seconds-Seconds);
|
||||||
|
PUTBCD(rtc.min, Minutes-Seconds);
|
||||||
|
PUTBCD(rtc.hour, Hours-Seconds);
|
||||||
|
PUTBCD(rtc.mday, Mday-Seconds);
|
||||||
|
PUTBCD(rtc.wday, Wday-Seconds);
|
||||||
|
PUTBCD(rtc.mon, Month-Seconds);
|
||||||
|
PUTBCD(rtc.year, Year-Seconds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* write the clock
|
||||||
|
*/
|
||||||
|
i2csend(i2c, bcdclock, Nbcd, Seconds);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
error(Ebadarg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dev rtcdevtab = {
|
||||||
|
'r',
|
||||||
|
"rtc",
|
||||||
|
|
||||||
|
devreset,
|
||||||
|
devinit,
|
||||||
|
devshutdown,
|
||||||
|
rtcattach,
|
||||||
|
rtcwalk,
|
||||||
|
rtcstat,
|
||||||
|
rtcopen,
|
||||||
|
devcreate,
|
||||||
|
rtcclose,
|
||||||
|
rtcread,
|
||||||
|
devbread,
|
||||||
|
rtcwrite,
|
||||||
|
devbwrite,
|
||||||
|
devremove,
|
||||||
|
devwstat,
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 y)
|
||||||
|
{
|
||||||
|
if((y%4) == 0 && ((y%100) != 0 || (y%400) == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ dev
|
||||||
uart
|
uart
|
||||||
usb
|
usb
|
||||||
i2c
|
i2c
|
||||||
|
rtc devi2c
|
||||||
sd
|
sd
|
||||||
|
|
||||||
link
|
link
|
||||||
|
|
Loading…
Reference in a new issue