Add a new HT-PCI bridge driver and the necessary glue to openpic(4)
authormpi <mpi@openbsd.org>
Tue, 2 Jun 2015 13:53:43 +0000 (13:53 +0000)
committermpi <mpi@openbsd.org>
Tue, 2 Jun 2015 13:53:43 +0000 (13:53 +0000)
to get interrupts working on U4 machines.

With this OpenBSD can run on PowerMac11,2 (Quad G5).

With inputs from and ok kettenis@, miod@, dlg@

sys/arch/macppc/conf/GENERIC
sys/arch/macppc/conf/files.macppc
sys/arch/macppc/dev/openpic.c
sys/arch/macppc/pci/hpb.c [new file with mode: 0644]

index b5cb4f3..dbfab3c 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.250 2015/05/30 18:14:08 jsg Exp $g
+#      $OpenBSD: GENERIC,v 1.251 2015/06/02 13:53:43 mpi Exp $g
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -46,7 +46,9 @@ smu*          at mainbus0
 pci*           at mpcpcibr?
 pci*           at ht?
 ppb*           at pci?         # PCI-PCI bridges
+hpb*           at pci?
 pci*           at ppb?
+pci*           at hpb?
 
 siop*          at pci?
 fxp*           at pci?
index 28c6cd7..9aa971e 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.macppc,v 1.81 2015/05/11 06:46:21 ratchov Exp $
+#      $OpenBSD: files.macppc,v 1.82 2015/06/02 13:53:43 mpi Exp $
 
 #
 # macppc-specific configuration info
@@ -86,7 +86,12 @@ include "dev/wscons/files.wscons"
 
 #file  arch/macppc/pci/bandit.c                        pci
 
-#PCI-Host bridge chipsets
+# HT bridge
+device  hpb {} : pcibus
+attach hpb at pci
+file   arch/macppc/pci/hpb.c                           hpb needs-flag
+
+# PCI-Host bridge chipsets
 device  pchb: agpbus
 attach pchb at pci
 file   arch/macppc/pci/pchb.c                          pchb
index 32eec12..9ddac5f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: openpic.c,v 1.79 2015/04/02 11:22:48 mpi Exp $        */
+/*     $OpenBSD: openpic.c,v 1.80 2015/06/02 13:53:43 mpi Exp $        */
 
 /*-
  * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org>
  *     @(#)isa.c       7.2 (Berkeley) 5/12/91
  */
 
+#include "hpb.h"
+
 #include <sys/param.h>
 #include <sys/device.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
+#include <sys/atomic.h>
 
 #include <uvm/uvm_extern.h>
-#include <ddb/db_var.h>
 
 #include <machine/autoconf.h>
 #include <machine/intr.h>
@@ -85,16 +87,41 @@ void        openpic_splx(int);
 
 u_int  openpic_read(int reg);
 void   openpic_write(int reg, u_int val);
-void   openpic_enable_irq(int, int);
-void   openpic_disable_irq(int);
+
+void   openpic_acknowledge_irq(int, int);
+void   openpic_enable_irq(int, int, int);
+void   openpic_disable_irq(int, int);
+
 void   openpic_calc_mask(void);
 void   openpic_set_priority(int, int);
 void   *openpic_intr_establish(void *, int, int, int, int (*)(void *), void *,
            const char *);
-void   openpic_intr_disestablish( void *lcp, void *arg);
+void   openpic_intr_disestablish(void *, void *);
 void   openpic_collect_preconf_intr(void);
 void   openpic_ext_intr(void);
 
+/* Generic IRQ management routines. */
+void   openpic_gen_acknowledge_irq(int, int);
+void   openpic_gen_enable_irq(int, int, int);
+void   openpic_gen_disable_irq(int, int);
+
+#if NHPB > 0
+/* CPC945 IRQ management routines. */
+void   openpic_cpc945_acknowledge_irq(int, int);
+void   openpic_cpc945_enable_irq(int, int, int);
+void   openpic_cpc945_disable_irq(int, int);
+#endif /* NHPB */
+
+struct openpic_ops {
+       void    (*acknowledge_irq)(int, int);
+       void    (*enable_irq)(int, int, int);
+       void    (*disable_irq)(int, int);
+} openpic_ops = {
+       openpic_gen_acknowledge_irq,
+       openpic_gen_enable_irq,
+       openpic_gen_disable_irq
+};
+
 #ifdef MULTIPROCESSOR
 void   openpic_ipi_ddb(void);
 
