summaryrefslogtreecommitdiffstats
path: root/sys/arch/sgi/hpc/hpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sgi/hpc/hpc.c')
-rw-r--r--sys/arch/sgi/hpc/hpc.c822
1 files changed, 822 insertions, 0 deletions
diff --git a/sys/arch/sgi/hpc/hpc.c b/sys/arch/sgi/hpc/hpc.c
new file mode 100644
index 00000000000..ca2c31c06ed
--- /dev/null
+++ b/sys/arch/sgi/hpc/hpc.c
@@ -0,0 +1,822 @@
+/* $OpenBSD: hpc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: hpc.c,v 1.66 2011/07/01 18:53:46 dyoung Exp $ */
+/* $NetBSD: ioc.c,v 1.9 2011/07/01 18:53:47 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2003 Christopher Sekiya
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2000 Soren S. Jorvang
+ * Copyright (c) 2001 Rafal K. Boni
+ * Copyright (c) 2001 Jason R. Thorpe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Combined driver for the HPC (High performance Peripheral Controller)
+ * and IOC2 (I/O Controller) chips.
+ *
+ * It would theoretically be better to attach an IOC driver to HPC on
+ * IOC systems (IP22/24/26/28), and attach the few onboard devices
+ * which attach directly to HPC on IP20, to IOC. But since IOC depends
+ * too much on HPC, the complexity this would introduce is not worth
+ * the hassle.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+
+#include <mips64/archtype.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <sgi/gio/gioreg.h>
+#include <sgi/gio/giovar.h>
+
+#include <sgi/hpc/hpcvar.h>
+#include <sgi/hpc/hpcreg.h>
+#include <sgi/hpc/iocreg.h>
+#include <sgi/sgi/ip22.h>
+
+#include <dev/ic/smc93cx6var.h>
+
+struct hpc_device {
+ const char *hd_name;
+ bus_addr_t hd_base;
+ bus_addr_t hd_devoff;
+ bus_addr_t hd_dmaoff;
+ int hd_irq;
+ int hd_sysmask;
+};
+
+static const struct hpc_device hpc1_devices[] = {
+ /* probe order is important for IP20 zs */
+
+ { "zs", /* Indigo serial 0/1 duart 1 */
+ HPC_BASE_ADDRESS_0,
+ 0x0d10, 0,
+ 5,
+ HPCDEV_IP20 },
+
+ { "zs", /* Indigo kbd/ms duart 0 */
+ HPC_BASE_ADDRESS_0,
+ 0x0d00, 0,
+ 5,
+ HPCDEV_IP20 },
+
+ { "sq", /* Indigo onboard ethernet */
+ HPC_BASE_ADDRESS_0,
+ HPC1_ENET_DEVREGS, HPC1_ENET_REGS,
+ 3,
+ HPCDEV_IP20 },
+
+ { "sq", /* E++ GIO adapter slot 0 (Indigo) */
+ HPC_BASE_ADDRESS_1,
+ HPC1_ENET_DEVREGS, HPC1_ENET_REGS,
+ 6,
+ HPCDEV_IP20 },
+
+ { "sq", /* E++ GIO adapter slot 0 (Indy) */
+ HPC_BASE_ADDRESS_1,
+ HPC1_ENET_DEVREGS, HPC1_ENET_REGS,
+ 16 + 6,
+ HPCDEV_IP24 },
+
+ { "sq", /* E++ GIO adapter slot 1 (Indigo) */
+ HPC_BASE_ADDRESS_2,
+ HPC1_ENET_DEVREGS, HPC1_ENET_REGS,
+ 6,
+ HPCDEV_IP20 },
+
+ { "sq", /* E++ GIO adapter slot 1 (Indy/Challenge S) */
+ HPC_BASE_ADDRESS_2,
+ HPC1_ENET_DEVREGS, HPC1_ENET_REGS,
+ 16 + 7,
+ HPCDEV_IP24 },
+
+ { "wdsc", /* Indigo onboard SCSI */
+ HPC_BASE_ADDRESS_0,
+ HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS,
+ 2,
+ HPCDEV_IP20 },
+
+ { "wdsc", /* GIO32 SCSI adapter slot 0 (Indigo) */
+ HPC_BASE_ADDRESS_1,
+ HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS,
+ 6,
+ HPCDEV_IP20 },
+
+ { "wdsc", /* GIO32 SCSI adapter slot 0 (Indy) */
+ HPC_BASE_ADDRESS_1,
+ HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS,
+ 16 + 6,
+ HPCDEV_IP24 },
+
+ { "wdsc", /* GIO32 SCSI adapter slot 1 (Indigo) */
+ HPC_BASE_ADDRESS_2,
+ HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS,
+ 6,
+ HPCDEV_IP20 },
+
+ { "wdsc", /* GIO32 SCSI adapter slot 1 (Indy/Challenge S) */
+ HPC_BASE_ADDRESS_2,
+ HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS,
+ 16 + 7,
+ HPCDEV_IP24 },
+
+ { NULL,
+ 0,
+ 0, 0,
+ 0,
+ 0
+ }
+};
+
+static const struct hpc_device hpc3_devices[] = {
+ { "zs", /* serial 0/1 duart 0 */
+ HPC_BASE_ADDRESS_0,
+ /* XXX Magic numbers */
+ HPC3_PBUS_CH6_DEVREGS + IOC_SERIAL_REGS, 0,
+ 24 + 5,
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "pckbc", /* Indigo2/Indy ps2 keyboard/mouse controller */
+ HPC_BASE_ADDRESS_0,
+ HPC3_PBUS_CH6_DEVREGS + IOC_KB_REGS, 0,
+ 24 + 4,
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "sq", /* Indigo2/Indy/Challenge S/Challenge M onboard enet */
+ HPC_BASE_ADDRESS_0,
+ HPC3_ENET_DEVREGS, HPC3_ENET_REGS,
+ 3,
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "sq", /* Challenge S IOPLUS secondary ethernet */
+ HPC_BASE_ADDRESS_1,
+ HPC3_ENET_DEVREGS, HPC3_ENET_REGS,
+ 0,
+ HPCDEV_IP24 },
+
+ { "wdsc", /* Indigo2/Indy/Challenge S/Challenge M onboard SCSI */
+ HPC_BASE_ADDRESS_0,
+ HPC3_SCSI0_DEVREGS, HPC3_SCSI0_REGS,
+ 1,
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "wdsc", /* Indigo2/Challenge M secondary onboard SCSI */
+ HPC_BASE_ADDRESS_0,
+ HPC3_SCSI1_DEVREGS, HPC3_SCSI1_REGS,
+ 2,
+ HPCDEV_IP22 },
+
+ { "haltwo", /* Indigo2/Indy onboard audio */
+ HPC_BASE_ADDRESS_0,
+ HPC3_PBUS_CH0_DEVREGS, HPC3_PBUS_DMAREGS,
+ 8 + 4, /* really the HPC DMA complete interrupt */
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "pione", /* Indigo2/Indy/Challenge S/Challenge M onboard pport */
+ HPC_BASE_ADDRESS_0,
+ HPC3_PBUS_CH6_DEVREGS + IOC_PLP_REGS, 0,
+ 5,
+ HPCDEV_IP22 | HPCDEV_IP24 },
+
+ { "panel", /* Indy front panel */
+ HPC_BASE_ADDRESS_0,
+ HPC3_PBUS_CH6_DEVREGS + IOC_PANEL, 0,
+ 9,
+ HPCDEV_IP24 },
+
+ { NULL,
+ 0,
+ 0, 0,
+ 0,
+ 0
+ }
+};
+
+struct hpc_softc {
+ struct device sc_dev;
+
+ bus_addr_t sc_base;
+
+ bus_space_tag_t sc_ct;
+ bus_space_handle_t sc_ch;
+ bus_dma_tag_t sc_dmat;
+
+ struct timeout sc_blink_tmo;
+};
+
+static struct hpc_values hpc1_values = {
+ .revision = 1,
+ .scsi0_regs = HPC1_SCSI0_REGS,
+ .scsi0_regs_size = HPC1_SCSI0_REGS_SIZE,
+ .scsi0_cbp = HPC1_SCSI0_CBP,
+ .scsi0_ndbp = HPC1_SCSI0_NDBP,
+ .scsi0_bc = HPC1_SCSI0_BC,
+ .scsi0_ctl = HPC1_SCSI0_CTL,
+ .scsi0_gio = HPC1_SCSI0_GIO,
+ .scsi0_dev = HPC1_SCSI0_DEV,
+ .scsi0_dmacfg = HPC1_SCSI0_DMACFG,
+ .scsi0_piocfg = HPC1_SCSI0_PIOCFG,
+ .scsi1_regs = 0,
+ .scsi1_regs_size = 0,
+ .scsi1_cbp = 0,
+ .scsi1_ndbp = 0,
+ .scsi1_bc = 0,
+ .scsi1_ctl = 0,
+ .scsi1_gio = 0,
+ .scsi1_dev = 0,
+ .scsi1_dmacfg = 0,
+ .scsi1_piocfg = 0,
+ .enet_regs = HPC1_ENET_REGS,
+ .enet_regs_size = HPC1_ENET_REGS_SIZE,
+ .enet_intdelay = HPC1_ENET_INTDELAY,
+ .enet_intdelayval = HPC1_ENET_INTDELAY_OFF,
+ .enetr_cbp = HPC1_ENETR_CBP,
+ .enetr_ndbp = HPC1_ENETR_NDBP,
+ .enetr_bc = HPC1_ENETR_BC,
+ .enetr_ctl = HPC1_ENETR_CTL,
+ .enetr_ctl_active = HPC1_ENETR_CTL_ACTIVE,
+ .enetr_reset = HPC1_ENETR_RESET,
+ .enetr_dmacfg = 0,
+ .enetr_piocfg = 0,
+ .enetx_cbp = HPC1_ENETX_CBP,
+ .enetx_ndbp = HPC1_ENETX_NDBP,
+ .enetx_bc = HPC1_ENETX_BC,
+ .enetx_ctl = HPC1_ENETX_CTL,
+ .enetx_ctl_active = HPC1_ENETX_CTL_ACTIVE,
+ .enetx_dev = 0,
+ .enetr_fifo = HPC1_ENETR_FIFO,
+ .enetr_fifo_size = HPC1_ENETR_FIFO_SIZE,
+ .enetx_fifo = HPC1_ENETX_FIFO,
+ .enetx_fifo_size = HPC1_ENETX_FIFO_SIZE,
+ .enet_dma_boundary = 4096,
+ .enet_devregs = HPC1_ENET_DEVREGS,
+ .enet_devregs_size = HPC1_ENET_DEVREGS_SIZE,
+ .pbus_fifo = 0,
+ .pbus_fifo_size = 0,
+ .pbus_bbram = 0,
+#define MAX_SCSI_XFER (roundup(MAXPHYS, PAGE_SIZE))
+ .scsi_dma_segs = (MAX_SCSI_XFER / 4096),
+ .scsi_dma_segs_size = 4096,
+ .scsi_dma_datain_cmd = (HPC1_SCSI_DMACTL_ACTIVE | HPC1_SCSI_DMACTL_DIR),
+ .scsi_dma_dataout_cmd = HPC1_SCSI_DMACTL_ACTIVE,
+ .scsi_dmactl_flush = HPC1_SCSI_DMACTL_FLUSH,
+ .scsi_dmactl_active = HPC1_SCSI_DMACTL_ACTIVE,
+ .scsi_dmactl_reset = HPC1_SCSI_DMACTL_RESET
+};
+
+static struct hpc_values hpc3_values = {
+ .revision = 3,
+ .scsi0_regs = HPC3_SCSI0_REGS,
+ .scsi0_regs_size = HPC3_SCSI0_REGS_SIZE,
+ .scsi0_cbp = HPC3_SCSI0_CBP,
+ .scsi0_ndbp = HPC3_SCSI0_NDBP,
+ .scsi0_bc = HPC3_SCSI0_BC,
+ .scsi0_ctl = HPC3_SCSI0_CTL,
+ .scsi0_gio = HPC3_SCSI0_GIO,
+ .scsi0_dev = HPC3_SCSI0_DEV,
+ .scsi0_dmacfg = HPC3_SCSI0_DMACFG,
+ .scsi0_piocfg = HPC3_SCSI0_PIOCFG,
+ .scsi1_regs = HPC3_SCSI1_REGS,
+ .scsi1_regs_size = HPC3_SCSI1_REGS_SIZE,
+ .scsi1_cbp = HPC3_SCSI1_CBP,
+ .scsi1_ndbp = HPC3_SCSI1_NDBP,
+ .scsi1_bc = HPC3_SCSI1_BC,
+ .scsi1_ctl = HPC3_SCSI1_CTL,
+ .scsi1_gio = HPC3_SCSI1_GIO,
+ .scsi1_dev = HPC3_SCSI1_DEV,
+ .scsi1_dmacfg = HPC3_SCSI1_DMACFG,
+ .scsi1_piocfg = HPC3_SCSI1_PIOCFG,
+ .enet_regs = HPC3_ENET_REGS,
+ .enet_regs_size = HPC3_ENET_REGS_SIZE,
+ .enet_intdelay = 0,
+ .enet_intdelayval = 0,
+ .enetr_cbp = HPC3_ENETR_CBP,
+ .enetr_ndbp = HPC3_ENETR_NDBP,
+ .enetr_bc = HPC3_ENETR_BC,
+ .enetr_ctl = HPC3_ENETR_CTL,
+ .enetr_ctl_active = HPC3_ENETR_CTL_ACTIVE,
+ .enetr_reset = HPC3_ENETR_RESET,
+ .enetr_dmacfg = HPC3_ENETR_DMACFG,
+ .enetr_piocfg = HPC3_ENETR_PIOCFG,
+ .enetx_cbp = HPC3_ENETX_CBP,
+ .enetx_ndbp = HPC3_ENETX_NDBP,
+ .enetx_bc = HPC3_ENETX_BC,
+ .enetx_ctl = HPC3_ENETX_CTL,
+ .enetx_ctl_active = HPC3_ENETX_CTL_ACTIVE,
+ .enetx_dev = HPC3_ENETX_DEV,
+ .enetr_fifo = HPC3_ENETR_FIFO,
+ .enetr_fifo_size = HPC3_ENETR_FIFO_SIZE,
+ .enetx_fifo = HPC3_ENETX_FIFO,
+ .enetx_fifo_size = HPC3_ENETX_FIFO_SIZE,
+ .enet_dma_boundary = 8192,
+ .enet_devregs = HPC3_ENET_DEVREGS,
+ .enet_devregs_size = HPC3_ENET_DEVREGS_SIZE,
+ .pbus_fifo = HPC3_PBUS_FIFO,
+ .pbus_fifo_size = HPC3_PBUS_FIFO_SIZE,
+ .pbus_bbram = HPC3_PBUS_BBRAM,
+ .scsi_dma_segs = (MAX_SCSI_XFER / 8192),
+ .scsi_dma_segs_size = 8192,
+ .scsi_dma_datain_cmd = HPC3_SCSI_DMACTL_ACTIVE,
+ .scsi_dma_dataout_cmd =(HPC3_SCSI_DMACTL_ACTIVE | HPC3_SCSI_DMACTL_DIR),
+ .scsi_dmactl_flush = HPC3_SCSI_DMACTL_FLUSH,
+ .scsi_dmactl_active = HPC3_SCSI_DMACTL_ACTIVE,
+ .scsi_dmactl_reset = HPC3_SCSI_DMACTL_RESET
+};
+
+int hpc_match(struct device *, void *, void *);
+void hpc_attach(struct device *, struct device *, void *);
+int hpc_print(void *, const char *);
+
+int hpc_revision(struct hpc_softc *, struct gio_attach_args *);
+int hpc_submatch(struct device *, void *, void *);
+int hpc_power_intr(void *);
+void hpc_blink(void *);
+void hpc_blink_ioc(void *);
+int hpc_read_eeprom(int, bus_space_tag_t, bus_space_handle_t, uint8_t *,
+ size_t);
+
+const struct cfattach hpc_ca = {
+ sizeof(struct hpc_softc), hpc_match, hpc_attach
+};
+
+struct cfdriver hpc_cd = {
+ NULL, "hpc", DV_DULL
+};
+
+uint8_t hpc_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint16_t hpc_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void hpc_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void hpc_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
+void hpc_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t);
+void hpc_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+uint32_t hpc_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint64_t hpc_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void hpc_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t);
+void hpc_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint64_t);
+void hpc_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void hpc_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+void hpc_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void hpc_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+int hpc_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+void hpc_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+int hpc_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+void *hpc_space_vaddr(bus_space_tag_t, bus_space_handle_t);
+void hpc_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, int);
+
+int
+hpc_match(struct device *parent, void *vcf, void *aux)
+{
+ struct gio_attach_args* ga = aux;
+ uint32_t dummy;
+
+ /* Make sure it's actually there and readable */
+ if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr, CCA_NC), &dummy) == 0)
+ return 1;
+
+ return 0;
+}
+
+void
+hpc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct hpc_softc *sc = (struct hpc_softc *)self;
+ struct gio_attach_args* ga = aux;
+ struct hpc_attach_args ha;
+ const struct hpc_device *hd;
+ uint32_t dummy;
+ uint32_t hpctype;
+ int isonboard;
+ int isioplus;
+ int sysmask = 0;
+
+ sc->sc_base = ga->ga_addr;
+ sc->sc_ct = ga->ga_iot;
+ sc->sc_ch = PHYS_TO_XKPHYS(sc->sc_base, CCA_NC);
+ sc->sc_dmat = ga->ga_dmat;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ sysmask = HPCDEV_IP20;
+ break;
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ if (sys_config.system_subtype == IP22_INDIGO2)
+ sysmask = HPCDEV_IP22;
+ else
+ sysmask = HPCDEV_IP24;
+ break;
+ };
+
+ if ((hpctype = hpc_revision(sc, ga)) == 0) {
+ printf(": could not identify HPC revision\n");
+ return;
+ }
+
+ /* force big-endian mode */
+ if (hpctype == 15)
+ bus_space_write_4(sc->sc_ct, sc->sc_ch, HPC1_BIGENDIAN, 0);
+
+ /*
+ * All machines have only one HPC on the mainboard itself. ''Extra''
+ * HPCs require bus arbiter and other magic to run happily.
+ */
+ isonboard = (sc->sc_base == HPC_BASE_ADDRESS_0);
+ isioplus = (sc->sc_base == HPC_BASE_ADDRESS_1 && hpctype == 3 &&
+ sysmask == HPCDEV_IP24);
+
+ printf(": SGI HPC%d%s (%s)\n", (hpctype == 3) ? 3 : 1,
+ (hpctype == 15) ? ".5" : "", (isonboard) ? "onboard" :
+ (isioplus) ? "IOPLUS mezzanine" : "GIO slot");
+
+ /*
+ * Configure the IOC.
+ */
+ if (isonboard && sys_config.system_type != SGI_IP20) {
+ /* Reset IOC */
+ bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET,
+ IOC_RESET_PARALLEL | IOC_RESET_PCKBC | IOC_RESET_EISA |
+ IOC_RESET_ISDN | IOC_RESET_LED_GREEN );
+
+ /*
+ * Set the 10BaseT port to use UTP cable, set autoselect mode
+ * for the Ethernet interface (AUI vs. TP), set the two serial
+ * ports to PC mode.
+ */
+ bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_WRITE,
+ IOC_WRITE_ENET_AUTO | IOC_WRITE_ENET_UTP |
+ IOC_WRITE_PC_UART2 | IOC_WRITE_PC_UART1);
+
+ /* XXX: the firmware should have taken care of this already */
+#if 0
+ if (sys_config.system_subtype == IP22_INDY) {
+ bus_space_write_4(sc->sc_ct, sc->sc_ch,
+ IOC_BASE + IOC_GCSEL, 0xff);
+ bus_space_write_4(sc->sc_ct, sc->sc_ch,
+ IOC_BASE + IOC_GCREG, 0xff);
+ }
+#endif
+ }
+
+ /*
+ * Configure the bus arbiter appropriately.
+ *
+ * In the case of Challenge S, we must tell the IOPLUS board which
+ * DMA channel to use (we steal it from one of the slots). SGI allows
+ * an HPC1.5 in slot 1, in which case IOPLUS must use EXP0, or any
+ * other DMA-capable board in slot 0, which leaves us to use EXP1. Of
+ * course, this means that only one GIO board may use DMA.
+ *
+ * Note that this never happens on Indigo2.
+ */
+ if (isioplus) {
+ int arb_slot;
+
+ if (guarded_read_4(PHYS_TO_XKPHYS(HPC_BASE_ADDRESS_2, CCA_NC),
+ &dummy) != 0)
+ arb_slot = GIO_SLOT_EXP1;
+ else
+ arb_slot = GIO_SLOT_EXP0;
+
+ if (gio_arb_config(arb_slot, GIO_ARB_LB | GIO_ARB_MST |
+ GIO_ARB_64BIT | GIO_ARB_HPC2_64BIT)) {
+ printf("%s: failed to configure GIO bus arbiter\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ printf("%s: using EXP%d's DMA channel\n",
+ sc->sc_dev.dv_xname,
+ (arb_slot == GIO_SLOT_EXP0) ? 0 : 1);
+
+ bus_space_write_4(sc->sc_ct, sc->sc_ch,
+ HPC3_PBUS_CFGPIO_REGS, 0x0003ffff);
+
+ if (arb_slot == GIO_SLOT_EXP0)
+ bus_space_write_4(sc->sc_ct, sc->sc_ch,
+ HPC3_PBUS_CH0_DEVREGS, 0x20202020);
+ else
+ bus_space_write_4(sc->sc_ct, sc->sc_ch,
+ HPC3_PBUS_CH0_DEVREGS, 0x30303030);
+ } else if (!isonboard) {
+ int arb_slot;
+
+ arb_slot = (sc->sc_base == HPC_BASE_ADDRESS_1) ?
+ GIO_SLOT_EXP0 : GIO_SLOT_EXP1;
+
+ if (gio_arb_config(arb_slot, GIO_ARB_RT | GIO_ARB_MST)) {
+ printf("%s: failed to configure GIO bus arbiter\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ }
+
+ hpc_read_eeprom(hpctype, sc->sc_ct, sc->sc_ch,
+ ha.hpc_eeprom, sizeof(ha.hpc_eeprom));
+
+ hd = hpctype == 3 ? hpc3_devices : hpc1_devices;
+ for (; hd->hd_name != NULL; hd++) {
+ if (!(hd->hd_sysmask & sysmask) || hd->hd_base != sc->sc_base)
+ continue;
+
+ ha.ha_name = hd->hd_name;
+ ha.ha_devoff = hd->hd_devoff;
+ ha.ha_dmaoff = hd->hd_dmaoff;
+ ha.ha_irq = hd->hd_irq;
+
+ ha.ha_st = sc->sc_ct;
+ ha.ha_sh = sc->sc_ch;
+ ha.ha_dmat = sc->sc_dmat;
+ if (hpctype == 3)
+ ha.hpc_regs = &hpc3_values;
+ else
+ ha.hpc_regs = &hpc1_values;
+ ha.hpc_regs->revision = hpctype;
+
+ /*
+ * XXX On hpc@gio boards such as the E++, this will cause
+ * XXX `wdsc not configured' messages (or sq on SCSI
+ * XXX boards. We need to move some device detection
+ * XXX in there, or figure out if there is a way to know
+ * XXX what is really connected.
+ */
+ config_found_sm(self, &ha, hpc_print, hpc_submatch);
+ }
+
+ /*
+ * Attach the clock chip as well if on hpc0.
+ */
+ if (isonboard) {
+ if (sys_config.system_type == SGI_IP20) {
+ ha.ha_name = "dpclock";
+ ha.ha_devoff = HPC1_PBUS_BBRAM;
+ } else {
+ ha.ha_name = "dsclock";
+ ha.ha_devoff = HPC3_PBUS_BBRAM;
+ }
+ ha.ha_dmaoff = 0;
+ ha.ha_irq = -1;
+ ha.ha_st = sc->sc_ct;
+ ha.ha_sh = sc->sc_ch;
+ ha.ha_dmat = sc->sc_dmat;
+ ha.hpc_regs = NULL;
+
+ config_found_sm(self, &ha, hpc_print, hpc_submatch);
+
+ if (sys_config.system_type == SGI_IP20) {
+ timeout_set(&sc->sc_blink_tmo, hpc_blink, sc);
+ hpc_blink(sc);
+ } else {
+ timeout_set(&sc->sc_blink_tmo, hpc_blink_ioc, sc);
+ hpc_blink_ioc(sc);
+ }
+ }
+}
+
+/*
+ * HPC revision detection isn't as simple as it should be. Devices probe
+ * differently depending on their slots, but luckily there is only one
+ * instance in which we have to decide the major revision (HPC1 vs HPC3).
+ *
+ * The HPC is found in the following configurations:
+ * o Indigo R4k
+ * One on-board HPC1 or HPC1.5.
+ * Up to two additional HPC1.5's in GIO slots 0 and 1.
+ * o Indy
+ * One on-board HPC3.
+ * Up to two additional HPC1.5's in GIO slots 0 and 1.
+ * o Challenge S
+ * One on-board HPC3.
+ * Up to one additional HPC3 on the IOPLUS board (if installed).
+ * Up to one additional HPC1.5 in slot 1 of the IOPLUS board.
+ * o Indigo2, Challenge M
+ * One on-board HPC3.
+ *
+ * All we really have to worry about is the IP22 case.
+ */
+int
+hpc_revision(struct hpc_softc *sc, struct gio_attach_args *ga)
+{
+ uint32_t reg;
+
+ /* No hardware ever supported the last hpc base address. */
+ if (ga->ga_addr == HPC_BASE_ADDRESS_3)
+ return 0;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr + HPC1_BIGENDIAN,
+ CCA_NC), &reg) != 0) {
+ if (((reg >> HPC1_REVSHIFT) & HPC1_REVMASK) ==
+ HPC1_REV15)
+ return 15;
+ else
+ return 1;
+ }
+ return 1;
+
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ /*
+ * If IP22, probe slot 0 to determine if HPC1.5 or HPC3. Slot 1
+ * must be HPC1.5.
+ */
+ if (ga->ga_addr == HPC_BASE_ADDRESS_0)
+ return 3;
+
+ if (ga->ga_addr == HPC_BASE_ADDRESS_2)
+ return 15;
+
+ /*
+ * Probe for it. We use one of the PBUS registers. Note
+ * that this probe succeeds with my E++ adapter in slot 1
+ * (bad), but it appears to always do the right thing in
+ * slot 0 (good!) and we're only worried about that one
+ * anyhow.
+ */
+ if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr +
+ HPC3_PBUS_CH7_BP, CCA_NC), &reg) != 0)
+ return 15;
+ else
+ return 3;
+ }
+
+ return 0;
+}
+
+int
+hpc_submatch(struct device *parent, void *vcf, void *aux)
+{
+ struct cfdata *cf = (struct cfdata *)vcf;
+ struct hpc_attach_args *ha = (struct hpc_attach_args *)aux;
+
+ if (cf->cf_loc[0 /*HPCCF_OFFSET*/] != -1 &&
+ (bus_addr_t)cf->cf_loc[0 /*HPCCF_OFFSET*/] != ha->ha_devoff)
+ return 0;
+
+ return (*cf->cf_attach->ca_match)(parent, cf, aux);
+}
+
+int
+hpc_print(void *aux, const char *pnp)
+{
+ struct hpc_attach_args *ha = aux;
+
+ if (pnp)
+ printf("%s at %s", ha->ha_name, pnp);
+
+ printf(" offset 0x%08lx", ha->ha_devoff);
+ if (ha->ha_irq >= 0)
+ printf(" irq %d", ha->ha_irq);
+
+ return UNCONF;
+}
+
+void
+hpc_blink(void *arg)
+{
+ struct hpc_softc *sc = arg;
+
+ bus_space_write_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS,
+ bus_space_read_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS) ^
+ HPC1_AUX_CONSLED);
+
+ timeout_add(&sc->sc_blink_tmo,
+ (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1)));
+}
+
+void
+hpc_blink_ioc(void *arg)
+{
+ struct hpc_softc *sc = arg;
+ uint32_t value;
+
+ /* This is a bit odd. To strobe the green LED, we have to toggle the
+ red control bit. */
+ value = bus_space_read_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET) &
+ 0xff;
+ value ^= IOC_RESET_LED_RED;
+ bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET, value);
+
+ timeout_add(&sc->sc_blink_tmo,
+ (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1)));
+}
+
+/*
+ * Read the eeprom associated with one of the HPC's.
+ *
+ * NB: An eeprom is not always present, but the HPC should be able to
+ * handle this gracefully. Any consumers should validate the data to
+ * ensure it's reasonable.
+ */
+int
+hpc_read_eeprom(int hpctype, bus_space_tag_t t, bus_space_handle_t h,
+ uint8_t *buf, size_t len)
+{
+ struct seeprom_descriptor sd;
+ bus_space_handle_t bsh;
+ bus_size_t offset;
+
+ if (!len || len & 0x1)
+ return (1);
+
+ offset = (hpctype == 3) ? HPC3_EEPROM_DATA : HPC1_AUX_REGS;
+
+ if (bus_space_subregion(t, h, offset, 1, &bsh) != 0)
+ return (1);
+
+ sd.sd_chip = C56_66;
+ sd.sd_tag = t;
+ sd.sd_bsh = bsh;
+ sd.sd_regsize = 1;
+ sd.sd_control_offset = 0;
+ sd.sd_status_offset = 0;
+ sd.sd_dataout_offset = 0;
+ sd.sd_DI = 0x10; /* EEPROM -> CPU */
+ sd.sd_DO = 0x08; /* CPU -> EEPROM */
+ sd.sd_CK = 0x04;
+ sd.sd_CS = 0x02;
+ sd.sd_MS = 0;
+ sd.sd_RDY = 0;
+
+ if (read_seeprom(&sd, (uint16_t *)buf, 0, len / 2) != 1)
+ return (1);
+
+ bus_space_unmap(t, bsh, 1);
+
+ return 0;
+}