Date: Thu, 23 Aug 2018 18:46:05 +0000 (UTC) From: Emmanuel Vadot <manu@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r338272 - in head/sys: arm/allwinner arm64/conf conf Message-ID: <201808231846.w7NIk5Jm087329@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: manu Date: Thu Aug 23 18:46:05 2018 New Revision: 338272 URL: https://svnweb.freebsd.org/changeset/base/338272 Log: a10_timer: Update the driver so we can use it on other SoC a10_timer is currently use in UP allwinner SoC (A10 and A13). Those don't have the generic arm timer. The arm generic timecounter is broken in the A64 SoC, some attempts have been made to fix the glitch but users still reported some minor ones. Since the A64 (and all Allwinner SoC) still have this timer controller, rework the driver so we can use it in any SoC. Since it doesn't have the 64 bits counter on all SoC, use one of the generic 32 bits counter as the timecounter source. PR: 229644 Modified: head/sys/arm/allwinner/a10_timer.c head/sys/arm64/conf/GENERIC head/sys/conf/files.arm64 Modified: head/sys/arm/allwinner/a10_timer.c ============================================================================== --- head/sys/arm/allwinner/a10_timer.c Thu Aug 23 18:33:42 2018 (r338271) +++ head/sys/arm/allwinner/a10_timer.c Thu Aug 23 18:46:05 2018 (r338272) @@ -38,7 +38,6 @@ __FBSDID("$FreeBSD$"); #include <sys/timetc.h> #include <sys/watchdog.h> #include <machine/bus.h> -#include <machine/cpu.h> #include <machine/intr.h> #include <machine/machdep.h> @@ -46,65 +45,86 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#include <machine/bus.h> +#include <dev/extres/clk/clk.h> -#include <sys/kdb.h> - +#if defined(__aarch64__) +#include "opt_soc.h" +#else #include <arm/allwinner/aw_machdep.h> +#endif /** * Timer registers addr * */ -#define SW_TIMER_IRQ_EN_REG 0x00 -#define SW_TIMER_IRQ_STA_REG 0x04 -#define SW_TIMER0_CTRL_REG 0x10 -#define SW_TIMER0_INT_VALUE_REG 0x14 -#define SW_TIMER0_CUR_VALUE_REG 0x18 +#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_ENABLE(x) (1 << x) -#define SW_COUNTER64LO_REG 0xa4 -#define SW_COUNTER64HI_REG 0xa8 -#define CNT64_CTRL_REG 0xa0 +#define TIMER_IRQ_STA_REG 0x04 +#define TIMER_IRQ_PENDING(x) (1 << x) -#define CNT64_RL_EN 0x02 /* read latch enable */ +/* + * On A10, A13, A20 and A31/A31s 6 timers are available + */ +#define TIMER_CTRL_REG(x) (0x10 + 0x10 * x) +#define TIMER_CTRL_START (1 << 0) +#define TIMER_CTRL_AUTORELOAD (1 << 1) +#define TIMER_CTRL_CLKSRC_MASK (3 << 2) +#define TIMER_CTRL_OSC24M (1 << 2) +#define TIMER_CTRL_PRESCALAR_MASK (0x7 << 4) +#define TIMER_CTRL_PRESCALAR(x) ((x - 1) << 4) +#define TIMER_CTRL_MODE_MASK (1 << 7) +#define TIMER_CTRL_MODE_SINGLE (1 << 7) +#define TIMER_CTRL_MODE_CONTINUOUS (0 << 7) +#define TIMER_INTV_REG(x) (0x14 + 0x10 * x) +#define TIMER_CURV_REG(x) (0x18 + 0x10 * x) -#define TIMER_ENABLE (1<<0) -#define TIMER_AUTORELOAD (1<<1) -#define TIMER_OSC24M (1<<2) /* oscillator = 24mhz */ -#define TIMER_PRESCALAR (0<<4) /* prescalar = 1 */ +/* 64 bit counter, available in A10 and A13 */ +#define CNT64_CTRL_REG 0xa0 +#define CNT64_CTRL_RL_EN 0x02 /* read latch enable */ +#define CNT64_LO_REG 0xa4 +#define CNT64_HI_REG 0xa8 -#define SYS_TIMER_CLKSRC 24000000 /* clock source */ +#define SYS_TIMER_CLKSRC 24000000 /* clock source */ +enum a10_timer_type { + A10_TIMER = 1, + A23_TIMER, +}; + struct a10_timer_softc { device_t sc_dev; struct resource *res[2]; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; void *sc_ih; /* interrupt handler */ uint32_t sc_period; - uint32_t timer0_freq; - struct eventtimer et; + uint64_t timer0_freq; + struct eventtimer et; + enum a10_timer_type type; }; -int a10_timer_get_timerfreq(struct a10_timer_softc *); - #define timer_read_4(sc, reg) \ - bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg) + bus_read_4(sc->res[A10_TIMER_MEMRES], reg) #define timer_write_4(sc, reg, val) \ - bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val) + bus_write_4(sc->res[A10_TIMER_MEMRES], reg, val) static u_int a10_timer_get_timecount(struct timecounter *); static int a10_timer_timer_start(struct eventtimer *, sbintime_t first, sbintime_t period); static int a10_timer_timer_stop(struct eventtimer *); -static uint64_t timer_read_counter64(void); +static uint64_t timer_read_counter64(struct a10_timer_softc *sc); +static void a10_timer_eventtimer_setup(struct a10_timer_softc *sc); -static int a10_timer_hardclock(void *); +static void a23_timer_timecounter_setup(struct a10_timer_softc *sc); +static u_int a23_timer_get_timecount(struct timecounter *tc); + +static int a10_timer_irq(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); +#if defined(__arm__) static delay_func a10_timer_delay; +#endif static struct timecounter a10_timer_timecounter = { .tc_name = "a10_timer timer0", @@ -114,47 +134,52 @@ static struct timecounter a10_timer_timecounter = { .tc_quality = 1000, }; -struct a10_timer_softc *a10_timer_sc = NULL; +static struct timecounter a23_timer_timecounter = { + .tc_name = "a10_timer timer0", + .tc_get_timecount = a23_timer_get_timecount, + .tc_counter_mask = ~0u, + .tc_frequency = 0, + /* We want it to be selected over the arm generic timecounter */ + .tc_quality = 2000, +}; +#define A10_TIMER_MEMRES 0 +#define A10_TIMER_IRQRES 1 + static struct resource_spec a10_timer_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; -static uint64_t -timer_read_counter64(void) -{ - uint32_t lo, hi; +static struct ofw_compat_data compat_data[] = { + {"allwinner,sun4i-a10-timer", A10_TIMER}, + {"allwinner,sun8i-a23-timer", A23_TIMER}, + {NULL, 0}, +}; - /* Latch counter, wait for it to be ready to read. */ - timer_write_4(a10_timer_sc, CNT64_CTRL_REG, CNT64_RL_EN); - while (timer_read_4(a10_timer_sc, CNT64_CTRL_REG) & CNT64_RL_EN) - continue; - - hi = timer_read_4(a10_timer_sc, SW_COUNTER64HI_REG); - lo = timer_read_4(a10_timer_sc, SW_COUNTER64LO_REG); - - return (((uint64_t)hi << 32) | lo); -} - static int a10_timer_probe(device_t dev) { struct a10_timer_softc *sc; +#if defined(__arm__) u_int soc_family; +#endif sc = device_get_softc(dev); - if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-timer")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); +#if defined(__arm__) + /* For SoC >= A10 we have the ARM Timecounter/Eventtimer */ soc_family = allwinner_soc_family(); if (soc_family != ALLWINNERSOC_SUN4I && soc_family != ALLWINNERSOC_SUN5I) return (ENXIO); +#endif - device_set_desc(dev, "Allwinner A10/A20 timer"); + device_set_desc(dev, "Allwinner timer"); return (BUS_PROBE_DEFAULT); } @@ -162,10 +187,11 @@ static int a10_timer_attach(device_t dev) { struct a10_timer_softc *sc; + clk_t clk; int err; - uint32_t val; sc = device_get_softc(dev); + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (bus_alloc_resources(dev, a10_timer_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); @@ -173,12 +199,10 @@ a10_timer_attach(device_t dev) } sc->sc_dev = dev; - sc->sc_bst = rman_get_bustag(sc->res[0]); - sc->sc_bsh = rman_get_bushandle(sc->res[0]); /* Setup and enable the timer interrupt */ - err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, a10_timer_hardclock, - NULL, sc, &sc->sc_ih); + err = bus_setup_intr(dev, sc->res[A10_TIMER_IRQRES], INTR_TYPE_CLK, + a10_timer_irq, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, a10_timer_spec, sc->res); device_printf(dev, "Unable to setup the clock irq handler, " @@ -186,18 +210,91 @@ a10_timer_attach(device_t dev) return (ENXIO); } - /* Set clock source to OSC24M, 16 pre-division */ - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - val |= TIMER_PRESCALAR | TIMER_OSC24M; - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); + if (clk_get_by_ofw_index(dev, 0, 0, &clk) != 0) + sc->timer0_freq = SYS_TIMER_CLKSRC; + else { + if (clk_get_freq(clk, &sc->timer0_freq) != 0) { + device_printf(dev, "Cannot get clock source frequency\n"); + return (ENXIO); + } + } - /* Enable timer0 */ - val = timer_read_4(sc, SW_TIMER_IRQ_EN_REG); - val |= TIMER_ENABLE; - timer_write_4(sc, SW_TIMER_IRQ_EN_REG, val); +#if defined(__arm__) + a10_timer_eventtimer_setup(sc); + arm_set_delay(a10_timer_delay, sc); + a10_timer_timecounter.tc_priv = sc; + a10_timer_timecounter.tc_frequency = sc->timer0_freq; + tc_init(&a10_timer_timecounter); +#elif defined(__aarch64__) + a23_timer_timecounter_setup(sc); +#endif - sc->timer0_freq = SYS_TIMER_CLKSRC; + if (bootverbose) { + device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz); + device_printf(sc->sc_dev, "event timer clock frequency %ju\n", + sc->timer0_freq); + device_printf(sc->sc_dev, "timecounter clock frequency %jd\n", + a10_timer_timecounter.tc_frequency); + } + + return (0); +} + +static int +a10_timer_irq(void *arg) +{ + struct a10_timer_softc *sc; + uint32_t val; + + sc = (struct a10_timer_softc *)arg; + + /* Clear interrupt pending bit. */ + timer_write_4(sc, TIMER_IRQ_STA_REG, TIMER_IRQ_PENDING(0)); + + val = timer_read_4(sc, TIMER_CTRL_REG(0)); + + /* + * Disabled autoreload and sc_period > 0 means + * timer_start was called with non NULL first value. + * Now we will set periodic timer with the given period + * value. + */ + if ((val & (1<<1)) == 0 && sc->sc_period > 0) { + /* Update timer */ + timer_write_4(sc, TIMER_CURV_REG(0), sc->sc_period); + + /* Make periodic and enable */ + val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START; + timer_write_4(sc, TIMER_CTRL_REG(0), val); + } + + if (sc->et.et_active) + sc->et.et_event_cb(&sc->et, sc->et.et_arg); + + return (FILTER_HANDLED); +} + +/* + * Event timer function for A10 and A13 + */ + +static void +a10_timer_eventtimer_setup(struct a10_timer_softc *sc) +{ + uint32_t val; + + /* Set clock source to OSC24M, 1 pre-division, continuous mode */ + val = timer_read_4(sc, TIMER_CTRL_REG(0)); + val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK; + val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M; + timer_write_4(sc, TIMER_CTRL_REG(0), val); + + /* Enable timer0 */ + val = timer_read_4(sc, TIMER_IRQ_EN_REG); + val |= TIMER_IRQ_ENABLE(0); + timer_write_4(sc, TIMER_IRQ_EN_REG, val); + /* Set desired frequency in event timer and timecounter */ sc->et.et_frequency = sc->timer0_freq; sc->et.et_name = "a10_timer Eventtimer"; @@ -209,25 +306,6 @@ a10_timer_attach(device_t dev) sc->et.et_stop = a10_timer_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); - - if (device_get_unit(dev) == 0) { - arm_set_delay(a10_timer_delay, sc); - a10_timer_sc = sc; - } - - a10_timer_timecounter.tc_frequency = sc->timer0_freq; - tc_init(&a10_timer_timecounter); - - if (bootverbose) { - device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz); - - device_printf(sc->sc_dev, "event timer clock frequency %u\n", - sc->timer0_freq); - device_printf(sc->sc_dev, "timecounter clock frequency %lld\n", - a10_timer_timecounter.tc_frequency); - } - - return (0); } static int @@ -250,20 +328,20 @@ a10_timer_timer_start(struct eventtimer *et, sbintime_ count = sc->sc_period; /* Update timer values */ - timer_write_4(sc, SW_TIMER0_INT_VALUE_REG, sc->sc_period); - timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, count); + timer_write_4(sc, TIMER_INTV_REG(0), sc->sc_period); + timer_write_4(sc, TIMER_CURV_REG(0), count); - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); + val = timer_read_4(sc, TIMER_CTRL_REG(0)); if (period != 0) { /* periodic */ - val |= TIMER_AUTORELOAD; + val |= TIMER_CTRL_AUTORELOAD; } else { /* oneshot */ - val &= ~TIMER_AUTORELOAD; + val &= ~TIMER_CTRL_AUTORELOAD; } /* Enable timer0 */ - val |= TIMER_ENABLE; - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); + val |= TIMER_IRQ_ENABLE(0); + timer_write_4(sc, TIMER_CTRL_REG(0), val); return (0); } @@ -277,62 +355,104 @@ a10_timer_timer_stop(struct eventtimer *et) sc = (struct a10_timer_softc *)et->et_priv; /* Disable timer0 */ - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - val &= ~TIMER_ENABLE; - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); + val = timer_read_4(sc, TIMER_CTRL_REG(0)); + val &= ~TIMER_CTRL_START; + timer_write_4(sc, TIMER_CTRL_REG(0), val); sc->sc_period = 0; return (0); } -int -a10_timer_get_timerfreq(struct a10_timer_softc *sc) +/* + * Timecounter functions for A23 and above + */ + +static void +a23_timer_timecounter_setup(struct a10_timer_softc *sc) { - return (sc->timer0_freq); + uint32_t val; + + /* Set clock source to OSC24M, 1 pre-division, continuous mode */ + val = timer_read_4(sc, TIMER_CTRL_REG(0)); + val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK; + val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M; + timer_write_4(sc, TIMER_CTRL_REG(0), val); + + /* Set reload value */ + timer_write_4(sc, TIMER_INTV_REG(0), ~0); + val = timer_read_4(sc, TIMER_INTV_REG(0)); + + /* Enable timer0 */ + val = timer_read_4(sc, TIMER_CTRL_REG(0)); + val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START; + timer_write_4(sc, TIMER_CTRL_REG(0), val); + + val = timer_read_4(sc, TIMER_CURV_REG(0)); + + a23_timer_timecounter.tc_priv = sc; + a23_timer_timecounter.tc_frequency = sc->timer0_freq; + tc_init(&a23_timer_timecounter); } -static int -a10_timer_hardclock(void *arg) +static u_int +a23_timer_get_timecount(struct timecounter *tc) { struct a10_timer_softc *sc; uint32_t val; - sc = (struct a10_timer_softc *)arg; + sc = (struct a10_timer_softc *)tc->tc_priv; + if (sc == NULL) + return (0); - /* Clear interrupt pending bit. */ - timer_write_4(sc, SW_TIMER_IRQ_STA_REG, 0x1); + val = timer_read_4(sc, TIMER_CURV_REG(0)); + /* Counter count backwards */ + return (~0u - val); +} - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - /* - * Disabled autoreload and sc_period > 0 means - * timer_start was called with non NULL first value. - * Now we will set periodic timer with the given period - * value. - */ - if ((val & (1<<1)) == 0 && sc->sc_period > 0) { - /* Update timer */ - timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, sc->sc_period); +/* + * Timecounter functions for A10 and A13, using the 64 bits counter + */ - /* Make periodic and enable */ - val |= TIMER_AUTORELOAD | TIMER_ENABLE; - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); - } +static uint64_t +timer_read_counter64(struct a10_timer_softc *sc) +{ + uint32_t lo, hi; - if (sc->et.et_active) - sc->et.et_event_cb(&sc->et, sc->et.et_arg); + /* Latch counter, wait for it to be ready to read. */ + timer_write_4(sc, CNT64_CTRL_REG, CNT64_CTRL_RL_EN); + while (timer_read_4(sc, CNT64_CTRL_REG) & CNT64_CTRL_RL_EN) + continue; - return (FILTER_HANDLED); + hi = timer_read_4(sc, CNT64_HI_REG); + lo = timer_read_4(sc, CNT64_LO_REG); + + return (((uint64_t)hi << 32) | lo); } -u_int +#if defined(__arm__) +static void +a10_timer_delay(int usec, void *arg) +{ + struct a10_timer_softc *sc = arg; + uint64_t end, now; + + now = timer_read_counter64(sc); + end = now + (sc->timer0_freq / 1000000) * (usec + 1); + + while (now < end) + now = timer_read_counter64(sc); +} +#endif + +static u_int a10_timer_get_timecount(struct timecounter *tc) { - if (a10_timer_sc == NULL) + if (tc->tc_priv == NULL) return (0); - return ((u_int)timer_read_counter64()); + return ((u_int)timer_read_counter64(tc->tc_priv)); } static device_method_t a10_timer_methods[] = { @@ -352,16 +472,3 @@ static devclass_t a10_timer_devclass; EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); - -static void -a10_timer_delay(int usec, void *arg) -{ - struct a10_timer_softc *sc = arg; - uint64_t end, now; - - now = timer_read_counter64(); - end = now + (sc->timer0_freq / 1000000) * (usec + 1); - - while (now < end) - now = timer_read_counter64(); -} Modified: head/sys/arm64/conf/GENERIC ============================================================================== --- head/sys/arm64/conf/GENERIC Thu Aug 23 18:33:42 2018 (r338271) +++ head/sys/arm64/conf/GENERIC Thu Aug 23 18:46:05 2018 (r338272) @@ -108,6 +108,9 @@ options SOC_BRCM_BCM2837 options SOC_ROCKCHIP_RK3328 options SOC_XILINX_ZYNQ +# Timer drivers +device a10_timer + # Annapurna Alpine drivers device al_ccu # Alpine Cache Coherency Unit device al_nb_service # Alpine North Bridge Service Modified: head/sys/conf/files.arm64 ============================================================================== --- head/sys/conf/files.arm64 Thu Aug 23 18:33:42 2018 (r338271) +++ head/sys/conf/files.arm64 Thu Aug 23 18:46:05 2018 (r338272) @@ -26,6 +26,7 @@ cloudabi64_vdso_blob.o optional compat_cloudabi64 \ # Allwinner common files arm/allwinner/a10_ehci.c optional ehci aw_ehci fdt +arm/allwinner/a10_timer.c optional a10_timer fdt arm/allwinner/aw_gpio.c optional gpio aw_gpio fdt arm/allwinner/aw_mmc.c optional mmc aw_mmc fdt | mmccam aw_mmc fdt arm/allwinner/aw_nmi.c optional aw_nmi fdt \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201808231846.w7NIk5Jm087329>