1*7ef649ccSjmcneill /* $NetBSD: a9ptmr.c,v 1.3 2022/11/05 17:30:20 jmcneill Exp $ */
23f3994cbSskrll
33f3994cbSskrll /*-
43f3994cbSskrll * Copyright (c) 2019 The NetBSD Foundation, Inc.
53f3994cbSskrll * All rights reserved.
63f3994cbSskrll *
73f3994cbSskrll * This code is derived from software contributed to The NetBSD Foundation
83f3994cbSskrll * by Nick Hudson
93f3994cbSskrll *
103f3994cbSskrll * Redistribution and use in source and binary forms, with or without
113f3994cbSskrll * modification, are permitted provided that the following conditions
123f3994cbSskrll * are met:
133f3994cbSskrll * 1. Redistributions of source code must retain the above copyright
143f3994cbSskrll * notice, this list of conditions and the following disclaimer.
153f3994cbSskrll * 2. Redistributions in binary form must reproduce the above copyright
163f3994cbSskrll * notice, this list of conditions and the following disclaimer in the
173f3994cbSskrll * documentation and/or other materials provided with the distribution.
183f3994cbSskrll *
193f3994cbSskrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
203f3994cbSskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
213f3994cbSskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223f3994cbSskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
233f3994cbSskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
243f3994cbSskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
253f3994cbSskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
263f3994cbSskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
273f3994cbSskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
283f3994cbSskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
293f3994cbSskrll * POSSIBILITY OF SUCH DAMAGE.
303f3994cbSskrll */
313f3994cbSskrll
323f3994cbSskrll #include <sys/cdefs.h>
33*7ef649ccSjmcneill __KERNEL_RCSID(0, "$NetBSD: a9ptmr.c,v 1.3 2022/11/05 17:30:20 jmcneill Exp $");
343f3994cbSskrll
353f3994cbSskrll #include <sys/param.h>
363f3994cbSskrll #include <sys/bus.h>
373f3994cbSskrll #include <sys/cpu.h>
383f3994cbSskrll #include <sys/device.h>
393f3994cbSskrll #include <sys/kernel.h>
40*7ef649ccSjmcneill #include <sys/xcall.h>
413f3994cbSskrll
423f3994cbSskrll #include <prop/proplib.h>
433f3994cbSskrll
443f3994cbSskrll #include <arm/cortex/a9tmr_reg.h>
453f3994cbSskrll #include <arm/cortex/a9ptmr_var.h>
463f3994cbSskrll
473f3994cbSskrll #include <arm/cortex/mpcore_var.h>
483f3994cbSskrll
493f3994cbSskrll static struct a9ptmr_softc *a9ptmr_sc;
503f3994cbSskrll
513f3994cbSskrll static int a9ptmr_match(device_t, cfdata_t, void *);
523f3994cbSskrll static void a9ptmr_attach(device_t, device_t, void *);
533f3994cbSskrll
543f3994cbSskrll struct a9ptmr_softc {
553f3994cbSskrll device_t sc_dev;
563f3994cbSskrll bus_space_tag_t sc_memt;
573f3994cbSskrll bus_space_handle_t sc_memh;
583f3994cbSskrll
593f3994cbSskrll uint32_t sc_ctl;
603f3994cbSskrll uint32_t sc_freq;
613f3994cbSskrll uint32_t sc_load;
623f3994cbSskrll
633f3994cbSskrll uint32_t sc_prescaler;
643f3994cbSskrll };
653f3994cbSskrll
663f3994cbSskrll
673f3994cbSskrll CFATTACH_DECL_NEW(arma9ptmr, sizeof(struct a9ptmr_softc),
683f3994cbSskrll a9ptmr_match, a9ptmr_attach, NULL, NULL);
693f3994cbSskrll
703f3994cbSskrll static bool attached;
713f3994cbSskrll
723f3994cbSskrll static inline uint32_t
a9ptmr_read(struct a9ptmr_softc * sc,bus_size_t o)733f3994cbSskrll a9ptmr_read(struct a9ptmr_softc *sc, bus_size_t o)
743f3994cbSskrll {
753f3994cbSskrll return bus_space_read_4(sc->sc_memt, sc->sc_memh, o);
763f3994cbSskrll }
773f3994cbSskrll
783f3994cbSskrll static inline void
a9ptmr_write(struct a9ptmr_softc * sc,bus_size_t o,uint32_t v)793f3994cbSskrll a9ptmr_write(struct a9ptmr_softc *sc, bus_size_t o, uint32_t v)
803f3994cbSskrll {
813f3994cbSskrll bus_space_write_4(sc->sc_memt, sc->sc_memh, o, v);
823f3994cbSskrll }
833f3994cbSskrll
843f3994cbSskrll /* ARGSUSED */
853f3994cbSskrll static int
a9ptmr_match(device_t parent,cfdata_t cf,void * aux)863f3994cbSskrll a9ptmr_match(device_t parent, cfdata_t cf, void *aux)
873f3994cbSskrll {
883f3994cbSskrll struct mpcore_attach_args * const mpcaa = aux;
893f3994cbSskrll
903f3994cbSskrll if (attached)
913f3994cbSskrll return 0;
923f3994cbSskrll
933f3994cbSskrll if (!CPU_ID_CORTEX_A9_P(curcpu()->ci_arm_cpuid) &&
943f3994cbSskrll !CPU_ID_CORTEX_A5_P(curcpu()->ci_arm_cpuid))
953f3994cbSskrll return 0;
963f3994cbSskrll
973f3994cbSskrll if (strcmp(mpcaa->mpcaa_name, cf->cf_name) != 0)
983f3994cbSskrll return 0;
993f3994cbSskrll
1003f3994cbSskrll #if 0
1013f3994cbSskrll /*
1023f3994cbSskrll * This isn't present on UP A9s (since CBAR isn't present).
1033f3994cbSskrll */
1043f3994cbSskrll uint32_t mpidr = armreg_mpidr_read();
1053f3994cbSskrll if (mpidr == 0 || (mpidr & MPIDR_U))
1063f3994cbSskrll return 0;
1073f3994cbSskrll #endif
1083f3994cbSskrll
1093f3994cbSskrll return 1;
1103f3994cbSskrll }
1113f3994cbSskrll
1123f3994cbSskrll
1133f3994cbSskrll static void
a9ptmr_attach(device_t parent,device_t self,void * aux)1143f3994cbSskrll a9ptmr_attach(device_t parent, device_t self, void *aux)
1153f3994cbSskrll {
1163f3994cbSskrll struct a9ptmr_softc * const sc = device_private(self);
1173f3994cbSskrll struct mpcore_attach_args * const mpcaa = aux;
1183f3994cbSskrll prop_dictionary_t dict = device_properties(self);
1193f3994cbSskrll char freqbuf[sizeof("XXX SHz")];
1203f3994cbSskrll const char *cpu_type;
1213f3994cbSskrll
1223f3994cbSskrll
1233f3994cbSskrll sc->sc_dev = self;
1243f3994cbSskrll sc->sc_memt = mpcaa->mpcaa_memt;
1253f3994cbSskrll
1263f3994cbSskrll bus_space_subregion(sc->sc_memt, mpcaa->mpcaa_memh,
1273f3994cbSskrll mpcaa->mpcaa_off1, TMR_PRIVATE_SIZE, &sc->sc_memh);
1283f3994cbSskrll
1293f3994cbSskrll /*
1303f3994cbSskrll * This runs at the ARM PERIPHCLOCK.
1313f3994cbSskrll * The MD code should have setup our frequency for us.
1323f3994cbSskrll */
1333f3994cbSskrll if (!prop_dictionary_get_uint32(dict, "frequency", &sc->sc_freq)) {
1343f3994cbSskrll dict = device_properties(parent);
1353f3994cbSskrll prop_dictionary_get_uint32(dict, "frequency", &sc->sc_freq);
1363f3994cbSskrll }
1373f3994cbSskrll
1383f3994cbSskrll humanize_number(freqbuf, sizeof(freqbuf), sc->sc_freq, "Hz", 1000);
1393f3994cbSskrll
1403f3994cbSskrll a9ptmr_sc = sc;
1413f3994cbSskrll sc->sc_dev = self;
1423f3994cbSskrll sc->sc_memt = mpcaa->mpcaa_memt;
1433f3994cbSskrll sc->sc_memh = mpcaa->mpcaa_memh;
1443f3994cbSskrll
1453f3994cbSskrll sc->sc_ctl = a9ptmr_read(sc, TMR_CTL);
1463f3994cbSskrll
1473f3994cbSskrll sc->sc_prescaler = 1;
148bfcaf5a7Sskrll #if 0
1493f3994cbSskrll /*
1503f3994cbSskrll * Let's hope the timer frequency isn't prime.
1513f3994cbSskrll */
1523f3994cbSskrll for (size_t div = 256; div >= 2; div--) {
1533f3994cbSskrll if (sc->sc_freq % div == 0) {
1543f3994cbSskrll sc->sc_prescaler = div;
1553f3994cbSskrll break;
1563f3994cbSskrll }
1573f3994cbSskrll }
1583f3994cbSskrll sc->sc_freq /= sc->sc_prescaler;
159bfcaf5a7Sskrll #endif
1603f3994cbSskrll
161bfcaf5a7Sskrll aprint_debug(": freq %d prescaler %d", sc->sc_freq,
1623f3994cbSskrll sc->sc_prescaler);
1633f3994cbSskrll sc->sc_ctl = TMR_CTL_INT_ENABLE | TMR_CTL_AUTO_RELOAD | TMR_CTL_ENABLE;
1643f3994cbSskrll sc->sc_ctl |= __SHIFTIN(sc->sc_prescaler - 1, TMR_CTL_PRESCALER);
1653f3994cbSskrll
1663f3994cbSskrll sc->sc_load = (sc->sc_freq / hz) - 1;
1673f3994cbSskrll
168bfcaf5a7Sskrll aprint_debug(": load %d ", sc->sc_load);
169bfcaf5a7Sskrll
1703f3994cbSskrll a9ptmr_init_cpu_clock(curcpu());
1713f3994cbSskrll
1723f3994cbSskrll aprint_naive("\n");
1733f3994cbSskrll if (CPU_ID_CORTEX_A5_P(curcpu()->ci_arm_cpuid)) {
1743f3994cbSskrll cpu_type = "A5";
1753f3994cbSskrll } else {
1763f3994cbSskrll cpu_type = "A9";
1773f3994cbSskrll }
1783f3994cbSskrll aprint_normal(": %s Private Timer (%s)\n", cpu_type, freqbuf);
1793f3994cbSskrll
1803f3994cbSskrll attached = true;
1813f3994cbSskrll }
1823f3994cbSskrll
1833f3994cbSskrll
1843f3994cbSskrll
1853f3994cbSskrll void
a9ptmr_delay(unsigned int n)1863f3994cbSskrll a9ptmr_delay(unsigned int n)
1873f3994cbSskrll {
1883f3994cbSskrll struct a9ptmr_softc * const sc = a9ptmr_sc;
1893f3994cbSskrll
1903f3994cbSskrll KASSERT(sc != NULL);
1913f3994cbSskrll
1923f3994cbSskrll uint32_t freq = sc->sc_freq ? sc->sc_freq :
1933f3994cbSskrll curcpu()->ci_data.cpu_cc_freq / 2;
1943f3994cbSskrll KASSERT(freq != 0);
1953f3994cbSskrll
1963f3994cbSskrll const uint64_t counts_per_usec = freq / 1000000;
1973f3994cbSskrll uint32_t delta, usecs, last, curr;
1983f3994cbSskrll
1993f3994cbSskrll KASSERT(sc != NULL);
2003f3994cbSskrll
2013f3994cbSskrll last = a9ptmr_read(sc, TMR_CTR);
2023f3994cbSskrll
2033f3994cbSskrll delta = usecs = 0;
2043f3994cbSskrll while (n > usecs) {
2053f3994cbSskrll curr = a9ptmr_read(sc, TMR_CTR);
2063f3994cbSskrll
207bfcaf5a7Sskrll /* Check to see if the timer has reloaded. */
208bfcaf5a7Sskrll if (curr > last)
209bfcaf5a7Sskrll delta += (sc->sc_load - curr) + last;
2103f3994cbSskrll else
211bfcaf5a7Sskrll delta += last - curr;
2123f3994cbSskrll
2133f3994cbSskrll last = curr;
2143f3994cbSskrll
2153f3994cbSskrll if (delta >= counts_per_usec) {
2163f3994cbSskrll usecs += delta / counts_per_usec;
2173f3994cbSskrll delta %= counts_per_usec;
2183f3994cbSskrll }
2193f3994cbSskrll }
2203f3994cbSskrll }
2213f3994cbSskrll
2223f3994cbSskrll
2233f3994cbSskrll void
a9ptmr_cpu_initclocks(void)2243f3994cbSskrll a9ptmr_cpu_initclocks(void)
2253f3994cbSskrll {
2263f3994cbSskrll struct a9ptmr_softc * const sc __diagused = a9ptmr_sc;
2273f3994cbSskrll
2283f3994cbSskrll KASSERT(sc->sc_dev != NULL);
2293f3994cbSskrll KASSERT(sc->sc_freq != 0);
2303f3994cbSskrll
2313f3994cbSskrll }
2323f3994cbSskrll
2333f3994cbSskrll void
a9ptmr_init_cpu_clock(struct cpu_info * ci)2343f3994cbSskrll a9ptmr_init_cpu_clock(struct cpu_info *ci)
2353f3994cbSskrll {
2363f3994cbSskrll struct a9ptmr_softc * const sc = a9ptmr_sc;
2373f3994cbSskrll
2383f3994cbSskrll /* Disable Private timer and acknowledge any event */
2393f3994cbSskrll a9ptmr_write(sc, TMR_CTL, 0);
2403f3994cbSskrll a9ptmr_write(sc, TMR_INT, TMR_INT_EVENT);
2413f3994cbSskrll
2423f3994cbSskrll /*
2433f3994cbSskrll * Provide the auto load value for the decrementing counter and
2443f3994cbSskrll * start it.
2453f3994cbSskrll */
2463f3994cbSskrll a9ptmr_write(sc, TMR_LOAD, sc->sc_load);
2473f3994cbSskrll a9ptmr_write(sc, TMR_CTL, sc->sc_ctl);
2483f3994cbSskrll
2493f3994cbSskrll }
2503f3994cbSskrll
2513f3994cbSskrll
2523f3994cbSskrll
2533f3994cbSskrll /*
2543f3994cbSskrll * a9ptmr_intr:
2553f3994cbSskrll *
2563f3994cbSskrll * Handle the hardclock interrupt.
2573f3994cbSskrll */
2583f3994cbSskrll int
a9ptmr_intr(void * arg)2593f3994cbSskrll a9ptmr_intr(void *arg)
2603f3994cbSskrll {
2613f3994cbSskrll struct clockframe * const cf = arg;
2623f3994cbSskrll struct a9ptmr_softc * const sc = a9ptmr_sc;
2633f3994cbSskrll
2643f3994cbSskrll a9ptmr_write(sc, TMR_INT, TMR_INT_EVENT);
2653f3994cbSskrll hardclock(cf);
2663f3994cbSskrll
2673f3994cbSskrll return 1;
2683f3994cbSskrll }
269*7ef649ccSjmcneill
270*7ef649ccSjmcneill static void
a9ptmr_update_freq_cb(void * arg1,void * arg2)271*7ef649ccSjmcneill a9ptmr_update_freq_cb(void *arg1, void *arg2)
272*7ef649ccSjmcneill {
273*7ef649ccSjmcneill a9ptmr_init_cpu_clock(curcpu());
274*7ef649ccSjmcneill }
275*7ef649ccSjmcneill
276*7ef649ccSjmcneill void
a9ptmr_update_freq(uint32_t freq)277*7ef649ccSjmcneill a9ptmr_update_freq(uint32_t freq)
278*7ef649ccSjmcneill {
279*7ef649ccSjmcneill struct a9ptmr_softc * const sc = a9ptmr_sc;
280*7ef649ccSjmcneill uint64_t xc;
281*7ef649ccSjmcneill
282*7ef649ccSjmcneill KASSERT(sc->sc_dev != NULL);
283*7ef649ccSjmcneill KASSERT(freq != 0);
284*7ef649ccSjmcneill
285*7ef649ccSjmcneill sc->sc_freq = freq;
286*7ef649ccSjmcneill sc->sc_load = (sc->sc_freq / hz) - 1;
287*7ef649ccSjmcneill
288*7ef649ccSjmcneill xc = xc_broadcast(0, a9ptmr_update_freq_cb, NULL, NULL);
289*7ef649ccSjmcneill xc_wait(xc);
290*7ef649ccSjmcneill }
291