From owner-svn-src-all@FreeBSD.ORG Thu Jan 24 09:36:50 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id B31BEAA9; Thu, 24 Jan 2013 09:36:50 +0000 (UTC) (envelope-from ganbold@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id A056126C; Thu, 24 Jan 2013 09:36:50 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id r0O9aoGU077830; Thu, 24 Jan 2013 09:36:50 GMT (envelope-from ganbold@svn.freebsd.org) Received: (from ganbold@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id r0O9ao0X077829; Thu, 24 Jan 2013 09:36:50 GMT (envelope-from ganbold@svn.freebsd.org) Message-Id: <201301240936.r0O9ao0X077829@svn.freebsd.org> From: Ganbold Tsagaankhuu Date: Thu, 24 Jan 2013 09:36:50 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r245876 - head/sys/arm/allwinner X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 Jan 2013 09:36:50 -0000 Author: ganbold (doc committer) Date: Thu Jan 24 09:36:50 2013 New Revision: 245876 URL: http://svnweb.freebsd.org/changeset/base/245876 Log: Fix timer to support oneshot and periodic mode Use 64 bit high and low counter for timecounter and delay Reviewed by: mav@, ian@ Approved by: gonzo@ Modified: head/sys/arm/allwinner/timer.c Modified: head/sys/arm/allwinner/timer.c ============================================================================== --- head/sys/arm/allwinner/timer.c Thu Jan 24 09:33:43 2013 (r245875) +++ head/sys/arm/allwinner/timer.c Thu Jan 24 09:36:50 2013 (r245876) @@ -62,12 +62,18 @@ __FBSDID("$FreeBSD$"); #define SW_TIMER0_INT_VALUE_REG 0x14 #define SW_TIMER0_CUR_VALUE_REG 0x18 -#define SYS_TIMER_SCAL 16 /* timer clock source pre-divsion */ -#define SYS_TIMER_CLKSRC 24000000 /* timer clock source */ -#define TMR_INTER_VAL SYS_TIMER_CLKSRC/(SYS_TIMER_SCAL * 1000) +#define SW_COUNTER64LO_REG 0xa4 +#define SW_COUNTER64HI_REG 0xa8 +#define CNT64_CTRL_REG 0xa0 + +#define CNT64_RL_EN 0x02 /* read latch enable */ + +#define TIMER_ENABLE (1<<0) +#define TIMER_AUTORELOAD (1<<1) +#define TIMER_OSC24M (1<<2) /* oscillator = 24mhz */ +#define TIMER_PRESCALAR (4<<4) /* prescalar = 16 */ -#define CLOCK_TICK_RATE TMR_INTER_VAL -#define INITIAL_TIMECOUNTER (0xffffffff) +#define SYS_TIMER_CLKSRC 24000000 /* clock source */ struct a10_timer_softc { device_t sc_dev; @@ -76,7 +82,7 @@ struct a10_timer_softc { bus_space_handle_t sc_bsh; void *sc_ih; /* interrupt handler */ uint32_t sc_period; - uint32_t clkfreq; + uint32_t timer0_freq; struct eventtimer et; }; @@ -92,8 +98,10 @@ static int a10_timer_timer_start(struct struct bintime *, struct bintime *); static int a10_timer_timer_stop(struct eventtimer *); +static uint64_t timer_read_counter64(void); + static int a10_timer_initialized = 0; -static int a10_timer_intr(void *); +static int a10_timer_hardclock(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); @@ -113,6 +121,22 @@ static struct resource_spec a10_timer_sp { -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 int a10_timer_probe(device_t dev) { @@ -130,7 +154,6 @@ a10_timer_attach(device_t dev) struct a10_timer_softc *sc; int err; uint32_t val; - uint32_t freq; sc = device_get_softc(dev); @@ -143,28 +166,8 @@ a10_timer_attach(device_t dev) sc->sc_bst = rman_get_bustag(sc->res[0]); sc->sc_bsh = rman_get_bushandle(sc->res[0]); - /* set interval */ - timer_write_4(sc, SW_TIMER0_INT_VALUE_REG, TMR_INTER_VAL); - - /* set clock source to HOSC, 16 pre-division */ - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - val &= ~(0x07<<4); - val &= ~(0x03<<2); - val |= (4<<4) | (1<<2); - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); - - /* set mode to auto reload */ - val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - val |= (1<<1); - timer_write_4(sc, SW_TIMER0_CTRL_REG, val); - - /* Enable timer0 */ - val = timer_read_4(sc, SW_TIMER_IRQ_EN_REG); - val |= (1<<0); - timer_write_4(sc, SW_TIMER_IRQ_EN_REG, val); - /* Setup and enable the timer interrupt */ - err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, a10_timer_intr, + err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, a10_timer_hardclock, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, a10_timer_spec, sc->res); @@ -172,17 +175,27 @@ a10_timer_attach(device_t dev) "err = %d\n", err); return (ENXIO); } - freq = SYS_TIMER_CLKSRC; - /* Set desired frequency in event timer and timecounter */ - sc->et.et_frequency = (uint64_t)freq; - sc->clkfreq = (uint64_t)freq; + /* 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); + + /* 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; + + /* Set desired frequency in event timer and timecounter */ + sc->et.et_frequency = sc->timer0_freq; sc->et.et_name = "a10_timer Eventtimer"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 1000; sc->et.et_min_period.sec = 0; sc->et.et_min_period.frac = - ((0x00000002LLU << 32) / sc->et.et_frequency) << 32; + ((0x00000005LLU << 32) / sc->et.et_frequency) << 32; sc->et.et_max_period.sec = 0xfffffff0U / sc->et.et_frequency; sc->et.et_max_period.frac = ((0xfffffffeLLU << 32) / sc->et.et_frequency) << 32; @@ -194,15 +207,20 @@ a10_timer_attach(device_t dev) if (device_get_unit(dev) == 0) a10_timer_sc = sc; - a10_timer_timecounter.tc_frequency = (uint64_t)freq; + a10_timer_timecounter.tc_frequency = sc->timer0_freq; tc_init(&a10_timer_timecounter); - printf("clock: hz=%d stathz = %d\n", hz, stathz); + if (bootverbose) { + device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz); - device_printf(sc->sc_dev, "timer clock frequency %d\n", sc->clkfreq); + 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); + } a10_timer_initialized = 1; - + return (0); } @@ -211,25 +229,42 @@ a10_timer_timer_start(struct eventtimer struct bintime *period) { struct a10_timer_softc *sc; - uint32_t clo, count; + uint32_t count; + uint32_t val; sc = (struct a10_timer_softc *)et->et_priv; - if (first != NULL) { + sc->sc_period = 0; + + if (period != NULL) { + sc->sc_period = (sc->et.et_frequency * (period->frac >> 32)) >> 32; + sc->sc_period += sc->et.et_frequency * period->sec; + } + if (first == NULL) + count = sc->sc_period; + else { count = (sc->et.et_frequency * (first->frac >> 32)) >> 32; if (first->sec != 0) count += sc->et.et_frequency * first->sec; + } - /* clear */ - timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, 0); - clo = timer_read_4(sc, SW_TIMER0_CUR_VALUE_REG); - clo += count; - timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, clo); + /* 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); - return (0); + val = timer_read_4(sc, SW_TIMER0_CTRL_REG); + if (first == NULL) { + /* periodic */ + val |= TIMER_AUTORELOAD; + } else { + /* oneshot */ + val &= ~TIMER_AUTORELOAD; } + /* Enable timer0 */ + val |= TIMER_ENABLE; + timer_write_4(sc, SW_TIMER0_CTRL_REG, val); - return (EINVAL); + return (0); } static int @@ -240,12 +275,9 @@ a10_timer_timer_stop(struct eventtimer * sc = (struct a10_timer_softc *)et->et_priv; - /* clear */ - timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, 0); - - /* disable */ + /* Disable timer0 */ val = timer_read_4(sc, SW_TIMER0_CTRL_REG); - val &= ~(1<<0); /* Disable timer0 */ + val &= ~TIMER_ENABLE; timer_write_4(sc, SW_TIMER0_CTRL_REG, val); sc->sc_period = 0; @@ -256,8 +288,7 @@ a10_timer_timer_stop(struct eventtimer * int a10_timer_get_timerfreq(struct a10_timer_softc *sc) { - - return (sc->clkfreq); + return (sc->timer0_freq); } void @@ -267,18 +298,35 @@ cpu_initclocks(void) } static int -a10_timer_intr(void *arg) +a10_timer_hardclock(void *arg) { struct a10_timer_softc *sc; + uint32_t val; sc = (struct a10_timer_softc *)arg; + /* 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); - /* pending */ - timer_write_4(sc, SW_TIMER_IRQ_STA_REG, 0x1); - return (FILTER_HANDLED); } @@ -289,7 +337,7 @@ a10_timer_get_timecount(struct timecount if (a10_timer_sc == NULL) return (0); - return (timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG)); + return ((u_int)timer_read_counter64()); } static device_method_t a10_timer_methods[] = { @@ -313,29 +361,19 @@ void DELAY(int usec) { uint32_t counter; - uint32_t val, val_temp; - int32_t nticks; + uint64_t end, now; - /* Timer is not initialized yet */ if (!a10_timer_initialized) { for (; usec > 0; usec--) - for (counter = 200; counter > 0; counter--) - /* Prevent optimizing out the loop */ + for (counter = 50; counter > 0; counter--) cpufunc_nullop(); return; } - val = timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG); - nticks = ((a10_timer_sc->clkfreq / 1000000 + 1) * usec); - - while (nticks > 0) { - val_temp = timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG); - if (val > val_temp) - nticks -= (val - val_temp); - else - nticks -= (val + (INITIAL_TIMECOUNTER - val_temp)); + now = timer_read_counter64(); + end = now + (a10_timer_sc->timer0_freq / 1000000) * (usec + 1); - val = val_temp; - } + while (now < end) + now = timer_read_counter64(); }