From ba99a0ed9a190d694621c8af1f241c30ba0378b3 Mon Sep 17 00:00:00 2001 From: mpi Date: Tue, 2 Jun 2015 13:53:43 +0000 Subject: [PATCH] Add a new HT-PCI bridge driver and the necessary glue to openpic(4) 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 | 4 +- sys/arch/macppc/conf/files.macppc | 9 +- sys/arch/macppc/dev/openpic.c | 178 ++++++++++++++++----- sys/arch/macppc/pci/hpb.c | 248 ++++++++++++++++++++++++++++++ 4 files changed, 398 insertions(+), 41 deletions(-) create mode 100644 sys/arch/macppc/pci/hpb.c diff --git a/sys/arch/macppc/conf/GENERIC b/sys/arch/macppc/conf/GENERIC index b5cb4f3a3f2..dbfab3c4d36 100644 --- a/sys/arch/macppc/conf/GENERIC +++ b/sys/arch/macppc/conf/GENERIC @@ -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? diff --git a/sys/arch/macppc/conf/files.macppc b/sys/arch/macppc/conf/files.macppc index 28c6cd7d6ec..9aa971eebc1 100644 --- a/sys/arch/macppc/conf/files.macppc +++ b/sys/arch/macppc/conf/files.macppc @@ -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 diff --git a/sys/arch/macppc/dev/openpic.c b/sys/arch/macppc/dev/openpic.c index 32eec128de7..9ddac5f32cb 100644 --- a/sys/arch/macppc/dev/openpic.c +++ b/sys/arch/macppc/dev/openpic.c @@ -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 @@ -37,13 +37,15 @@ * @(#)isa.c 7.2 (Berkeley) 5/12/91 */ +#include "hpb.h" + #include #include #include #include +#include #include -#include #include #include @@ -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 index 00000000000..7633847a947 --- /dev/null +++ b/sys/arch/macppc/pci/hpb.c @@ -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 +#include +#include + +#include + +#include +#include +#include +#include + +/* + * 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); +} -- 2.20.1