sunxi_hstimer.c revision 1.4
11.4Sthorpej/* $NetBSD: sunxi_hstimer.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $ */
21.1Stnn
31.1Stnn/*-
41.1Stnn * Copyright (c) 2019 Tobias Nygren <tnn@NetBSD.org>
51.1Stnn * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
61.1Stnn * All rights reserved.
71.1Stnn *
81.1Stnn * Redistribution and use in source and binary forms, with or without
91.1Stnn * modification, are permitted provided that the following conditions
101.1Stnn * are met:
111.1Stnn * 1. Redistributions of source code must retain the above copyright
121.1Stnn *    notice, this list of conditions and the following disclaimer.
131.1Stnn * 2. Redistributions in binary form must reproduce the above copyright
141.1Stnn *    notice, this list of conditions and the following disclaimer in the
151.1Stnn *    documentation and/or other materials provided with the distribution.
161.1Stnn *
171.1Stnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
181.1Stnn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
191.1Stnn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
201.1Stnn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
211.1Stnn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
221.1Stnn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
231.1Stnn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
241.1Stnn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
251.1Stnn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261.1Stnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271.1Stnn * SUCH DAMAGE.
281.1Stnn */
291.1Stnn
301.1Stnn#include <sys/cdefs.h>
311.4Sthorpej__KERNEL_RCSID(0, "$NetBSD: sunxi_hstimer.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $");
321.1Stnn
331.1Stnn#include <sys/bus.h>
341.1Stnn#include <sys/device.h>
351.1Stnn#include <sys/timetc.h>
361.1Stnn#include <dev/fdt/fdtvar.h>
371.1Stnn
381.1Stnn/* High Speed Timer registers */
391.1Stnn#define	HS_TMR_IRQ_EN_REG	0x0
401.1Stnn#define	HS_TMR_IRQ_EN(n)	__BIT(n)
411.1Stnn#define	HS_TMR_IRQ_STAS_REG	0x4
421.1Stnn#define	HS_TMR_STAS_PEND(n)	__BIT(n)
431.1Stnn#define	HS_TMR0_CTRL_REG	0x10
441.1Stnn#define	 HS_TMR0_CTRL_MODE	__BIT(7)
451.1Stnn#define	 HS_TMR0_CTRL_CLK_PRESCALE	__BITS(6,4)
461.1Stnn#define	 HS_TMR0_CTRL_RELOAD	__BIT(1)
471.1Stnn#define	 HS_TMR0_CTRL_EN	__BIT(0)
481.1Stnn#define	HS_TMR0_INTV_LO_REG	0x14
491.1Stnn#define	HS_TMR0_INTV_HI_REG	0x18
501.1Stnn#define	HS_TMR0_CURNT_LO_REG	0x1c
511.1Stnn#define	HS_TMR0_CURNT_HI_REG	0x20
521.1Stnn#define	HS_TMR1_CTRL_REG	0x30
531.1Stnn#define	 HS_TMR1_CTRL_MODE	__BIT(7)
541.1Stnn#define	 HS_TMR1_CTRL_CLK_PRESCALE	__BITS(6,4)
551.1Stnn#define	 HS_TMR1_CTRL_RELOAD	__BIT(1)
561.1Stnn#define	 HS_TMR1_CTRL_EN	__BIT(0)
571.1Stnn#define	HS_TMR1_INTV_LO_REG	0x34
581.1Stnn#define	HS_TMR1_INTV_HI_REG	0x38
591.1Stnn#define	HS_TMR1_CURNT_LO_REG	0x3c
601.1Stnn#define	HS_TMR1_CURNT_HI_REG	0x40
611.1Stnn#define	HS_TMR2_CTRL_REG	0x50
621.1Stnn#define	 HS_TMR2_CTRL_MODE	__BIT(7)
631.1Stnn#define	 HS_TMR2_CTRL_CLK_PRESCALE	__BITS(6,4)
641.1Stnn#define	 HS_TMR2_CTRL_RELOAD	__BIT(1)
651.1Stnn#define	 HS_TMR2_CTRL_EN	__BIT(0)
661.1Stnn#define	HS_TMR2_INTV_LO_REG	0x54
671.1Stnn#define	HS_TMR2_INTV_HI_REG	0x58
681.1Stnn#define	HS_TMR2_CURNT_LO_REG	0x5c
691.1Stnn#define	HS_TMR2_CURNT_HI_REG	0x60
701.1Stnn#define	HS_TMR3_CTRL_REG	0x70
711.1Stnn#define	 HS_TMR3_CTRL_MODE	__BIT(7)
721.1Stnn#define	 HS_TMR3_CTRL_CLK_PRESCALE	__BITS(6,4)
731.1Stnn#define	 HS_TMR3_CTRL_RELOAD	__BIT(1)
741.1Stnn#define	 HS_TMR3_CTRL_EN	__BIT(0)
751.1Stnn#define	HS_TMR3_INTV_LO_REG	0x74
761.1Stnn#define	HS_TMR3_INTV_HI_REG	0x78
771.1Stnn#define	HS_TMR3_CURNT_LO_REG	0x7c
781.1Stnn#define	HS_TMR3_CURNT_HI_REG	0x80
791.1Stnn
801.4Sthorpejstatic const struct device_compatible_entry compat_data[] = {
811.4Sthorpej	{ .compat = "allwinner,sun5i-a13-hstimer" },
821.4Sthorpej	{ .compat = "allwinner,sun6i-a31-hstimer" },
831.4Sthorpej	{ .compat = "allwinner,sun7i-a20-hstimer" },
841.4Sthorpej	DEVICE_COMPAT_EOL
851.1Stnn};
861.1Stnn
871.1Stnnstruct sunxi_hstimer_softc {
881.1Stnn	device_t		sc_dev;
891.1Stnn	bus_space_tag_t		sc_bst;
901.1Stnn	bus_space_handle_t	sc_bsh;
911.1Stnn	int			sc_phandle;
921.1Stnn	void			*sc_ih;
931.1Stnn	struct timecounter	sc_tc;
941.1Stnn};
951.1Stnn
961.1Stnn#define TIMER_READ(sc, reg) \
971.1Stnn    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
981.1Stnn#define TIMER_WRITE(sc, reg, val) \
991.1Stnn    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1001.1Stnn
1011.1Stnnstatic int
1021.1Stnnsunxi_hstimer_intr(void *arg)
1031.1Stnn{
1041.1Stnn	struct sunxi_hstimer_softc *sc = arg;
1051.1Stnn	uint32_t stas;
1061.1Stnn
1071.1Stnn	stas = TIMER_READ(sc, HS_TMR_IRQ_STAS_REG);
1081.1Stnn	if (stas == 0)
1091.1Stnn		return 0;
1101.1Stnn	TIMER_WRITE(sc, HS_TMR_IRQ_STAS_REG, stas);
1111.1Stnn
1121.1Stnn	return 1;
1131.1Stnn}
1141.1Stnn
1151.1Stnnstatic u_int
1161.1Stnnsunxi_hstimer_get_timecount(struct timecounter *tc)
1171.1Stnn{
1181.1Stnn	struct sunxi_hstimer_softc *sc = tc->tc_priv;
1191.1Stnn
1201.1Stnn	/*
1211.1Stnn	 * Timer current value is a 56-bit down counter.
1221.1Stnn	 * But we only need the lower 32 bits for timecounter.
1231.1Stnn	 */
1241.1Stnn	return ~TIMER_READ(sc, HS_TMR0_CURNT_LO_REG);
1251.1Stnn}
1261.1Stnn
1271.1Stnnstatic int
1281.1Stnnsunxi_hstimer_match(device_t parent, cfdata_t cf, void *aux)
1291.1Stnn{
1301.1Stnn	struct fdt_attach_args * const faa = aux;
1311.1Stnn
1321.4Sthorpej	return of_compatible_match(faa->faa_phandle, compat_data);
1331.1Stnn}
1341.1Stnn
1351.1Stnnstatic void
1361.1Stnnsunxi_hstimer_attach(device_t parent, device_t self, void *aux)
1371.1Stnn{
1381.1Stnn	struct sunxi_hstimer_softc *sc = device_private(self);
1391.1Stnn	struct fdt_attach_args *faa = aux;
1401.1Stnn	struct timecounter *tc = &sc->sc_tc;
1411.1Stnn	const int phandle = faa->faa_phandle;
1421.1Stnn	bus_addr_t addr;
1431.1Stnn	bus_size_t size;
1441.1Stnn	char intrstr[128];
1451.1Stnn	struct clk *clk;
1461.1Stnn
1471.1Stnn	sc->sc_dev = self;
1481.1Stnn	sc->sc_phandle = phandle;
1491.1Stnn	sc->sc_bst = faa->faa_bst;
1501.1Stnn
1511.1Stnn	if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL
1521.1Stnn	    || clk_enable(clk) != 0) {
1531.1Stnn		aprint_error(": couldn't enable clock\n");
1541.1Stnn		return;
1551.1Stnn	}
1561.1Stnn
1571.1Stnn	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0
1581.1Stnn	    || bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
1591.1Stnn		aprint_error(": couldn't map registers\n");
1601.1Stnn		return;
1611.1Stnn	}
1621.1Stnn
1631.1Stnn	aprint_naive("\n");
1641.1Stnn	aprint_normal(": High Speed Timer\n");
1651.1Stnn
1661.1Stnn	/* Disable IRQs and all timers */
1671.1Stnn	TIMER_WRITE(sc, HS_TMR_IRQ_EN_REG, 0);
1681.1Stnn	TIMER_WRITE(sc, HS_TMR_IRQ_STAS_REG, TIMER_READ(sc, HS_TMR_IRQ_STAS_REG));
1691.1Stnn	/* Enable Timer 0 (timecounter) */
1701.1Stnn	TIMER_WRITE(sc, HS_TMR0_CTRL_REG, 0);
1711.1Stnn	TIMER_WRITE(sc, HS_TMR0_INTV_LO_REG, ~0u);
1721.1Stnn	TIMER_WRITE(sc, HS_TMR0_INTV_HI_REG, ~0u);
1731.1Stnn	TIMER_WRITE(sc, HS_TMR0_CTRL_REG,
1741.1Stnn	    HS_TMR0_CTRL_RELOAD | HS_TMR0_CTRL_EN);
1751.1Stnn
1761.1Stnn	/* Timecounter setup */
1771.1Stnn	tc->tc_get_timecount = sunxi_hstimer_get_timecount;
1781.2Stnn	tc->tc_counter_mask = ~0u;
1791.1Stnn	tc->tc_frequency = clk_get_rate(clk);
1801.1Stnn	tc->tc_name = "hstimer";
1811.1Stnn	tc->tc_quality = 300;
1821.1Stnn	tc->tc_priv = sc;
1831.1Stnn	tc_init(tc);
1841.1Stnn
1851.1Stnn	if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) {
1861.1Stnn		aprint_error_dev(self, "failed to decode interrupt\n");
1871.1Stnn		return;
1881.1Stnn	}
1891.3Sjmcneill	sc->sc_ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK,
1901.3Sjmcneill	    FDT_INTR_MPSAFE, sunxi_hstimer_intr, sc, device_xname(sc->sc_dev));
1911.1Stnn	if (sc->sc_ih == NULL) {
1921.1Stnn		aprint_error_dev(self, "failed to establish interrupt on %s\n",
1931.1Stnn				 intrstr);
1941.1Stnn		return;
1951.1Stnn	}
1961.1Stnn	aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
1971.1Stnn}
1981.1Stnn
1991.1StnnCFATTACH_DECL_NEW(sunxi_hstimer, sizeof(struct sunxi_hstimer_softc),
2001.1Stnn	sunxi_hstimer_match, sunxi_hstimer_attach, NULL, NULL);
2011.1Stnn
202