-/* $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>
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);
{
char *addr = (void *)(openpic_base + reg);
- asm volatile("eieio"::: "memory");
+ membar_sync();
if (openpic_big_endian)
return in32(addr);
else
out32(addr, val);
else
out32rb(addr, val);
- asm volatile("eieio"::: "memory");
+ membar_sync();
}
static inline int
}
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;
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;
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
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;
}
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;
}
void
-openpic_disable_irq(int irq)
+openpic_gen_disable_irq(int irq, int ist)
{
u_int x;
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;
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) {
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
#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 */
+
--- /dev/null
+/* $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);
+}