ref: 951ab17262c61dca249baf5aca1810b55117e684
parent: dd8a2d3964f430228dc00717e2055399593d30de
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed Jul 20 14:49:28 EDT 2022
imx8: add simple i2c rtc driver for NXP PCF8523 datasheet: http://nxp.com/docs/en/data-sheet/PCF8523.pdf
--- /dev/null
+++ b/sys/src/9/imx8/devrtc.c
@@ -1,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;
+}
--- a/sys/src/9/imx8/reform
+++ b/sys/src/9/imx8/reform
@@ -20,6 +20,7 @@
uart
usb
i2c
+ rtc devi2c
sd
link