xref: /netbsd-src/sys/arch/riscv/fdt/clint_fdt.c (revision b856448b7957f3789b8bddb70c3fc8f553519db6)
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