xref: /freebsd-src/sys/arm/nvidia/tegra_rtc.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun  * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun  * All rights reserved.
4ef2ee5d0SMichal Meloun  *
5ef2ee5d0SMichal Meloun  * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun  * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun  * are met:
8ef2ee5d0SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun  *    documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun  *
14ef2ee5d0SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun  * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun  */
26ef2ee5d0SMichal Meloun 
27ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28ef2ee5d0SMichal Meloun /*
29ef2ee5d0SMichal Meloun  * RTC driver for Tegra SoCs.
30ef2ee5d0SMichal Meloun  */
31ef2ee5d0SMichal Meloun #include <sys/param.h>
32ef2ee5d0SMichal Meloun #include <sys/systm.h>
33ef2ee5d0SMichal Meloun #include <sys/bus.h>
34ef2ee5d0SMichal Meloun #include <sys/clock.h>
35ef2ee5d0SMichal Meloun #include <sys/kernel.h>
36ef2ee5d0SMichal Meloun #include <sys/limits.h>
37ef2ee5d0SMichal Meloun #include <sys/lock.h>
38ef2ee5d0SMichal Meloun #include <sys/mutex.h>
39ef2ee5d0SMichal Meloun #include <sys/module.h>
40ef2ee5d0SMichal Meloun #include <sys/resource.h>
41ef2ee5d0SMichal Meloun 
42ef2ee5d0SMichal Meloun #include <machine/bus.h>
43ef2ee5d0SMichal Meloun #include <machine/resource.h>
44ef2ee5d0SMichal Meloun #include <sys/rman.h>
45ef2ee5d0SMichal Meloun 
46be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
47ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
48ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
49ef2ee5d0SMichal Meloun 
50ef2ee5d0SMichal Meloun #include "clock_if.h"
51ef2ee5d0SMichal Meloun 
52ef2ee5d0SMichal Meloun #define	RTC_CONTROL				0x00
53ef2ee5d0SMichal Meloun #define	RTC_BUSY				0x04
54ef2ee5d0SMichal Meloun #define	 RTC_BUSY_STATUS				(1 << 0)
55ef2ee5d0SMichal Meloun #define	RTC_SECONDS				0x08
56ef2ee5d0SMichal Meloun #define	RTC_SHADOW_SECONDS			0x0c
57ef2ee5d0SMichal Meloun #define	RTC_MILLI_SECONDS			0x10
58ef2ee5d0SMichal Meloun #define	RTC_SECONDS_ALARM0			0x14
59ef2ee5d0SMichal Meloun #define	RTC_SECONDS_ALARM1			0x18
60ef2ee5d0SMichal Meloun #define	RTC_MILLI_SECONDS_ALARM			0x1c
61ef2ee5d0SMichal Meloun #define	RTC_SECONDS_COUNTDOWN_ALARM		0x20
62ef2ee5d0SMichal Meloun #define	RTC_MILLI_SECONDS_COUNTDOW_ALARM	0x24
63ef2ee5d0SMichal Meloun #define	RTC_INTR_MASK				0x28
64ef2ee5d0SMichal Meloun #define	 RTC_INTR_MSEC_CDN_ALARM			(1 << 4)
65ef2ee5d0SMichal Meloun #define	 RTC_INTR_SEC_CDN_ALARM				(1 << 3)
66ef2ee5d0SMichal Meloun #define	 RTC_INTR_MSEC_ALARM				(1 << 2)
67ef2ee5d0SMichal Meloun #define	 RTC_INTR_SEC_ALARM1				(1 << 1)
68ef2ee5d0SMichal Meloun #define	 RTC_INTR_SEC_ALARM0				(1 << 0)
69ef2ee5d0SMichal Meloun 
70ef2ee5d0SMichal Meloun #define	RTC_INTR_STATUS				0x2c
71ef2ee5d0SMichal Meloun #define	RTC_INTR_SOURCE				0x30
72ef2ee5d0SMichal Meloun #define	RTC_INTR_SET				0x34
73ef2ee5d0SMichal Meloun #define	RTC_CORRECTION_FACTOR			0x38
74ef2ee5d0SMichal Meloun 
75ef2ee5d0SMichal Meloun #define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
76ef2ee5d0SMichal Meloun #define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
77ef2ee5d0SMichal Meloun 
78ef2ee5d0SMichal Meloun #define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
79ef2ee5d0SMichal Meloun #define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
80ef2ee5d0SMichal Meloun #define	SLEEP(_sc, timeout)						\
81ef2ee5d0SMichal Meloun 	mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout);
82ef2ee5d0SMichal Meloun #define	LOCK_INIT(_sc)							\
83ef2ee5d0SMichal Meloun 	mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF)
84ef2ee5d0SMichal Meloun #define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
85ef2ee5d0SMichal Meloun #define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
86ef2ee5d0SMichal Meloun #define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
87ef2ee5d0SMichal Meloun 
88ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = {
89ef2ee5d0SMichal Meloun 	{"nvidia,tegra124-rtc",	1},
90ef2ee5d0SMichal Meloun 	{NULL,			0}
91ef2ee5d0SMichal Meloun };
92ef2ee5d0SMichal Meloun 
93ef2ee5d0SMichal Meloun struct tegra_rtc_softc {
94ef2ee5d0SMichal Meloun 	device_t		dev;
95ef2ee5d0SMichal Meloun 	struct mtx		mtx;
96ef2ee5d0SMichal Meloun 
97ef2ee5d0SMichal Meloun 	struct resource		*mem_res;
98ef2ee5d0SMichal Meloun 	struct resource		*irq_res;
99ef2ee5d0SMichal Meloun 	void			*irq_h;
100ef2ee5d0SMichal Meloun 
101ef2ee5d0SMichal Meloun 	clk_t			clk;
102ef2ee5d0SMichal Meloun 	uint32_t		core_freq;
103ef2ee5d0SMichal Meloun };
104ef2ee5d0SMichal Meloun 
105ef2ee5d0SMichal Meloun static void
106ef2ee5d0SMichal Meloun tegra_rtc_wait(struct tegra_rtc_softc *sc)
107ef2ee5d0SMichal Meloun {
108ef2ee5d0SMichal Meloun 	int timeout;
109ef2ee5d0SMichal Meloun 
110ef2ee5d0SMichal Meloun 	for (timeout = 500; timeout >0; timeout--) {
111ef2ee5d0SMichal Meloun 		if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0)
112ef2ee5d0SMichal Meloun 			break;
113ef2ee5d0SMichal Meloun 		DELAY(1);
114ef2ee5d0SMichal Meloun 	}
115ef2ee5d0SMichal Meloun 	if (timeout <= 0)
116ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Device busy timeouted\n");
117ef2ee5d0SMichal Meloun 
118ef2ee5d0SMichal Meloun }
119ef2ee5d0SMichal Meloun 
120ef2ee5d0SMichal Meloun /*
121ef2ee5d0SMichal Meloun  * Get the time of day clock and return it in ts.
122ef2ee5d0SMichal Meloun  * Return 0 on success, an error number otherwise.
123ef2ee5d0SMichal Meloun  */
124ef2ee5d0SMichal Meloun static int
125ef2ee5d0SMichal Meloun tegra_rtc_gettime(device_t dev, struct timespec *ts)
126ef2ee5d0SMichal Meloun {
127ef2ee5d0SMichal Meloun 	struct tegra_rtc_softc *sc;
128ef2ee5d0SMichal Meloun 	struct timeval tv;
129ef2ee5d0SMichal Meloun 	uint32_t msec, sec;
130ef2ee5d0SMichal Meloun 
131ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
132ef2ee5d0SMichal Meloun 
133ef2ee5d0SMichal Meloun 	LOCK(sc);
134ef2ee5d0SMichal Meloun 	msec = RD4(sc, RTC_MILLI_SECONDS);
135ef2ee5d0SMichal Meloun 	sec = RD4(sc, RTC_SHADOW_SECONDS);
136ef2ee5d0SMichal Meloun 	UNLOCK(sc);
137ef2ee5d0SMichal Meloun 	tv.tv_sec = sec;
138ef2ee5d0SMichal Meloun 	tv.tv_usec = msec * 1000;
139ef2ee5d0SMichal Meloun 	TIMEVAL_TO_TIMESPEC(&tv, ts);
140ef2ee5d0SMichal Meloun 	return (0);
141ef2ee5d0SMichal Meloun }
142ef2ee5d0SMichal Meloun 
143ef2ee5d0SMichal Meloun static int
144ef2ee5d0SMichal Meloun tegra_rtc_settime(device_t dev, struct timespec *ts)
145ef2ee5d0SMichal Meloun {
146ef2ee5d0SMichal Meloun 	struct tegra_rtc_softc *sc;
147ef2ee5d0SMichal Meloun 	struct timeval tv;
148ef2ee5d0SMichal Meloun 
149ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
150ef2ee5d0SMichal Meloun 
151ef2ee5d0SMichal Meloun 	LOCK(sc);
152ef2ee5d0SMichal Meloun 	TIMESPEC_TO_TIMEVAL(&tv, ts);
153ef2ee5d0SMichal Meloun 	tegra_rtc_wait(sc);
154ef2ee5d0SMichal Meloun 	WR4(sc, RTC_SECONDS, tv.tv_sec);
155ef2ee5d0SMichal Meloun 	UNLOCK(sc);
156ef2ee5d0SMichal Meloun 
157ef2ee5d0SMichal Meloun 	return (0);
158ef2ee5d0SMichal Meloun }
159ef2ee5d0SMichal Meloun 
160ef2ee5d0SMichal Meloun static void
161ef2ee5d0SMichal Meloun tegra_rtc_intr(void *arg)
162ef2ee5d0SMichal Meloun {
163ef2ee5d0SMichal Meloun 	struct tegra_rtc_softc *sc;
164ef2ee5d0SMichal Meloun 	uint32_t status;
165ef2ee5d0SMichal Meloun 
166ef2ee5d0SMichal Meloun 	sc = (struct tegra_rtc_softc *)arg;
167ef2ee5d0SMichal Meloun 	LOCK(sc);
168ef2ee5d0SMichal Meloun 	status = RD4(sc, RTC_INTR_STATUS);
169ef2ee5d0SMichal Meloun 	WR4(sc, RTC_INTR_STATUS, status);
170ef2ee5d0SMichal Meloun 	UNLOCK(sc);
171ef2ee5d0SMichal Meloun }
172ef2ee5d0SMichal Meloun 
173ef2ee5d0SMichal Meloun static int
174ef2ee5d0SMichal Meloun tegra_rtc_probe(device_t dev)
175ef2ee5d0SMichal Meloun {
176ef2ee5d0SMichal Meloun 	if (!ofw_bus_status_okay(dev))
177ef2ee5d0SMichal Meloun 		return (ENXIO);
178ef2ee5d0SMichal Meloun 
179ef2ee5d0SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
180ef2ee5d0SMichal Meloun 		return (ENXIO);
181ef2ee5d0SMichal Meloun 
182ef2ee5d0SMichal Meloun 	return (BUS_PROBE_DEFAULT);
183ef2ee5d0SMichal Meloun }
184ef2ee5d0SMichal Meloun 
185ef2ee5d0SMichal Meloun static int
186ef2ee5d0SMichal Meloun tegra_rtc_attach(device_t dev)
187ef2ee5d0SMichal Meloun {
188ef2ee5d0SMichal Meloun 	int rv, rid;
189ef2ee5d0SMichal Meloun 	struct tegra_rtc_softc *sc;
190ef2ee5d0SMichal Meloun 
191ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
192ef2ee5d0SMichal Meloun 	sc->dev = dev;
193ef2ee5d0SMichal Meloun 
194ef2ee5d0SMichal Meloun 	LOCK_INIT(sc);
195ef2ee5d0SMichal Meloun 
196ef2ee5d0SMichal Meloun 	/* Get the memory resource for the register mapping. */
197ef2ee5d0SMichal Meloun 	rid = 0;
198ef2ee5d0SMichal Meloun 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
199ef2ee5d0SMichal Meloun 	    RF_ACTIVE);
200ef2ee5d0SMichal Meloun 	if (sc->mem_res == NULL) {
201ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot map registers.\n");
202ef2ee5d0SMichal Meloun 		rv = ENXIO;
203ef2ee5d0SMichal Meloun 		goto fail;
204ef2ee5d0SMichal Meloun 	}
205ef2ee5d0SMichal Meloun 
206ef2ee5d0SMichal Meloun 	/* Allocate our IRQ resource. */
207ef2ee5d0SMichal Meloun 	rid = 0;
208ef2ee5d0SMichal Meloun 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
209ef2ee5d0SMichal Meloun 	    RF_ACTIVE);
210ef2ee5d0SMichal Meloun 	if (sc->irq_res == NULL) {
211ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot allocate interrupt.\n");
212ef2ee5d0SMichal Meloun 		rv = ENXIO;
213ef2ee5d0SMichal Meloun 		goto fail;
214ef2ee5d0SMichal Meloun 	}
215ef2ee5d0SMichal Meloun 
216ef2ee5d0SMichal Meloun 	/* OFW resources. */
217dac93553SMichal Meloun 	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
218ef2ee5d0SMichal Meloun 	if (rv != 0) {
219ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot get i2c clock: %d\n", rv);
220ef2ee5d0SMichal Meloun 		goto fail;
221ef2ee5d0SMichal Meloun 	}
222ef2ee5d0SMichal Meloun 	rv = clk_enable(sc->clk);
223ef2ee5d0SMichal Meloun 	if (rv != 0) {
224ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot enable clock: %d\n", rv);
225ef2ee5d0SMichal Meloun 		goto fail;
226ef2ee5d0SMichal Meloun 	}
227ef2ee5d0SMichal Meloun 
228ef2ee5d0SMichal Meloun 	/* Init hardware. */
229ef2ee5d0SMichal Meloun 	WR4(sc, RTC_SECONDS_ALARM0, 0);
230ef2ee5d0SMichal Meloun 	WR4(sc, RTC_SECONDS_ALARM1, 0);
231ef2ee5d0SMichal Meloun 	WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF);
232ef2ee5d0SMichal Meloun 	WR4(sc, RTC_INTR_MASK, 0);
233ef2ee5d0SMichal Meloun 
234ef2ee5d0SMichal Meloun 	/* Setup  interrupt */
235ef2ee5d0SMichal Meloun 	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
236ef2ee5d0SMichal Meloun 	    NULL, tegra_rtc_intr, sc, &sc->irq_h);
237ef2ee5d0SMichal Meloun 	if (rv) {
238ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot setup interrupt.\n");
239ef2ee5d0SMichal Meloun 		goto fail;
240ef2ee5d0SMichal Meloun 	}
241ef2ee5d0SMichal Meloun 
242ef2ee5d0SMichal Meloun 	/*
243ef2ee5d0SMichal Meloun 	 * Register as a time of day clock with 1-second resolution.
244ef2ee5d0SMichal Meloun 	 *
245ef2ee5d0SMichal Meloun 	 * XXXX Not yet, we don't have support for multiple RTCs
246ef2ee5d0SMichal Meloun 	 */
247ef2ee5d0SMichal Meloun 	/* clock_register(dev, 1000000); */
248ef2ee5d0SMichal Meloun 
249*18250ec6SJohn Baldwin 	bus_attach_children(dev);
250*18250ec6SJohn Baldwin 	return (0);
251ef2ee5d0SMichal Meloun 
252ef2ee5d0SMichal Meloun fail:
253ef2ee5d0SMichal Meloun 	if (sc->clk != NULL)
254ef2ee5d0SMichal Meloun 		clk_release(sc->clk);
255ef2ee5d0SMichal Meloun 	if (sc->irq_h != NULL)
256ef2ee5d0SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
257ef2ee5d0SMichal Meloun 	if (sc->irq_res != NULL)
258ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
259ef2ee5d0SMichal Meloun 	if (sc->mem_res != NULL)
260ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
261ef2ee5d0SMichal Meloun 	LOCK_DESTROY(sc);
262ef2ee5d0SMichal Meloun 
263ef2ee5d0SMichal Meloun 	return (rv);
264ef2ee5d0SMichal Meloun }
265ef2ee5d0SMichal Meloun 
266ef2ee5d0SMichal Meloun static int
267ef2ee5d0SMichal Meloun tegra_rtc_detach(device_t dev)
268ef2ee5d0SMichal Meloun {
269ef2ee5d0SMichal Meloun 	struct tegra_rtc_softc *sc;
270d412c076SJohn Baldwin 	int error;
271d412c076SJohn Baldwin 
272d412c076SJohn Baldwin 	error = bus_generic_detach(dev);
273d412c076SJohn Baldwin 	if (error != 0)
274d412c076SJohn Baldwin 		return (error);
275ef2ee5d0SMichal Meloun 
276ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
277ef2ee5d0SMichal Meloun 	if (sc->irq_h != NULL)
278ef2ee5d0SMichal Meloun 		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
279ef2ee5d0SMichal Meloun 	if (sc->irq_res != NULL)
280ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
281ef2ee5d0SMichal Meloun 	if (sc->mem_res != NULL)
282ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
283ef2ee5d0SMichal Meloun 
284ef2ee5d0SMichal Meloun 	LOCK_DESTROY(sc);
285d412c076SJohn Baldwin 	return (0);
286ef2ee5d0SMichal Meloun }
287ef2ee5d0SMichal Meloun 
288ef2ee5d0SMichal Meloun static device_method_t tegra_rtc_methods[] = {
289ef2ee5d0SMichal Meloun 	/* Device interface */
290ef2ee5d0SMichal Meloun 	DEVMETHOD(device_probe,		tegra_rtc_probe),
291ef2ee5d0SMichal Meloun 	DEVMETHOD(device_attach,	tegra_rtc_attach),
292ef2ee5d0SMichal Meloun 	DEVMETHOD(device_detach,	tegra_rtc_detach),
293ef2ee5d0SMichal Meloun 
294ef2ee5d0SMichal Meloun 	/* clock interface */
295ef2ee5d0SMichal Meloun 	DEVMETHOD(clock_gettime,	tegra_rtc_gettime),
296ef2ee5d0SMichal Meloun 	DEVMETHOD(clock_settime,	tegra_rtc_settime),
297ef2ee5d0SMichal Meloun 
298ef2ee5d0SMichal Meloun 	DEVMETHOD_END
299ef2ee5d0SMichal Meloun };
300ef2ee5d0SMichal Meloun 
3014bda238aSMichal Meloun static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods,
3024bda238aSMichal Meloun     sizeof(struct tegra_rtc_softc));
303289f133bSJohn Baldwin DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, NULL, NULL);
304