xref: /openbsd-src/sys/arch/powerpc64/dev/xicp.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /*	$OpenBSD: xicp.c,v 1.5 2022/04/06 18:59:27 naddy Exp $	*/
29eafc29eSkettenis /*
39eafc29eSkettenis  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
49eafc29eSkettenis  *
59eafc29eSkettenis  * Permission to use, copy, modify, and distribute this software for any
69eafc29eSkettenis  * purpose with or without fee is hereby granted, provided that the above
79eafc29eSkettenis  * copyright notice and this permission notice appear in all copies.
89eafc29eSkettenis  *
99eafc29eSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109eafc29eSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119eafc29eSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129eafc29eSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139eafc29eSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149eafc29eSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159eafc29eSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169eafc29eSkettenis  */
179eafc29eSkettenis 
189eafc29eSkettenis #include <sys/param.h>
199eafc29eSkettenis #include <sys/systm.h>
209eafc29eSkettenis #include <sys/device.h>
219eafc29eSkettenis #include <sys/evcount.h>
229eafc29eSkettenis #include <sys/malloc.h>
239eafc29eSkettenis #include <sys/queue.h>
249eafc29eSkettenis 
259eafc29eSkettenis #include <machine/bus.h>
269eafc29eSkettenis #include <machine/fdt.h>
279eafc29eSkettenis #include <machine/opal.h>
289eafc29eSkettenis 
299eafc29eSkettenis #include <dev/ofw/openfirm.h>
309eafc29eSkettenis #include <dev/ofw/fdt.h>
319eafc29eSkettenis 
329eafc29eSkettenis #define XICP_NUM_IRQS	1024
339eafc29eSkettenis 
349eafc29eSkettenis #define XICP_CPPR		0x04
359eafc29eSkettenis #define XICP_XIRR		0x04
369eafc29eSkettenis #define  XICP_XIRR_XISR_MASK	0x00ffffff
379eafc29eSkettenis #define  XICP_XIRR_CPPR_SHIFT	24
389eafc29eSkettenis #define XICP_MFRR		0x0c
399eafc29eSkettenis 
409eafc29eSkettenis static inline uint8_t
xicp_prio(int ipl)419eafc29eSkettenis xicp_prio(int ipl)
429eafc29eSkettenis {
439eafc29eSkettenis 	return ((IPL_IPI - ipl) > 7 ? 0xff : IPL_IPI - ipl);
449eafc29eSkettenis }
459eafc29eSkettenis 
469eafc29eSkettenis struct intrhand {
479eafc29eSkettenis 	LIST_ENTRY(intrhand)	ih_hash;
489eafc29eSkettenis 	int			(*ih_func)(void *);
499eafc29eSkettenis 	void			*ih_arg;
509eafc29eSkettenis 	int			ih_ipl;
519eafc29eSkettenis 	int			ih_flags;
529eafc29eSkettenis 	uint32_t		ih_girq;
539eafc29eSkettenis 	struct evcount		ih_count;
549eafc29eSkettenis 	const char		*ih_name;
559eafc29eSkettenis };
569eafc29eSkettenis 
579eafc29eSkettenis struct xicp_softc {
589eafc29eSkettenis 	struct device		sc_dev;
599eafc29eSkettenis 	bus_space_tag_t		sc_iot;
609eafc29eSkettenis 	bus_space_handle_t	sc_ioh;
619eafc29eSkettenis };
629eafc29eSkettenis 
639eafc29eSkettenis struct xicp_softc *xicp_sc[MAXCPUS];
649eafc29eSkettenis 
659eafc29eSkettenis /* Hash table for interrupt handlers. */
669eafc29eSkettenis #define XICP_GIRQHASH(girq)	(&xicp_girqhashtbl[(girq) & xicp_girqhash])
679eafc29eSkettenis LIST_HEAD(,intrhand) *xicp_girqhashtbl;
689eafc29eSkettenis u_long	xicp_girqhash;
699eafc29eSkettenis 
709eafc29eSkettenis static inline void
xicp_write_1(struct xicp_softc * sc,bus_size_t off,uint8_t val)719eafc29eSkettenis xicp_write_1(struct xicp_softc *sc, bus_size_t off, uint8_t val)
729eafc29eSkettenis {
739eafc29eSkettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
749eafc29eSkettenis }
759eafc29eSkettenis 
769eafc29eSkettenis static inline uint32_t
xicp_read_4(struct xicp_softc * sc,bus_size_t off)779eafc29eSkettenis xicp_read_4(struct xicp_softc *sc, bus_size_t off)
789eafc29eSkettenis {
799eafc29eSkettenis 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
809eafc29eSkettenis }
819eafc29eSkettenis 
829eafc29eSkettenis static inline void
xicp_write_4(struct xicp_softc * sc,bus_size_t off,uint32_t val)839eafc29eSkettenis xicp_write_4(struct xicp_softc *sc, bus_size_t off, uint32_t val)
849eafc29eSkettenis {
859eafc29eSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
869eafc29eSkettenis }
879eafc29eSkettenis 
889eafc29eSkettenis int	xicp_match(struct device *, void *, void *);
899eafc29eSkettenis void	xicp_attach(struct device *, struct device *, void *);
909eafc29eSkettenis 
91*471aeecfSnaddy const struct cfattach xicp_ca = {
929eafc29eSkettenis 	sizeof (struct xicp_softc), xicp_match, xicp_attach
939eafc29eSkettenis };
949eafc29eSkettenis 
959eafc29eSkettenis struct cfdriver xicp_cd = {
969eafc29eSkettenis 	NULL, "xicp", DV_DULL
979eafc29eSkettenis };
989eafc29eSkettenis 
999eafc29eSkettenis void	xicp_exi(struct trapframe *);
100a940e3deSkettenis void 	*xicp_intr_establish(uint32_t, int, int, struct cpu_info *,
1019eafc29eSkettenis 	    int (*)(void *), void *, const char *);
1029eafc29eSkettenis void	xicp_intr_send_ipi(void *);
1039eafc29eSkettenis void	xicp_setipl(int);
1049eafc29eSkettenis 
1059eafc29eSkettenis int
xicp_match(struct device * parent,void * match,void * aux)1069eafc29eSkettenis xicp_match(struct device *parent, void *match, void *aux)
1079eafc29eSkettenis {
1089eafc29eSkettenis 	struct fdt_attach_args *faa = aux;
1099eafc29eSkettenis 
1109eafc29eSkettenis 	return (OF_is_compatible(faa->fa_node, "ibm,ppc-xicp") ||
1119eafc29eSkettenis 	    OF_is_compatible(faa->fa_node, "IBM,ppc-xicp"));
1129eafc29eSkettenis }
1139eafc29eSkettenis 
1149eafc29eSkettenis void
xicp_attach(struct device * parent,struct device * self,void * aux)1159eafc29eSkettenis xicp_attach(struct device *parent, struct device *self, void *aux)
1169eafc29eSkettenis {
1179eafc29eSkettenis 	struct xicp_softc *sc = (struct xicp_softc *)self;
1189eafc29eSkettenis 	struct fdt_attach_args *faa = aux;
1199eafc29eSkettenis 	struct cpu_info *ci;
1209eafc29eSkettenis 	CPU_INFO_ITERATOR cii;
1219eafc29eSkettenis 	uint32_t ranges[2];
1229eafc29eSkettenis 
1239eafc29eSkettenis 	if (faa->fa_nreg < 1) {
1249eafc29eSkettenis 		printf(": no registers\n");
1259eafc29eSkettenis 		return;
1269eafc29eSkettenis 	}
1279eafc29eSkettenis 
1289eafc29eSkettenis 	ranges[0] = ranges[1] = 0;
1299eafc29eSkettenis 	OF_getpropintarray(faa->fa_node, "ibm,interrupt-server-ranges",
1309eafc29eSkettenis 	    ranges, sizeof(ranges));
1319eafc29eSkettenis 	if (ranges[1] == 0)
1329eafc29eSkettenis 		return;
1339eafc29eSkettenis 
1349eafc29eSkettenis 	/*
1359eafc29eSkettenis 	 * There is supposed to be one ICP node for each core.  Since
1369eafc29eSkettenis 	 * we only support a single thread, we only need to map the
1379eafc29eSkettenis 	 * first set of registers.
1389eafc29eSkettenis 	 */
1399eafc29eSkettenis 	sc->sc_iot = faa->fa_iot;
1409eafc29eSkettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1419eafc29eSkettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1429eafc29eSkettenis 		printf(": can't map registers\n");
1439eafc29eSkettenis 		return;
1449eafc29eSkettenis 	}
1459eafc29eSkettenis 
1469eafc29eSkettenis 	printf("\n");
1479eafc29eSkettenis 
1489eafc29eSkettenis 	/*
1499eafc29eSkettenis 	 * Allocate global hash table for interrupt handlers if we
1509eafc29eSkettenis 	 * haven't done so already.
1519eafc29eSkettenis 	 */
1529eafc29eSkettenis 	if (xicp_girqhash == 0) {
1539eafc29eSkettenis 		xicp_girqhashtbl = hashinit(XICP_NUM_IRQS,
1549eafc29eSkettenis 		    M_DEVBUF, M_WAITOK, &xicp_girqhash);
1559eafc29eSkettenis 	}
1569eafc29eSkettenis 
1579eafc29eSkettenis 	CPU_INFO_FOREACH(cii, ci) {
1589eafc29eSkettenis 		if (ranges[0] == ci->ci_pir)
1599eafc29eSkettenis 			xicp_sc[ci->ci_cpuid] = sc;
1609eafc29eSkettenis 	}
1619eafc29eSkettenis 
1629eafc29eSkettenis 	_exi = xicp_exi;
1639eafc29eSkettenis 	_intr_establish = xicp_intr_establish;
1649eafc29eSkettenis 	_intr_send_ipi = xicp_intr_send_ipi;
1659eafc29eSkettenis 	_setipl = xicp_setipl;
1669eafc29eSkettenis 
1679eafc29eSkettenis 	/* Synchronize hardware state to software state. */
1689eafc29eSkettenis 	xicp_write_1(sc, XICP_CPPR, xicp_prio(curcpu()->ci_cpl));
1699eafc29eSkettenis }
1709eafc29eSkettenis 
1719eafc29eSkettenis void
xicp_intr_send_ipi(void * cookie)1729eafc29eSkettenis xicp_intr_send_ipi(void *cookie)
1739eafc29eSkettenis {
1749eafc29eSkettenis 	panic("%s", __func__);
1759eafc29eSkettenis }
1769eafc29eSkettenis 
1779eafc29eSkettenis void *
xicp_intr_establish(uint32_t girq,int type,int level,struct cpu_info * ci,int (* func)(void *),void * arg,const char * name)178a940e3deSkettenis xicp_intr_establish(uint32_t girq, int type, int level, struct cpu_info *ci,
1799eafc29eSkettenis     int (*func)(void *), void *arg, const char *name)
1809eafc29eSkettenis {
1819eafc29eSkettenis 	struct intrhand *ih;
1829eafc29eSkettenis 	int64_t error;
1839eafc29eSkettenis 	uint16_t server;
1849eafc29eSkettenis 
185a4fe4c27Skettenis 	if (ci == NULL)
186a4fe4c27Skettenis 		ci = cpu_info_primary;
187a4fe4c27Skettenis 
1889eafc29eSkettenis 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
1899eafc29eSkettenis 	ih->ih_func = func;
1909eafc29eSkettenis 	ih->ih_arg = arg;
1919eafc29eSkettenis 	ih->ih_ipl = level & IPL_IRQMASK;
1929eafc29eSkettenis 	ih->ih_flags = level & IPL_FLAGMASK;
1939eafc29eSkettenis 	ih->ih_girq = girq;
1949eafc29eSkettenis 	ih->ih_name = name;
1959eafc29eSkettenis 	LIST_INSERT_HEAD(XICP_GIRQHASH(girq), ih, ih_hash);
1969eafc29eSkettenis 
1979eafc29eSkettenis 	if (name != NULL)
1989eafc29eSkettenis 		evcount_attach(&ih->ih_count, name, &ih->ih_girq);
1999eafc29eSkettenis 
2009eafc29eSkettenis 	server = ci->ci_pir << 2;
2019eafc29eSkettenis 	error = opal_set_xive(girq, server, xicp_prio(level & IPL_IRQMASK));
2029eafc29eSkettenis 	if (error != OPAL_SUCCESS) {
2039eafc29eSkettenis 		if (name)
2049eafc29eSkettenis 			evcount_detach(&ih->ih_count);
2059eafc29eSkettenis 		LIST_REMOVE(ih, ih_hash);
206db454026Sjsg 		free(ih, M_DEVBUF, sizeof(*ih));
2079eafc29eSkettenis 		return NULL;
2089eafc29eSkettenis 	}
2099eafc29eSkettenis 
2109eafc29eSkettenis 	return ih;
2119eafc29eSkettenis }
2129eafc29eSkettenis 
2139eafc29eSkettenis void
xicp_setipl(int new)2149eafc29eSkettenis xicp_setipl(int new)
2159eafc29eSkettenis {
2169eafc29eSkettenis 	struct xicp_softc *sc = xicp_sc[cpu_number()];
2179eafc29eSkettenis 	struct cpu_info *ci = curcpu();
2189eafc29eSkettenis 	uint8_t oldprio = xicp_prio(ci->ci_cpl);
2199eafc29eSkettenis 	uint8_t newprio = xicp_prio(new);
2209eafc29eSkettenis 	u_long msr;
2219eafc29eSkettenis 
2229eafc29eSkettenis 	msr = intr_disable();
2239eafc29eSkettenis 	ci->ci_cpl = new;
2249eafc29eSkettenis 	if (newprio != oldprio)
2259eafc29eSkettenis 		xicp_write_1(sc, XICP_CPPR, newprio);
2269eafc29eSkettenis 	intr_restore(msr);
2279eafc29eSkettenis }
2289eafc29eSkettenis 
2299eafc29eSkettenis void
xicp_exi(struct trapframe * frame)2309eafc29eSkettenis xicp_exi(struct trapframe *frame)
2319eafc29eSkettenis {
2329eafc29eSkettenis 	struct xicp_softc *sc = xicp_sc[cpu_number()];
2339eafc29eSkettenis 	struct cpu_info *ci = curcpu();
2349eafc29eSkettenis 	struct intrhand *ih;
2359eafc29eSkettenis 	uint32_t xirr, xisr;
2369eafc29eSkettenis 	int handled, old;
2379eafc29eSkettenis 
2389eafc29eSkettenis 	KASSERT(sc);
2399eafc29eSkettenis 
2409eafc29eSkettenis 	old = ci->ci_cpl;
2419eafc29eSkettenis 
2429eafc29eSkettenis 	while (1) {
2439eafc29eSkettenis 		xirr = xicp_read_4(sc, XICP_XIRR);
2449eafc29eSkettenis 		xisr = xirr & XICP_XIRR_XISR_MASK;
2459eafc29eSkettenis 
2469eafc29eSkettenis 		if (xisr == 0)
2479eafc29eSkettenis 			break;
2489eafc29eSkettenis 
2499eafc29eSkettenis 		/* Lookup the interrupt handle in the has table. */
2509eafc29eSkettenis 		LIST_FOREACH(ih, XICP_GIRQHASH(xisr), ih_hash) {
2519eafc29eSkettenis 			if (ih->ih_girq == xisr)
2529eafc29eSkettenis 				break;
2539eafc29eSkettenis 		}
2549eafc29eSkettenis 
2559eafc29eSkettenis 		if (ih != NULL) {
2569eafc29eSkettenis #ifdef MULTIPROCESSOR
2579eafc29eSkettenis 			int need_lock;
2589eafc29eSkettenis 
2599eafc29eSkettenis 			if (ih->ih_flags & IPL_MPSAFE)
2609eafc29eSkettenis 				need_lock = 0;
2619eafc29eSkettenis 			else
2629eafc29eSkettenis 				need_lock = (ih->ih_ipl < IPL_SCHED);
2639eafc29eSkettenis 
2649eafc29eSkettenis 			if (need_lock)
2659eafc29eSkettenis 				KERNEL_LOCK();
2669eafc29eSkettenis #endif
2679eafc29eSkettenis 			ci->ci_cpl = ih->ih_ipl;
2689eafc29eSkettenis 			xicp_write_1(sc, XICP_CPPR, xicp_prio(ih->ih_ipl));
2699eafc29eSkettenis 
2709eafc29eSkettenis 			intr_enable();
2719eafc29eSkettenis 			handled = ih->ih_func(ih->ih_arg);
2729eafc29eSkettenis 			intr_disable();
2739eafc29eSkettenis 			if (handled)
2749eafc29eSkettenis 				ih->ih_count.ec_count++;
2759eafc29eSkettenis #ifdef MULTIPROCESSOR
2769eafc29eSkettenis 			if (need_lock)
2779eafc29eSkettenis 				KERNEL_UNLOCK();
2789eafc29eSkettenis #endif
2799eafc29eSkettenis 		}
2809eafc29eSkettenis 
2819eafc29eSkettenis 		/* Signal EOI. */
2829eafc29eSkettenis 		xicp_write_4(sc, XICP_XIRR, xirr);
2839eafc29eSkettenis 		ci->ci_cpl = old;
2849eafc29eSkettenis 	}
2859eafc29eSkettenis }
286