imxrtc.c revision 1.1
1/*	$OpenBSD: imxrtc.c,v 1.1 2018/06/16 14:11:35 kettenis Exp $	*/
2/*
3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21
22#include <machine/intr.h>
23#include <machine/bus.h>
24#include <machine/fdt.h>
25
26#include <dev/ofw/openfirm.h>
27#include <dev/ofw/fdt.h>
28#include <dev/ofw/ofw_misc.h>
29
30#include <dev/clock_subr.h>
31
32extern todr_chip_handle_t todr_handle;
33
34/* Registers. */
35#define LPCR			0x38
36#define  LPCR_SRTC_ENV		(1 << 0)
37#define LPSR			0x4c
38#define LPSRTCMR		0x50
39#define LPSRTCLR		0x54
40
41#define HREAD4(sc, reg)							\
42	(regmap_read_4((sc)->sc_rm, (reg)))
43#define HWRITE4(sc, reg, val)						\
44	regmap_write_4((sc)->sc_rm, (reg), (val))
45
46struct imxrtc_softc {
47	struct device		sc_dev;
48	struct regmap		*sc_rm;
49
50	struct todr_chip_handle sc_todr;
51};
52
53int imxrtc_match(struct device *, void *, void *);
54void imxrtc_attach(struct device *, struct device *, void *);
55
56struct cfattach	imxrtc_ca = {
57	sizeof (struct imxrtc_softc), imxrtc_match, imxrtc_attach
58};
59
60struct cfdriver imxrtc_cd = {
61	NULL, "imxrtc", DV_DULL
62};
63
64int	imxrtc_gettime(struct todr_chip_handle *, struct timeval *);
65int	imxrtc_settime(struct todr_chip_handle *, struct timeval *);
66
67int
68imxrtc_match(struct device *parent, void *match, void *aux)
69{
70	struct fdt_attach_args *faa = aux;
71
72	return OF_is_compatible(faa->fa_node, "fsl,sec-v4.0-mon-rtc-lp");
73}
74
75void
76imxrtc_attach(struct device *parent, struct device *self, void *aux)
77{
78	struct imxrtc_softc *sc = (struct imxrtc_softc *)self;
79	struct fdt_attach_args *faa = aux;
80	uint32_t regmap;
81
82	regmap = OF_getpropint(faa->fa_node, "regmap", 0);
83	sc->sc_rm = regmap_byphandle(regmap);
84	if (sc->sc_rm == NULL) {
85		printf(": no registers\n");
86		return;
87	}
88
89	printf("\n");
90
91	sc->sc_todr.cookie = sc;
92	sc->sc_todr.todr_gettime = imxrtc_gettime;
93	sc->sc_todr.todr_settime = imxrtc_settime;
94	todr_handle = &sc->sc_todr;
95}
96
97int
98imxrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
99{
100	struct imxrtc_softc *sc = handle->cookie;
101	uint64_t mr, lr, srtc, srtc2;
102	uint32_t cr;
103	int retries;
104	int s;
105
106	cr = HREAD4(sc, LPCR);
107	if ((cr & LPCR_SRTC_ENV) == 0)
108		return EINVAL;
109
110	/*
111	 * Read counters until we read back the same values twice.
112	 * This shouldn't take more than two attempts; throw in an
113	 * extra round just in case.
114	 */
115	s = splhigh();
116	mr = HREAD4(sc, LPSRTCMR);
117	lr = HREAD4(sc, LPSRTCLR);
118	srtc = (mr << 32) | lr;
119	for (retries = 3; retries > 0; retries--) {
120		mr = HREAD4(sc, LPSRTCMR);
121		lr = HREAD4(sc, LPSRTCLR);
122		srtc2 = (mr << 32) | lr;
123		if (srtc == srtc2)
124			break;
125		srtc = srtc2;
126	}
127	splx(s);
128	if (retries == 0)
129		return EIO;
130
131	tv->tv_sec = srtc / 32768;
132	tv->tv_usec = ((srtc % 32768) * 1000000U) / 32768U;
133	return 0;
134}
135
136int
137imxrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
138{
139	struct imxrtc_softc *sc = handle->cookie;
140	uint64_t srtc;
141	uint32_t cr;
142	int timeout;
143
144	/* Disable RTC. */
145	cr = HREAD4(sc, LPCR);
146	cr &= ~LPCR_SRTC_ENV;
147	HWRITE4(sc, LPCR, cr);
148	for (timeout = 1000000; timeout > 0; timeout--) {
149		if ((HREAD4(sc, LPCR) & LPCR_SRTC_ENV) == 0)
150			break;
151	}
152
153	srtc = tv->tv_sec * 32768 + (tv->tv_usec * 32768U / 1000000U);
154	HWRITE4(sc, LPSRTCMR, srtc >> 32);
155	HWRITE4(sc, LPSRTCLR, srtc & 0xffffffff);
156
157	/* Enable RTC. */
158	cr |= LPCR_SRTC_ENV;
159	HWRITE4(sc, LPCR, cr);
160	for (timeout = 1000000; timeout > 0; timeout--) {
161		if (HREAD4(sc, LPCR) & LPCR_SRTC_ENV)
162			break;
163	}
164
165	return 0;
166}
167