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