@@ -124,7 +151,7 @@ openpic_read(int reg)
 {
        char *addr = (void *)(openpic_base + reg);
 
-       asm volatile("eieio"::: "memory");
+       membar_sync();
        if (openpic_big_endian)
                return in32(addr);
        else
@@ -140,7 +167,7 @@ openpic_write(int reg, u_int val)
                out32(addr, val);
        else
                out32rb(addr, val);
-       asm volatile("eieio"::: "memory");
+       membar_sync();
 }
 
 static inline int
@@ -183,7 +210,7 @@ openpic_match(struct device *parent, void *cf, void *aux)
 }
 
 void
-openpic_attach(struct device *parent, struct device  *self, void *aux)
+openpic_attach(struct device *parent, struct device *self, void *aux)
 {
        struct cpu_info *ci = curcpu();
        struct confargs *ca = aux;
@@ -198,6 +225,13 @@ openpic_attach(struct device *parent, struct device  *self, void *aux)
        openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr +
                        ca->ca_reg[0], 0x40000);
 
+       /* Reset the PIC */
+       x = openpic_read(OPENPIC_CONFIG) | OPENPIC_CONFIG_RESET;
+       openpic_write(OPENPIC_CONFIG, x);
+
+       while (openpic_read(OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET)
+               delay(100);
+
        /* openpic may support more than 128 interupts but driver doesn't */
        openpic_numirq = ((openpic_read(OPENPIC_FEATURE) >> 16) & 0x7f)+1;
 
@@ -270,12 +304,21 @@ openpic_attach(struct device *parent, struct device  *self, void *aux)
        openpic_write(OPENPIC_SPURIOUS_VECTOR, 255);
 #endif
 
+#if NHPB > 0
+       /* Only U4 systems have a big-endian MPIC. */
+       if (openpic_big_endian) {
+               openpic_ops.acknowledge_irq = openpic_cpc945_acknowledge_irq;
+               openpic_ops.enable_irq = openpic_cpc945_enable_irq;
+               openpic_ops.disable_irq = openpic_cpc945_disable_irq;
+       }
+#endif
+
        install_extint(openpic_ext_intr);
 
        openpic_set_priority(ci->ci_cpuid, 0);
 
        intr_establish_func  = openpic_intr_establish;
-       intr_disestablish_func  = openpic_intr_disestablish;
+       intr_disestablish_func = openpic_intr_disestablish;
 #ifdef MULTIPROCESSOR
        intr_send_ipi_func = openpic_send_ipi;
 #endif
@@ -498,12 +541,12 @@ openpic_calc_mask()
                if (maxipl == IPL_NONE) {
                        minipl = IPL_NONE; /* Interrupt not enabled */
 
-                       openpic_disable_irq(irq);
+                       openpic_disable_irq(irq, iq->iq_ist);
                } else {
                        for (i = minipl; i <= maxipl; i++) {
                                openpic_pri_share[i] = maxipl;
                        }
-                       openpic_enable_irq(irq, maxipl);
+                       openpic_enable_irq(irq, iq->iq_ist, maxipl);
                }
 
                iq->iq_ipl = maxipl;
@@ -514,13 +557,19 @@ openpic_calc_mask()
 }
 
 void
-openpic_enable_irq(int irq, int pri)
+openpic_gen_acknowledge_irq(int irq, int cpuid)
+{
+       openpic_eoi(cpuid);
+}
+
+void
+openpic_gen_enable_irq(int irq, int ist, int pri)
 {
        u_int x;
-       struct intrq *iq = &openpic_handler[irq];
 
        x = irq;
-       if (iq->iq_ist == IST_LEVEL)
+
+       if (ist == IST_LEVEL)
                x |= OPENPIC_SENSE_LEVEL;
        else
                x |= OPENPIC_SENSE_EDGE;
@@ -530,7 +579,7 @@ openpic_enable_irq(int irq, int pri)
 }
 
 void
