xref: /openbsd-src/sys/arch/octeon/dev/octrtc.c (revision 0701a1582b980c3a7d09f2b4176795598cce62d3)
1*0701a158Skettenis /*	$OpenBSD: octrtc.c,v 1.15 2022/10/12 13:39:50 kettenis Exp $	*/
21d0b786fSpirofti 
31d0b786fSpirofti /*
41d0b786fSpirofti  * Copyright (c) 2013, 2014 Paul Irofti.
51d0b786fSpirofti  *
61d0b786fSpirofti  * Permission to use, copy, modify, and distribute this software for any
71d0b786fSpirofti  * purpose with or without fee is hereby granted, provided that the above
81d0b786fSpirofti  * copyright notice and this permission notice appear in all copies.
91d0b786fSpirofti  *
101d0b786fSpirofti  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111d0b786fSpirofti  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121d0b786fSpirofti  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131d0b786fSpirofti  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141d0b786fSpirofti  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151d0b786fSpirofti  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161d0b786fSpirofti  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171d0b786fSpirofti  */
181d0b786fSpirofti 
191d0b786fSpirofti #include <sys/param.h>
201d0b786fSpirofti #include <sys/systm.h>
211d0b786fSpirofti #include <sys/device.h>
221d0b786fSpirofti #include <sys/proc.h>
231d0b786fSpirofti 
242cef8dbaSvisa #include <dev/clock_subr.h>
251d0b786fSpirofti 
261d0b786fSpirofti #include <machine/bus.h>
271d0b786fSpirofti #include <machine/autoconf.h>
281d0b786fSpirofti #include <machine/octeonvar.h>
291d0b786fSpirofti 
301d0b786fSpirofti #ifdef OCTRTC_DEBUG
311d0b786fSpirofti #define DPRINTF(x)	printf x
321d0b786fSpirofti #else
331d0b786fSpirofti #define DPRINTF(x)
341d0b786fSpirofti #endif
351d0b786fSpirofti 
361d0b786fSpirofti #define MIO_TWS_SW_TWSI		0x0001180000001000ULL
371d0b786fSpirofti #define MIO_TWS_SW_TWSI_EXT	0x0001180000001018ULL
381d0b786fSpirofti #define OCTRTC_REG	0x68
391d0b786fSpirofti 
402cef8dbaSvisa struct octrtc_softc {
412cef8dbaSvisa 	struct device			sc_dev;
422cef8dbaSvisa 	struct todr_chip_handle		sc_todr;
432cef8dbaSvisa };
442cef8dbaSvisa 
451d0b786fSpirofti struct cfdriver octrtc_cd = {
461d0b786fSpirofti 	NULL, "octrtc", DV_DULL
471d0b786fSpirofti };
481d0b786fSpirofti 
491d0b786fSpirofti int	octrtc_match(struct device *, void *, void *);
501d0b786fSpirofti void	octrtc_attach(struct device *, struct device *, void *);
511d0b786fSpirofti 
522cef8dbaSvisa int	octrtc_gettime(struct todr_chip_handle *, struct timeval *);
531d0b786fSpirofti int	octrtc_read(uint8_t *, char);
541d0b786fSpirofti 
552cef8dbaSvisa int	octrtc_settime(struct todr_chip_handle *, struct timeval *);
561d0b786fSpirofti int	octrtc_write(uint8_t);
571d0b786fSpirofti 
581d0b786fSpirofti 
59471aeecfSnaddy const struct cfattach octrtc_ca = {
602cef8dbaSvisa 	sizeof(struct octrtc_softc), octrtc_match, octrtc_attach,
611d0b786fSpirofti };
621d0b786fSpirofti 
631d0b786fSpirofti 
641d0b786fSpirofti union mio_tws_sw_twsi_reg {
651d0b786fSpirofti 	uint64_t reg;
661d0b786fSpirofti 	struct cvmx_mio_twsx_sw_twsi_s {
671d0b786fSpirofti 		uint64_t v:1;		/* Valid bit */
681d0b786fSpirofti 		uint64_t slonly:1;	/* Slave Only Mode */
691d0b786fSpirofti 		uint64_t eia:1;		/* Extended Internal Address */
701d0b786fSpirofti 		uint64_t op:4;		/* Opcode field */
711d0b786fSpirofti 		uint64_t r:1;		/* Read bit or result */
721d0b786fSpirofti 		uint64_t sovr:1;	/* Size Override */
731d0b786fSpirofti 		uint64_t size:3;	/* Size in bytes */
741d0b786fSpirofti 		uint64_t scr:2;		/* Scratch, unused */
751d0b786fSpirofti 		uint64_t a:10;		/* Address field */
761d0b786fSpirofti 		uint64_t ia:5;		/* Internal Address */
771d0b786fSpirofti 		uint64_t eop_ia:3;	/* Extra opcode */
781d0b786fSpirofti 		uint64_t d:32;		/* Data Field */
791d0b786fSpirofti 	} field;
801d0b786fSpirofti };
811d0b786fSpirofti 
821d0b786fSpirofti 
832bbf581cSvisa static const enum octeon_board no_rtc_boards[] = {
842bbf581cSvisa 	BOARD_UBIQUITI_E100,
852bbf581cSvisa 	BOARD_UBIQUITI_E120,
862bbf581cSvisa 	BOARD_UBIQUITI_E200,
872bbf581cSvisa 	BOARD_UBIQUITI_E220,
882bbf581cSvisa 	BOARD_UBIQUITI_E300,
892bbf581cSvisa 	BOARD_UBIQUITI_E1000,
902bbf581cSvisa 	BOARD_RHINOLABS_UTM8,
91adbafb79Svisa };
92adbafb79Svisa 
931d0b786fSpirofti int
octrtc_match(struct device * parent,void * match,void * aux)941d0b786fSpirofti octrtc_match(struct device *parent, void *match, void *aux)
951d0b786fSpirofti {
961d0b786fSpirofti 	struct mainbus_attach_args *maa = aux;
971d0b786fSpirofti 	struct cfdata *cf = match;
98adbafb79Svisa 	int i;
991d0b786fSpirofti 
1001d0b786fSpirofti 	if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0)
1011d0b786fSpirofti 		return 0;
102adbafb79Svisa 	for (i = 0; i < nitems(no_rtc_boards); i++)
1032bbf581cSvisa 		if (octeon_board == no_rtc_boards[i])
1041d0b786fSpirofti 			return 0;
1051d0b786fSpirofti 	return 1;
1061d0b786fSpirofti }
1071d0b786fSpirofti 
1081d0b786fSpirofti void
octrtc_attach(struct device * parent,struct device * self,void * aux)1091d0b786fSpirofti octrtc_attach(struct device *parent, struct device *self, void *aux)
1101d0b786fSpirofti {
1111d0b786fSpirofti 	struct octrtc_softc *sc = (struct octrtc_softc *)self;
1121d0b786fSpirofti 
1132cef8dbaSvisa 	sc->sc_todr.cookie = sc;
1142cef8dbaSvisa 	sc->sc_todr.todr_gettime = octrtc_gettime;
1152cef8dbaSvisa 	sc->sc_todr.todr_settime = octrtc_settime;
116*0701a158Skettenis 	sc->sc_todr.todr_quality = 0;
1172cef8dbaSvisa 	todr_attach(&sc->sc_todr);
1181d0b786fSpirofti 
1191d0b786fSpirofti 	printf(": DS1337\n");
1201d0b786fSpirofti }
1211d0b786fSpirofti 
1222cef8dbaSvisa int
octrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)1232cef8dbaSvisa octrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
1241d0b786fSpirofti {
1252cef8dbaSvisa 	struct clock_ymdhms dt;
1261d0b786fSpirofti 	uint8_t tod[8];
1271d0b786fSpirofti 	uint8_t check;
1281d0b786fSpirofti 	int i, rc;
1291d0b786fSpirofti 
1301d0b786fSpirofti 	int nretries = 2;
1311d0b786fSpirofti 
1321d0b786fSpirofti 	DPRINTF(("\nTOD: "));
1331d0b786fSpirofti 	while (nretries--) {
1341d0b786fSpirofti 		rc = octrtc_read(&tod[0], 1);	/* ia read */
1351d0b786fSpirofti 		if (rc) {
1361d0b786fSpirofti 			DPRINTF(("octrtc_read(0) failed %d", rc));
1372cef8dbaSvisa 			return EIO;
1381d0b786fSpirofti 		}
1391d0b786fSpirofti 
1401d0b786fSpirofti 		for (i = 1; i < 8; i++) {
1411d0b786fSpirofti 			rc = octrtc_read(&tod[i], 0);	/* current address */
1421d0b786fSpirofti 			if (rc) {
1431d0b786fSpirofti 				DPRINTF(("octrtc_read(%d) failed %d", i, rc));
1442cef8dbaSvisa 				return EIO;
1451d0b786fSpirofti 			}
1461d0b786fSpirofti 			DPRINTF(("%#X ", tod[i]));
1471d0b786fSpirofti 		}
1481d0b786fSpirofti 
1491d0b786fSpirofti 		/* Check against time-wrap */
1501d0b786fSpirofti 		rc = octrtc_read(&check, 1);	/* ia read */
1511d0b786fSpirofti 		if (rc) {
1522cef8dbaSvisa 			DPRINTF(("octrtc_read(check) failed %d", rc));
1532cef8dbaSvisa 			return EIO;
1541d0b786fSpirofti 		}
1551d0b786fSpirofti 		if ((check & 0xf) == (tod[0] & 0xf))
1561d0b786fSpirofti 			break;
1571d0b786fSpirofti 	}
1581d0b786fSpirofti 	DPRINTF(("\n"));
1591d0b786fSpirofti 
1601d0b786fSpirofti 	DPRINTF(("Time: %d %d %d (%d) %02d:%02d:%02d\n",
1611d0b786fSpirofti 	    ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]),	/* year */
1621d0b786fSpirofti 	    FROMBCD(tod[5] & 0x1f),	/* month */
1631d0b786fSpirofti 	    FROMBCD(tod[4] & 0x3f),	/* day */
1641d0b786fSpirofti 	    (tod[3] & 0x7),		/* day of the week */
1651d0b786fSpirofti 	    FROMBCD(tod[2] & 0x3f),	/* hour */
1661d0b786fSpirofti 	    FROMBCD(tod[1] & 0x7f),	/* minute */
1671d0b786fSpirofti 	    FROMBCD(tod[0] & 0x7f)));	/* second */
1681d0b786fSpirofti 
1692cef8dbaSvisa 	dt.dt_year = ((tod[5] & 0x80) ? 2000 : 1900) + FROMBCD(tod[6]);
1702cef8dbaSvisa 	dt.dt_mon = FROMBCD(tod[5] & 0x1f);
1712cef8dbaSvisa 	dt.dt_day = FROMBCD(tod[4] & 0x3f);
1722cef8dbaSvisa 	dt.dt_hour = FROMBCD(tod[2] & 0x3f);
1731d0b786fSpirofti 	if ((tod[2] & 0x40) && (tod[2] & 0x20))	/* adjust AM/PM format */
1742cef8dbaSvisa 		dt.dt_hour = (dt.dt_hour + 12) % 24;
1752cef8dbaSvisa 	dt.dt_min = FROMBCD(tod[1] & 0x7f);
1762cef8dbaSvisa 	dt.dt_sec = FROMBCD(tod[0] & 0x7f);
1772cef8dbaSvisa 
1782cef8dbaSvisa 	if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
1792cef8dbaSvisa 	    dt.dt_day > 31 || dt.dt_day == 0 ||
1802cef8dbaSvisa 	    dt.dt_mon > 12 || dt.dt_mon == 0 ||
1812cef8dbaSvisa 	    dt.dt_year < POSIX_BASE_YEAR)
1822cef8dbaSvisa 		return EINVAL;
1832cef8dbaSvisa 
1842cef8dbaSvisa 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
1852cef8dbaSvisa 	tv->tv_usec = 0;
1862cef8dbaSvisa 	return 0;
1871d0b786fSpirofti }
1881d0b786fSpirofti 
1891d0b786fSpirofti int
octrtc_read(uint8_t * data,char ia)1901d0b786fSpirofti octrtc_read(uint8_t *data, char ia)
1911d0b786fSpirofti {
1921d0b786fSpirofti 	int nretries = 5;
1931d0b786fSpirofti 	union mio_tws_sw_twsi_reg twsi;
1941d0b786fSpirofti 
1951d0b786fSpirofti again:
1961d0b786fSpirofti 	twsi.reg = 0;
1971d0b786fSpirofti 	twsi.field.v = 1;
1981d0b786fSpirofti 	twsi.field.r = 1;
1991d0b786fSpirofti 	twsi.field.sovr = 1;
2001d0b786fSpirofti 	twsi.field.a = OCTRTC_REG;
2011d0b786fSpirofti 	if (ia) {
2021d0b786fSpirofti 		twsi.field.op = 1;
2031d0b786fSpirofti 	}
2041d0b786fSpirofti 
2051d0b786fSpirofti 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg);
2061d0b786fSpirofti 	/* The 1st bit is cleared when the operation is complete */
2071d0b786fSpirofti 	do {
2081d0b786fSpirofti 		delay(1000);
2091d0b786fSpirofti 		twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI);
2101d0b786fSpirofti 	} while (twsi.field.v);
2111d0b786fSpirofti 	DPRINTF(("%#llX ", twsi.reg));
2121d0b786fSpirofti 
2131d0b786fSpirofti 	/*
2141d0b786fSpirofti 	 * The data field is in the upper 32 bits and we're only
2151d0b786fSpirofti 	 * interested in the first byte.
2161d0b786fSpirofti 	 */
2171d0b786fSpirofti 	*data = twsi.field.d & 0xff;
2181d0b786fSpirofti 
2191d0b786fSpirofti 	/* 8th bit is the read result: 1 = success, 0 = failure */
2201d0b786fSpirofti 	if (twsi.field.r == 0) {
2211d0b786fSpirofti 		/*
2221d0b786fSpirofti 		 * Lost arbitration : 0x38, 0x68, 0xB0, 0x78
2231d0b786fSpirofti 		 * Core busy as slave: 0x80, 0x88, 0xA0, 0xA8, 0xB8, 0xC0, 0xC8
2241d0b786fSpirofti 		 */
2251d0b786fSpirofti 		if (*data == 0x38 || *data == 0x68 || *data == 0xB0 ||
2261d0b786fSpirofti 		    *data == 0x78 || *data == 0x80 || *data == 0x88 ||
2271d0b786fSpirofti 		    *data == 0xA0 || *data == 0xA8 || *data == 0xB8 ||
2281d0b786fSpirofti 		    *data == 0xC0 || *data == 0xC8)
2291d0b786fSpirofti 			if (nretries--)
2301d0b786fSpirofti 				goto again;
2311d0b786fSpirofti 		return EIO;
2321d0b786fSpirofti 	}
2331d0b786fSpirofti 
2341d0b786fSpirofti 	return 0;
2351d0b786fSpirofti }
2361d0b786fSpirofti 
2372cef8dbaSvisa int
octrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)2382cef8dbaSvisa octrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
2391d0b786fSpirofti {
2402cef8dbaSvisa 	struct clock_ymdhms dt;
2411d0b786fSpirofti 	int nretries = 2;
2421d0b786fSpirofti 	int rc, i;
2431d0b786fSpirofti 	uint8_t tod[8];
2441d0b786fSpirofti 	uint8_t check;
2451d0b786fSpirofti 
2462cef8dbaSvisa 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
2471d0b786fSpirofti 
2482cef8dbaSvisa 	DPRINTF(("settime: %d %d %d (%d) %02d:%02d:%02d\n",
2492cef8dbaSvisa 	    dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_wday,
2502cef8dbaSvisa 	    dt.dt_hour, dt.dt_min, dt.dt_sec));
2512cef8dbaSvisa 
2522cef8dbaSvisa 	tod[0] = TOBCD(dt.dt_sec);
2532cef8dbaSvisa 	tod[1] = TOBCD(dt.dt_min);
2542cef8dbaSvisa 	tod[2] = TOBCD(dt.dt_hour);
2552cef8dbaSvisa 	tod[3] = TOBCD(dt.dt_wday + 1);
2562cef8dbaSvisa 	tod[4] = TOBCD(dt.dt_day);
2572cef8dbaSvisa 	tod[5] = TOBCD(dt.dt_mon);
2582cef8dbaSvisa 	if (dt.dt_year >= 2000)
2591d0b786fSpirofti 		tod[5] |= 0x80;
2602cef8dbaSvisa 	tod[6] = TOBCD(dt.dt_year % 100);
2611d0b786fSpirofti 
2621d0b786fSpirofti 	while (nretries--) {
2631d0b786fSpirofti 		for (i = 0; i < 7; i++) {
2641d0b786fSpirofti 			rc = octrtc_write(tod[i]);
2651d0b786fSpirofti 			if (rc) {
2661d0b786fSpirofti 				DPRINTF(("octrtc_write(%d) failed %d", i, rc));
2672cef8dbaSvisa 				return EIO;
2681d0b786fSpirofti 			}
2691d0b786fSpirofti 		}
2701d0b786fSpirofti 
2711d0b786fSpirofti 		rc = octrtc_read(&check, 1);
2721d0b786fSpirofti 		if (rc) {
2731d0b786fSpirofti 			DPRINTF(("octrtc_read(check) failed %d", rc));
2742cef8dbaSvisa 			return EIO;
2751d0b786fSpirofti 		}
2761d0b786fSpirofti 
2771d0b786fSpirofti 		if ((check & 0xf) == (tod[0] & 0xf))
2781d0b786fSpirofti 			break;
2791d0b786fSpirofti 	}
2802cef8dbaSvisa 
2812cef8dbaSvisa 	return 0;
2821d0b786fSpirofti }
2831d0b786fSpirofti 
2841d0b786fSpirofti int
octrtc_write(uint8_t data)2851d0b786fSpirofti octrtc_write(uint8_t data)
2861d0b786fSpirofti {
2871d0b786fSpirofti 	union mio_tws_sw_twsi_reg twsi;
2881d0b786fSpirofti 	int npoll = 128;
2891d0b786fSpirofti 
2901d0b786fSpirofti 	twsi.reg = 0;
2911d0b786fSpirofti 	twsi.field.v = 1;
2921d0b786fSpirofti 	twsi.field.sovr = 1;
2931d0b786fSpirofti 	twsi.field.op = 1;
2941d0b786fSpirofti 	twsi.field.a = OCTRTC_REG;
2951d0b786fSpirofti 	twsi.field.d = data & 0xffffffff;
2961d0b786fSpirofti 
2971d0b786fSpirofti 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI_EXT, 0);
2981d0b786fSpirofti 	octeon_xkphys_write_8(MIO_TWS_SW_TWSI, twsi.reg);
2991d0b786fSpirofti 	do {
3001d0b786fSpirofti 		delay(1000);
3011d0b786fSpirofti 		twsi.reg = octeon_xkphys_read_8(MIO_TWS_SW_TWSI);
3021d0b786fSpirofti 	} while (twsi.field.v);
3031d0b786fSpirofti 
3041d0b786fSpirofti 	/* Try to read back */
3051d0b786fSpirofti 	while (npoll-- && octrtc_read(&data, 0));
3061d0b786fSpirofti 
3071d0b786fSpirofti 	return npoll ? 0 : EIO;
3081d0b786fSpirofti }
309