# cpu0
cpu0 at mainbus0
-timer0 at cpu0
intc0 at cpu0
# NS16550 compatible serial ports
-# $OpenBSD: RAMDISK,v 1.5 2021/04/25 02:48:00 jsg Exp $
+# $OpenBSD: RAMDISK,v 1.6 2021/05/04 16:38:06 kettenis Exp $
#
# GENERIC machine description file
#
# cpu0
cpu0 at mainbus0
-timer0 at cpu0
intc0 at cpu0
# NS16550 compatible serial ports
file arch/riscv64/riscv64/ast.c
file arch/riscv64/riscv64/bus_space.c
file arch/riscv64/riscv64/bus_dma.c
+file arch/riscv64/riscv64/clock.c
file arch/riscv64/riscv64/conf.c
file arch/riscv64/riscv64/disksubr.c disk
file arch/riscv64/riscv64/locore.S
attach cpu at mainbus
file arch/riscv64/riscv64/cpu.c cpu
-#
-# timer
-#
-device timer
-attach timer at cpu
-file arch/riscv64/dev/timer.c timer
-
#
# HART-specific interrupt controller
#
-/* $OpenBSD: mainbus.c,v 1.4 2021/05/04 12:46:28 kettenis Exp $ */
+/* $OpenBSD: mainbus.c,v 1.5 2021/05/04 16:38:06 kettenis Exp $ */
/*
* Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
* Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
return (1);
}
-void riscv_timer_init(void);
-
void
mainbus_attach(struct device *parent, struct device *self, void *aux)
{
int node, len;
riscv_intr_init_fdt();
- riscv_timer_init();
sc->sc_node = OF_peer(0);
sc->sc_iot = &riscv64_bs_tag;
+++ /dev/null
-/*-
- * Copyright (c) 2015-2017 Ruslan Bukin <br@bsdpad.com>
- * All rights reserved.
- *
- * Portions of this software were developed by SRI International and the
- * University of Cambridge Computer Laboratory under DARPA/AFRL contract
- * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
- *
- * Portions of this software were developed by the University of Cambridge
- * Computer Laboratory as part of the CTSRD Project, with support from the
- * UK Higher Education Innovation Fund (HEIF).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * RISC-V Timer
- */
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/queue.h>
-#include <sys/malloc.h>
-#include <sys/device.h>
-#include <sys/kernel.h>
-#include <sys/timetc.h>
-
-#include <machine/intr.h>
-#include <machine/bus.h>
-#include <machine/cpufunc.h>
-#include <machine/fdt.h>
-#include <machine/sbi.h>
-
-#include "riscv_cpu_intc.h"
-
-#include <dev/ofw/fdt.h>
-#include <dev/ofw/openfirm.h>
-
-
-#define TIMER_COUNTS 0x00
-#define TIMER_MTIMECMP(cpu) (cpu * 8)
-#define TIMER_FREQUENCY 10 * 1000 * 1000 /* RISC-V time clock */
-
-unsigned riscv_timer_get_timecount(struct timecounter *);
-
-static struct timecounter riscv_timer_timecount = {
- .tc_name = "RISC-V Timecounter",
- .tc_get_timecount = riscv_timer_get_timecount,
- .tc_poll_pps = NULL,
- .tc_counter_mask = ~0u,
- .tc_frequency = 0,
- .tc_quality = 1000,
- .tc_priv = NULL,
-};
-
-struct riscv_timer_pcpu_softc {
- uint64_t pc_nexttickevent;
- uint64_t pc_nextstatevent;
- u_int32_t pc_ticks_err_sum;
-};
-
-struct riscv_timer_softc {
- struct device sc_dev;
- int sc_node;
-
- struct riscv_timer_pcpu_softc sc_pstat[MAXCPUS];
-
- u_int32_t sc_ticks_err_cnt;
- u_int32_t sc_ticks_per_second; // sc_clkfreq
- u_int32_t sc_ticks_per_intr;
- u_int32_t sc_statvar;
- u_int32_t sc_statmin;
-
- void *sc_ih;
-};
-
-static struct riscv_timer_softc *riscv_timer_sc = NULL;
-
-int riscv_timer_get_freq();
-int riscv_timer_match(struct device *, void *, void *);
-void riscv_timer_attach(struct device *, struct device *, void *);
-int riscv_timer_intr(void *);
-void riscv_timer_cpu_initclocks();
-void riscv_timer_delay(u_int);
-void riscv_timer_setstatclockrate(int);
-void riscv_timer_startclock();
-
-struct cfattach timer_ca = {
- sizeof (struct riscv_timer_softc), riscv_timer_match,
- riscv_timer_attach
-};
-
-struct cfdriver timer_cd = {
- NULL, "timer", DV_DULL
-};
-
-static inline uint64_t
-get_cycles()
-{
- return (rdtime());
-}
-
-long
-get_counts(struct riscv_timer_softc *sc)
-{
- uint64_t counts;
-
- counts = get_cycles();
-
- return (counts);
-}
-
-unsigned
-riscv_timer_get_timecount(struct timecounter *tc)
-{
- struct riscv_timer_softc *sc;
-
- sc = tc->tc_priv;
-
- return (get_counts(sc));
-}
-
-int
-riscv_timer_get_freq()
-{
- int node, len;
-
- node = OF_finddevice("/cpus");
- if (node == -1) {
- printf("Can't find cpus node.\n");
- return (0);
- }
-
- len = OF_getproplen(node, "timebase-frequency");
- if (len != 4) {
- printf("Can't find timebase-frequency property.\n");
- return (0);
- }
-
- return OF_getpropint(node, "timebase-frequency", 0);
-}
-
-int
-riscv_timer_match(struct device *parent, void *cfdata, void *aux)
-{
- if (riscv_timer_sc) //already attached
- return 0;
-
- int node;
- // struct fdt_attach_args *fa = (struct fdt_attach_args *)aux;
-
- /*
- * return 1 if:
- * we can find valid "timebase-frequency" property from cpus
- */
- if ( (node = OF_finddevice("/cpus")) == 0)
- return 0;
-
- return (OF_getproplen(node, "timebase-frequency") == 4);//32bit uint
-}
-
-void
-riscv_timer_attach(struct device *parent, struct device *self, void *aux)
-{
- struct riscv_timer_softc *sc = (struct riscv_timer_softc *)self;
-
- if (riscv_timer_sc)/* already attached */
- return;
-
- sc->sc_ticks_per_second = riscv_timer_get_freq();
- if (sc->sc_ticks_per_second == 0) {
- printf("Failed to resolve RISC-V Timer timebase\n");
- return;
- }
- printf(": tick rate %d KHz\n", sc->sc_ticks_per_second/1000);
-
- riscv_timer_sc = sc;
- stathz = 0;
-
- riscv_clock_register(riscv_timer_cpu_initclocks, riscv_timer_delay,
- riscv_timer_setstatclockrate, riscv_timer_startclock);
-
- riscv_timer_timecount.tc_frequency = sc->sc_ticks_per_second;
- riscv_timer_timecount.tc_priv = sc;
-
- tc_init(&riscv_timer_timecount);
-}
-
-
-int timer_mindelta = 0; /* what should this be? */
-int
-riscv_timer_intr(void *frame)
-{
- struct riscv_timer_softc *sc;
- uint64_t next, now, newnow;
- int timermissed = 0;
- u_int new_hz = 100;
- int s;
-
-#ifdef DEBUG_TIMER
- printf("RISC-V Timer Interrupt\n");
-#endif
-
- sc = riscv_timer_sc;
-
- s = splclock();
-
- if (s < IPL_CLOCK)
- hardclock(frame);
-
- // XXX should base timer interval from the expected
- // time of expiration, not 'now'
- now = get_cycles();
- next = now + ((sc->sc_ticks_per_second / new_hz));
-
- do {
- newnow = get_cycles();
- if (next < (newnow + timer_mindelta)) {
- /* slowly scale up miss timer. */
- if (timermissed > 1)
- timer_mindelta ++;
- }
- next = newnow + timer_mindelta;
- sbi_set_timer(next);
- csr_set(sip, SIE_STIE);
-
- /* re-read current time to verif
- * time hasn't been set into the past
- */
-
- newnow = get_cycles();
- /* if we missed more than once, increment the min period */
- timermissed++;
- } while (next <= newnow);
-
- splx(s);
- return (1); // Handled
-}
-
-void
-riscv_timer_cpu_initclocks()
-{
- struct riscv_timer_softc *sc = timer_cd.cd_devs[0];
- struct riscv_timer_pcpu_softc *pc =
- &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
- uint64_t next;
-
- stathz = hz;
- profhz = hz * 10;
-
- riscv_timer_setstatclockrate(stathz);
-
- sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz;
- sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz;
- pc->pc_ticks_err_sum = 0;
-
- /* configure virtual timer interrupt */
- sc->sc_ih = riscv_intc_intr_establish(IRQ_TIMER_SUPERVISOR, 0,
- riscv_timer_intr, NULL, "timer");
-
- next = get_cycles() + sc->sc_ticks_per_intr;
- pc->pc_nexttickevent = pc->pc_nextstatevent = next;
-
- sbi_set_timer(next);
- csr_set(sie, SIE_STIE);
-}
-
-void
-riscv_timer_delay(u_int usec)
-{
- int64_t counts, counts_per_usec;
- uint64_t first, last;
-
- /*
- * Check the timers are setup, if not just
- * use a for loop for the meantime
- */
- if (riscv_timer_sc == NULL) {
- for (; usec > 0; usec--)
- for (counts = 200; counts > 0; counts--)
- /*
- * Prevent the compiler from optimizing
- * out the loop
- */
- cpufunc_nullop();
- return;
- }
-
- /* Get the number of times to count */
- counts_per_usec = ((riscv_timer_timecount.tc_frequency / 1000000) + 1);
-
- /*
- * Clamp the timeout at a maximum value (about 32 seconds with
- * a 66MHz clock). *Nobody* should be delay()ing for anywhere
- * near that length of time and if they are, they should be hung
- * out to dry.
- */
- if (usec >= (0x80000000U / counts_per_usec))
- counts = (0x80000000U / counts_per_usec) - 1;
- else
- counts = usec * counts_per_usec;
-
- first = get_counts(riscv_timer_sc);
-
- while (counts > 0) {
- last = get_counts(riscv_timer_sc);
- counts -= (int64_t)(last - first);
- first = last;
- }
-}
-
-void
-riscv_timer_setstatclockrate(int newhz)
-{
- /* dummy: clockrate on riscv is fixed*/
-}
-
-/* is only called from secondary cpu */
-void
-riscv_timer_startclock()
-{
- struct riscv_timer_softc *sc = timer_cd.cd_devs[0];
- struct riscv_timer_pcpu_softc *pc =
- &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
- uint64_t nextevent;
-
- nextevent = get_cycles() + sc->sc_ticks_per_intr;
- pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent;
-
- riscv_intr_route(sc->sc_ih, 1, curcpu());
-
- sbi_set_timer(nextevent);
- csr_set(sie, SIE_STIE);
-}
-
-/*
- * called at early mainbus_attach, to provide delay func
- * before timer and interrupt is ready
- */
-void
-riscv_timer_init(void)
-{
- uint64_t cntfrq = 0;
-
- cntfrq = riscv_timer_get_freq();
-
- if (cntfrq != 0) {
- riscv_clock_register(NULL, riscv_timer_delay, NULL, NULL);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2020 Mars Li <mengshi.li.mars@gmail.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#ifndef _MACHINE_TIMER_H_
-#define _MACHINE_TIMER_H_
-
-int riscv_timer_match(struct device *, void *, void *);
-
-#endif /* _MACHINE_TIMER_H_ */
u_int32_t ci_ctrl; /* The CPU control register */
+ uint64_t ci_lasttb;
+ uint64_t ci_nexttimerevent;
+ uint64_t ci_nextstatevent;
+ int ci_statspending;
+
uint32_t ci_cpl;
uint32_t ci_ipending;
uint32_t ci_idepth;
--- /dev/null
+/* $OpenBSD: clock.c,v 1.1 2021/05/04 16:38:06 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
+ * Copyright (c) 2003 Dale Rahn <drahn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/evcount.h>
+#include <sys/timetc.h>
+
+#include <machine/cpufunc.h>
+#include <machine/sbi.h>
+
+#include <riscv64/dev/riscv_cpu_intc.h>
+
+extern uint32_t tb_freq; /* machdep.c */
+
+uint64_t tick_increment;
+uint64_t statmin;
+uint32_t statvar;
+
+struct evcount clock_count;
+struct evcount stat_count;
+
+u_int tb_get_timecount(struct timecounter *);
+
+static struct timecounter tb_timecounter = {
+ .tc_get_timecount = tb_get_timecount,
+ .tc_poll_pps = NULL,
+ .tc_counter_mask = 0xffffffff,
+ .tc_frequency = 0,
+ .tc_name = "tb",
+ .tc_quality = 0,
+ .tc_priv = NULL,
+};
+
+void cpu_startclock(void);
+int clock_intr(void *);
+
+u_int
+tb_get_timecount(struct timecounter *tc)
+{
+ return rdtime();
+}
+
+void
+cpu_initclocks(void)
+{
+ tick_increment = tb_freq / hz;
+
+ stathz = 100;
+ profhz = 1000; /* must be a multiple of stathz */
+
+ setstatclockrate(stathz);
+
+ riscv_intc_intr_establish(IRQ_TIMER_SUPERVISOR, 0,
+ clock_intr, NULL, NULL);
+
+ evcount_attach(&clock_count, "clock", NULL);
+ evcount_attach(&stat_count, "stat", NULL);
+
+ cpu_startclock();
+
+ tb_timecounter.tc_frequency = tb_freq;
+ tc_init(&tb_timecounter);
+}
+
+void
+cpu_startclock(void)
+{
+ struct cpu_info *ci = curcpu();
+ uint64_t nextevent;
+
+ ci->ci_lasttb = rdtime();
+ ci->ci_nexttimerevent = ci->ci_lasttb + tick_increment;
+ nextevent = ci->ci_nextstatevent = ci->ci_nexttimerevent;
+
+ sbi_set_timer(nextevent);
+ csr_set(sie, SIE_STIE);
+}
+
+int
+clock_intr(void *frame)
+{
+ struct cpu_info *ci = curcpu();
+ uint64_t tb, prevtb;
+ uint64_t nextevent;
+ uint32_t r;
+ int nstats;
+ int s;
+
+ /*
+ * Based on the actual time delay since the last clock interrupt,
+ * we arrange for earlier interrupt next time.
+ */
+
+ tb = rdtime();
+
+ while (ci->ci_nexttimerevent <= tb)
+ ci->ci_nexttimerevent += tick_increment;
+
+ prevtb = ci->ci_nexttimerevent - tick_increment;
+
+ for (nstats = 0; ci->ci_nextstatevent <= tb; nstats++) {
+ do {
+ r = random() & (statvar - 1);
+ } while (r == 0); /* random == 0 not allowed */
+ ci->ci_nextstatevent += statmin + r;
+ }
+ stat_count.ec_count += nstats;
+
+ if (ci->ci_nexttimerevent < ci->ci_nextstatevent)
+ nextevent = ci->ci_nexttimerevent;
+ else
+ nextevent = ci->ci_nextstatevent;
+
+ sbi_set_timer(nextevent);
+
+ if (ci->ci_cpl >= IPL_CLOCK) {
+ ci->ci_statspending += nstats;
+ } else {
+ nstats += ci->ci_statspending;
+ ci->ci_statspending = 0;
+
+ s = splclock();
+ intr_enable();
+
+ /*
+ * Do standard timer interrupt stuff.
+ */
+ while (ci->ci_lasttb < prevtb) {
+ ci->ci_lasttb += tick_increment;
+ clock_count.ec_count++;
+ hardclock((struct clockframe *)frame);
+ }
+
+ while (nstats-- > 0)
+ statclock((struct clockframe *)frame);
+
+ intr_disable();
+ splx(s);
+ }
+
+ return 0;
+}
+
+void
+setstatclockrate(int newhz)
+{
+ uint64_t stat_increment;
+ uint64_t min_increment;
+ uint32_t var;
+ u_long msr;
+
+ msr = intr_disable();
+
+ stat_increment = tb_freq / newhz;
+ var = 0x40000000; /* really big power of two */
+ /* Find largest 2^n which is nearly smaller than statint/2. */
+ min_increment = stat_increment / 2 + 100;
+ while (var > min_increment)
+ var >>= 1;
+
+ /* Not atomic, but we can probably live with that. */
+ statmin = stat_increment - (var >> 1);
+ statvar = var;
+
+ intr_restore(msr);
+}
+
+void
+delay(u_int us)
+{
+ uint64_t tb;
+
+ tb = rdtime();
+ tb += (us * tb_freq + 999999) / 1000000;
+ while (tb > rdtime())
+ continue;
+}
#include <machine/fdt.h>
#include <machine/riscvreg.h>
-#include "../dev/timer.h"
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
cpu_cpuspeed = cpu_clockspeed;
}
- /*
- * attach cpu-embedded timer
- * Trick: timer has no fdt node to match,
- * riscv_timer_match will always return 1 at first call,
- * and return 0 for all following calls,
- * therefore, must attach timer before any node
- */
- config_found_sm(dev, NULL, NULL, riscv_timer_match);
-
/*
* attach cpu's children node, so far there is only the
* cpu-embedded interrupt controller
}
#endif
-/*
- ********* timer interrupt relevant **************
- */
-
-void riscv_dflt_delay(u_int usecs);
-
-struct {
- void (*delay)(u_int);
- void (*initclocks)(void);
- void (*setstatclockrate)(int);
- void (*mpstartclock)(void);
-} riscv_clock_func = {
- riscv_dflt_delay,
- NULL,
- NULL,
- NULL
-};
-
-void
-riscv_clock_register(void (*initclock)(void), void (*delay)(u_int),
- void (*statclock)(int), void(*mpstartclock)(void))
-{
- if (riscv_clock_func.initclocks)
- return;
-
- riscv_clock_func.initclocks = initclock;
- riscv_clock_func.delay = delay;
- riscv_clock_func.setstatclockrate = statclock;
- riscv_clock_func.mpstartclock = mpstartclock;
-}
-
-void
-delay(u_int usec)
-{
- riscv_clock_func.delay(usec);
-}
-
-void
-cpu_initclocks(void)
-{
- if (riscv_clock_func.initclocks == NULL)
- panic("initclocks function not initialized yet");
-
- riscv_clock_func.initclocks();
-}
-
-void
-cpu_startclock(void)
-{
- if (riscv_clock_func.mpstartclock == NULL)
- panic("startclock function not initialized yet");
-
- riscv_clock_func.mpstartclock();
-}
-
-void
-riscv_dflt_delay(u_int usecs)
-{
- int j;
- /* BAH - there is no good way to make this close */
- /* but this isn't supposed to be used after the real clock attaches */
- for (; usecs > 0; usecs--)
- for (j = 100; j > 0; j--)
- ;
-}
-
-void
-setstatclockrate(int new)
-{
- if (riscv_clock_func.setstatclockrate == NULL) {
- panic("riscv_clock_func.setstatclockrate not initialized");
- }
- riscv_clock_func.setstatclockrate(new);
-}
-
void
intr_barrier(void *ih)
{
struct cpu_info cpu_info_primary;
struct cpu_info *cpu_info[MAXCPUS] = { &cpu_info_primary };
+uint32_t tb_freq = 1000000;
+
struct fdt_reg memreg[VM_PHYSSEG_MAX];
int nmemreg;
struct fdt_reg reg;
void *node;
+ node = fdt_find_node("/cpus");
+ if (node != NULL) {
+ char *prop;
+ int len;
+
+ len = fdt_node_property(node, "timebase-frequency", &prop);
+ if (len == sizeof(tb_freq))
+ tb_freq = bemtoh32((uint32_t *)prop);
+ }
+
node = fdt_find_node("/chosen");
if (node != NULL) {
char *prop;