-openpic_disable_irq(int irq)
+openpic_gen_disable_irq(int irq, int ist)
 {
        u_int x;
 
@@ -545,30 +594,11 @@ openpic_set_priority(int cpu, int pri)
        openpic_write(OPENPIC_CPU_PRIORITY(cpu), pri);
 }
 
-#ifdef MULTIPROCESSOR
-void
-openpic_send_ipi(struct cpu_info *ci, int id)
-{
-       switch (id) {
-       case PPC_IPI_NOP:
-               id = 0;
-               break;
-       case PPC_IPI_DDB:
-               id = 1;
-               break;
-       default:
-               panic("invalid ipi send to cpu %d %d", ci->ci_cpuid, id);
-       }
-
-       openpic_write(OPENPIC_IPI(curcpu()->ci_cpuid, id), 1 << ci->ci_cpuid);
-}
-
-#endif
-
 int openpic_irqnest[PPC_MAXPROCS];
 int openpic_irqloop[PPC_MAXPROCS];
+
 void
-openpic_ext_intr()
+openpic_ext_intr(void)
 {
        struct cpu_info *ci = curcpu();
        int irq, pcpl, ret;
@@ -624,7 +654,7 @@ openpic_ext_intr()
                if (iq->iq_ipl > maxipl)
                        maxipl = iq->iq_ipl;
                openpic_splraise(iq->iq_ipl);
-               openpic_eoi(ci->ci_cpuid);
+               openpic_acknowledge_irq(irq, ci->ci_cpuid);
 
                spurious = 1;
                TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
@@ -656,9 +686,44 @@ openpic_ext_intr()
        openpic_irqnest[ci->ci_cpuid]--;
 }
 
+void
+openpic_acknowledge_irq(int irq, int cpuid)
+{
+       (openpic_ops.acknowledge_irq)(irq, cpuid);
+}
+
+void
+openpic_enable_irq(int irq, int ist, int pri)
+{
+       (openpic_ops.enable_irq)(irq, ist, pri);
+}
+
+void
+openpic_disable_irq(int irq, int ist)
+{
+       (openpic_ops.disable_irq)(irq, ist);
+}
+
 #ifdef MULTIPROCESSOR
 void
-openpic_ipi_ddb()
+openpic_send_ipi(struct cpu_info *ci, int id)
+{
+       switch (id) {
+       case PPC_IPI_NOP:
+               id = 0;
+               break;
+       case PPC_IPI_DDB:
+               id = 1;
+               break;
+       default:
+               panic("invalid ipi send to cpu %d %d", ci->ci_cpuid, id);
+       }
+
+       openpic_write(OPENPIC_IPI(curcpu()->ci_cpuid, id), 1 << ci->ci_cpuid);
+}
+
+void
+openpic_ipi_ddb(void)
 {
        DPRINTF("ipi_ddb() called\n");
 #ifdef DDB
@@ -666,3 +731,40 @@ openpic_ipi_ddb()
 #endif
 }
 #endif /* MULTIPROCESSOR */
