1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 Benno Rice.
5 * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
6 * All rights reserved.
7 *
8 * Adapted to Marvell SoC by Semihalf.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_timer.c, rev 1
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/bus.h>
39#include <sys/eventhandler.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/malloc.h>
43#include <sys/rman.h>
44#include <sys/timeet.h>
45#include <sys/timetc.h>
46#include <sys/watchdog.h>
47#include <machine/bus.h>
48#include <machine/cpu.h>
49#include <machine/intr.h>
50#include <machine/machdep.h>
51
52#include <arm/mv/mvreg.h>
53#include <arm/mv/mvvar.h>
54
55#include <dev/ofw/ofw_bus.h>
56#include <dev/ofw/ofw_bus_subr.h>
57
58#define INITIAL_TIMECOUNTER	(0xffffffff)
59#define MAX_WATCHDOG_TICKS	(0xffffffff)
60
61#define	MV_TMR	0x1
62#define	MV_WDT	0x2
63#define	MV_NONE	0x0
64
65#define	MV_CLOCK_SRC_ARMV7	25000000	/* Timers' 25MHz mode */
66
67#define	WATCHDOG_TIMER_ARMV5		2
68
69typedef void (*mv_watchdog_enable_t)(void);
70typedef void (*mv_watchdog_disable_t)(void);
71
72struct mv_timer_config {
73	enum soc_family		soc_family;
74	mv_watchdog_enable_t	watchdog_enable;
75	mv_watchdog_disable_t	watchdog_disable;
76	unsigned int 		clock_src;
77	uint32_t 		bridge_irq_cause;
78	uint32_t 		irq_timer0_clr;
79	uint32_t 		irq_timer_wd_clr;
80};
81
82struct mv_timer_softc {
83	struct resource	*	timer_res[2];
84	bus_space_tag_t		timer_bst;
85	bus_space_handle_t	timer_bsh;
86	struct mtx		timer_mtx;
87	struct eventtimer	et;
88	boolean_t		has_wdt;
89	struct mv_timer_config* config;
90};
91
92static struct resource_spec mv_timer_spec[] = {
93	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
94	{ SYS_RES_IRQ,		0,	RF_ACTIVE | RF_OPTIONAL },
95	{ -1, 0 }
96};
97
98/* Interrupt is not required by MV_WDT devices */
99static struct ofw_compat_data mv_timer_compat[] = {
100	{"marvell,armada-380-timer",	MV_NONE },
101	{"marvell,armada-xp-timer",	MV_TMR | MV_WDT },
102	{"mrvl,timer",			MV_TMR | MV_WDT },
103	{NULL,				MV_NONE }
104};
105
106static struct mv_timer_softc *timer_softc = NULL;
107static int timers_initialized = 0;
108
109static int	mv_timer_probe(device_t);
110static int	mv_timer_attach(device_t);
111
112static int	mv_hardclock(void *);
113static unsigned mv_timer_get_timecount(struct timecounter *);
114
115static uint32_t	mv_get_timer_control(void);
116static void	mv_set_timer_control(uint32_t);
117static uint32_t	mv_get_timer(uint32_t);
118static void	mv_set_timer(uint32_t, uint32_t);
119static void	mv_set_timer_rel(uint32_t, uint32_t);
120static void	mv_watchdog_event(void *, unsigned int, int *);
121static int	mv_timer_start(struct eventtimer *et,
122    sbintime_t first, sbintime_t period);
123static int	mv_timer_stop(struct eventtimer *et);
124static void	mv_setup_timers(void);
125
126static void mv_watchdog_enable_armv5(void);
127static void mv_watchdog_enable_armadaxp(void);
128static void mv_watchdog_disable_armv5(void);
129static void mv_watchdog_disable_armadaxp(void);
130
131static void mv_delay(int usec, void* arg);
132
133static struct mv_timer_config timer_armadaxp_config =
134{
135	MV_SOC_ARMADA_XP,
136	&mv_watchdog_enable_armadaxp,
137	&mv_watchdog_disable_armadaxp,
138	MV_CLOCK_SRC_ARMV7,
139	BRIDGE_IRQ_CAUSE_ARMADAXP,
140	IRQ_TIMER0_CLR_ARMADAXP,
141	IRQ_TIMER_WD_CLR_ARMADAXP,
142};
143static struct mv_timer_config timer_armv5_config =
144{
145	MV_SOC_ARMV5,
146	&mv_watchdog_enable_armv5,
147	&mv_watchdog_disable_armv5,
148	0,
149	BRIDGE_IRQ_CAUSE,
150	IRQ_TIMER0_CLR,
151	IRQ_TIMER_WD_CLR,
152};
153
154static struct ofw_compat_data mv_timer_soc_config[] = {
155	{"marvell,armada-xp-timer",	(uintptr_t)&timer_armadaxp_config },
156	{"mrvl,timer",			(uintptr_t)&timer_armv5_config },
157	{NULL,				(uintptr_t)NULL },
158};
159
160static struct timecounter mv_timer_timecounter = {
161	.tc_get_timecount = mv_timer_get_timecount,
162	.tc_name = "CPUTimer1",
163	.tc_frequency = 0,	/* This is assigned on the fly in the init sequence */
164	.tc_counter_mask = ~0u,
165	.tc_quality = 1000,
166};
167
168static int
169mv_timer_probe(device_t dev)
170{
171
172	if (!ofw_bus_status_okay(dev))
173		return (ENXIO);
174
175	if (ofw_bus_search_compatible(dev, mv_timer_compat)->ocd_data == MV_NONE)
176		return (ENXIO);
177
178	device_set_desc(dev, "Marvell CPU Timer");
179	return (0);
180}
181
182static int
183mv_timer_attach(device_t dev)
184{
185	int	error;
186	void	*ihl;
187	struct	mv_timer_softc *sc;
188	uint32_t irq_cause, irq_mask;
189
190	if (timer_softc != NULL)
191		return (ENXIO);
192
193	sc = (struct mv_timer_softc *)device_get_softc(dev);
194	timer_softc = sc;
195
196	sc->config = (struct mv_timer_config*)
197	    ofw_bus_search_compatible(dev, mv_timer_soc_config)->ocd_data;
198
199	if (sc->config->clock_src == 0)
200		sc->config->clock_src = get_tclk();
201
202	error = bus_alloc_resources(dev, mv_timer_spec, sc->timer_res);
203	if (error) {
204		device_printf(dev, "could not allocate resources\n");
205		return (ENXIO);
206	}
207
208	sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
209	sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
210
211	sc->has_wdt = ofw_bus_has_prop(dev, "mrvl,has-wdt");
212
213	mtx_init(&timer_softc->timer_mtx, "watchdog", NULL, MTX_DEF);
214
215	if (sc->has_wdt) {
216		if (sc->config->watchdog_disable)
217			sc->config->watchdog_disable();
218		EVENTHANDLER_REGISTER(watchdog_list, mv_watchdog_event, sc, 0);
219	}
220
221	if (ofw_bus_search_compatible(dev, mv_timer_compat)->ocd_data
222	    == MV_WDT) {
223		/* Don't set timers for wdt-only entry. */
224		device_printf(dev, "only watchdog attached\n");
225		return (0);
226	} else if (sc->timer_res[1] == NULL) {
227		device_printf(dev, "no interrupt resource\n");
228		bus_release_resources(dev, mv_timer_spec, sc->timer_res);
229		return (ENXIO);
230	}
231
232	if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
233	    mv_hardclock, NULL, sc, &ihl) != 0) {
234		bus_release_resources(dev, mv_timer_spec, sc->timer_res);
235		device_printf(dev, "Could not setup interrupt.\n");
236		return (ENXIO);
237	}
238
239	mv_setup_timers();
240	if (sc->config->soc_family != MV_SOC_ARMADA_XP ) {
241		irq_cause = read_cpu_ctrl(sc->config->bridge_irq_cause);
242		irq_cause &= sc->config->irq_timer0_clr;
243
244		write_cpu_ctrl(sc->config->bridge_irq_cause, irq_cause);
245		irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
246		irq_mask |= IRQ_TIMER0_MASK;
247		irq_mask &= ~IRQ_TIMER1_MASK;
248		write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
249	}
250	sc->et.et_name = "CPUTimer0";
251	sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
252	sc->et.et_quality = 1000;
253
254	sc->et.et_frequency = sc->config->clock_src;
255	sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
256	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
257	sc->et.et_start = mv_timer_start;
258	sc->et.et_stop = mv_timer_stop;
259	sc->et.et_priv = sc;
260	et_register(&sc->et);
261	mv_timer_timecounter.tc_frequency = sc->config->clock_src;
262	tc_init(&mv_timer_timecounter);
263
264#ifdef PLATFORM
265	arm_set_delay(mv_delay, NULL);
266#endif
267	return (0);
268}
269
270static int
271mv_hardclock(void *arg)
272{
273	struct	mv_timer_softc *sc;
274	uint32_t irq_cause;
275
276	irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
277	irq_cause &= timer_softc->config->irq_timer0_clr;
278	write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
279
280	sc = (struct mv_timer_softc *)arg;
281	if (sc->et.et_active)
282		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
283
284	return (FILTER_HANDLED);
285}
286
287static device_method_t mv_timer_methods[] = {
288	DEVMETHOD(device_probe, mv_timer_probe),
289	DEVMETHOD(device_attach, mv_timer_attach),
290	{ 0, 0 }
291};
292
293static driver_t mv_timer_driver = {
294	"timer",
295	mv_timer_methods,
296	sizeof(struct mv_timer_softc),
297};
298
299static devclass_t mv_timer_devclass;
300
301DRIVER_MODULE(timer_mv, simplebus, mv_timer_driver, mv_timer_devclass, 0, 0);
302
303static unsigned
304mv_timer_get_timecount(struct timecounter *tc)
305{
306
307	return (INITIAL_TIMECOUNTER - mv_get_timer(1));
308}
309
310static void
311mv_delay(int usec, void* arg)
312{
313	uint32_t	val, val_temp;
314	int32_t		nticks;
315
316	val = mv_get_timer(1);
317	nticks = ((timer_softc->config->clock_src / 1000000 + 1) * usec);
318
319	while (nticks > 0) {
320		val_temp = mv_get_timer(1);
321		if (val > val_temp)
322			nticks -= (val - val_temp);
323		else
324			nticks -= (val + (INITIAL_TIMECOUNTER - val_temp));
325
326		val = val_temp;
327	}
328}
329
330#ifndef PLATFORM
331void
332DELAY(int usec)
333{
334	uint32_t	val;
335
336	if (!timers_initialized) {
337		for (; usec > 0; usec--)
338			for (val = 100; val > 0; val--)
339				__asm __volatile("nop" ::: "memory");
340	} else {
341		TSENTER();
342		mv_delay(usec, NULL);
343		TSEXIT();
344	}
345}
346#endif
347
348static uint32_t
349mv_get_timer_control(void)
350{
351
352	return (bus_space_read_4(timer_softc->timer_bst,
353	    timer_softc->timer_bsh, CPU_TIMER_CONTROL));
354}
355
356static void
357mv_set_timer_control(uint32_t val)
358{
359
360	bus_space_write_4(timer_softc->timer_bst,
361	    timer_softc->timer_bsh, CPU_TIMER_CONTROL, val);
362}
363
364static uint32_t
365mv_get_timer(uint32_t timer)
366{
367
368	return (bus_space_read_4(timer_softc->timer_bst,
369	    timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8));
370}
371
372static void
373mv_set_timer(uint32_t timer, uint32_t val)
374{
375
376	bus_space_write_4(timer_softc->timer_bst,
377	    timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8, val);
378}
379
380static void
381mv_set_timer_rel(uint32_t timer, uint32_t val)
382{
383
384	bus_space_write_4(timer_softc->timer_bst,
385	    timer_softc->timer_bsh, CPU_TIMER0_REL + timer * 0x8, val);
386}
387
388static void
389mv_watchdog_enable_armv5(void)
390{
391	uint32_t val, irq_cause, irq_mask;
392
393	irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
394	irq_cause &= timer_softc->config->irq_timer_wd_clr;
395	write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
396
397	irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
398	irq_mask |= IRQ_TIMER_WD_MASK;
399	write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
400
401	val = read_cpu_ctrl(RSTOUTn_MASK);
402	val |= WD_RST_OUT_EN;
403	write_cpu_ctrl(RSTOUTn_MASK, val);
404
405	val = mv_get_timer_control();
406	val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO;
407	mv_set_timer_control(val);
408}
409
410static void
411mv_watchdog_enable_armadaxp(void)
412{
413	uint32_t irq_cause, val;
414
415	irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
416	irq_cause &= timer_softc->config->irq_timer_wd_clr;
417	write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
418
419	val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
420	val |= (WD_GLOBAL_MASK | WD_CPU0_MASK);
421	write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
422
423	val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
424	val &= ~RSTOUTn_MASK_WD;
425	write_cpu_misc(RSTOUTn_MASK_ARMV7, val);
426
427	val = mv_get_timer_control();
428	val |= CPU_TIMER2_EN | CPU_TIMER2_AUTO | CPU_TIMER_WD_25MHZ_EN;
429	mv_set_timer_control(val);
430}
431
432static void
433mv_watchdog_disable_armv5(void)
434{
435	uint32_t val, irq_cause,irq_mask;
436
437	val = mv_get_timer_control();
438	val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
439	mv_set_timer_control(val);
440
441	val = read_cpu_ctrl(RSTOUTn_MASK);
442	val &= ~WD_RST_OUT_EN;
443	write_cpu_ctrl(RSTOUTn_MASK, val);
444
445	irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
446	irq_mask &= ~(IRQ_TIMER_WD_MASK);
447	write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
448
449	irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
450	irq_cause &= timer_softc->config->irq_timer_wd_clr;
451	write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
452}
453
454static void
455mv_watchdog_disable_armadaxp(void)
456{
457	uint32_t val, irq_cause;
458
459	val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
460	val &= ~(WD_GLOBAL_MASK | WD_CPU0_MASK);
461	write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
462
463	val = read_cpu_misc(RSTOUTn_MASK_ARMV7);
464	val |= RSTOUTn_MASK_WD;
465	write_cpu_misc(RSTOUTn_MASK_ARMV7, RSTOUTn_MASK_WD);
466
467	irq_cause = read_cpu_ctrl(timer_softc->config->bridge_irq_cause);
468	irq_cause &= timer_softc->config->irq_timer_wd_clr;
469	write_cpu_ctrl(timer_softc->config->bridge_irq_cause, irq_cause);
470
471	val = mv_get_timer_control();
472	val &= ~(CPU_TIMER2_EN | CPU_TIMER2_AUTO);
473	mv_set_timer_control(val);
474}
475
476/*
477 * Watchdog event handler.
478 */
479static void
480mv_watchdog_event(void *arg, unsigned int cmd, int *error)
481{
482	uint64_t ns;
483	uint64_t ticks;
484
485	mtx_lock(&timer_softc->timer_mtx);
486	if (cmd == 0) {
487		if (timer_softc->config->watchdog_disable != NULL)
488			timer_softc->config->watchdog_disable();
489	} else {
490		/*
491		 * Watchdog timeout is in nanosecs, calculation according to
492		 * watchdog(9)
493		 */
494		ns = (uint64_t)1 << (cmd & WD_INTERVAL);
495		ticks = (uint64_t)(ns * timer_softc->config->clock_src) / 1000000000;
496		if (ticks > MAX_WATCHDOG_TICKS) {
497			if (timer_softc->config->watchdog_disable != NULL)
498				timer_softc->config->watchdog_disable();
499		} else {
500			mv_set_timer(WATCHDOG_TIMER_ARMV5, ticks);
501			if (timer_softc->config->watchdog_enable != NULL)
502				timer_softc->config->watchdog_enable();
503			*error = 0;
504		}
505	}
506	mtx_unlock(&timer_softc->timer_mtx);
507}
508
509static int
510mv_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
511{
512	struct	mv_timer_softc *sc;
513	uint32_t val, val1;
514
515	/* Calculate dividers. */
516	sc = (struct mv_timer_softc *)et->et_priv;
517	if (period != 0)
518		val = ((uint32_t)sc->et.et_frequency * period) >> 32;
519	else
520		val = 0;
521	if (first != 0)
522		val1 = ((uint32_t)sc->et.et_frequency * first) >> 32;
523	else
524		val1 = val;
525
526	/* Apply configuration. */
527	mv_set_timer_rel(0, val);
528	mv_set_timer(0, val1);
529	val = mv_get_timer_control();
530	val |= CPU_TIMER0_EN;
531	if (period != 0)
532		val |= CPU_TIMER0_AUTO;
533	else
534		val &= ~CPU_TIMER0_AUTO;
535	mv_set_timer_control(val);
536	return (0);
537}
538
539static int
540mv_timer_stop(struct eventtimer *et)
541{
542	uint32_t val;
543
544	val = mv_get_timer_control();
545	val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
546	mv_set_timer_control(val);
547	return (0);
548}
549
550static void
551mv_setup_timers(void)
552{
553	uint32_t val;
554
555	mv_set_timer_rel(1, INITIAL_TIMECOUNTER);
556	mv_set_timer(1, INITIAL_TIMECOUNTER);
557	val = mv_get_timer_control();
558	val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
559	val |= CPU_TIMER1_EN | CPU_TIMER1_AUTO;
560
561	if (timer_softc->config->soc_family == MV_SOC_ARMADA_XP) {
562		/* Enable 25MHz mode */
563		val |= CPU_TIMER0_25MHZ_EN | CPU_TIMER1_25MHZ_EN;
564	}
565
566	mv_set_timer_control(val);
567	timers_initialized = 1;
568}
569