1*b856448bSskrll /* $NetBSD: clint_fdt.c,v 1.2 2023/07/26 06:36:34 skrll Exp $ */
275b842b8Sskrll
375b842b8Sskrll /*-
475b842b8Sskrll * Copyright (c) 2023 The NetBSD Foundation, Inc.
575b842b8Sskrll * All rights reserved.
675b842b8Sskrll *
775b842b8Sskrll * This code is derived from software contributed to The NetBSD Foundation
875b842b8Sskrll * by Nick Hudson
975b842b8Sskrll *
1075b842b8Sskrll * Redistribution and use in source and binary forms, with or without
1175b842b8Sskrll * modification, are permitted provided that the following conditions
1275b842b8Sskrll * are met:
1375b842b8Sskrll * 1. Redistributions of source code must retain the above copyright
1475b842b8Sskrll * notice, this list of conditions and the following disclaimer.
1575b842b8Sskrll * 2. Redistributions in binary form must reproduce the above copyright
1675b842b8Sskrll * notice, this list of conditions and the following disclaimer in the
1775b842b8Sskrll * documentation and/or other materials provided with the distribution.
1875b842b8Sskrll *
1975b842b8Sskrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2075b842b8Sskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2175b842b8Sskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2275b842b8Sskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2375b842b8Sskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2475b842b8Sskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2575b842b8Sskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2675b842b8Sskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2775b842b8Sskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2875b842b8Sskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2975b842b8Sskrll * POSSIBILITY OF SUCH DAMAGE.
3075b842b8Sskrll */
3175b842b8Sskrll
3275b842b8Sskrll #include <sys/cdefs.h>
33*b856448bSskrll __KERNEL_RCSID(0, "$NetBSD: clint_fdt.c,v 1.2 2023/07/26 06:36:34 skrll Exp $");
3475b842b8Sskrll
3575b842b8Sskrll #include <sys/param.h>
3675b842b8Sskrll
3775b842b8Sskrll #include <sys/bus.h>
3875b842b8Sskrll #include <sys/cpu.h>
3975b842b8Sskrll #include <sys/device.h>
4075b842b8Sskrll #include <sys/evcnt.h>
4175b842b8Sskrll #include <sys/intr.h>
4275b842b8Sskrll
4375b842b8Sskrll #include <dev/fdt/fdtvar.h>
4475b842b8Sskrll
4575b842b8Sskrll #include <riscv/fdt/riscv_fdtvar.h>
4675b842b8Sskrll
4775b842b8Sskrll #include <machine/sysreg.h>
4875b842b8Sskrll
4975b842b8Sskrll
5075b842b8Sskrll #define CLINT_MSIP_HARTN(n) 0x0000 + 4 * (n)
5175b842b8Sskrll #define CLINT_MTIMECMP_HARTN(n) 0x4000 + 8 * (n)
5275b842b8Sskrll #define CLINT_MTIME 0xbff8
5375b842b8Sskrll #define CLINT_MTIME_LO CLINT_MTIME + 0x0
5475b842b8Sskrll #define CLINT_MTIME_HI CLINT_MTIME + 0x4
5575b842b8Sskrll
5675b842b8Sskrll static const struct device_compatible_entry compat_data[] = {
5775b842b8Sskrll { .compat = "riscv,clint0" },
5875b842b8Sskrll DEVICE_COMPAT_EOL
5975b842b8Sskrll };
6075b842b8Sskrll
6175b842b8Sskrll struct clint_fdt_softc {
6275b842b8Sskrll device_t sc_dev;
6375b842b8Sskrll bus_space_tag_t sc_bst;
6475b842b8Sskrll bus_space_handle_t sc_bsh;
6575b842b8Sskrll
6675b842b8Sskrll uint64_t sc_timebase_frequency;
6775b842b8Sskrll uint64_t sc_ticks_per_hz;
6875b842b8Sskrll };
6975b842b8Sskrll
7075b842b8Sskrll static struct clint_fdt_softc *clint_sc;
7175b842b8Sskrll
7275b842b8Sskrll #define CLINT_READ(sc, reg) \
7375b842b8Sskrll bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
7475b842b8Sskrll #define CLINT_WRITE(sc, reg, val) \
7575b842b8Sskrll bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
7675b842b8Sskrll
7775b842b8Sskrll static int
clint_ipi_handler(void * arg)7875b842b8Sskrll clint_ipi_handler(void *arg)
7975b842b8Sskrll {
8075b842b8Sskrll cpu_Debugger();
8175b842b8Sskrll return 1;
8275b842b8Sskrll }
8375b842b8Sskrll
8475b842b8Sskrll
8575b842b8Sskrll static uint64_t
clint_time_read(struct clint_fdt_softc * sc)8675b842b8Sskrll clint_time_read(struct clint_fdt_softc *sc)
8775b842b8Sskrll {
8875b842b8Sskrll #if _LP64
8975b842b8Sskrll return bus_space_read_8(sc->sc_bst, sc->sc_bsh, CLINT_MTIME);
9075b842b8Sskrll #else
9175b842b8Sskrll uint32_t hi, lo;
9275b842b8Sskrll do {
9375b842b8Sskrll hi = CLINT_READ(sc, CLINT_MTIME_HI);
9475b842b8Sskrll lo = CLINT_READ(sc, CLINT_MTIME_LO);
9575b842b8Sskrll
9675b842b8Sskrll } while (hi != CLINT_READ(sc, CLINT_MTIME_HI));
9775b842b8Sskrll return
9875b842b8Sskrll __SHIFTIN(hi, __BITS(63, 32)) |
9975b842b8Sskrll __SHIFTIN(lo, __BITS(31, 0));
10075b842b8Sskrll #endif
10175b842b8Sskrll }
10275b842b8Sskrll
10375b842b8Sskrll
10475b842b8Sskrll static void
clint_timer_set(struct clint_fdt_softc * sc,struct cpu_info * ci)10575b842b8Sskrll clint_timer_set(struct clint_fdt_softc *sc, struct cpu_info *ci)
10675b842b8Sskrll {
10775b842b8Sskrll const uint64_t now = clint_time_read(sc);
10875b842b8Sskrll
10975b842b8Sskrll ci->ci_lastintr = now;
11075b842b8Sskrll ci->ci_ev_timer.ev_count++;
11175b842b8Sskrll
11275b842b8Sskrll ci->ci_lastintr_scheduled += sc->sc_ticks_per_hz;
11375b842b8Sskrll
11475b842b8Sskrll CLINT_WRITE(sc, CLINT_MTIMECMP_HARTN(ci->ci_cpuid),
11575b842b8Sskrll ci->ci_lastintr_scheduled);
11675b842b8Sskrll }
11775b842b8Sskrll
11875b842b8Sskrll static int
clint_timer_intr(void * arg)11975b842b8Sskrll clint_timer_intr(void *arg)
12075b842b8Sskrll {
12175b842b8Sskrll struct cpu_info * const ci = curcpu();
12275b842b8Sskrll struct clockframe * const cf = arg;
12375b842b8Sskrll struct clint_fdt_softc * const sc = clint_sc;
12475b842b8Sskrll
12575b842b8Sskrll csr_sip_clear(SIP_STIP); /* clean pending interrupt status */
12675b842b8Sskrll
12775b842b8Sskrll clint_timer_set(sc, ci);
12875b842b8Sskrll
12975b842b8Sskrll hardclock(cf);
13075b842b8Sskrll
13175b842b8Sskrll return 1;
13275b842b8Sskrll }
13375b842b8Sskrll
13475b842b8Sskrll static void
clint_cpu_initclocks(void)13575b842b8Sskrll clint_cpu_initclocks(void)
13675b842b8Sskrll {
13775b842b8Sskrll struct cpu_info * const ci = curcpu();
13875b842b8Sskrll struct clint_fdt_softc * const sc = clint_sc;
13975b842b8Sskrll
14075b842b8Sskrll clint_timer_set(sc, ci);
14175b842b8Sskrll
14275b842b8Sskrll csr_sie_set(SIE_STIE); /* enable supervisor timer intr */
14375b842b8Sskrll }
14475b842b8Sskrll
14575b842b8Sskrll static int
clint_match(device_t parent,cfdata_t cf,void * aux)14675b842b8Sskrll clint_match(device_t parent, cfdata_t cf, void *aux)
14775b842b8Sskrll {
14875b842b8Sskrll struct fdt_attach_args * const faa = aux;
14975b842b8Sskrll
15075b842b8Sskrll return of_compatible_match(faa->faa_phandle, compat_data);
15175b842b8Sskrll }
15275b842b8Sskrll
15375b842b8Sskrll static void
clint_attach(device_t parent,device_t self,void * aux)15475b842b8Sskrll clint_attach(device_t parent, device_t self, void *aux)
15575b842b8Sskrll {
15675b842b8Sskrll struct clint_fdt_softc * const sc = device_private(self);
15775b842b8Sskrll const struct fdt_attach_args * const faa = aux;
15875b842b8Sskrll const int phandle = faa->faa_phandle;
15975b842b8Sskrll bus_addr_t addr;
16075b842b8Sskrll bus_size_t size;
16175b842b8Sskrll
16275b842b8Sskrll if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
16375b842b8Sskrll aprint_error(": couldn't get registers\n");
16475b842b8Sskrll return;
16575b842b8Sskrll }
16675b842b8Sskrll
16775b842b8Sskrll const int cpus = OF_finddevice("/cpus");
16875b842b8Sskrll if (cpus == -1) {
16975b842b8Sskrll aprint_error(": couldn't get 'cpus' node\n");
17075b842b8Sskrll return;
17175b842b8Sskrll }
17275b842b8Sskrll
17375b842b8Sskrll uint32_t tbfreq;
17475b842b8Sskrll int ret = of_getprop_uint32(cpus, "timebase-frequency", &tbfreq);
17575b842b8Sskrll if (ret < 0) {
17675b842b8Sskrll aprint_error(": can't get timebase frequency\n");
17775b842b8Sskrll return;
17875b842b8Sskrll }
17975b842b8Sskrll
18075b842b8Sskrll sc->sc_dev = self;
18175b842b8Sskrll sc->sc_bst = faa->faa_bst;
18275b842b8Sskrll sc->sc_timebase_frequency = tbfreq;
18375b842b8Sskrll
18475b842b8Sskrll if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
18575b842b8Sskrll aprint_error(": couldn't map registers\n");
18675b842b8Sskrll return;
18775b842b8Sskrll }
18875b842b8Sskrll
18975b842b8Sskrll printf("%s: addr %#" PRIxBUSADDR " size %#" PRIxBUSSIZE "\n", __func__,
19075b842b8Sskrll addr, size);
19175b842b8Sskrll
19275b842b8Sskrll aprint_naive("\n");
19375b842b8Sskrll aprint_normal(": local interrupt control. %" PRId64 " KHz timer.\n",
19475b842b8Sskrll sc->sc_timebase_frequency / 1000);
19575b842b8Sskrll
19675b842b8Sskrll sc->sc_ticks_per_hz = sc->sc_timebase_frequency / hz;
19775b842b8Sskrll int len;
19875b842b8Sskrll const uint32_t *data =
19975b842b8Sskrll fdtbus_get_prop(phandle, "interrupts-extended", &len);
20075b842b8Sskrll if (data == NULL) {
20175b842b8Sskrll aprint_error_dev(self, "couldn't get context data\n");
20275b842b8Sskrll return;
20375b842b8Sskrll }
20475b842b8Sskrll
20575b842b8Sskrll clint_sc = sc;
20675b842b8Sskrll // riscv_fdt_timer_register(clint_cpu_initclocks);
20775b842b8Sskrll
20875b842b8Sskrll int context = 0;
20975b842b8Sskrll while (len > 0) {
21075b842b8Sskrll const int pphandle = be32toh(data[0]);
21175b842b8Sskrll const int xref = fdtbus_get_phandle_from_native(pphandle);
21275b842b8Sskrll const int intr_source = be32toh(data[1]);
21375b842b8Sskrll uint32_t intr_cells;
21475b842b8Sskrll
21575b842b8Sskrll int error =
21675b842b8Sskrll of_getprop_uint32(xref, "#interrupt-cells", &intr_cells);
21775b842b8Sskrll if (error) {
21875b842b8Sskrll aprint_error_dev(self, "couldn't get cell length "
21975b842b8Sskrll "for parent CPU for context %d", context);
22075b842b8Sskrll return;
22175b842b8Sskrll }
22275b842b8Sskrll int (*handler)(void *) = NULL;
22375b842b8Sskrll void *arg = NULL;
22475b842b8Sskrll int ipl = IPL_HIGH;
22575b842b8Sskrll switch (intr_source) {
22675b842b8Sskrll case IRQ_MACHINE_SOFTWARE:
22775b842b8Sskrll handler = clint_ipi_handler;
22875b842b8Sskrll arg = sc;
22975b842b8Sskrll break;
23075b842b8Sskrll case IRQ_MACHINE_TIMER:
23175b842b8Sskrll handler = clint_timer_intr;
23275b842b8Sskrll arg = NULL; /* clock frame */
23375b842b8Sskrll ipl = IPL_SCHED;
23475b842b8Sskrll break;
23575b842b8Sskrll default:
23675b842b8Sskrll aprint_error_dev(self, "unknown interrupt source %d",
23775b842b8Sskrll intr_source);
23875b842b8Sskrll }
23975b842b8Sskrll
24075b842b8Sskrll if (handler) {
24175b842b8Sskrll void *ih = fdtbus_intr_establish_xname(phandle,
24275b842b8Sskrll context, ipl, FDT_INTR_MPSAFE,
24375b842b8Sskrll handler, arg, device_xname(self));
24475b842b8Sskrll if (ih == NULL) {
24575b842b8Sskrll aprint_error_dev(self, "couldn't install "
24675b842b8Sskrll "interrupt handler\n");
24775b842b8Sskrll } else {
24875b842b8Sskrll char intrstr[128];
24975b842b8Sskrll bool ok = fdtbus_intr_str(phandle, context,
25075b842b8Sskrll intrstr, sizeof(intrstr));
25175b842b8Sskrll aprint_verbose_dev(self, "interrupt %s handler "
25275b842b8Sskrll "installed\n", ok ? intrstr : "(unk)");
25375b842b8Sskrll }
25475b842b8Sskrll }
25575b842b8Sskrll
25675b842b8Sskrll len -= (intr_cells + 1) * 4;
25775b842b8Sskrll data += (intr_cells + 1);
25875b842b8Sskrll context++;
25975b842b8Sskrll }
26075b842b8Sskrll }
26175b842b8Sskrll
26275b842b8Sskrll CFATTACH_DECL_NEW(clint_fdt, sizeof(struct clint_fdt_softc),
26375b842b8Sskrll clint_match, clint_attach, NULL, NULL);
264