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