1241844Seadler/*
2239922Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3239922Sgonzo * Copyright (c) 2012 Damjan Marion <dmarion@freebsd.org>
4239922Sgonzo * All rights reserved.
5239922Sgonzo *
6239922Sgonzo * Redistribution and use in source and binary forms, with or without
7239922Sgonzo * modification, are permitted provided that the following conditions
8239922Sgonzo * are met:
9239922Sgonzo * 1. Redistributions of source code must retain the above copyright
10239922Sgonzo *    notice, this list of conditions and the following disclaimer.
11239922Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12239922Sgonzo *    notice, this list of conditions and the following disclaimer in the
13239922Sgonzo *    documentation and/or other materials provided with the distribution.
14239922Sgonzo *
15239922Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16239922Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17239922Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18239922Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19239922Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20239922Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21239922Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22239922Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23239922Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24239922Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25239922Sgonzo * SUCH DAMAGE.
26239922Sgonzo */
27239922Sgonzo
28239922Sgonzo#include <sys/cdefs.h>
29239922Sgonzo__FBSDID("$FreeBSD$");
30239922Sgonzo
31239922Sgonzo#include <sys/param.h>
32239922Sgonzo#include <sys/systm.h>
33239922Sgonzo#include <sys/bus.h>
34239922Sgonzo#include <sys/kernel.h>
35239922Sgonzo#include <sys/module.h>
36239922Sgonzo#include <sys/malloc.h>
37239922Sgonzo#include <sys/rman.h>
38239922Sgonzo#include <sys/timeet.h>
39239922Sgonzo#include <sys/timetc.h>
40239922Sgonzo#include <sys/watchdog.h>
41239922Sgonzo#include <machine/bus.h>
42239922Sgonzo#include <machine/cpu.h>
43239922Sgonzo#include <machine/intr.h>
44239922Sgonzo
45239922Sgonzo#include <dev/fdt/fdt_common.h>
46239922Sgonzo#include <dev/ofw/openfirm.h>
47239922Sgonzo#include <dev/ofw/ofw_bus.h>
48239922Sgonzo#include <dev/ofw/ofw_bus_subr.h>
49239922Sgonzo
50239922Sgonzo#include <machine/bus.h>
51239922Sgonzo#include <machine/fdt.h>
52239922Sgonzo
53239922Sgonzo#define	BCM2835_NUM_TIMERS	4
54239922Sgonzo
55239922Sgonzo#define	DEFAULT_TIMER		3
56239922Sgonzo#define	DEFAULT_FREQUENCY	1000000
57248585Smav#define	MIN_PERIOD		5LLU
58239922Sgonzo
59239922Sgonzo#define	SYSTIMER_CS	0x00
60239922Sgonzo#define	SYSTIMER_CLO	0x04
61239922Sgonzo#define	SYSTIMER_CHI	0x08
62239922Sgonzo#define	SYSTIMER_C0	0x0C
63239922Sgonzo#define	SYSTIMER_C1	0x10
64239922Sgonzo#define	SYSTIMER_C2	0x14
65239922Sgonzo#define	SYSTIMER_C3	0x18
66239922Sgonzo
67239922Sgonzostruct systimer {
68239922Sgonzo	int			index;
69239922Sgonzo	bool			enabled;
70239922Sgonzo	struct eventtimer	et;
71239922Sgonzo};
72239922Sgonzo
73239922Sgonzostruct bcm_systimer_softc {
74239922Sgonzo	struct resource*	mem_res;
75239922Sgonzo	struct resource*	irq_res[BCM2835_NUM_TIMERS];
76239922Sgonzo	void*			intr_hl[BCM2835_NUM_TIMERS];
77239922Sgonzo	uint32_t		sysclk_freq;
78239922Sgonzo	bus_space_tag_t		bst;
79239922Sgonzo	bus_space_handle_t	bsh;
80239922Sgonzo	struct systimer		st[BCM2835_NUM_TIMERS];
81239922Sgonzo};
82239922Sgonzo
83239922Sgonzostatic struct resource_spec bcm_systimer_irq_spec[] = {
84239922Sgonzo	{ SYS_RES_IRQ,      0,  RF_ACTIVE },
85239922Sgonzo	{ SYS_RES_IRQ,      1,  RF_ACTIVE },
86239922Sgonzo	{ SYS_RES_IRQ,      2,  RF_ACTIVE },
87239922Sgonzo	{ SYS_RES_IRQ,      3,  RF_ACTIVE },
88239922Sgonzo	{ -1,               0,  0 }
89239922Sgonzo};
90239922Sgonzo
91239922Sgonzostatic struct bcm_systimer_softc *bcm_systimer_sc = NULL;
92239922Sgonzo
93239922Sgonzo/* Read/Write macros for Timer used as timecounter */
94239922Sgonzo#define bcm_systimer_tc_read_4(reg)		\
95239922Sgonzo	bus_space_read_4(bcm_systimer_sc->bst, \
96239922Sgonzo		bcm_systimer_sc->bsh, reg)
97239922Sgonzo
98239922Sgonzo#define bcm_systimer_tc_write_4(reg, val)	\
99239922Sgonzo	bus_space_write_4(bcm_systimer_sc->bst, \
100239922Sgonzo		bcm_systimer_sc->bsh, reg, val)
101239922Sgonzo
102239922Sgonzostatic unsigned bcm_systimer_tc_get_timecount(struct timecounter *);
103239922Sgonzo
104239922Sgonzostatic struct timecounter bcm_systimer_tc = {
105243523Skientzle	.tc_name           = "BCM2835 Timecounter",
106239922Sgonzo	.tc_get_timecount  = bcm_systimer_tc_get_timecount,
107239922Sgonzo	.tc_poll_pps       = NULL,
108239922Sgonzo	.tc_counter_mask   = ~0u,
109239922Sgonzo	.tc_frequency      = 0,
110239922Sgonzo	.tc_quality        = 1000,
111239922Sgonzo};
112239922Sgonzo
113239922Sgonzostatic unsigned
114239922Sgonzobcm_systimer_tc_get_timecount(struct timecounter *tc)
115239922Sgonzo{
116239922Sgonzo	return bcm_systimer_tc_read_4(SYSTIMER_CLO);
117239922Sgonzo}
118239922Sgonzo
119239922Sgonzostatic int
120247463Smavbcm_systimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
121239922Sgonzo{
122239922Sgonzo	struct systimer *st = et->et_priv;
123248585Smav	uint32_t clo, clo1;
124239922Sgonzo	uint32_t count;
125244758Sgonzo	register_t s;
126239922Sgonzo
127247463Smav	if (first != 0) {
128239922Sgonzo
129247463Smav		count = ((uint32_t)et->et_frequency * first) >> 32;
130239922Sgonzo
131244758Sgonzo		s = intr_disable();
132239922Sgonzo		clo = bcm_systimer_tc_read_4(SYSTIMER_CLO);
133248585Smavrestart:
134239922Sgonzo		clo += count;
135244758Sgonzo		/*
136244758Sgonzo		 * Clear pending interrupts
137244758Sgonzo		 */
138244758Sgonzo		bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index));
139239922Sgonzo		bcm_systimer_tc_write_4(SYSTIMER_C0 + st->index*4, clo);
140248585Smav		clo1 = bcm_systimer_tc_read_4(SYSTIMER_CLO);
141248585Smav		if ((int32_t)(clo1 - clo) >= 0) {
142248585Smav			count *= 2;
143248585Smav			clo = clo1;
144248585Smav			goto restart;
145248585Smav		}
146244758Sgonzo		st->enabled = 1;
147244758Sgonzo		intr_restore(s);
148239922Sgonzo
149239922Sgonzo		return (0);
150239922Sgonzo	}
151239922Sgonzo
152239922Sgonzo	return (EINVAL);
153239922Sgonzo}
154239922Sgonzo
155239922Sgonzostatic int
156239922Sgonzobcm_systimer_stop(struct eventtimer *et)
157239922Sgonzo{
158239922Sgonzo	struct systimer *st = et->et_priv;
159239922Sgonzo	st->enabled = 0;
160239922Sgonzo
161239922Sgonzo	return (0);
162239922Sgonzo}
163239922Sgonzo
164239922Sgonzostatic int
165239922Sgonzobcm_systimer_intr(void *arg)
166239922Sgonzo{
167239922Sgonzo	struct systimer *st = (struct systimer *)arg;
168244758Sgonzo	uint32_t cs;
169239922Sgonzo
170244758Sgonzo 	cs = bcm_systimer_tc_read_4(SYSTIMER_CS);
171244758Sgonzo	if ((cs & (1 << st->index)) == 0)
172244758Sgonzo		return (FILTER_STRAY);
173244758Sgonzo
174244758Sgonzo	/* ACK interrupt */
175239922Sgonzo	bcm_systimer_tc_write_4(SYSTIMER_CS, (1 << st->index));
176239922Sgonzo	if (st->enabled) {
177239922Sgonzo		if (st->et.et_active) {
178239922Sgonzo			st->et.et_event_cb(&st->et, st->et.et_arg);
179239922Sgonzo		}
180239922Sgonzo	}
181239922Sgonzo
182239922Sgonzo	return (FILTER_HANDLED);
183239922Sgonzo}
184239922Sgonzo
185239922Sgonzostatic int
186239922Sgonzobcm_systimer_probe(device_t dev)
187239922Sgonzo{
188239922Sgonzo
189266152Sian	if (!ofw_bus_status_okay(dev))
190266152Sian		return (ENXIO);
191266152Sian
192239922Sgonzo	if (ofw_bus_is_compatible(dev, "broadcom,bcm2835-system-timer")) {
193239922Sgonzo		device_set_desc(dev, "BCM2835 System Timer");
194239922Sgonzo		return (BUS_PROBE_DEFAULT);
195239922Sgonzo	}
196239922Sgonzo
197239922Sgonzo	return (ENXIO);
198239922Sgonzo}
199239922Sgonzo
200239922Sgonzostatic int
201239922Sgonzobcm_systimer_attach(device_t dev)
202239922Sgonzo{
203239922Sgonzo	struct bcm_systimer_softc *sc = device_get_softc(dev);
204239922Sgonzo	int err;
205239922Sgonzo	int rid = 0;
206239922Sgonzo
207239922Sgonzo	if (bcm_systimer_sc != NULL)
208239922Sgonzo		return (EINVAL);
209239922Sgonzo
210239922Sgonzo	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
211239922Sgonzo	if (sc->mem_res == NULL) {
212239922Sgonzo		device_printf(dev, "could not allocate memory resource\n");
213239922Sgonzo		return (ENXIO);
214239922Sgonzo	}
215239922Sgonzo
216239922Sgonzo	sc->bst = rman_get_bustag(sc->mem_res);
217239922Sgonzo	sc->bsh = rman_get_bushandle(sc->mem_res);
218239922Sgonzo
219239922Sgonzo	/* Request the IRQ resources */
220239922Sgonzo	err = bus_alloc_resources(dev, bcm_systimer_irq_spec,
221239922Sgonzo		sc->irq_res);
222239922Sgonzo	if (err) {
223239922Sgonzo		device_printf(dev, "Error: could not allocate irq resources\n");
224239922Sgonzo		return (ENXIO);
225239922Sgonzo	}
226239922Sgonzo
227239922Sgonzo	/* TODO: get frequency from FDT */
228239922Sgonzo	sc->sysclk_freq = DEFAULT_FREQUENCY;
229239922Sgonzo
230239922Sgonzo	/* Setup and enable the timer */
231239922Sgonzo	if (bus_setup_intr(dev, sc->irq_res[DEFAULT_TIMER], INTR_TYPE_CLK,
232239922Sgonzo			bcm_systimer_intr, NULL, &sc->st[DEFAULT_TIMER],
233239922Sgonzo			&sc->intr_hl[DEFAULT_TIMER]) != 0) {
234239922Sgonzo		bus_release_resources(dev, bcm_systimer_irq_spec,
235239922Sgonzo			sc->irq_res);
236239922Sgonzo		device_printf(dev, "Unable to setup the clock irq handler.\n");
237239922Sgonzo		return (ENXIO);
238239922Sgonzo	}
239239922Sgonzo
240239922Sgonzo	sc->st[DEFAULT_TIMER].index = DEFAULT_TIMER;
241239922Sgonzo	sc->st[DEFAULT_TIMER].enabled = 0;
242239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_name = malloc(64, M_DEVBUF, M_NOWAIT | M_ZERO);
243239922Sgonzo	sprintf(sc->st[DEFAULT_TIMER].et.et_name, "BCM2835 Event Timer %d", DEFAULT_TIMER);
244239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_flags = ET_FLAGS_ONESHOT;
245239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_quality = 1000;
246239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_frequency = sc->sysclk_freq;
247247463Smav	sc->st[DEFAULT_TIMER].et.et_min_period =
248248585Smav	    (MIN_PERIOD << 32) / sc->st[DEFAULT_TIMER].et.et_frequency + 1;
249247463Smav	sc->st[DEFAULT_TIMER].et.et_max_period =
250248585Smav	    (0x7ffffffeLLU << 32) / sc->st[DEFAULT_TIMER].et.et_frequency;
251239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_start = bcm_systimer_start;
252239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_stop = bcm_systimer_stop;
253239922Sgonzo	sc->st[DEFAULT_TIMER].et.et_priv = &sc->st[DEFAULT_TIMER];
254239922Sgonzo	et_register(&sc->st[DEFAULT_TIMER].et);
255239922Sgonzo
256239922Sgonzo	bcm_systimer_sc = sc;
257239922Sgonzo
258239922Sgonzo	bcm_systimer_tc.tc_frequency = DEFAULT_FREQUENCY;
259239922Sgonzo	tc_init(&bcm_systimer_tc);
260239922Sgonzo
261239922Sgonzo	return (0);
262239922Sgonzo}
263239922Sgonzo
264239922Sgonzostatic device_method_t bcm_systimer_methods[] = {
265239922Sgonzo	DEVMETHOD(device_probe,		bcm_systimer_probe),
266239922Sgonzo	DEVMETHOD(device_attach,	bcm_systimer_attach),
267239922Sgonzo	{ 0, 0 }
268239922Sgonzo};
269239922Sgonzo
270239922Sgonzostatic driver_t bcm_systimer_driver = {
271239922Sgonzo	"systimer",
272239922Sgonzo	bcm_systimer_methods,
273239922Sgonzo	sizeof(struct bcm_systimer_softc),
274239922Sgonzo};
275239922Sgonzo
276239922Sgonzostatic devclass_t bcm_systimer_devclass;
277239922Sgonzo
278239922SgonzoDRIVER_MODULE(bcm_systimer, simplebus, bcm_systimer_driver, bcm_systimer_devclass, 0, 0);
279239922Sgonzo
280239922Sgonzovoid
281239922SgonzoDELAY(int usec)
282239922Sgonzo{
283239922Sgonzo	int32_t counts;
284239922Sgonzo	uint32_t first, last;
285239922Sgonzo
286239922Sgonzo	if (bcm_systimer_sc == NULL) {
287239922Sgonzo		for (; usec > 0; usec--)
288239922Sgonzo			for (counts = 200; counts > 0; counts--)
289239922Sgonzo				/* Prevent gcc from optimizing  out the loop */
290239922Sgonzo				cpufunc_nullop();
291239922Sgonzo		return;
292239922Sgonzo	}
293239922Sgonzo
294239922Sgonzo	/* Get the number of times to count */
295255816Sloos	counts = usec * (bcm_systimer_tc.tc_frequency / 1000000) + 1;
296239922Sgonzo
297239922Sgonzo	first = bcm_systimer_tc_read_4(SYSTIMER_CLO);
298239922Sgonzo
299239922Sgonzo	while (counts > 0) {
300239922Sgonzo		last = bcm_systimer_tc_read_4(SYSTIMER_CLO);
301239922Sgonzo		if (last == first)
302239922Sgonzo			continue;
303239922Sgonzo		if (last>first) {
304239922Sgonzo			counts -= (int32_t)(last - first);
305239922Sgonzo		} else {
306239922Sgonzo			counts -= (int32_t)((0xFFFFFFFF - first) + last);
307239922Sgonzo		}
308239922Sgonzo		first = last;
309239922Sgonzo	}
310239922Sgonzo}
311