+
+#if NHPB > 0
+extern int     hpb_enable_irq(int, int);
+extern int     hpb_disable_irq(int, int);
+extern void    hpb_eoi(int);
+
+void
+openpic_cpc945_acknowledge_irq(int irq, int cpuid)
+{
+       hpb_eoi(irq);
+       openpic_gen_acknowledge_irq(irq, cpuid);
+}
+
+void
+openpic_cpc945_enable_irq(int irq, int ist, int pri)
+{
+       if (hpb_enable_irq(irq, ist)) {
+               u_int x = irq;
+
+               x |= OPENPIC_SENSE_EDGE;
+               x |= OPENPIC_POLARITY_POSITIVE;
+               x |= pri << OPENPIC_PRIORITY_SHIFT;
+               openpic_write(OPENPIC_SRC_VECTOR(irq), x);
+
+               hpb_eoi(irq);
+       } else
+               openpic_gen_enable_irq(irq, ist, pri);
+}
+
+void
+openpic_cpc945_disable_irq(int irq, int ist)
+{
+       hpb_disable_irq(irq, ist);
+       openpic_gen_disable_irq(irq, ist);
+}
+#endif /* NHPB */
+
diff --git a/sys/arch/macppc/pci/hpb.c b/sys/arch/macppc/pci/hpb.c
new file mode 100644 (file)
index 0000000..7633847
--- /dev/null
@@ -0,0 +1,248 @@
+/*     $OpenBSD: hpb.c,v 1.1 2015/06/02 13:53:43 mpi Exp $     */
+
+/*
+ * Copyright (c) 2015 Martin Pieuchot
+ *
+ * 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/systm.h>
+#include <sys/device.h>
+
+#include <machine/intr.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/ppbreg.h>
+
+/*
+ * Section 7.6.2  Interrupt Message Definition Register
+ */
+#define PCI_HT_LAST_ISMG               (0x01 << 16)
+#define PCI_HT_IMSG_LO(idx)            (((2 * (idx)) + 0x10) << 16)
+#define  HT_MASK       (1 << 0)
+#define  HT_ACTIVELOW  (1 << 1)
+#define  HT_EOI                (1 << 5)
+
+#define PCI_HT_IMSG_HI(idx)            (PCI_HT_IMSG_LO(idx) + 1)
+#define  HT_PASSPW     (1U << 30)
+#define  HT_WAITEOI    (1U << 31)      /* Waiting for EOI */
+
+
+/* Apple hardware is special... */
+#define HT_APPL_EOIOFF(idx)            (0x60 + (((idx) >> 3) & ~3))
+#define        HT_APPL_WEOI(idx)               (1 << ((idx) & 0x1f))
+
+struct ht_intr_msg {
+       unsigned int    him_idx;        /* Index */
+       int             him_ist;        /* Share type */
+       pcireg_t        him_weoi;       /* Cached wait for interrupt data */
+};
+
+#define        hpb_MAX_IMSG    128
+
+struct hpb_softc {
+       struct device           sc_dev;
+       pci_chipset_tag_t       sc_pc;
+       pcitag_t                sc_tag;
+       pcireg_t                sc_id;          /* Needed for Apple hardware */
+       unsigned int            sc_off;         /* Interrupt cap. offset */
+       unsigned int            sc_nirq;
+       struct ht_intr_msg      sc_imap[hpb_MAX_IMSG];
+};
+
+int    hpb_match(struct device *, void *, void *);
+void   hpb_attach(struct device *, struct device *, void *);
+
+int    hpb_print(void *, const char *);
+
+void   hpb_eoi(int);
+int    hpb_enable_irq(int, int);
+int    hpb_disable_irq(int, int);
+
+const struct cfattach hpb_ca = {
+       sizeof(struct hpb_softc), hpb_match, hpb_attach
+};
+
+struct cfdriver hpb_cd = {
+       NULL, "hpb", DV_DULL,
+};
+
+int
+hpb_match(struct device *parent, void *match, void *aux)
+{
+       struct pci_attach_args *pa = aux;
+       pci_chipset_tag_t pc = pa->pa_pc;
+       pcitag_t tag = pa->pa_tag;
+
+       if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
+           PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_PCI)
+               return (0);
+
+       if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, NULL, NULL))
+               return (0);
+
+       return (10);
+}
+
+void
+hpb_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct hpb_softc *sc = (struct hpb_softc *)self;
+       struct pci_attach_args *pa = aux;
+       struct pcibus_attach_args pba;
+       pci_chipset_tag_t pc = pa->pa_pc;
+       pcitag_t tag = pa->pa_tag;
+       int idx, irq, off;
+       pcireg_t busdata, reg;
+
+       if (!pci_get_ht_capability(pc, tag, PCI_HT_CAP_INTR, &off, NULL))
+               panic("A clown ate your HT capability");
+
+       /* Interrupt definitions are numbered beginning with 0. */
+       pci_conf_write(pc, tag, off, PCI_HT_LAST_ISMG);
+       reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
+
+       sc->sc_pc = pc;
+       sc->sc_tag = tag;
+       sc->sc_id = pa->pa_id;
+       sc->sc_off = off;
+       sc->sc_nirq = ((reg >> 16) & 0xff);
+
+       if (sc->sc_nirq == 0 || sc->sc_nirq > hpb_MAX_IMSG)
+               return;
+
+       printf(": %u sources\n", sc->sc_nirq);
+
+       for (idx = 0; idx < sc->sc_nirq; idx++) {
+               pci_conf_write(pc, tag, off, PCI_HT_IMSG_LO(idx));
+               reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
+
+               pci_conf_write(pc, tag, off + PCI_HT_INTR_DATA, reg | HT_MASK);
+               irq = (reg >> 16) & 0xff;
+
+#ifdef DIAGNOSTIC
+               if (sc->sc_imap[irq].him_idx != 0) {
+                       printf("%s: multiple definition for irq %d\n",
+                           sc->sc_dev.dv_xname, irq);
+                       continue;
+               }
+#endif
+               pci_conf_write(pc, tag, off, PCI_HT_IMSG_HI(idx));
+               reg = pci_conf_read(pc, tag, off + PCI_HT_INTR_DATA);
+
+               sc->sc_imap[irq].him_idx = idx;
+               sc->sc_imap[irq].him_weoi = reg | HT_WAITEOI;
+       }
+
+       busdata = pci_conf_read(pc, pa->pa_tag, PPB_REG_BUSINFO);
+
+       memset(&pba, 0, sizeof(pba));
+       pba.pba_busname = "pci";
+       pba.pba_iot = pa->pa_iot;
+       pba.pba_memt = pa->pa_memt;
+       pba.pba_dmat = pa->pa_dmat;
+       pba.pba_pc = pc;
+       pba.pba_domain = pa->pa_domain;
+       pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
+       pba.pba_bridgetag = &sc->sc_tag;
+
+       config_found(self, &pba, hpb_print);
+}
+
+int
+hpb_print(void *aux, const char *pnp)
+{
+       struct pcibus_attach_args *pba = aux;
+
+       if (pnp)
+               printf("%s at %s", pba->pba_busname, pnp);
+       printf(" bus %d", pba->pba_bus);
+       return (UNCONF);
+}
+
+void
+hpb_eoi(int irq)
+{
+       struct hpb_softc *sc = hpb_cd.cd_devs[0];
+       pci_chipset_tag_t pc = sc->sc_pc;
+       pcitag_t tag = sc->sc_tag;
+       int idx;
+
+       if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0 ||
+           sc->sc_imap[irq].him_ist != IST_LEVEL)
+               return;
+
+       idx = sc->sc_imap[irq].him_idx;
+
+       if (PCI_VENDOR(sc->sc_id) == PCI_VENDOR_APPLE) {
+               pci_conf_write(pc, tag, HT_APPL_EOIOFF(idx), HT_APPL_WEOI(idx));
+       } else {
+               pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_HI(idx));
+               pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA,
+                   sc->sc_imap[irq].him_weoi);
+       }
+}
+
+int
+hpb_enable_irq(int irq, int ist)
+{
+       struct hpb_softc *sc = hpb_cd.cd_devs[0];
+       pci_chipset_tag_t pc = sc->sc_pc;
+       pcitag_t tag = sc->sc_tag;
+       pcireg_t reg;
+       int idx;
+
+       if (irq >= sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0)
+               return (0);
+
+       idx = sc->sc_imap[irq].him_idx;
+       sc->sc_imap[irq].him_ist = ist;
+
+       pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx));
+       reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA);
+
+       pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK);
+
+       reg &= ~(HT_ACTIVELOW | HT_EOI | HT_MASK);
+       if (ist == IST_LEVEL)
+               reg |= HT_ACTIVELOW | HT_EOI;
+
+       pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg);
+
+       return (1);
+}
+
+int
+hpb_disable_irq(int irq, int ist)
+{
+       struct hpb_softc *sc = hpb_cd.cd_devs[0];
+       pci_chipset_tag_t pc = sc->sc_pc;
+       pcitag_t tag = sc->sc_tag;
+       pcireg_t reg;
+       int idx;
+
+       if (irq > sc->sc_nirq || sc->sc_imap[irq].him_weoi == 0)
+               return (0);
+
+       idx = sc->sc_imap[irq].him_idx;
+
+       pci_conf_write(pc, tag, sc->sc_off, PCI_HT_IMSG_LO(idx));
+       reg = pci_conf_read(pc, tag, sc->sc_off + PCI_HT_INTR_DATA);
+
+       pci_conf_write(pc, tag, sc->sc_off + PCI_HT_INTR_DATA, reg | HT_MASK);
+
+       return (1);
+}