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