1183840Sraj/*-
2183840Sraj * Copyright (c) 2006 Benno Rice.
3183840Sraj * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD.
4183840Sraj * All rights reserved.
5183840Sraj *
6183840Sraj * Adapted to Marvell SoC by Semihalf.
7183840Sraj *
8183840Sraj * Redistribution and use in source and binary forms, with or without
9183840Sraj * modification, are permitted provided that the following conditions
10183840Sraj * are met:
11183840Sraj * 1. Redistributions of source code must retain the above copyright
12183840Sraj *    notice, this list of conditions and the following disclaimer.
13183840Sraj * 2. Redistributions in binary form must reproduce the above copyright
14183840Sraj *    notice, this list of conditions and the following disclaimer in the
15183840Sraj *    documentation and/or other materials provided with the distribution.
16183840Sraj *
17183840Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18183840Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19183840Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20183840Sraj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21183840Sraj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22183840Sraj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23183840Sraj * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24183840Sraj * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25183840Sraj * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26183840Sraj * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27183840Sraj *
28183840Sraj * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_timer.c, rev 1
29183840Sraj */
30183840Sraj
31183840Sraj#include <sys/cdefs.h>
32183840Sraj__FBSDID("$FreeBSD$");
33183840Sraj
34183840Sraj#include <sys/param.h>
35183840Sraj#include <sys/systm.h>
36183840Sraj#include <sys/bus.h>
37183840Sraj#include <sys/kernel.h>
38183840Sraj#include <sys/module.h>
39183840Sraj#include <sys/malloc.h>
40183840Sraj#include <sys/rman.h>
41210293Smav#include <sys/timeet.h>
42183840Sraj#include <sys/timetc.h>
43183840Sraj#include <sys/watchdog.h>
44183840Sraj#include <machine/bus.h>
45183840Sraj#include <machine/cpu.h>
46183840Sraj#include <machine/intr.h>
47183840Sraj
48183840Sraj#include <arm/mv/mvreg.h>
49183840Sraj#include <arm/mv/mvvar.h>
50183840Sraj
51209131Sraj#include <dev/ofw/ofw_bus.h>
52209131Sraj#include <dev/ofw/ofw_bus_subr.h>
53209131Sraj
54183840Sraj#define INITIAL_TIMECOUNTER	(0xffffffff)
55183840Sraj#define MAX_WATCHDOG_TICKS	(0xffffffff)
56183840Sraj
57239277Sgonzo#if defined(SOC_MV_ARMADAXP)
58251371Sgber#define MV_CLOCK_SRC		25000000	/* Timers' 25MHz mode */
59239277Sgonzo#else
60239277Sgonzo#define MV_CLOCK_SRC		get_tclk()
61239277Sgonzo#endif
62239277Sgonzo
63183840Srajstruct mv_timer_softc {
64183840Sraj	struct resource	*	timer_res[2];
65183840Sraj	bus_space_tag_t		timer_bst;
66183840Sraj	bus_space_handle_t	timer_bsh;
67183840Sraj	struct mtx		timer_mtx;
68210293Smav	struct eventtimer	et;
69183840Sraj};
70183840Sraj
71183840Srajstatic struct resource_spec mv_timer_spec[] = {
72183840Sraj	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
73183840Sraj	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
74183840Sraj	{ -1, 0 }
75183840Sraj};
76183840Sraj
77183840Srajstatic struct mv_timer_softc *timer_softc = NULL;
78183840Srajstatic int timers_initialized = 0;
79183840Sraj
80183840Srajstatic int	mv_timer_probe(device_t);
81183840Srajstatic int	mv_timer_attach(device_t);
82183840Sraj
83183840Srajstatic int	mv_hardclock(void *);
84183840Srajstatic unsigned mv_timer_get_timecount(struct timecounter *);
85183840Sraj
86183840Srajstatic uint32_t	mv_get_timer_control(void);
87183840Srajstatic void	mv_set_timer_control(uint32_t);
88183840Srajstatic uint32_t	mv_get_timer(uint32_t);
89183840Srajstatic void	mv_set_timer(uint32_t, uint32_t);
90183840Srajstatic void	mv_set_timer_rel(uint32_t, uint32_t);
91183840Srajstatic void	mv_watchdog_enable(void);
92183840Srajstatic void	mv_watchdog_disable(void);
93183840Srajstatic void	mv_watchdog_event(void *, unsigned int, int *);
94210293Smavstatic int	mv_timer_start(struct eventtimer *et,
95247463Smav    sbintime_t first, sbintime_t period);
96210293Smavstatic int	mv_timer_stop(struct eventtimer *et);
97210293Smavstatic void	mv_setup_timers(void);
98183840Sraj
99183840Srajstatic struct timecounter mv_timer_timecounter = {
100183840Sraj	.tc_get_timecount = mv_timer_get_timecount,
101210293Smav	.tc_name = "CPUTimer1",
102183840Sraj	.tc_frequency = 0,	/* This is assigned on the fly in the init sequence */
103183840Sraj	.tc_counter_mask = ~0u,
104183840Sraj	.tc_quality = 1000,
105183840Sraj};
106183840Sraj
107183840Srajstatic int
108183840Srajmv_timer_probe(device_t dev)
109183840Sraj{
110183840Sraj
111266152Sian	if (!ofw_bus_status_okay(dev))
112266152Sian		return (ENXIO);
113266152Sian
114209131Sraj	if (!ofw_bus_is_compatible(dev, "mrvl,timer"))
115209131Sraj		return (ENXIO);
116209131Sraj
117183840Sraj	device_set_desc(dev, "Marvell CPU Timer");
118183840Sraj	return (0);
119183840Sraj}
120183840Sraj
121183840Srajstatic int
122183840Srajmv_timer_attach(device_t dev)
123183840Sraj{
124183840Sraj	int	error;
125183840Sraj	void	*ihl;
126183840Sraj	struct	mv_timer_softc *sc;
127239277Sgonzo#if !defined(SOC_MV_ARMADAXP)
128210293Smav	uint32_t irq_cause, irq_mask;
129239277Sgonzo#endif
130183840Sraj
131183840Sraj	if (timer_softc != NULL)
132183840Sraj		return (ENXIO);
133183840Sraj
134183840Sraj	sc = (struct mv_timer_softc *)device_get_softc(dev);
135183840Sraj	timer_softc = sc;
136183840Sraj
137183840Sraj	error = bus_alloc_resources(dev, mv_timer_spec, sc->timer_res);
138183840Sraj	if (error) {
139183840Sraj		device_printf(dev, "could not allocate resources\n");
140183840Sraj		return (ENXIO);
141183840Sraj	}
142183840Sraj
143183840Sraj	sc->timer_bst = rman_get_bustag(sc->timer_res[0]);
144183840Sraj	sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]);
145183840Sraj
146183840Sraj	mtx_init(&timer_softc->timer_mtx, "watchdog", NULL, MTX_DEF);
147183840Sraj	mv_watchdog_disable();
148183840Sraj	EVENTHANDLER_REGISTER(watchdog_list, mv_watchdog_event, sc, 0);
149183840Sraj
150183840Sraj	if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK,
151210293Smav	    mv_hardclock, NULL, sc, &ihl) != 0) {
152183840Sraj		bus_release_resources(dev, mv_timer_spec, sc->timer_res);
153210293Smav		device_printf(dev, "Could not setup interrupt.\n");
154183840Sraj		return (ENXIO);
155183840Sraj	}
156183840Sraj
157210293Smav	mv_setup_timers();
158239277Sgonzo#if !defined(SOC_MV_ARMADAXP)
159210293Smav	irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
160239277Sgonzo        irq_cause &= IRQ_TIMER0_CLR;
161239277Sgonzo
162210293Smav	write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
163210293Smav	irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
164210293Smav	irq_mask |= IRQ_TIMER0_MASK;
165218426Smarcel	irq_mask &= ~IRQ_TIMER1_MASK;
166210293Smav	write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
167239277Sgonzo#endif
168210293Smav	sc->et.et_name = "CPUTimer0";
169210293Smav	sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
170210293Smav	sc->et.et_quality = 1000;
171239277Sgonzo
172239277Sgonzo	sc->et.et_frequency = MV_CLOCK_SRC;
173247463Smav	sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
174247463Smav	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
175210293Smav	sc->et.et_start = mv_timer_start;
176210293Smav	sc->et.et_stop = mv_timer_stop;
177210293Smav	sc->et.et_priv = sc;
178210293Smav	et_register(&sc->et);
179239277Sgonzo	mv_timer_timecounter.tc_frequency = MV_CLOCK_SRC;
180210293Smav	tc_init(&mv_timer_timecounter);
181210293Smav
182183840Sraj	return (0);
183183840Sraj}
184183840Sraj
185183840Srajstatic int
186183840Srajmv_hardclock(void *arg)
187183840Sraj{
188210293Smav	struct	mv_timer_softc *sc;
189183840Sraj	uint32_t irq_cause;
190183840Sraj
191212823Smav	irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
192239277Sgonzo	irq_cause &= IRQ_TIMER0_CLR;
193212823Smav	write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
194212823Smav
195210293Smav	sc = (struct mv_timer_softc *)arg;
196210293Smav	if (sc->et.et_active)
197210293Smav		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
198183840Sraj
199183840Sraj	return (FILTER_HANDLED);
200183840Sraj}
201183840Sraj
202183840Srajstatic device_method_t mv_timer_methods[] = {
203183840Sraj	DEVMETHOD(device_probe, mv_timer_probe),
204183840Sraj	DEVMETHOD(device_attach, mv_timer_attach),
205183840Sraj
206183840Sraj	{ 0, 0 }
207183840Sraj};
208183840Sraj
209183840Srajstatic driver_t mv_timer_driver = {
210183840Sraj	"timer",
211183840Sraj	mv_timer_methods,
212183840Sraj	sizeof(struct mv_timer_softc),
213183840Sraj};
214183840Sraj
215183840Srajstatic devclass_t mv_timer_devclass;
216183840Sraj
217209131SrajDRIVER_MODULE(timer, simplebus, mv_timer_driver, mv_timer_devclass, 0, 0);
218183840Sraj
219183840Srajstatic unsigned
220183840Srajmv_timer_get_timecount(struct timecounter *tc)
221183840Sraj{
222183840Sraj
223183840Sraj	return (INITIAL_TIMECOUNTER - mv_get_timer(1));
224183840Sraj}
225183840Sraj
226183840Srajvoid
227183840SrajDELAY(int usec)
228183840Sraj{
229183840Sraj	uint32_t	val, val_temp;
230183840Sraj	int32_t		nticks;
231183840Sraj
232183840Sraj	if (!timers_initialized) {
233183840Sraj		for (; usec > 0; usec--)
234183840Sraj			for (val = 100; val > 0; val--)
235218426Smarcel				__asm __volatile("nop" ::: "memory");
236183840Sraj		return;
237183840Sraj	}
238183840Sraj
239183840Sraj	val = mv_get_timer(1);
240239277Sgonzo	nticks = ((MV_CLOCK_SRC / 1000000 + 1) * usec);
241183840Sraj
242183840Sraj	while (nticks > 0) {
243183840Sraj		val_temp = mv_get_timer(1);
244183840Sraj		if (val > val_temp)
245183840Sraj			nticks -= (val - val_temp);
246183840Sraj		else
247183840Sraj			nticks -= (val + (INITIAL_TIMECOUNTER - val_temp));
248183840Sraj
249183840Sraj		val = val_temp;
250183840Sraj	}
251183840Sraj}
252183840Sraj
253183840Srajstatic uint32_t
254183840Srajmv_get_timer_control(void)
255183840Sraj{
256183840Sraj
257183840Sraj	return (bus_space_read_4(timer_softc->timer_bst,
258183840Sraj	    timer_softc->timer_bsh, CPU_TIMER_CONTROL));
259183840Sraj}
260183840Sraj
261183840Srajstatic void
262183840Srajmv_set_timer_control(uint32_t val)
263183840Sraj{
264183840Sraj
265183840Sraj	bus_space_write_4(timer_softc->timer_bst,
266183840Sraj	    timer_softc->timer_bsh, CPU_TIMER_CONTROL, val);
267183840Sraj}
268183840Sraj
269183840Srajstatic uint32_t
270183840Srajmv_get_timer(uint32_t timer)
271183840Sraj{
272183840Sraj
273183840Sraj	return (bus_space_read_4(timer_softc->timer_bst,
274183840Sraj	    timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8));
275183840Sraj}
276183840Sraj
277183840Srajstatic void
278183840Srajmv_set_timer(uint32_t timer, uint32_t val)
279183840Sraj{
280183840Sraj
281183840Sraj	bus_space_write_4(timer_softc->timer_bst,
282183840Sraj	    timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8, val);
283183840Sraj}
284183840Sraj
285183840Srajstatic void
286183840Srajmv_set_timer_rel(uint32_t timer, uint32_t val)
287183840Sraj{
288183840Sraj
289183840Sraj	bus_space_write_4(timer_softc->timer_bst,
290183840Sraj	    timer_softc->timer_bsh, CPU_TIMER0_REL + timer * 0x8, val);
291183840Sraj}
292183840Sraj
293183840Srajstatic void
294183840Srajmv_watchdog_enable(void)
295183840Sraj{
296239277Sgonzo	uint32_t val, irq_cause;
297239277Sgonzo#if !defined(SOC_MV_ARMADAXP)
298239277Sgonzo	uint32_t irq_mask;
299239277Sgonzo#endif
300183840Sraj
301183840Sraj	irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
302239277Sgonzo	irq_cause &= IRQ_TIMER_WD_CLR;
303183840Sraj	write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
304183840Sraj
305240488Sgber#if defined(SOC_MV_ARMADAXP)
306240488Sgber	val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
307240488Sgber	val |= (WD_GLOBAL_MASK | WD_CPU0_MASK);
308240488Sgber	write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
309240488Sgber#else
310183840Sraj	irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
311183840Sraj	irq_mask |= IRQ_TIMER_WD_MASK;
312183840Sraj	write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
313183840Sraj
314183840Sraj	val = read_cpu_ctrl(RSTOUTn_MASK);
315183840Sraj	val |= WD_RST_OUT_EN;
316183840Sraj	write_cpu_ctrl(RSTOUTn_MASK, val);
317240488Sgber#endif
318183840Sraj
319183840Sraj	val = mv_get_timer_control();
320183840Sraj	val |= CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO;
321251371Sgber#if defined(SOC_MV_ARMADAXP)
322251371Sgber	val |= CPU_TIMER_WD_25MHZ_EN;
323251371Sgber#endif
324183840Sraj	mv_set_timer_control(val);
325183840Sraj}
326183840Sraj
327183840Srajstatic void
328183840Srajmv_watchdog_disable(void)
329183840Sraj{
330239277Sgonzo	uint32_t val, irq_cause;
331239277Sgonzo#if !defined(SOC_MV_ARMADAXP)
332239277Sgonzo	uint32_t irq_mask;
333239277Sgonzo#endif
334183840Sraj
335183840Sraj	val = mv_get_timer_control();
336183840Sraj	val &= ~(CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO);
337183840Sraj	mv_set_timer_control(val);
338183840Sraj
339240488Sgber#if defined(SOC_MV_ARMADAXP)
340240488Sgber	val = read_cpu_mp_clocks(WD_RSTOUTn_MASK);
341240488Sgber	val &= ~(WD_GLOBAL_MASK | WD_CPU0_MASK);
342240488Sgber	write_cpu_mp_clocks(WD_RSTOUTn_MASK, val);
343240488Sgber#else
344183840Sraj	val = read_cpu_ctrl(RSTOUTn_MASK);
345183840Sraj	val &= ~WD_RST_OUT_EN;
346183840Sraj	write_cpu_ctrl(RSTOUTn_MASK, val);
347183840Sraj
348183840Sraj	irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK);
349183840Sraj	irq_mask &= ~(IRQ_TIMER_WD_MASK);
350183840Sraj	write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask);
351239277Sgonzo#endif
352183840Sraj
353183840Sraj	irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE);
354239277Sgonzo	irq_cause &= IRQ_TIMER_WD_CLR;
355183840Sraj	write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause);
356183840Sraj}
357183840Sraj
358183840Sraj
359183840Sraj/*
360183840Sraj * Watchdog event handler.
361183840Sraj */
362183840Srajstatic void
363183840Srajmv_watchdog_event(void *arg, unsigned int cmd, int *error)
364183840Sraj{
365183840Sraj	uint64_t ns;
366183840Sraj	uint64_t ticks;
367183840Sraj
368183840Sraj	mtx_lock(&timer_softc->timer_mtx);
369183840Sraj	if (cmd == 0)
370183840Sraj		mv_watchdog_disable();
371183840Sraj	else {
372183840Sraj		/*
373183840Sraj		 * Watchdog timeout is in nanosecs, calculation according to
374183840Sraj		 * watchdog(9)
375183840Sraj		 */
376183840Sraj		ns = (uint64_t)1 << (cmd & WD_INTERVAL);
377239277Sgonzo		ticks = (uint64_t)(ns * MV_CLOCK_SRC) / 1000000000;
378183840Sraj		if (ticks > MAX_WATCHDOG_TICKS)
379183840Sraj			mv_watchdog_disable();
380183840Sraj		else {
381183840Sraj			/* Timer 2 is the watchdog */
382183840Sraj			mv_set_timer(2, ticks);
383183840Sraj			mv_watchdog_enable();
384183840Sraj			*error = 0;
385183840Sraj		}
386183840Sraj	}
387183840Sraj	mtx_unlock(&timer_softc->timer_mtx);
388183840Sraj}
389183840Sraj
390210293Smavstatic int
391247463Smavmv_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
392183840Sraj{
393210293Smav	struct	mv_timer_softc *sc;
394210293Smav	uint32_t val, val1;
395210293Smav
396210293Smav	/* Calculate dividers. */
397210293Smav	sc = (struct mv_timer_softc *)et->et_priv;
398247463Smav	if (period != 0)
399247463Smav		val = ((uint32_t)sc->et.et_frequency * period) >> 32;
400247463Smav	else
401210293Smav		val = 0;
402247463Smav	if (first != 0)
403247463Smav		val1 = ((uint32_t)sc->et.et_frequency * first) >> 32;
404247463Smav	else
405210293Smav		val1 = val;
406210293Smav
407210293Smav	/* Apply configuration. */
408210293Smav	mv_set_timer_rel(0, val);
409210293Smav	mv_set_timer(0, val1);
410210293Smav	val = mv_get_timer_control();
411210293Smav	val |= CPU_TIMER0_EN;
412247463Smav	if (period != 0)
413210293Smav		val |= CPU_TIMER0_AUTO;
414212823Smav	else
415212823Smav		val &= ~CPU_TIMER0_AUTO;
416210293Smav	mv_set_timer_control(val);
417210293Smav	return (0);
418210293Smav}
419210293Smav
420210293Smavstatic int
421210293Smavmv_timer_stop(struct eventtimer *et)
422210293Smav{
423183840Sraj	uint32_t val;
424183840Sraj
425183840Sraj	val = mv_get_timer_control();
426210293Smav	val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
427183840Sraj	mv_set_timer_control(val);
428210293Smav	return (0);
429183840Sraj}
430183840Sraj
431183840Srajstatic void
432210293Smavmv_setup_timers(void)
433183840Sraj{
434183840Sraj	uint32_t val;
435183840Sraj
436183840Sraj	mv_set_timer_rel(1, INITIAL_TIMECOUNTER);
437183840Sraj	mv_set_timer(1, INITIAL_TIMECOUNTER);
438183840Sraj	val = mv_get_timer_control();
439210293Smav	val &= ~(CPU_TIMER0_EN | CPU_TIMER0_AUTO);
440183840Sraj	val |= CPU_TIMER1_EN | CPU_TIMER1_AUTO;
441251371Sgber#if defined(SOC_MV_ARMADAXP)
442251371Sgber	/* Enable 25MHz mode */
443251371Sgber	val |= CPU_TIMER0_25MHZ_EN | CPU_TIMER1_25MHZ_EN;
444251371Sgber#endif
445183840Sraj	mv_set_timer_control(val);
446210293Smav	timers_initialized = 1;
447183840Sraj}
448