xref: /openbsd-src/sys/dev/fdt/imxrtc.c (revision 62d244ed99f17c1263ee095bc7d8fa1f61df02fd)
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