xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_timer.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: sunxi_timer.c,v 1.9 2021/01/27 03:10:20 thorpej Exp $ */
269b44ac7Sjmcneill 
369b44ac7Sjmcneill /*-
469b44ac7Sjmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
569b44ac7Sjmcneill  * All rights reserved.
669b44ac7Sjmcneill  *
769b44ac7Sjmcneill  * Redistribution and use in source and binary forms, with or without
869b44ac7Sjmcneill  * modification, are permitted provided that the following conditions
969b44ac7Sjmcneill  * are met:
1069b44ac7Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1169b44ac7Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1269b44ac7Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1369b44ac7Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1469b44ac7Sjmcneill  *    documentation and/or other materials provided with the distribution.
1569b44ac7Sjmcneill  *
1669b44ac7Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1769b44ac7Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1869b44ac7Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1969b44ac7Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2069b44ac7Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2169b44ac7Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2269b44ac7Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2369b44ac7Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2469b44ac7Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2569b44ac7Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2669b44ac7Sjmcneill  * SUCH DAMAGE.
2769b44ac7Sjmcneill  */
2869b44ac7Sjmcneill 
2969b44ac7Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: sunxi_timer.c,v 1.9 2021/01/27 03:10:20 thorpej Exp $");
3169b44ac7Sjmcneill 
3269b44ac7Sjmcneill #include <sys/param.h>
3369b44ac7Sjmcneill #include <sys/kernel.h>
3469b44ac7Sjmcneill #include <sys/bus.h>
3569b44ac7Sjmcneill #include <sys/device.h>
3669b44ac7Sjmcneill #include <sys/intr.h>
3769b44ac7Sjmcneill #include <sys/systm.h>
3869b44ac7Sjmcneill #include <sys/timetc.h>
3969b44ac7Sjmcneill 
4069b44ac7Sjmcneill #include <arm/locore.h>
4169b44ac7Sjmcneill 
4269b44ac7Sjmcneill #include <dev/fdt/fdtvar.h>
4369b44ac7Sjmcneill 
4469b44ac7Sjmcneill #include <arm/fdt/arm_fdtvar.h>
4569b44ac7Sjmcneill 
4691f7c998Sjmcneill /* Timer 0 registers */
4769b44ac7Sjmcneill #define	TMR_IRQ_EN_REG		0x00
4869b44ac7Sjmcneill #define	 TMR_IRQ_EN(n)		__BIT(n)
4969b44ac7Sjmcneill #define	TMR_IRQ_STAS_REG	0x04
5069b44ac7Sjmcneill #define	 TMR_IRQ_STAS_PEND(n)	__BIT(n)
5169b44ac7Sjmcneill #define	TMR0_CTRL_REG		0x10
5269b44ac7Sjmcneill #define	 TMR0_CTRL_MODE		__BIT(7)
5369b44ac7Sjmcneill #define	 TMR0_CTRL_CLK_PRESCALE	__BITS(6,4)
5469b44ac7Sjmcneill #define	 TMR0_CTRL_CLK_SRC	__BITS(3,2)
5569b44ac7Sjmcneill #define	  TMR0_CTRL_CLK_SRC_OSC24M	1
5669b44ac7Sjmcneill #define	  TMR0_CTRL_CLK_SRC_PLL6_6	2
5769b44ac7Sjmcneill #define	 TMR0_CTRL_RELOAD	__BIT(1)
5869b44ac7Sjmcneill #define	 TMR0_CTRL_EN		__BIT(0)
5969b44ac7Sjmcneill #define	TMR0_INTV_VALUE_REG	0x14
6069b44ac7Sjmcneill #define	TMR0_CURNT_VALUE_REG	0x18
6191f7c998Sjmcneill 
6291f7c998Sjmcneill /* Timer 1 is used for delay() */
6391f7c998Sjmcneill 
6491f7c998Sjmcneill /* Timer 2 registers */
6591f7c998Sjmcneill #define	TMR2_CTRL_REG		0x30
6691f7c998Sjmcneill #define	 TMR2_CTRL_MODE		__BIT(7)
6791f7c998Sjmcneill #define	 TMR2_CTRL_CLK_SRC	__BITS(3,2)
6891f7c998Sjmcneill #define	  TMR2_CTRL_CLK_SRC_OSC24M	1
6991f7c998Sjmcneill #define	 TMR2_CTRL_RELOAD	__BIT(1)
7091f7c998Sjmcneill #define	 TMR2_CTRL_EN		__BIT(0)
7191f7c998Sjmcneill #define	TMR2_INTV_VALUE_REG	0x34
7291f7c998Sjmcneill #define	TMR2_CURNT_VALUE_REG	0x38
7369b44ac7Sjmcneill 
74da24f7deStnn /* Timer 4 registers */
75da24f7deStnn #define	TMR4_CTRL_REG		0x50
76da24f7deStnn #define	 TMR4_CTRL_RELOAD	__BIT(1)
77da24f7deStnn #define	 TMR4_CTRL_EN		__BIT(0)
78da24f7deStnn #define	TMR4_INTV_VALUE_REG	0x54
79da24f7deStnn #define	TMR4_CURNT_VALUE_REG	0x58
80da24f7deStnn 
812a873fc7Stnn /* Control registers */
822a873fc7Stnn #define	AVS_CNT_CTL_REG		0x80
832a873fc7Stnn #define	AVS_CNT0_REG		0x84
842a873fc7Stnn #define	AVS_CNT1_REG		0x88
852a873fc7Stnn #define	AVS_CNT_DIV_REG		0x8c
862a873fc7Stnn #define	WDOG_CTRL_REG		0x90
872a873fc7Stnn #define	WDOG_MODE_REG		0x94
882a873fc7Stnn #define	LOSC_CTRL_REG		0x100
892a873fc7Stnn #define	 LOSC_CTRL_KEY_FIELD	__BITS(31,16)
902a873fc7Stnn #define	 LOSC_CTRL_KEY_FIELD_V	0x16aa
912a873fc7Stnn #define  LOSC_CTRL_OSC32K_AUTO_SWT_EN	__BIT(14)
922a873fc7Stnn #define	 LOSC_CTRL_OSC32K_SEL	__BIT(0)
932a873fc7Stnn 
94*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
95*6e54367aSthorpej 	{ .compat = "allwinner,sun4i-a10-timer" },
96*6e54367aSthorpej 	DEVICE_COMPAT_EOL
9769b44ac7Sjmcneill };
9869b44ac7Sjmcneill 
9969b44ac7Sjmcneill struct sunxi_timer_softc {
10069b44ac7Sjmcneill 	device_t sc_dev;
10169b44ac7Sjmcneill 	bus_space_tag_t sc_bst;
10269b44ac7Sjmcneill 	bus_space_handle_t sc_bsh;
10369b44ac7Sjmcneill 	int sc_phandle;
10469b44ac7Sjmcneill 	struct clk *sc_clk;
10569b44ac7Sjmcneill 
10669b44ac7Sjmcneill 	struct timecounter sc_tc;
107da24f7deStnn 	struct timecounter sc_tc_losc;
10869b44ac7Sjmcneill };
10969b44ac7Sjmcneill 
11069b44ac7Sjmcneill #define TIMER_READ(sc, reg) \
11169b44ac7Sjmcneill     bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
11269b44ac7Sjmcneill #define TIMER_WRITE(sc, reg, val) \
11369b44ac7Sjmcneill     bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
11469b44ac7Sjmcneill 
11569b44ac7Sjmcneill static struct sunxi_timer_softc *timer_softc;
11669b44ac7Sjmcneill 
11769b44ac7Sjmcneill static int
sunxi_timer_intr(void * arg)11869b44ac7Sjmcneill sunxi_timer_intr(void *arg)
11969b44ac7Sjmcneill {
12069b44ac7Sjmcneill 	struct sunxi_timer_softc * const sc = timer_softc;
12169b44ac7Sjmcneill 	struct clockframe *frame = arg;
12269b44ac7Sjmcneill 	uint32_t stas;
12369b44ac7Sjmcneill 
12469b44ac7Sjmcneill 	stas = TIMER_READ(sc, TMR_IRQ_STAS_REG);
12569b44ac7Sjmcneill 	if (stas == 0)
12669b44ac7Sjmcneill 		return 0;
12769b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR_IRQ_STAS_REG, stas);
12869b44ac7Sjmcneill 
12969b44ac7Sjmcneill 	if ((stas & TMR_IRQ_STAS_PEND(0)) != 0)
13069b44ac7Sjmcneill 		hardclock(frame);
13169b44ac7Sjmcneill 
13269b44ac7Sjmcneill 	return 1;
13369b44ac7Sjmcneill }
13469b44ac7Sjmcneill 
13569b44ac7Sjmcneill static void
sunxi_timer_cpu_initclocks(void)13669b44ac7Sjmcneill sunxi_timer_cpu_initclocks(void)
13769b44ac7Sjmcneill {
13869b44ac7Sjmcneill 	struct sunxi_timer_softc * const sc = timer_softc;
13969b44ac7Sjmcneill 	char intrstr[128];
14069b44ac7Sjmcneill 	void *ih;
14169b44ac7Sjmcneill 
14269b44ac7Sjmcneill 	KASSERT(sc != NULL);
14369b44ac7Sjmcneill 
14469b44ac7Sjmcneill 	if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr)))
14569b44ac7Sjmcneill 		panic("%s: failed to decode interrupt", __func__);
14669b44ac7Sjmcneill 
147076a1169Sjmcneill 	ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK,
148076a1169Sjmcneill 	    FDT_INTR_MPSAFE, sunxi_timer_intr, NULL, device_xname(sc->sc_dev));
14969b44ac7Sjmcneill 	if (ih == NULL)
15069b44ac7Sjmcneill 		panic("%s: failed to establish timer interrupt", __func__);
15169b44ac7Sjmcneill 
15269b44ac7Sjmcneill 	aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
15369b44ac7Sjmcneill 
15469b44ac7Sjmcneill 	/* Enable Timer 0 IRQ */
15569b44ac7Sjmcneill 	const uint32_t irq_en = TIMER_READ(sc, TMR_IRQ_EN_REG);
15669b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR_IRQ_EN_REG, irq_en | TMR_IRQ_EN(0));
15769b44ac7Sjmcneill }
15869b44ac7Sjmcneill 
15969b44ac7Sjmcneill static u_int
sunxi_timer_get_timecount(struct timecounter * tc)16069b44ac7Sjmcneill sunxi_timer_get_timecount(struct timecounter *tc)
16169b44ac7Sjmcneill {
16269b44ac7Sjmcneill 	struct sunxi_timer_softc * const sc = tc->tc_priv;
16369b44ac7Sjmcneill 
16491f7c998Sjmcneill 	/* Timer current value is a 32-bit down counter. */
16591f7c998Sjmcneill 	return ~TIMER_READ(sc, TMR2_CURNT_VALUE_REG);
16669b44ac7Sjmcneill }
16769b44ac7Sjmcneill 
168da24f7deStnn static u_int
sunxi_timer_get_timecount_losc(struct timecounter * tc)169da24f7deStnn sunxi_timer_get_timecount_losc(struct timecounter *tc)
170da24f7deStnn {
171da24f7deStnn 	struct sunxi_timer_softc * const sc = tc->tc_priv;
172da24f7deStnn 
173da24f7deStnn 	return ~TIMER_READ(sc, TMR4_CURNT_VALUE_REG);
174da24f7deStnn }
175da24f7deStnn 
17669b44ac7Sjmcneill static int
sunxi_timer_match(device_t parent,cfdata_t cf,void * aux)17769b44ac7Sjmcneill sunxi_timer_match(device_t parent, cfdata_t cf, void *aux)
17869b44ac7Sjmcneill {
17969b44ac7Sjmcneill 	struct fdt_attach_args * const faa = aux;
18069b44ac7Sjmcneill 
181*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
18269b44ac7Sjmcneill }
18369b44ac7Sjmcneill 
18469b44ac7Sjmcneill static void
sunxi_timer_attach(device_t parent,device_t self,void * aux)18569b44ac7Sjmcneill sunxi_timer_attach(device_t parent, device_t self, void *aux)
18669b44ac7Sjmcneill {
18769b44ac7Sjmcneill 	struct sunxi_timer_softc * const sc = device_private(self);
18869b44ac7Sjmcneill 	struct fdt_attach_args * const faa = aux;
18969b44ac7Sjmcneill 	struct timecounter *tc = &sc->sc_tc;
190da24f7deStnn 	struct timecounter *tc_losc = &sc->sc_tc_losc;
19169b44ac7Sjmcneill 	const int phandle = faa->faa_phandle;
19269b44ac7Sjmcneill 	bus_addr_t addr;
19369b44ac7Sjmcneill 	bus_size_t size;
194da24f7deStnn 	u_int ticks;
1952a873fc7Stnn 	u_int reg;
19669b44ac7Sjmcneill 
19769b44ac7Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
19869b44ac7Sjmcneill 		aprint_error(": couldn't get registers\n");
19969b44ac7Sjmcneill 		return;
20069b44ac7Sjmcneill 	}
20169b44ac7Sjmcneill 
20269b44ac7Sjmcneill 	if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) {
20369b44ac7Sjmcneill 		aprint_error(": couldn't get clock\n");
20469b44ac7Sjmcneill 		return;
20569b44ac7Sjmcneill 	}
20669b44ac7Sjmcneill 
20769b44ac7Sjmcneill 	sc->sc_dev = self;
20869b44ac7Sjmcneill 	sc->sc_phandle = phandle;
20969b44ac7Sjmcneill 	sc->sc_bst = faa->faa_bst;
21069b44ac7Sjmcneill 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
21169b44ac7Sjmcneill 		aprint_error(": couldn't map registers\n");
21269b44ac7Sjmcneill 		return;
21369b44ac7Sjmcneill 	}
21469b44ac7Sjmcneill 
21569b44ac7Sjmcneill 	aprint_naive("\n");
21669b44ac7Sjmcneill 	aprint_normal(": Timer\n");
21769b44ac7Sjmcneill 
21869b44ac7Sjmcneill 	const u_int rate = clk_get_rate(sc->sc_clk);
21969b44ac7Sjmcneill 
22069b44ac7Sjmcneill 	/* Disable IRQs and all timers */
22169b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR_IRQ_EN_REG, 0);
22269b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR_IRQ_STAS_REG, TIMER_READ(sc, TMR_IRQ_STAS_REG));
22391f7c998Sjmcneill 	/* Enable Timer 0 (hardclock) */
22469b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR0_INTV_VALUE_REG, rate / hz);
22569b44ac7Sjmcneill 	TIMER_WRITE(sc, TMR0_CTRL_REG,
22669b44ac7Sjmcneill 	    __SHIFTIN(TMR0_CTRL_CLK_SRC_OSC24M, TMR0_CTRL_CLK_SRC) |
22769b44ac7Sjmcneill 	    TMR0_CTRL_RELOAD | TMR0_CTRL_EN);
22891f7c998Sjmcneill 	/* Enable Timer 2 (timecounter) */
22991f7c998Sjmcneill 	TIMER_WRITE(sc, TMR2_INTV_VALUE_REG, ~0u);
23091f7c998Sjmcneill 	TIMER_WRITE(sc, TMR2_CTRL_REG,
23191f7c998Sjmcneill 	    __SHIFTIN(TMR2_CTRL_CLK_SRC_OSC24M, TMR2_CTRL_CLK_SRC) |
23291f7c998Sjmcneill 	    TMR2_CTRL_RELOAD | TMR2_CTRL_EN);
233da24f7deStnn 	/* Enable Timer 4 (timecounter for LOSC) */
234da24f7deStnn 	TIMER_WRITE(sc, TMR4_INTV_VALUE_REG, ~0u);
235da24f7deStnn 	TIMER_WRITE(sc, TMR4_CTRL_REG, TMR4_CTRL_RELOAD | TMR4_CTRL_EN);
23668cff0d7Sjmcneill 
23769b44ac7Sjmcneill 	/* Timecounter setup */
23869b44ac7Sjmcneill 	tc->tc_get_timecount = sunxi_timer_get_timecount;
239349b1fb4Stnn 	tc->tc_counter_mask = ~0u;
240da24f7deStnn 	tc->tc_frequency = rate;
24191f7c998Sjmcneill 	tc->tc_name = "Timer 2";
24291f7c998Sjmcneill 	tc->tc_quality = 200;
24369b44ac7Sjmcneill 	tc->tc_priv = sc;
24469b44ac7Sjmcneill 	tc_init(tc);
245da24f7deStnn 	tc_losc->tc_get_timecount = sunxi_timer_get_timecount_losc;
246a66a6894Stnn 	tc_losc->tc_counter_mask = ~0u;
247da24f7deStnn 	tc_losc->tc_frequency = 32768;
248da24f7deStnn 	tc_losc->tc_name = "LOSC";
249da24f7deStnn 	tc_losc->tc_quality = 150;
250da24f7deStnn 	tc_losc->tc_priv = sc;
251da24f7deStnn 	/*
252da24f7deStnn 	 * LOSC is optional to implement in hardware.
253da24f7deStnn 	 * Make sure it ticks before registering it.
254da24f7deStnn 	 */
2552a873fc7Stnn 	reg = __SHIFTIN(LOSC_CTRL_KEY_FIELD_V, LOSC_CTRL_KEY_FIELD) |
2562a873fc7Stnn 	    LOSC_CTRL_OSC32K_AUTO_SWT_EN |
2572a873fc7Stnn 	    LOSC_CTRL_OSC32K_SEL;
2582a873fc7Stnn 	TIMER_WRITE(sc, LOSC_CTRL_REG, reg);
259da24f7deStnn 	ticks = sunxi_timer_get_timecount_losc(tc_losc);
260da24f7deStnn 	delay(100);
261da24f7deStnn 	if (ticks != sunxi_timer_get_timecount_losc(tc_losc))
262da24f7deStnn 		tc_init(tc_losc);
2632a873fc7Stnn 	else
2642a873fc7Stnn 		TIMER_WRITE(sc, LOSC_CTRL_REG, reg & ~LOSC_CTRL_OSC32K_SEL);
26569b44ac7Sjmcneill 
26669b44ac7Sjmcneill 	/* Use this as the OS timer in UP configurations */
26769b44ac7Sjmcneill 	if (!arm_has_mpext_p) {
26869b44ac7Sjmcneill 		timer_softc = sc;
26969b44ac7Sjmcneill 		arm_fdt_timer_register(sunxi_timer_cpu_initclocks);
27069b44ac7Sjmcneill 	}
27169b44ac7Sjmcneill }
27269b44ac7Sjmcneill 
27369b44ac7Sjmcneill CFATTACH_DECL_NEW(sunxi_timer, sizeof(struct sunxi_timer_softc),
27469b44ac7Sjmcneill 	sunxi_timer_match, sunxi_timer_attach, NULL, NULL);
275