xref: /netbsd-src/sys/arch/arm/imx/imx23_timrot.c (revision 94de07308fa702688d56949a9efc73b223f72255)
1*94de0730Smatt /* $Id: imx23_timrot.c,v 1.3 2013/10/07 17:36:40 matt Exp $ */
2eba5cacbSjkunz 
3eba5cacbSjkunz /*
4eba5cacbSjkunz  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5eba5cacbSjkunz  * All rights reserved.
6eba5cacbSjkunz  *
7eba5cacbSjkunz  * This code is derived from software contributed to The NetBSD Foundation
8eba5cacbSjkunz  * by Petri Laakso.
9eba5cacbSjkunz  *
10eba5cacbSjkunz  * Redistribution and use in source and binary forms, with or without
11eba5cacbSjkunz  * modification, are permitted provided that the following conditions
12eba5cacbSjkunz  * are met:
13eba5cacbSjkunz  * 1. Redistributions of source code must retain the above copyright
14eba5cacbSjkunz  *    notice, this list of conditions and the following disclaimer.
15eba5cacbSjkunz  * 2. Redistributions in binary form must reproduce the above copyright
16eba5cacbSjkunz  *    notice, this list of conditions and the following disclaimer in the
17eba5cacbSjkunz  *    documentation and/or other materials provided with the distribution.
18eba5cacbSjkunz  *
19eba5cacbSjkunz  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20eba5cacbSjkunz  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21eba5cacbSjkunz  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22eba5cacbSjkunz  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23eba5cacbSjkunz  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24eba5cacbSjkunz  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25eba5cacbSjkunz  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26eba5cacbSjkunz  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27eba5cacbSjkunz  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28eba5cacbSjkunz  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29eba5cacbSjkunz  * POSSIBILITY OF SUCH DAMAGE.
30eba5cacbSjkunz  */
31eba5cacbSjkunz 
32eba5cacbSjkunz #include <sys/param.h>
33eba5cacbSjkunz #include <sys/bus.h>
34eba5cacbSjkunz #include <sys/device.h>
35eba5cacbSjkunz #include <sys/errno.h>
36eba5cacbSjkunz #include <sys/systm.h>
37eba5cacbSjkunz 
38eba5cacbSjkunz #include <arm/pic/picvar.h>
39eba5cacbSjkunz 
40eba5cacbSjkunz #include <arm/imx/imx23_icollreg.h>
41eba5cacbSjkunz #include <arm/imx/imx23_timrotreg.h>
42eba5cacbSjkunz #include <arm/imx/imx23var.h>
43eba5cacbSjkunz 
44eba5cacbSjkunz extern int hz;
45eba5cacbSjkunz extern int stathz;
46eba5cacbSjkunz 
47eba5cacbSjkunz static int	timrot_match(device_t, cfdata_t, void *);
48eba5cacbSjkunz static void	timrot_attach(device_t, device_t, void *);
49eba5cacbSjkunz static int	timrot_activate(device_t, enum devact);
50eba5cacbSjkunz 
51eba5cacbSjkunz static void	timrot_reset(void);
52eba5cacbSjkunz 
53eba5cacbSjkunz /*
54eba5cacbSjkunz  * Timer IRQ handler definitions.
55eba5cacbSjkunz  */
56eba5cacbSjkunz static int	systimer_irq(void *);
57eba5cacbSjkunz static int	stattimer_irq(void *);
58eba5cacbSjkunz 
59eba5cacbSjkunz void	cpu_initclocks(void);
60eba5cacbSjkunz void 	setstatclockrate(int);
61eba5cacbSjkunz 
62eba5cacbSjkunz /* Allocated for each timer instance. */
63eba5cacbSjkunz struct timrot_softc {
64eba5cacbSjkunz 	device_t sc_dev;
65eba5cacbSjkunz 	bus_space_tag_t sc_iot;
66eba5cacbSjkunz 	bus_space_handle_t sc_hdl;
67eba5cacbSjkunz 	int8_t sc_irq;
68eba5cacbSjkunz 	int (*irq_handler)(void *);
69eba5cacbSjkunz 	int freq;
70eba5cacbSjkunz };
71eba5cacbSjkunz 
72eba5cacbSjkunz static bus_space_tag_t timrot_iot;
73eba5cacbSjkunz static bus_space_handle_t timrot_hdl;
74eba5cacbSjkunz 
75eba5cacbSjkunz CFATTACH_DECL3_NEW(timrot,
76eba5cacbSjkunz 	sizeof(struct timrot_softc),
77eba5cacbSjkunz 	timrot_match,
78eba5cacbSjkunz 	timrot_attach,
79eba5cacbSjkunz 	NULL,
80eba5cacbSjkunz 	timrot_activate,
81eba5cacbSjkunz 	NULL,
82eba5cacbSjkunz 	NULL,
83eba5cacbSjkunz 	0);
84eba5cacbSjkunz 
85eba5cacbSjkunz #define MAX_TIMERS	4
86eba5cacbSjkunz #define SYS_TIMER	0
87eba5cacbSjkunz #define STAT_TIMER	1
88eba5cacbSjkunz #define SCHED_TIMER	2
89eba5cacbSjkunz 
90eba5cacbSjkunz struct timrot_softc *timer_sc[MAX_TIMERS];
91eba5cacbSjkunz 
92eba5cacbSjkunz static void	timer_init(struct timrot_softc *);
93eba5cacbSjkunz 
94eba5cacbSjkunz #define TIMROT_SOFT_RST_LOOP 455 /* At least 1 us ... */
95eba5cacbSjkunz #define TIMROT_READ(reg)						\
96eba5cacbSjkunz 	bus_space_read_4(timrot_iot, timrot_hdl, (reg))
97eba5cacbSjkunz #define TIMROT_WRITE(reg, val)						\
98eba5cacbSjkunz 	bus_space_write_4(timrot_iot, timrot_hdl, (reg), (val))
99eba5cacbSjkunz 
100eba5cacbSjkunz #define TIMER_REGS_SIZE 0x20
101eba5cacbSjkunz 
102eba5cacbSjkunz #define TIMER_CTRL	0x00
103eba5cacbSjkunz #define TIMER_CTRL_SET	0x04
104eba5cacbSjkunz #define TIMER_CTRL_CLR	0x08
105eba5cacbSjkunz #define TIMER_CTRL_TOG	0x0C
106eba5cacbSjkunz #define TIMER_COUNT	0x10
107eba5cacbSjkunz 
108eba5cacbSjkunz #define TIMER_READ(sc, reg)						\
109eba5cacbSjkunz 	bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
110eba5cacbSjkunz #define TIMER_WRITE(sc, reg, val)					\
111eba5cacbSjkunz 	bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
112eba5cacbSjkunz #define TIMER_WRITE_2(sc, reg, val)					\
113eba5cacbSjkunz 	bus_space_write_2(sc->sc_iot, sc->sc_hdl, (reg), (val))
114eba5cacbSjkunz 
115eba5cacbSjkunz #define SELECT_32KHZ	0x8	/* Use 32kHz clock source. */
116eba5cacbSjkunz #define SOURCE_32KHZ_HZ	32000	/* Above source in Hz. */
117eba5cacbSjkunz 
118eba5cacbSjkunz #define IRQ HW_TIMROT_TIMCTRL0_IRQ
119eba5cacbSjkunz #define IRQ_EN HW_TIMROT_TIMCTRL0_IRQ_EN
120eba5cacbSjkunz #define UPDATE HW_TIMROT_TIMCTRL0_UPDATE
121eba5cacbSjkunz #define RELOAD HW_TIMROT_TIMCTRL0_RELOAD
122eba5cacbSjkunz 
123eba5cacbSjkunz static int
timrot_match(device_t parent,cfdata_t match,void * aux)124eba5cacbSjkunz timrot_match(device_t parent, cfdata_t match, void *aux)
125eba5cacbSjkunz {
126eba5cacbSjkunz 	struct apb_attach_args *aa = aux;
127eba5cacbSjkunz 
128eba5cacbSjkunz 	if ((aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL0
129eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE))
130eba5cacbSjkunz 		return 1;
131eba5cacbSjkunz 
132eba5cacbSjkunz 	if ((aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL1
133eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE))
134eba5cacbSjkunz 		return 1;
135eba5cacbSjkunz 
136eba5cacbSjkunz #if 0
137eba5cacbSjkunz 	if ((aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL2
138eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE))
139eba5cacbSjkunz 		return 1;
140eba5cacbSjkunz 
141eba5cacbSjkunz 	if ((aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL3
142eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE))
143eba5cacbSjkunz 		return 1;
144eba5cacbSjkunz #endif
145eba5cacbSjkunz 	return 0;
146eba5cacbSjkunz }
147eba5cacbSjkunz 
148eba5cacbSjkunz static void
timrot_attach(device_t parent,device_t self,void * aux)149eba5cacbSjkunz timrot_attach(device_t parent, device_t self, void *aux)
150eba5cacbSjkunz {
151eba5cacbSjkunz 	struct apb_attach_args *aa = aux;
152eba5cacbSjkunz 	struct timrot_softc *sc = device_private(self);
153eba5cacbSjkunz 	static int timrot_attached = 0;
154eba5cacbSjkunz 
155eba5cacbSjkunz 	if (!timrot_attached) {
156eba5cacbSjkunz 		timrot_iot = aa->aa_iot;
157eba5cacbSjkunz 		if (bus_space_map(timrot_iot, HW_TIMROT_BASE, HW_TIMROT_SIZE,
158eba5cacbSjkunz 		    0, &timrot_hdl)) {
159eba5cacbSjkunz 			aprint_error_dev(sc->sc_dev,
160eba5cacbSjkunz 			    "unable to map bus space\n");
161eba5cacbSjkunz 			return;
162eba5cacbSjkunz 		}
163eba5cacbSjkunz 		timrot_reset();
164eba5cacbSjkunz 		timrot_attached = 1;
165eba5cacbSjkunz 	}
166eba5cacbSjkunz 
167eba5cacbSjkunz 	if (aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL0
168eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE
169eba5cacbSjkunz 	    && timer_sc[SYS_TIMER] == NULL) {
170eba5cacbSjkunz 		if (bus_space_subregion(timrot_iot, timrot_hdl,
171eba5cacbSjkunz 		    HW_TIMROT_TIMCTRL0, TIMER_REGS_SIZE,
172eba5cacbSjkunz 		    &sc->sc_hdl)) {
173eba5cacbSjkunz 			aprint_error_dev(sc->sc_dev,
174eba5cacbSjkunz 			    "unable to map subregion\n");
175eba5cacbSjkunz 			return;
176eba5cacbSjkunz 		}
177eba5cacbSjkunz 
178eba5cacbSjkunz 		sc->sc_iot = aa->aa_iot;
179eba5cacbSjkunz 		sc->sc_irq = aa->aa_irq;
180eba5cacbSjkunz 		sc->irq_handler = &systimer_irq;
181eba5cacbSjkunz 		sc->freq = hz;
182eba5cacbSjkunz 
183eba5cacbSjkunz 		timer_sc[SYS_TIMER] = sc;
184eba5cacbSjkunz 
185eba5cacbSjkunz 		aprint_normal("\n");
186eba5cacbSjkunz 
187eba5cacbSjkunz 	} else if (aa->aa_addr == HW_TIMROT_BASE + HW_TIMROT_TIMCTRL1
188eba5cacbSjkunz 	    && aa->aa_size == TIMER_REGS_SIZE
189eba5cacbSjkunz 	    && timer_sc[STAT_TIMER] == NULL) {
190eba5cacbSjkunz 		if (bus_space_subregion(timrot_iot, timrot_hdl,
191eba5cacbSjkunz 		    HW_TIMROT_TIMCTRL1, TIMER_REGS_SIZE, &sc->sc_hdl)) {
192eba5cacbSjkunz 			aprint_error_dev(sc->sc_dev,
193eba5cacbSjkunz 			    "unable to map subregion\n");
194eba5cacbSjkunz 			return;
195eba5cacbSjkunz 		}
196eba5cacbSjkunz 
197eba5cacbSjkunz 		sc->sc_iot = aa->aa_iot;
198eba5cacbSjkunz 		sc->sc_irq = aa->aa_irq;
199eba5cacbSjkunz 		sc->irq_handler = &stattimer_irq;
200eba5cacbSjkunz 		stathz = (hz>>1);
201eba5cacbSjkunz 		sc->freq = stathz;
202eba5cacbSjkunz 
203eba5cacbSjkunz 		timer_sc[STAT_TIMER] = sc;
204eba5cacbSjkunz 
205eba5cacbSjkunz 		aprint_normal("\n");
206eba5cacbSjkunz 	}
207eba5cacbSjkunz 
208eba5cacbSjkunz 	return;
209eba5cacbSjkunz }
210eba5cacbSjkunz 
211eba5cacbSjkunz static int
timrot_activate(device_t self,enum devact act)212eba5cacbSjkunz timrot_activate(device_t self, enum devact act)
213eba5cacbSjkunz {
214eba5cacbSjkunz 	return EOPNOTSUPP;
215eba5cacbSjkunz }
216eba5cacbSjkunz 
217eba5cacbSjkunz /*
218eba5cacbSjkunz  * cpu_initclock is called once at the boot time.
219eba5cacbSjkunz  */
220eba5cacbSjkunz void
cpu_initclocks(void)221eba5cacbSjkunz cpu_initclocks(void)
222eba5cacbSjkunz {
223eba5cacbSjkunz 	if (timer_sc[SYS_TIMER] != NULL)
224eba5cacbSjkunz 		timer_init(timer_sc[SYS_TIMER]);
225eba5cacbSjkunz 
226eba5cacbSjkunz 	if (timer_sc[STAT_TIMER] != NULL)
227eba5cacbSjkunz 		timer_init(timer_sc[STAT_TIMER]);
228eba5cacbSjkunz 
229eba5cacbSjkunz 	return;
230eba5cacbSjkunz }
231eba5cacbSjkunz 
232eba5cacbSjkunz /*
233eba5cacbSjkunz  * Change statclock rate when profiling takes place.
234eba5cacbSjkunz  */
235eba5cacbSjkunz void
setstatclockrate(int newhz)236eba5cacbSjkunz setstatclockrate(int newhz)
237eba5cacbSjkunz {
238eba5cacbSjkunz 	struct timrot_softc *sc = timer_sc[STAT_TIMER];
239eba5cacbSjkunz 	sc->freq = newhz;
240eba5cacbSjkunz 
241eba5cacbSjkunz 	TIMER_WRITE_2(sc, TIMER_COUNT,
242*94de0730Smatt 	    __SHIFTIN(SOURCE_32KHZ_HZ / sc->freq - 1,
243eba5cacbSjkunz 	    HW_TIMROT_TIMCOUNT0_FIXED_COUNT));
244eba5cacbSjkunz 
245eba5cacbSjkunz 	return;
246eba5cacbSjkunz }
247eba5cacbSjkunz 
248eba5cacbSjkunz /*
249eba5cacbSjkunz  * Generic timer initialization function.
250eba5cacbSjkunz  */
251eba5cacbSjkunz static void
timer_init(struct timrot_softc * sc)252eba5cacbSjkunz timer_init(struct timrot_softc *sc)
253eba5cacbSjkunz {
254eba5cacbSjkunz 	uint32_t ctrl;
255eba5cacbSjkunz 
256eba5cacbSjkunz 	TIMER_WRITE_2(sc, TIMER_COUNT,
257*94de0730Smatt 	    __SHIFTIN(SOURCE_32KHZ_HZ / sc->freq - 1,
258eba5cacbSjkunz 	    HW_TIMROT_TIMCOUNT0_FIXED_COUNT));
259eba5cacbSjkunz 	ctrl = IRQ_EN | UPDATE | RELOAD | SELECT_32KHZ;
260eba5cacbSjkunz 	TIMER_WRITE(sc, TIMER_CTRL, ctrl);
261eba5cacbSjkunz 
262eba5cacbSjkunz 	intr_establish(sc->sc_irq, IPL_SCHED, IST_LEVEL, sc->irq_handler, NULL);
263eba5cacbSjkunz 
264eba5cacbSjkunz 	return;
265eba5cacbSjkunz }
266eba5cacbSjkunz 
267eba5cacbSjkunz /*
268eba5cacbSjkunz  * Timer IRQ handlers.
269eba5cacbSjkunz  */
270eba5cacbSjkunz static int
systimer_irq(void * frame)271eba5cacbSjkunz systimer_irq(void *frame)
272eba5cacbSjkunz {
273eba5cacbSjkunz 	hardclock(frame);
274eba5cacbSjkunz 
275eba5cacbSjkunz 	TIMER_WRITE(timer_sc[SYS_TIMER], TIMER_CTRL_CLR, IRQ);
276eba5cacbSjkunz 
277eba5cacbSjkunz 	return 1;
278eba5cacbSjkunz }
279eba5cacbSjkunz 
280eba5cacbSjkunz static int
stattimer_irq(void * frame)281eba5cacbSjkunz stattimer_irq(void *frame)
282eba5cacbSjkunz {
283eba5cacbSjkunz 	statclock(frame);
284eba5cacbSjkunz 
285eba5cacbSjkunz 	TIMER_WRITE(timer_sc[STAT_TIMER], TIMER_CTRL_CLR, IRQ);
286eba5cacbSjkunz 
287eba5cacbSjkunz 	return 1;
288eba5cacbSjkunz }
289eba5cacbSjkunz 
290eba5cacbSjkunz /*
291eba5cacbSjkunz  * Reset the TIMROT block.
292eba5cacbSjkunz  *
2938a7d722dSjkunz  * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
294eba5cacbSjkunz  */
295eba5cacbSjkunz static void
timrot_reset(void)296eba5cacbSjkunz timrot_reset(void)
297eba5cacbSjkunz {
298eba5cacbSjkunz 	unsigned int loop;
299eba5cacbSjkunz 
300eba5cacbSjkunz 	/* Prepare for soft-reset by making sure that SFTRST is not currently
301eba5cacbSjkunz 	* asserted. Also clear CLKGATE so we can wait for its assertion below.
302eba5cacbSjkunz 	*/
303eba5cacbSjkunz 	TIMROT_WRITE(HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_SFTRST);
304eba5cacbSjkunz 
305eba5cacbSjkunz 	/* Wait at least a microsecond for SFTRST to deassert. */
306eba5cacbSjkunz 	loop = 0;
307eba5cacbSjkunz 	while ((TIMROT_READ(HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_SFTRST) ||
308eba5cacbSjkunz 	    (loop < TIMROT_SOFT_RST_LOOP))
309eba5cacbSjkunz 		loop++;
310eba5cacbSjkunz 
311eba5cacbSjkunz 	/* Clear CLKGATE so we can wait for its assertion below. */
312eba5cacbSjkunz 	TIMROT_WRITE(HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_CLKGATE);
313eba5cacbSjkunz 
314eba5cacbSjkunz 	/* Soft-reset the block. */
315eba5cacbSjkunz 	TIMROT_WRITE(HW_TIMROT_ROTCTRL_SET, HW_TIMROT_ROTCTRL_SFTRST);
316eba5cacbSjkunz 
317eba5cacbSjkunz 	/* Wait until clock is in the gated state. */
318eba5cacbSjkunz 	while (!(TIMROT_READ(HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_CLKGATE));
319eba5cacbSjkunz 
320eba5cacbSjkunz 	/* Bring block out of reset. */
321eba5cacbSjkunz 	TIMROT_WRITE(HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_SFTRST);
322eba5cacbSjkunz 
323eba5cacbSjkunz 	loop = 0;
324eba5cacbSjkunz 	while ((TIMROT_READ(HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_SFTRST) ||
325eba5cacbSjkunz 	    (loop < TIMROT_SOFT_RST_LOOP))
326eba5cacbSjkunz 		loop++;
327eba5cacbSjkunz 
328eba5cacbSjkunz 	TIMROT_WRITE(HW_TIMROT_ROTCTRL_CLR, HW_TIMROT_ROTCTRL_CLKGATE);
329eba5cacbSjkunz 	/* Wait until clock is in the NON-gated state. */
330eba5cacbSjkunz 	while (TIMROT_READ(HW_TIMROT_ROTCTRL) & HW_TIMROT_ROTCTRL_CLKGATE);
331eba5cacbSjkunz 
332eba5cacbSjkunz 	return;
333eba5cacbSjkunz }
334