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