code: plan9front

Download patch

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