1*62d244edSkettenis /* $OpenBSD: imxrtc.c,v 1.3 2022/10/17 19:09:46 kettenis Exp $ */
24bcbbc99Skettenis /*
34bcbbc99Skettenis * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
44bcbbc99Skettenis *
54bcbbc99Skettenis * Permission to use, copy, modify, and distribute this software for any
64bcbbc99Skettenis * purpose with or without fee is hereby granted, provided that the above
74bcbbc99Skettenis * copyright notice and this permission notice appear in all copies.
84bcbbc99Skettenis *
94bcbbc99Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104bcbbc99Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114bcbbc99Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124bcbbc99Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134bcbbc99Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144bcbbc99Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154bcbbc99Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164bcbbc99Skettenis */
174bcbbc99Skettenis
184bcbbc99Skettenis #include <sys/param.h>
194bcbbc99Skettenis #include <sys/systm.h>
204bcbbc99Skettenis #include <sys/device.h>
214bcbbc99Skettenis
224bcbbc99Skettenis #include <machine/intr.h>
234bcbbc99Skettenis #include <machine/bus.h>
244bcbbc99Skettenis #include <machine/fdt.h>
254bcbbc99Skettenis
264bcbbc99Skettenis #include <dev/ofw/openfirm.h>
274bcbbc99Skettenis #include <dev/ofw/fdt.h>
284bcbbc99Skettenis #include <dev/ofw/ofw_misc.h>
294bcbbc99Skettenis
304bcbbc99Skettenis #include <dev/clock_subr.h>
314bcbbc99Skettenis
324bcbbc99Skettenis /* Registers. */
334bcbbc99Skettenis #define LPCR 0x38
344bcbbc99Skettenis #define LPCR_SRTC_ENV (1 << 0)
354bcbbc99Skettenis #define LPSR 0x4c
364bcbbc99Skettenis #define LPSRTCMR 0x50
374bcbbc99Skettenis #define LPSRTCLR 0x54
384bcbbc99Skettenis
394bcbbc99Skettenis #define HREAD4(sc, reg) \
404bcbbc99Skettenis (regmap_read_4((sc)->sc_rm, (reg)))
414bcbbc99Skettenis #define HWRITE4(sc, reg, val) \
424bcbbc99Skettenis regmap_write_4((sc)->sc_rm, (reg), (val))
434bcbbc99Skettenis
444bcbbc99Skettenis struct imxrtc_softc {
454bcbbc99Skettenis struct device sc_dev;
464bcbbc99Skettenis struct regmap *sc_rm;
474bcbbc99Skettenis
484bcbbc99Skettenis struct todr_chip_handle sc_todr;
494bcbbc99Skettenis };
504bcbbc99Skettenis
514bcbbc99Skettenis int imxrtc_match(struct device *, void *, void *);
524bcbbc99Skettenis void imxrtc_attach(struct device *, struct device *, void *);
534bcbbc99Skettenis
549fdf0c62Smpi const struct cfattach imxrtc_ca = {
554bcbbc99Skettenis sizeof (struct imxrtc_softc), imxrtc_match, imxrtc_attach
564bcbbc99Skettenis };
574bcbbc99Skettenis
584bcbbc99Skettenis struct cfdriver imxrtc_cd = {
594bcbbc99Skettenis NULL, "imxrtc", DV_DULL
604bcbbc99Skettenis };
614bcbbc99Skettenis
624bcbbc99Skettenis int imxrtc_gettime(struct todr_chip_handle *, struct timeval *);
634bcbbc99Skettenis int imxrtc_settime(struct todr_chip_handle *, struct timeval *);
644bcbbc99Skettenis
654bcbbc99Skettenis int
imxrtc_match(struct device * parent,void * match,void * aux)664bcbbc99Skettenis imxrtc_match(struct device *parent, void *match, void *aux)
674bcbbc99Skettenis {
684bcbbc99Skettenis struct fdt_attach_args *faa = aux;
694bcbbc99Skettenis
704bcbbc99Skettenis return OF_is_compatible(faa->fa_node, "fsl,sec-v4.0-mon-rtc-lp");
714bcbbc99Skettenis }
724bcbbc99Skettenis
734bcbbc99Skettenis void
imxrtc_attach(struct device * parent,struct device * self,void * aux)744bcbbc99Skettenis imxrtc_attach(struct device *parent, struct device *self, void *aux)
754bcbbc99Skettenis {
764bcbbc99Skettenis struct imxrtc_softc *sc = (struct imxrtc_softc *)self;
774bcbbc99Skettenis struct fdt_attach_args *faa = aux;
784bcbbc99Skettenis uint32_t regmap;
794bcbbc99Skettenis
804bcbbc99Skettenis regmap = OF_getpropint(faa->fa_node, "regmap", 0);
814bcbbc99Skettenis sc->sc_rm = regmap_byphandle(regmap);
824bcbbc99Skettenis if (sc->sc_rm == NULL) {
834bcbbc99Skettenis printf(": no registers\n");
844bcbbc99Skettenis return;
854bcbbc99Skettenis }
864bcbbc99Skettenis
874bcbbc99Skettenis printf("\n");
884bcbbc99Skettenis
894bcbbc99Skettenis sc->sc_todr.cookie = sc;
904bcbbc99Skettenis sc->sc_todr.todr_gettime = imxrtc_gettime;
914bcbbc99Skettenis sc->sc_todr.todr_settime = imxrtc_settime;
92*62d244edSkettenis sc->sc_todr.todr_quality = 0;
93*62d244edSkettenis todr_attach(&sc->sc_todr);
944bcbbc99Skettenis }
954bcbbc99Skettenis
964bcbbc99Skettenis int
imxrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)974bcbbc99Skettenis imxrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
984bcbbc99Skettenis {
994bcbbc99Skettenis struct imxrtc_softc *sc = handle->cookie;
1004bcbbc99Skettenis uint64_t mr, lr, srtc, srtc2;
1014bcbbc99Skettenis uint32_t cr;
1024bcbbc99Skettenis int retries;
1034bcbbc99Skettenis int s;
1044bcbbc99Skettenis
1054bcbbc99Skettenis cr = HREAD4(sc, LPCR);
1064bcbbc99Skettenis if ((cr & LPCR_SRTC_ENV) == 0)
1074bcbbc99Skettenis return EINVAL;
1084bcbbc99Skettenis
1094bcbbc99Skettenis /*
1104bcbbc99Skettenis * Read counters until we read back the same values twice.
1114bcbbc99Skettenis * This shouldn't take more than two attempts; throw in an
1124bcbbc99Skettenis * extra round just in case.
1134bcbbc99Skettenis */
1144bcbbc99Skettenis s = splhigh();
1154bcbbc99Skettenis mr = HREAD4(sc, LPSRTCMR);
1164bcbbc99Skettenis lr = HREAD4(sc, LPSRTCLR);
1174bcbbc99Skettenis srtc = (mr << 32) | lr;
1184bcbbc99Skettenis for (retries = 3; retries > 0; retries--) {
1194bcbbc99Skettenis mr = HREAD4(sc, LPSRTCMR);
1204bcbbc99Skettenis lr = HREAD4(sc, LPSRTCLR);
1214bcbbc99Skettenis srtc2 = (mr << 32) | lr;
1224bcbbc99Skettenis if (srtc == srtc2)
1234bcbbc99Skettenis break;
1244bcbbc99Skettenis srtc = srtc2;
1254bcbbc99Skettenis }
1264bcbbc99Skettenis splx(s);
1274bcbbc99Skettenis if (retries == 0)
1284bcbbc99Skettenis return EIO;
1294bcbbc99Skettenis
1304bcbbc99Skettenis tv->tv_sec = srtc / 32768;
1314bcbbc99Skettenis tv->tv_usec = ((srtc % 32768) * 1000000U) / 32768U;
1324bcbbc99Skettenis return 0;
1334bcbbc99Skettenis }
1344bcbbc99Skettenis
1354bcbbc99Skettenis int
imxrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)1364bcbbc99Skettenis imxrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
1374bcbbc99Skettenis {
1384bcbbc99Skettenis struct imxrtc_softc *sc = handle->cookie;
1394bcbbc99Skettenis uint64_t srtc;
1404bcbbc99Skettenis uint32_t cr;
1414bcbbc99Skettenis int timeout;
1424bcbbc99Skettenis
1434bcbbc99Skettenis /* Disable RTC. */
1444bcbbc99Skettenis cr = HREAD4(sc, LPCR);
1454bcbbc99Skettenis cr &= ~LPCR_SRTC_ENV;
1464bcbbc99Skettenis HWRITE4(sc, LPCR, cr);
1474bcbbc99Skettenis for (timeout = 1000000; timeout > 0; timeout--) {
1484bcbbc99Skettenis if ((HREAD4(sc, LPCR) & LPCR_SRTC_ENV) == 0)
1494bcbbc99Skettenis break;
1504bcbbc99Skettenis }
1514bcbbc99Skettenis
1524bcbbc99Skettenis srtc = tv->tv_sec * 32768 + (tv->tv_usec * 32768U / 1000000U);
1534bcbbc99Skettenis HWRITE4(sc, LPSRTCMR, srtc >> 32);
1544bcbbc99Skettenis HWRITE4(sc, LPSRTCLR, srtc & 0xffffffff);
1554bcbbc99Skettenis
1564bcbbc99Skettenis /* Enable RTC. */
1574bcbbc99Skettenis cr |= LPCR_SRTC_ENV;
1584bcbbc99Skettenis HWRITE4(sc, LPCR, cr);
1594bcbbc99Skettenis for (timeout = 1000000; timeout > 0; timeout--) {
1604bcbbc99Skettenis if (HREAD4(sc, LPCR) & LPCR_SRTC_ENV)
1614bcbbc99Skettenis break;
1624bcbbc99Skettenis }
1634bcbbc99Skettenis
1644bcbbc99Skettenis return 0;
1654bcbbc99Skettenis }
166