xref: /freebsd/sys/arm/mv/armada38x/armada38x_rtc.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1cb0c98fcSMarcin Wojtas /*-
2cb0c98fcSMarcin Wojtas  * Copyright (c) 2015 Semihalf.
3cb0c98fcSMarcin Wojtas  * Copyright (c) 2015 Stormshield.
4cb0c98fcSMarcin Wojtas  * All rights reserved.
5cb0c98fcSMarcin Wojtas  *
6cb0c98fcSMarcin Wojtas  * Redistribution and use in source and binary forms, with or without
7cb0c98fcSMarcin Wojtas  * modification, are permitted provided that the following conditions
8cb0c98fcSMarcin Wojtas  * are met:
9cb0c98fcSMarcin Wojtas  * 1. Redistributions of source code must retain the above copyright
10cb0c98fcSMarcin Wojtas  *    notice, this list of conditions and the following disclaimer.
11cb0c98fcSMarcin Wojtas  * 2. Redistributions in binary form must reproduce the above copyright
12cb0c98fcSMarcin Wojtas  *    notice, this list of conditions and the following disclaimer in the
13cb0c98fcSMarcin Wojtas  *    documentation and/or other materials provided with the distribution.
14cb0c98fcSMarcin Wojtas  *
15cb0c98fcSMarcin Wojtas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16cb0c98fcSMarcin Wojtas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17cb0c98fcSMarcin Wojtas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18cb0c98fcSMarcin Wojtas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19cb0c98fcSMarcin Wojtas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20cb0c98fcSMarcin Wojtas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21cb0c98fcSMarcin Wojtas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22cb0c98fcSMarcin Wojtas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23cb0c98fcSMarcin Wojtas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24cb0c98fcSMarcin Wojtas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25cb0c98fcSMarcin Wojtas  * SUCH DAMAGE.
26cb0c98fcSMarcin Wojtas  */
27cb0c98fcSMarcin Wojtas 
28cb0c98fcSMarcin Wojtas #include <sys/param.h>
29cb0c98fcSMarcin Wojtas #include <sys/bus.h>
30cb0c98fcSMarcin Wojtas #include <sys/lock.h>
31cb0c98fcSMarcin Wojtas #include <sys/time.h>
32cb0c98fcSMarcin Wojtas #include <sys/proc.h>
33cb0c98fcSMarcin Wojtas #include <sys/conf.h>
34cb0c98fcSMarcin Wojtas #include <sys/rman.h>
35cb0c98fcSMarcin Wojtas #include <sys/clock.h>
36cb0c98fcSMarcin Wojtas #include <sys/systm.h>
37cb0c98fcSMarcin Wojtas #include <sys/mutex.h>
38cb0c98fcSMarcin Wojtas #include <sys/types.h>
39cb0c98fcSMarcin Wojtas #include <sys/kernel.h>
40cb0c98fcSMarcin Wojtas #include <sys/module.h>
41cb0c98fcSMarcin Wojtas #include <sys/resource.h>
42cb0c98fcSMarcin Wojtas 
43cb0c98fcSMarcin Wojtas #include <machine/bus.h>
44cb0c98fcSMarcin Wojtas #include <machine/resource.h>
45cb0c98fcSMarcin Wojtas 
46cb0c98fcSMarcin Wojtas #include <dev/ofw/ofw_bus.h>
47cb0c98fcSMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h>
48cb0c98fcSMarcin Wojtas 
49cb0c98fcSMarcin Wojtas #include "clock_if.h"
50cb0c98fcSMarcin Wojtas 
51cb0c98fcSMarcin Wojtas #define	RTC_RES_US		1000000
52cb0c98fcSMarcin Wojtas #define	HALF_OF_SEC_NS		500000000
53cb0c98fcSMarcin Wojtas 
54cb0c98fcSMarcin Wojtas #define	RTC_STATUS		0x0
55cb0c98fcSMarcin Wojtas #define	RTC_TIME		0xC
56cb0c98fcSMarcin Wojtas #define	RTC_TEST_CONFIG		0x1C
57cb0c98fcSMarcin Wojtas #define	RTC_IRQ_1_CONFIG	0x4
58cb0c98fcSMarcin Wojtas #define	RTC_IRQ_2_CONFIG	0x8
59cb0c98fcSMarcin Wojtas #define	RTC_ALARM_1		0x10
60cb0c98fcSMarcin Wojtas #define	RTC_ALARM_2		0x14
61cb0c98fcSMarcin Wojtas #define	RTC_CLOCK_CORR		0x18
62cb0c98fcSMarcin Wojtas 
63cb0c98fcSMarcin Wojtas #define	RTC_NOMINAL_TIMING	0x2000
64cb0c98fcSMarcin Wojtas #define	RTC_NOMINAL_TIMING_MASK	0x7fff
65cb0c98fcSMarcin Wojtas 
66cb0c98fcSMarcin Wojtas #define	RTC_STATUS_ALARM1_MASK	0x1
67cb0c98fcSMarcin Wojtas #define	RTC_STATUS_ALARM2_MASK	0x2
68cb0c98fcSMarcin Wojtas 
69cb0c98fcSMarcin Wojtas #define	MV_RTC_LOCK(sc)		mtx_lock_spin(&(sc)->mutex)
70cb0c98fcSMarcin Wojtas #define	MV_RTC_UNLOCK(sc)	mtx_unlock_spin(&(sc)->mutex)
71cb0c98fcSMarcin Wojtas 
72d5d4dd38SMichal Meloun #define	A38X_RTC_BRIDGE_TIMING_CTRL		0x0
73d5d4dd38SMichal Meloun #define	A38X_RTC_WRCLK_PERIOD_SHIFT		0
74d5d4dd38SMichal Meloun #define	A38X_RTC_WRCLK_PERIOD_MASK		0x00000003FF
75d5d4dd38SMichal Meloun #define	A38X_RTC_WRCLK_PERIOD_MAX		0x3FF
76d5d4dd38SMichal Meloun #define	A38X_RTC_READ_OUTPUT_DELAY_SHIFT	26
77d5d4dd38SMichal Meloun #define	A38X_RTC_READ_OUTPUT_DELAY_MASK		0x007C000000
78d5d4dd38SMichal Meloun #define	A38X_RTC_READ_OUTPUT_DELAY_MAX		0x1F
79d5d4dd38SMichal Meloun 
80d5d4dd38SMichal Meloun #define	A8K_RTC_BRIDGE_TIMING_CTRL0		0x0
81d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_PERIOD_SHIFT		0
82d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_PERIOD_MASK		0x000000FFFF
83d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_PERIOD_VAL		0x3FF
84d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_SETUP_SHIFT		16
85d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_SETUP_MASK		0x00FFFF0000
86d5d4dd38SMichal Meloun #define	A8K_RTC_WRCLK_SETUP_VAL			29
87d5d4dd38SMichal Meloun #define	A8K_RTC_BRIDGE_TIMING_CTRL1		0x4
88d5d4dd38SMichal Meloun #define	A8K_RTC_READ_OUTPUT_DELAY_SHIFT		0
89d5d4dd38SMichal Meloun #define	A8K_RTC_READ_OUTPUT_DELAY_MASK		0x000000FFFF
90d5d4dd38SMichal Meloun #define	A8K_RTC_READ_OUTPUT_DELAY_VAL		0x3F
91d5d4dd38SMichal Meloun 
92cb0c98fcSMarcin Wojtas #define	RTC_RES		0
93cb0c98fcSMarcin Wojtas #define	RTC_SOC_RES	1
94cb0c98fcSMarcin Wojtas 
95cb0c98fcSMarcin Wojtas static struct resource_spec res_spec[] = {
96cb0c98fcSMarcin Wojtas 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
97cb0c98fcSMarcin Wojtas 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
98cb0c98fcSMarcin Wojtas 	{ -1, 0 }
99cb0c98fcSMarcin Wojtas };
100cb0c98fcSMarcin Wojtas 
101cb0c98fcSMarcin Wojtas struct mv_rtc_softc {
102cb0c98fcSMarcin Wojtas 	device_t	dev;
103cb0c98fcSMarcin Wojtas 	struct resource	*res[2];
104cb0c98fcSMarcin Wojtas 	struct mtx	mutex;
105d5d4dd38SMichal Meloun 	int		rtc_type;
106cb0c98fcSMarcin Wojtas };
107cb0c98fcSMarcin Wojtas 
108cb0c98fcSMarcin Wojtas static int mv_rtc_probe(device_t dev);
109cb0c98fcSMarcin Wojtas static int mv_rtc_attach(device_t dev);
110cb0c98fcSMarcin Wojtas static int mv_rtc_detach(device_t dev);
111cb0c98fcSMarcin Wojtas 
112cb0c98fcSMarcin Wojtas static int mv_rtc_gettime(device_t dev, struct timespec *ts);
113cb0c98fcSMarcin Wojtas static int mv_rtc_settime(device_t dev, struct timespec *ts);
114cb0c98fcSMarcin Wojtas 
115cb0c98fcSMarcin Wojtas static inline uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc,
116cb0c98fcSMarcin Wojtas     bus_size_t off);
117cb0c98fcSMarcin Wojtas static inline int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off,
118cb0c98fcSMarcin Wojtas     uint32_t val);
119d5d4dd38SMichal Meloun static inline void mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc);
120d5d4dd38SMichal Meloun static inline void mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc);
121cb0c98fcSMarcin Wojtas 
122cb0c98fcSMarcin Wojtas static device_method_t mv_rtc_methods[] = {
123cb0c98fcSMarcin Wojtas 	DEVMETHOD(device_probe,		mv_rtc_probe),
124cb0c98fcSMarcin Wojtas 	DEVMETHOD(device_attach,	mv_rtc_attach),
125cb0c98fcSMarcin Wojtas 	DEVMETHOD(device_detach,	mv_rtc_detach),
126cb0c98fcSMarcin Wojtas 
127cb0c98fcSMarcin Wojtas 	DEVMETHOD(clock_gettime,	mv_rtc_gettime),
128cb0c98fcSMarcin Wojtas 	DEVMETHOD(clock_settime,	mv_rtc_settime),
129cb0c98fcSMarcin Wojtas 
130cb0c98fcSMarcin Wojtas 	{ 0, 0 },
131cb0c98fcSMarcin Wojtas };
132cb0c98fcSMarcin Wojtas 
133cb0c98fcSMarcin Wojtas static driver_t mv_rtc_driver = {
134cb0c98fcSMarcin Wojtas 	"rtc",
135cb0c98fcSMarcin Wojtas 	mv_rtc_methods,
136cb0c98fcSMarcin Wojtas 	sizeof(struct mv_rtc_softc),
137cb0c98fcSMarcin Wojtas };
138cb0c98fcSMarcin Wojtas 
139d5d4dd38SMichal Meloun #define  RTC_A38X	1
140d5d4dd38SMichal Meloun #define  RTC_A8K	2
141d5d4dd38SMichal Meloun 
142ee1c891dSMarcin Wojtas static struct ofw_compat_data mv_rtc_compat[] = {
143d5d4dd38SMichal Meloun 	{"marvell,armada-380-rtc",	RTC_A38X},
144d5d4dd38SMichal Meloun 	{"marvell,armada-8k-rtc",	RTC_A8K},
145d5d4dd38SMichal Meloun 	{NULL,				0},
146ee1c891dSMarcin Wojtas };
147ee1c891dSMarcin Wojtas 
148*a3b866cbSJohn Baldwin DRIVER_MODULE(a38x_rtc, simplebus, mv_rtc_driver, 0, 0);
149cb0c98fcSMarcin Wojtas 
150cb0c98fcSMarcin Wojtas static void
mv_rtc_reset(device_t dev)151cb0c98fcSMarcin Wojtas mv_rtc_reset(device_t dev)
152cb0c98fcSMarcin Wojtas {
153cb0c98fcSMarcin Wojtas 	struct mv_rtc_softc *sc;
154cb0c98fcSMarcin Wojtas 
155cb0c98fcSMarcin Wojtas 	sc = device_get_softc(dev);
156cb0c98fcSMarcin Wojtas 
157cb0c98fcSMarcin Wojtas 	/* Reset Test register */
158cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_TEST_CONFIG, 0);
159cb0c98fcSMarcin Wojtas 	DELAY(500000);
160cb0c98fcSMarcin Wojtas 
161cb0c98fcSMarcin Wojtas 	/* Reset Time register */
162cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_TIME, 0);
163cb0c98fcSMarcin Wojtas 	DELAY(62);
164cb0c98fcSMarcin Wojtas 
165cb0c98fcSMarcin Wojtas 	/* Reset Status register */
166cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
167cb0c98fcSMarcin Wojtas 	DELAY(62);
168cb0c98fcSMarcin Wojtas 
169cb0c98fcSMarcin Wojtas 	/* Turn off Int1 and Int2 sources & clear the Alarm count */
170cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_IRQ_1_CONFIG, 0);
171cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_IRQ_2_CONFIG, 0);
172cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_ALARM_1, 0);
173cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_ALARM_2, 0);
174cb0c98fcSMarcin Wojtas 
175cb0c98fcSMarcin Wojtas 	/* Setup nominal register access timing */
176cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_CLOCK_CORR, RTC_NOMINAL_TIMING);
177cb0c98fcSMarcin Wojtas 
178cb0c98fcSMarcin Wojtas 	/* Reset Time register */
179cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_TIME, 0);
180cb0c98fcSMarcin Wojtas 	DELAY(10);
181cb0c98fcSMarcin Wojtas 
182cb0c98fcSMarcin Wojtas 	/* Reset Status register */
183cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_STATUS, (RTC_STATUS_ALARM1_MASK | RTC_STATUS_ALARM2_MASK));
184cb0c98fcSMarcin Wojtas 	DELAY(50);
185cb0c98fcSMarcin Wojtas }
186cb0c98fcSMarcin Wojtas 
187cb0c98fcSMarcin Wojtas static int
mv_rtc_probe(device_t dev)188cb0c98fcSMarcin Wojtas mv_rtc_probe(device_t dev)
189cb0c98fcSMarcin Wojtas {
190cb0c98fcSMarcin Wojtas 
191cb0c98fcSMarcin Wojtas 	if (!ofw_bus_status_okay(dev))
192cb0c98fcSMarcin Wojtas 		return (ENXIO);
193cb0c98fcSMarcin Wojtas 
194ee1c891dSMarcin Wojtas 	if (!ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data)
195cb0c98fcSMarcin Wojtas 		return (ENXIO);
196cb0c98fcSMarcin Wojtas 
197cb0c98fcSMarcin Wojtas 	device_set_desc(dev, "Marvell Integrated RTC");
198cb0c98fcSMarcin Wojtas 
199cb0c98fcSMarcin Wojtas 	return (BUS_PROBE_DEFAULT);
200cb0c98fcSMarcin Wojtas }
201cb0c98fcSMarcin Wojtas 
202cb0c98fcSMarcin Wojtas static int
mv_rtc_attach(device_t dev)203cb0c98fcSMarcin Wojtas mv_rtc_attach(device_t dev)
204cb0c98fcSMarcin Wojtas {
205cb0c98fcSMarcin Wojtas 	struct mv_rtc_softc *sc;
206b20f0f72SWarner Losh 	int ret;
207cb0c98fcSMarcin Wojtas 
208cb0c98fcSMarcin Wojtas 	sc = device_get_softc(dev);
209cb0c98fcSMarcin Wojtas 	sc->dev = dev;
210d5d4dd38SMichal Meloun 	sc->rtc_type = ofw_bus_search_compatible(dev, mv_rtc_compat)->ocd_data;
211cb0c98fcSMarcin Wojtas 
212cb0c98fcSMarcin Wojtas 	mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
213cb0c98fcSMarcin Wojtas 
214cb0c98fcSMarcin Wojtas 	ret = bus_alloc_resources(dev, res_spec, sc->res);
215cb0c98fcSMarcin Wojtas 	if (ret != 0) {
216cb0c98fcSMarcin Wojtas 		device_printf(dev, "could not allocate resources\n");
217cb0c98fcSMarcin Wojtas 		mtx_destroy(&sc->mutex);
218cb0c98fcSMarcin Wojtas 		return (ENXIO);
219cb0c98fcSMarcin Wojtas 	}
220d5d4dd38SMichal Meloun 
221d5d4dd38SMichal Meloun 	switch (sc->rtc_type) {
222d5d4dd38SMichal Meloun 	case RTC_A38X:
223d5d4dd38SMichal Meloun 		mv_rtc_configure_bus_a38x(sc);
224d5d4dd38SMichal Meloun 		break;
225d5d4dd38SMichal Meloun 	case RTC_A8K:
226d5d4dd38SMichal Meloun 		mv_rtc_configure_bus_a8k(sc);
227d5d4dd38SMichal Meloun 		break;
228d5d4dd38SMichal Meloun 	default:
229d5d4dd38SMichal Meloun 		panic("Unknown RTC type: %d", sc->rtc_type);
230d5d4dd38SMichal Meloun 	}
231d5d4dd38SMichal Meloun 	clock_register(dev, RTC_RES_US);
232cb0c98fcSMarcin Wojtas 
233cb0c98fcSMarcin Wojtas 	return (0);
234cb0c98fcSMarcin Wojtas }
235cb0c98fcSMarcin Wojtas 
236cb0c98fcSMarcin Wojtas static int
mv_rtc_detach(device_t dev)237cb0c98fcSMarcin Wojtas mv_rtc_detach(device_t dev)
238cb0c98fcSMarcin Wojtas {
239cb0c98fcSMarcin Wojtas 	struct mv_rtc_softc *sc;
240cb0c98fcSMarcin Wojtas 
241cb0c98fcSMarcin Wojtas 	sc = device_get_softc(dev);
242cb0c98fcSMarcin Wojtas 
243cb0c98fcSMarcin Wojtas 	mtx_destroy(&sc->mutex);
244cb0c98fcSMarcin Wojtas 
245cb0c98fcSMarcin Wojtas 	bus_release_resources(dev, res_spec, sc->res);
246cb0c98fcSMarcin Wojtas 
247cb0c98fcSMarcin Wojtas 	return (0);
248cb0c98fcSMarcin Wojtas }
249cb0c98fcSMarcin Wojtas 
250cb0c98fcSMarcin Wojtas static int
mv_rtc_gettime(device_t dev,struct timespec * ts)251cb0c98fcSMarcin Wojtas mv_rtc_gettime(device_t dev, struct timespec *ts)
252cb0c98fcSMarcin Wojtas {
253cb0c98fcSMarcin Wojtas 	struct mv_rtc_softc *sc;
254cb0c98fcSMarcin Wojtas 	uint32_t val, val_check;
255cb0c98fcSMarcin Wojtas 
256cb0c98fcSMarcin Wojtas 	sc = device_get_softc(dev);
257cb0c98fcSMarcin Wojtas 
258cb0c98fcSMarcin Wojtas 	MV_RTC_LOCK(sc);
259cb0c98fcSMarcin Wojtas 	/*
260d5d4dd38SMichal Meloun 	 * According to HW Errata, if more than one second is detected
261d5d4dd38SMichal Meloun 	 * between two time reads, then at least one of the reads gave
262d5d4dd38SMichal Meloun 	 * an invalid value.
263cb0c98fcSMarcin Wojtas 	 */
264d5d4dd38SMichal Meloun 	do {
265cb0c98fcSMarcin Wojtas 		val = mv_rtc_reg_read(sc, RTC_TIME);
266d5d4dd38SMichal Meloun 		DELAY(100);
267cb0c98fcSMarcin Wojtas 		val_check = mv_rtc_reg_read(sc, RTC_TIME);
268d5d4dd38SMichal Meloun 	} while ((val_check - val) > 1);
269cb0c98fcSMarcin Wojtas 
270cb0c98fcSMarcin Wojtas 	MV_RTC_UNLOCK(sc);
271cb0c98fcSMarcin Wojtas 
272cb0c98fcSMarcin Wojtas 	ts->tv_sec = val_check;
273cb0c98fcSMarcin Wojtas 	/* RTC resolution is 1 sec */
274cb0c98fcSMarcin Wojtas 	ts->tv_nsec = 0;
275cb0c98fcSMarcin Wojtas 
276cb0c98fcSMarcin Wojtas 	return (0);
277cb0c98fcSMarcin Wojtas }
278cb0c98fcSMarcin Wojtas 
279cb0c98fcSMarcin Wojtas static int
mv_rtc_settime(device_t dev,struct timespec * ts)280cb0c98fcSMarcin Wojtas mv_rtc_settime(device_t dev, struct timespec *ts)
281cb0c98fcSMarcin Wojtas {
282cb0c98fcSMarcin Wojtas 	struct mv_rtc_softc *sc;
283cb0c98fcSMarcin Wojtas 
284cb0c98fcSMarcin Wojtas 	sc = device_get_softc(dev);
285cb0c98fcSMarcin Wojtas 
286cb0c98fcSMarcin Wojtas 	/* RTC resolution is 1 sec */
287cb0c98fcSMarcin Wojtas 	if (ts->tv_nsec >= HALF_OF_SEC_NS)
288cb0c98fcSMarcin Wojtas 		ts->tv_sec++;
289cb0c98fcSMarcin Wojtas 	ts->tv_nsec = 0;
290cb0c98fcSMarcin Wojtas 
291cb0c98fcSMarcin Wojtas 	MV_RTC_LOCK(sc);
292cb0c98fcSMarcin Wojtas 
293cb0c98fcSMarcin Wojtas 	if ((mv_rtc_reg_read(sc, RTC_CLOCK_CORR) & RTC_NOMINAL_TIMING_MASK) !=
294cb0c98fcSMarcin Wojtas 	    RTC_NOMINAL_TIMING) {
295cb0c98fcSMarcin Wojtas 		/* RTC was not resetted yet */
296cb0c98fcSMarcin Wojtas 		mv_rtc_reset(dev);
297cb0c98fcSMarcin Wojtas 	}
298cb0c98fcSMarcin Wojtas 
299cb0c98fcSMarcin Wojtas 	/*
300cb0c98fcSMarcin Wojtas 	 * According to errata FE-3124064, Write to RTC TIME register
301cb0c98fcSMarcin Wojtas 	 * may fail. As a workaround, before writing to RTC TIME register,
302cb0c98fcSMarcin Wojtas 	 * issue a dummy write of 0x0 twice to RTC Status register.
303cb0c98fcSMarcin Wojtas 	 */
304cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
305cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_STATUS, 0x0);
306cb0c98fcSMarcin Wojtas 	mv_rtc_reg_write(sc, RTC_TIME, ts->tv_sec);
307cb0c98fcSMarcin Wojtas 	MV_RTC_UNLOCK(sc);
308cb0c98fcSMarcin Wojtas 
309cb0c98fcSMarcin Wojtas 	return (0);
310cb0c98fcSMarcin Wojtas }
311cb0c98fcSMarcin Wojtas 
312cb0c98fcSMarcin Wojtas static inline uint32_t
mv_rtc_reg_read(struct mv_rtc_softc * sc,bus_size_t off)313cb0c98fcSMarcin Wojtas mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off)
314cb0c98fcSMarcin Wojtas {
315cb0c98fcSMarcin Wojtas 
316cb0c98fcSMarcin Wojtas 	return (bus_read_4(sc->res[RTC_RES], off));
317cb0c98fcSMarcin Wojtas }
318cb0c98fcSMarcin Wojtas 
319cb0c98fcSMarcin Wojtas /*
320cb0c98fcSMarcin Wojtas  * According to the datasheet, the OS should wait 5us after every
321cb0c98fcSMarcin Wojtas  * register write to the RTC hard macro so that the required update
322cb0c98fcSMarcin Wojtas  * can occur without holding off the system bus
323cb0c98fcSMarcin Wojtas  */
324cb0c98fcSMarcin Wojtas static inline int
mv_rtc_reg_write(struct mv_rtc_softc * sc,bus_size_t off,uint32_t val)325cb0c98fcSMarcin Wojtas mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val)
326cb0c98fcSMarcin Wojtas {
327cb0c98fcSMarcin Wojtas 
328cb0c98fcSMarcin Wojtas 	bus_write_4(sc->res[RTC_RES], off, val);
329cb0c98fcSMarcin Wojtas 	DELAY(5);
330cb0c98fcSMarcin Wojtas 
331cb0c98fcSMarcin Wojtas 	return (0);
332cb0c98fcSMarcin Wojtas }
333cb0c98fcSMarcin Wojtas 
334cb0c98fcSMarcin Wojtas static inline void
mv_rtc_configure_bus_a38x(struct mv_rtc_softc * sc)335d5d4dd38SMichal Meloun mv_rtc_configure_bus_a38x(struct mv_rtc_softc *sc)
336cb0c98fcSMarcin Wojtas {
337cb0c98fcSMarcin Wojtas 	int val;
338cb0c98fcSMarcin Wojtas 
339d5d4dd38SMichal Meloun 	val = bus_read_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL);
340d5d4dd38SMichal Meloun 	val &= ~(A38X_RTC_WRCLK_PERIOD_MASK | A38X_RTC_READ_OUTPUT_DELAY_MASK);
341d5d4dd38SMichal Meloun 	val |= A38X_RTC_WRCLK_PERIOD_MAX << A38X_RTC_WRCLK_PERIOD_SHIFT;
342d5d4dd38SMichal Meloun 	val |= A38X_RTC_READ_OUTPUT_DELAY_MAX << A38X_RTC_READ_OUTPUT_DELAY_SHIFT;
343d5d4dd38SMichal Meloun 	bus_write_4(sc->res[RTC_SOC_RES], A38X_RTC_BRIDGE_TIMING_CTRL, val);
344d5d4dd38SMichal Meloun }
345d5d4dd38SMichal Meloun 
346d5d4dd38SMichal Meloun static inline void
mv_rtc_configure_bus_a8k(struct mv_rtc_softc * sc)347d5d4dd38SMichal Meloun mv_rtc_configure_bus_a8k(struct mv_rtc_softc *sc)
348d5d4dd38SMichal Meloun {
349d5d4dd38SMichal Meloun 	int val;
350d5d4dd38SMichal Meloun 
351d5d4dd38SMichal Meloun 	val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
352d5d4dd38SMichal Meloun 	val &= ~(A8K_RTC_WRCLK_PERIOD_MASK | A8K_RTC_WRCLK_SETUP_MASK);
353d5d4dd38SMichal Meloun 	val |= A8K_RTC_WRCLK_PERIOD_VAL << A8K_RTC_WRCLK_PERIOD_SHIFT;
354d5d4dd38SMichal Meloun 	val |= A8K_RTC_WRCLK_SETUP_VAL << A8K_RTC_WRCLK_SETUP_SHIFT;
355d5d4dd38SMichal Meloun 	bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
356d5d4dd38SMichal Meloun 
357d5d4dd38SMichal Meloun 	val = bus_read_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL0);
358d5d4dd38SMichal Meloun 	val &= ~A8K_RTC_READ_OUTPUT_DELAY_MASK;
359d5d4dd38SMichal Meloun 	val |= A8K_RTC_READ_OUTPUT_DELAY_VAL << A8K_RTC_READ_OUTPUT_DELAY_SHIFT;
360d5d4dd38SMichal Meloun 	bus_write_4(sc->res[RTC_SOC_RES], A8K_RTC_BRIDGE_TIMING_CTRL1, val);
361cb0c98fcSMarcin Wojtas }
362