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
This commit is contained in:
Emmanuel Vadot 2018-08-23 18:46:05 +00:00
parent 55f3f71ca0
commit 4ca213c07a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338272
3 changed files with 245 additions and 134 deletions

View File

@ -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 <sys/kdb.h>
#include <dev/extres/clk/clk.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,7 +134,17 @@ 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 },
@ -122,39 +152,34 @@ static struct resource_spec a10_timer_spec[] = {
{ -1, 0 }
};
static uint64_t
timer_read_counter64(void)
{
uint32_t lo, hi;
/* 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 struct ofw_compat_data compat_data[] = {
{"allwinner,sun4i-a10-timer", A10_TIMER},
{"allwinner,sun8i-a23-timer", A23_TIMER},
{NULL, 0},
};
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,17 +210,90 @@ 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);
}
}
#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
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, SW_TIMER_IRQ_EN_REG);
val |= TIMER_ENABLE;
timer_write_4(sc, SW_TIMER_IRQ_EN_REG, val);
sc->timer0_freq = SYS_TIMER_CLKSRC;
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;
@ -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_t first,
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, 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);
/* Make periodic and enable */
val |= TIMER_AUTORELOAD | TIMER_ENABLE;
timer_write_4(sc, SW_TIMER0_CTRL_REG, val);
}
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
val = timer_read_4(sc, TIMER_CURV_REG(0));
/* Counter count backwards */
return (~0u - val);
}
u_int
/*
* Timecounter functions for A10 and A13, using the 64 bits counter
*/
static uint64_t
timer_read_counter64(struct a10_timer_softc *sc)
{
uint32_t lo, hi;
/* 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;
hi = timer_read_4(sc, CNT64_HI_REG);
lo = timer_read_4(sc, CNT64_LO_REG);
return (((uint64_t)hi << 32) | lo);
}
#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();
}

View File

@ -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

View File

@ -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 \