1/* $NetBSD: sunxi_timer.c,v 1.9 2021/01/27 03:10:20 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: sunxi_timer.c,v 1.9 2021/01/27 03:10:20 thorpej Exp $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/bus.h>
35#include <sys/device.h>
36#include <sys/intr.h>
37#include <sys/systm.h>
38#include <sys/timetc.h>
39
40#include <arm/locore.h>
41
42#include <dev/fdt/fdtvar.h>
43
44#include <arm/fdt/arm_fdtvar.h>
45
46/* Timer 0 registers */
47#define	TMR_IRQ_EN_REG		0x00
48#define	 TMR_IRQ_EN(n)		__BIT(n)
49#define	TMR_IRQ_STAS_REG	0x04
50#define	 TMR_IRQ_STAS_PEND(n)	__BIT(n)
51#define	TMR0_CTRL_REG		0x10
52#define	 TMR0_CTRL_MODE		__BIT(7)
53#define	 TMR0_CTRL_CLK_PRESCALE	__BITS(6,4)
54#define	 TMR0_CTRL_CLK_SRC	__BITS(3,2)
55#define	  TMR0_CTRL_CLK_SRC_OSC24M	1
56#define	  TMR0_CTRL_CLK_SRC_PLL6_6	2
57#define	 TMR0_CTRL_RELOAD	__BIT(1)
58#define	 TMR0_CTRL_EN		__BIT(0)
59#define	TMR0_INTV_VALUE_REG	0x14
60#define	TMR0_CURNT_VALUE_REG	0x18
61
62/* Timer 1 is used for delay() */
63
64/* Timer 2 registers */
65#define	TMR2_CTRL_REG		0x30
66#define	 TMR2_CTRL_MODE		__BIT(7)
67#define	 TMR2_CTRL_CLK_SRC	__BITS(3,2)
68#define	  TMR2_CTRL_CLK_SRC_OSC24M	1
69#define	 TMR2_CTRL_RELOAD	__BIT(1)
70#define	 TMR2_CTRL_EN		__BIT(0)
71#define	TMR2_INTV_VALUE_REG	0x34
72#define	TMR2_CURNT_VALUE_REG	0x38
73
74/* Timer 4 registers */
75#define	TMR4_CTRL_REG		0x50
76#define	 TMR4_CTRL_RELOAD	__BIT(1)
77#define	 TMR4_CTRL_EN		__BIT(0)
78#define	TMR4_INTV_VALUE_REG	0x54
79#define	TMR4_CURNT_VALUE_REG	0x58
80
81/* Control registers */
82#define	AVS_CNT_CTL_REG		0x80
83#define	AVS_CNT0_REG		0x84
84#define	AVS_CNT1_REG		0x88
85#define	AVS_CNT_DIV_REG		0x8c
86#define	WDOG_CTRL_REG		0x90
87#define	WDOG_MODE_REG		0x94
88#define	LOSC_CTRL_REG		0x100
89#define	 LOSC_CTRL_KEY_FIELD	__BITS(31,16)
90#define	 LOSC_CTRL_KEY_FIELD_V	0x16aa
91#define  LOSC_CTRL_OSC32K_AUTO_SWT_EN	__BIT(14)
92#define	 LOSC_CTRL_OSC32K_SEL	__BIT(0)
93
94static const struct device_compatible_entry compat_data[] = {
95	{ .compat = "allwinner,sun4i-a10-timer" },
96	DEVICE_COMPAT_EOL
97};
98
99struct sunxi_timer_softc {
100	device_t sc_dev;
101	bus_space_tag_t sc_bst;
102	bus_space_handle_t sc_bsh;
103	int sc_phandle;
104	struct clk *sc_clk;
105
106	struct timecounter sc_tc;
107	struct timecounter sc_tc_losc;
108};
109
110#define TIMER_READ(sc, reg) \
111    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
112#define TIMER_WRITE(sc, reg, val) \
113    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
114
115static struct sunxi_timer_softc *timer_softc;
116
117static int
118sunxi_timer_intr(void *arg)
119{
120	struct sunxi_timer_softc * const sc = timer_softc;
121	struct clockframe *frame = arg;
122	uint32_t stas;
123
124	stas = TIMER_READ(sc, TMR_IRQ_STAS_REG);
125	if (stas == 0)
126		return 0;
127	TIMER_WRITE(sc, TMR_IRQ_STAS_REG, stas);
128
129	if ((stas & TMR_IRQ_STAS_PEND(0)) != 0)
130		hardclock(frame);
131
132	return 1;
133}
134
135static void
136sunxi_timer_cpu_initclocks(void)
137{
138	struct sunxi_timer_softc * const sc = timer_softc;
139	char intrstr[128];
140	void *ih;
141
142	KASSERT(sc != NULL);
143
144	if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr)))
145		panic("%s: failed to decode interrupt", __func__);
146
147	ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK,
148	    FDT_INTR_MPSAFE, sunxi_timer_intr, NULL, device_xname(sc->sc_dev));
149	if (ih == NULL)
150		panic("%s: failed to establish timer interrupt", __func__);
151
152	aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
153
154	/* Enable Timer 0 IRQ */
155	const uint32_t irq_en = TIMER_READ(sc, TMR_IRQ_EN_REG);
156	TIMER_WRITE(sc, TMR_IRQ_EN_REG, irq_en | TMR_IRQ_EN(0));
157}
158
159static u_int
160sunxi_timer_get_timecount(struct timecounter *tc)
161{
162	struct sunxi_timer_softc * const sc = tc->tc_priv;
163
164	/* Timer current value is a 32-bit down counter. */
165	return ~TIMER_READ(sc, TMR2_CURNT_VALUE_REG);
166}
167
168static u_int
169sunxi_timer_get_timecount_losc(struct timecounter *tc)
170{
171	struct sunxi_timer_softc * const sc = tc->tc_priv;
172
173	return ~TIMER_READ(sc, TMR4_CURNT_VALUE_REG);
174}
175
176static int
177sunxi_timer_match(device_t parent, cfdata_t cf, void *aux)
178{
179	struct fdt_attach_args * const faa = aux;
180
181	return of_compatible_match(faa->faa_phandle, compat_data);
182}
183
184static void
185sunxi_timer_attach(device_t parent, device_t self, void *aux)
186{
187	struct sunxi_timer_softc * const sc = device_private(self);
188	struct fdt_attach_args * const faa = aux;
189	struct timecounter *tc = &sc->sc_tc;
190	struct timecounter *tc_losc = &sc->sc_tc_losc;
191	const int phandle = faa->faa_phandle;
192	bus_addr_t addr;
193	bus_size_t size;
194	u_int ticks;
195	u_int reg;
196
197	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
198		aprint_error(": couldn't get registers\n");
199		return;
200	}
201
202	if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) {
203		aprint_error(": couldn't get clock\n");
204		return;
205	}
206
207	sc->sc_dev = self;
208	sc->sc_phandle = phandle;
209	sc->sc_bst = faa->faa_bst;
210	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
211		aprint_error(": couldn't map registers\n");
212		return;
213	}
214
215	aprint_naive("\n");
216	aprint_normal(": Timer\n");
217
218	const u_int rate = clk_get_rate(sc->sc_clk);
219
220	/* Disable IRQs and all timers */
221	TIMER_WRITE(sc, TMR_IRQ_EN_REG, 0);
222	TIMER_WRITE(sc, TMR_IRQ_STAS_REG, TIMER_READ(sc, TMR_IRQ_STAS_REG));
223	/* Enable Timer 0 (hardclock) */
224	TIMER_WRITE(sc, TMR0_INTV_VALUE_REG, rate / hz);
225	TIMER_WRITE(sc, TMR0_CTRL_REG,
226	    __SHIFTIN(TMR0_CTRL_CLK_SRC_OSC24M, TMR0_CTRL_CLK_SRC) |
227	    TMR0_CTRL_RELOAD | TMR0_CTRL_EN);
228	/* Enable Timer 2 (timecounter) */
229	TIMER_WRITE(sc, TMR2_INTV_VALUE_REG, ~0u);
230	TIMER_WRITE(sc, TMR2_CTRL_REG,
231	    __SHIFTIN(TMR2_CTRL_CLK_SRC_OSC24M, TMR2_CTRL_CLK_SRC) |
232	    TMR2_CTRL_RELOAD | TMR2_CTRL_EN);
233	/* Enable Timer 4 (timecounter for LOSC) */
234	TIMER_WRITE(sc, TMR4_INTV_VALUE_REG, ~0u);
235	TIMER_WRITE(sc, TMR4_CTRL_REG, TMR4_CTRL_RELOAD | TMR4_CTRL_EN);
236
237	/* Timecounter setup */
238	tc->tc_get_timecount = sunxi_timer_get_timecount;
239	tc->tc_counter_mask = ~0u;
240	tc->tc_frequency = rate;
241	tc->tc_name = "Timer 2";
242	tc->tc_quality = 200;
243	tc->tc_priv = sc;
244	tc_init(tc);
245	tc_losc->tc_get_timecount = sunxi_timer_get_timecount_losc;
246	tc_losc->tc_counter_mask = ~0u;
247	tc_losc->tc_frequency = 32768;
248	tc_losc->tc_name = "LOSC";
249	tc_losc->tc_quality = 150;
250	tc_losc->tc_priv = sc;
251	/*
252	 * LOSC is optional to implement in hardware.
253	 * Make sure it ticks before registering it.
254	 */
255	reg = __SHIFTIN(LOSC_CTRL_KEY_FIELD_V, LOSC_CTRL_KEY_FIELD) |
256	    LOSC_CTRL_OSC32K_AUTO_SWT_EN |
257	    LOSC_CTRL_OSC32K_SEL;
258	TIMER_WRITE(sc, LOSC_CTRL_REG, reg);
259	ticks = sunxi_timer_get_timecount_losc(tc_losc);
260	delay(100);
261	if (ticks != sunxi_timer_get_timecount_losc(tc_losc))
262		tc_init(tc_losc);
263	else
264		TIMER_WRITE(sc, LOSC_CTRL_REG, reg & ~LOSC_CTRL_OSC32K_SEL);
265
266	/* Use this as the OS timer in UP configurations */
267	if (!arm_has_mpext_p) {
268		timer_softc = sc;
269		arm_fdt_timer_register(sunxi_timer_cpu_initclocks);
270	}
271}
272
273CFATTACH_DECL_NEW(sunxi_timer, sizeof(struct sunxi_timer_softc),
274	sunxi_timer_match, sunxi_timer_attach, NULL, NULL);
275