am335x_dmtimer.c revision 331907
1/*-
2 * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/arm/ti/am335x/am335x_dmtimer.c 331907 2018-04-03 06:06:39Z gonzo $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/module.h>
35#include <sys/malloc.h>
36#include <sys/rman.h>
37#include <sys/timeet.h>
38#include <sys/timetc.h>
39#include <machine/bus.h>
40
41#ifdef MULTIDELAY
42#include <machine/machdep.h> /* For arm_set_delay */
43#endif
44
45#include <dev/ofw/openfirm.h>
46#include <dev/ofw/ofw_bus.h>
47#include <dev/ofw/ofw_bus_subr.h>
48
49#include <arm/ti/ti_prcm.h>
50#include <arm/ti/ti_hwmods.h>
51
52#include "am335x_dmtreg.h"
53
54struct am335x_dmtimer_softc {
55	device_t		dev;
56	int			tmr_mem_rid;
57	struct resource *	tmr_mem_res;
58	int			tmr_irq_rid;
59	struct resource *	tmr_irq_res;
60	void			*tmr_irq_handler;
61	uint32_t		sysclk_freq;
62	uint32_t		tclr;		/* Cached TCLR register. */
63	union {
64		struct timecounter tc;
65		struct eventtimer et;
66	} func;
67	int			tmr_num;	/* Hardware unit number. */
68	char			tmr_name[12];	/* "DMTimerN", N = tmr_num */
69};
70
71static struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL;
72static struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL;
73
74static void am335x_dmtimer_delay(int, void *);
75
76/*
77 * We use dmtimer2 for eventtimer and dmtimer3 for timecounter.
78 */
79#define ET_TMR_NUM      2
80#define TC_TMR_NUM      3
81
82/* List of compatible strings for FDT tree */
83static struct ofw_compat_data compat_data[] = {
84	{"ti,am335x-timer",     1},
85	{"ti,am335x-timer-1ms", 1},
86	{NULL,                  0},
87};
88
89#define	DMTIMER_READ4(sc, reg)		bus_read_4((sc)->tmr_mem_res, (reg))
90#define	DMTIMER_WRITE4(sc, reg, val)	bus_write_4((sc)->tmr_mem_res, (reg), (val))
91
92static int
93am335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
94{
95	struct am335x_dmtimer_softc *sc;
96	uint32_t initial_count, reload_count;
97
98	sc = et->et_priv;
99
100	/*
101	 * Stop the timer before changing it.  This routine will often be called
102	 * while the timer is still running, to either lengthen or shorten the
103	 * current event time.  We need to ensure the timer doesn't expire while
104	 * we're working with it.
105	 *
106	 * Also clear any pending interrupt status, because it's at least
107	 * theoretically possible that we're running in a primary interrupt
108	 * context now, and a timer interrupt could be pending even before we
109	 * stopped the timer.  The more likely case is that we're being called
110	 * from the et_event_cb() routine dispatched from our own handler, but
111	 * it's not clear to me that that's the only case possible.
112	 */
113	sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD);
114	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
115	DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
116
117	if (period != 0) {
118		reload_count = ((uint32_t)et->et_frequency * period) >> 32;
119		sc->tclr |= DMT_TCLR_AUTOLOAD;
120	} else {
121		reload_count = 0;
122	}
123
124	if (first != 0)
125		initial_count = ((uint32_t)et->et_frequency * first) >> 32;
126	else
127		initial_count = reload_count;
128
129	/*
130	 * Set auto-reload and current-count values.  This timer hardware counts
131	 * up from the initial/reload value and interrupts on the zero rollover.
132	 */
133	DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count);
134	DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count);
135
136	/* Enable overflow interrupt, and start the timer. */
137	DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF);
138	sc->tclr |= DMT_TCLR_START;
139	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
140
141	return (0);
142}
143
144static int
145am335x_dmtimer_et_stop(struct eventtimer *et)
146{
147	struct am335x_dmtimer_softc *sc;
148
149	sc = et->et_priv;
150
151	/* Stop timer, disable and clear interrupt. */
152	sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD);
153	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
154	DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF);
155	DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
156	return (0);
157}
158
159static int
160am335x_dmtimer_et_intr(void *arg)
161{
162	struct am335x_dmtimer_softc *sc;
163
164	sc = arg;
165
166	/* Ack the interrupt, and invoke the callback if it's still enabled. */
167	DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF);
168	if (sc->func.et.et_active)
169		sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg);
170
171	return (FILTER_HANDLED);
172}
173
174static int
175am335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc)
176{
177	KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer"));
178
179	/*
180	 * Setup eventtimer interrupt handling.  Panic if anything goes wrong,
181	 * because the system just isn't going to run without an eventtimer.
182	 */
183	sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ,
184	    &sc->tmr_irq_rid, RF_ACTIVE);
185	if (sc->tmr_irq_res == NULL)
186		panic("am335x_dmtimer: could not allocate irq resources");
187	if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK,
188	    am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0)
189		panic("am335x_dmtimer: count not setup irq handler");
190
191	sc->func.et.et_name = sc->tmr_name;
192	sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT;
193	sc->func.et.et_quality = 500;
194	sc->func.et.et_frequency = sc->sysclk_freq;
195	sc->func.et.et_min_period =
196	    ((0x00000005LLU << 32) / sc->func.et.et_frequency);
197	sc->func.et.et_max_period =
198	    (0xfffffffeLLU << 32) / sc->func.et.et_frequency;
199	sc->func.et.et_start = am335x_dmtimer_et_start;
200	sc->func.et.et_stop = am335x_dmtimer_et_stop;
201	sc->func.et.et_priv = sc;
202
203	am335x_dmtimer_et_sc = sc;
204	et_register(&sc->func.et);
205
206	return (0);
207}
208
209static unsigned
210am335x_dmtimer_tc_get_timecount(struct timecounter *tc)
211{
212	struct am335x_dmtimer_softc *sc;
213
214	sc = tc->tc_priv;
215
216	return (DMTIMER_READ4(sc, DMT_TCRR));
217}
218
219static int
220am335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc)
221{
222	KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter"));
223
224	/* Set up timecounter, start it, register it. */
225	DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET);
226	while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET)
227		continue;
228
229	sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD;
230	DMTIMER_WRITE4(sc, DMT_TLDR, 0);
231	DMTIMER_WRITE4(sc, DMT_TCRR, 0);
232	DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr);
233
234	sc->func.tc.tc_name           = sc->tmr_name;
235	sc->func.tc.tc_get_timecount  = am335x_dmtimer_tc_get_timecount;
236	sc->func.tc.tc_counter_mask   = ~0u;
237	sc->func.tc.tc_frequency      = sc->sysclk_freq;
238	sc->func.tc.tc_quality        = 500;
239	sc->func.tc.tc_priv           = sc;
240
241	am335x_dmtimer_tc_sc = sc;
242	tc_init(&sc->func.tc);
243
244#ifdef MULTIDELAY
245	arm_set_delay(am335x_dmtimer_delay, sc);
246#endif
247
248	return (0);
249}
250
251static int
252am335x_dmtimer_probe(device_t dev)
253{
254	char strbuf[32];
255	int tmr_num;
256
257	if (!ofw_bus_status_okay(dev))
258		return (ENXIO);
259
260	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
261		return (ENXIO);
262
263	/*
264	 * Get the hardware unit number (the N from ti,hwmods="timerN").
265	 * If this isn't the hardware unit we're going to use for either the
266	 * eventtimer or the timecounter, no point in instantiating the device.
267	 */
268	tmr_num = ti_hwmods_get_unit(dev, "timer");
269	if (tmr_num != ET_TMR_NUM && tmr_num != TC_TMR_NUM)
270		return (ENXIO);
271
272	snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num);
273	device_set_desc_copy(dev, strbuf);
274
275	return(BUS_PROBE_DEFAULT);
276}
277
278static int
279am335x_dmtimer_attach(device_t dev)
280{
281	struct am335x_dmtimer_softc *sc;
282	clk_ident_t timer_id;
283	int err;
284
285	sc = device_get_softc(dev);
286	sc->dev = dev;
287
288	/* Get the base clock frequency. */
289	if ((err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq)) != 0)
290		return (err);
291
292	/* Enable clocks and power on the device. */
293	if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT)
294		return (ENXIO);
295	if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0)
296		return (err);
297	if ((err = ti_prcm_clk_enable(timer_id)) != 0)
298		return (err);
299
300	/* Request the memory resources. */
301	sc->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
302	    &sc->tmr_mem_rid, RF_ACTIVE);
303	if (sc->tmr_mem_res == NULL) {
304		return (ENXIO);
305	}
306
307	sc->tmr_num = ti_hwmods_get_unit(dev, "timer");
308	snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num);
309
310	/*
311	 * Go set up either a timecounter or eventtimer.  We wouldn't have
312	 * attached if we weren't one or the other.
313	 */
314	if (sc->tmr_num == ET_TMR_NUM)
315		am335x_dmtimer_et_init(sc);
316	else if (sc->tmr_num == TC_TMR_NUM)
317		am335x_dmtimer_tc_init(sc);
318	else
319		panic("am335x_dmtimer: bad timer number %d", sc->tmr_num);
320
321	return (0);
322}
323
324static device_method_t am335x_dmtimer_methods[] = {
325	DEVMETHOD(device_probe,		am335x_dmtimer_probe),
326	DEVMETHOD(device_attach,	am335x_dmtimer_attach),
327	{ 0, 0 }
328};
329
330static driver_t am335x_dmtimer_driver = {
331	"am335x_dmtimer",
332	am335x_dmtimer_methods,
333	sizeof(struct am335x_dmtimer_softc),
334};
335
336static devclass_t am335x_dmtimer_devclass;
337
338DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0);
339MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1);
340
341static void
342am335x_dmtimer_delay(int usec, void *arg)
343{
344	struct am335x_dmtimer_softc *sc = arg;
345	int32_t counts;
346	uint32_t first, last;
347
348	/* Get the number of times to count */
349	counts = (usec + 1) * (sc->sysclk_freq / 1000000);
350
351	first = DMTIMER_READ4(sc, DMT_TCRR);
352
353	while (counts > 0) {
354		last = DMTIMER_READ4(sc, DMT_TCRR);
355		if (last > first) {
356			counts -= (int32_t)(last - first);
357		} else {
358			counts -= (int32_t)((0xFFFFFFFF - first) + last);
359		}
360		first = last;
361	}
362}
363
364#ifndef MULTIDELAY
365void
366DELAY(int usec)
367{
368	int32_t counts;
369
370	if (am335x_dmtimer_tc_sc == NULL) {
371		for (; usec > 0; usec--)
372			for (counts = 200; counts > 0; counts--)
373				/* Prevent gcc from optimizing  out the loop */
374				cpufunc_nullop();
375		return;
376	} else
377		am335x_dmtimer_delay(usec, am335x_dmtimer_tc_sc);
378}
379#endif
380