summaryrefslogtreecommitdiffstats
path: root/sys/dev/advansys/adv_isa.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/advansys/adv_isa.c')
-rw-r--r--sys/dev/advansys/adv_isa.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/sys/dev/advansys/adv_isa.c b/sys/dev/advansys/adv_isa.c
new file mode 100644
index 0000000..7162a9b
--- /dev/null
+++ b/sys/dev/advansys/adv_isa.c
@@ -0,0 +1,417 @@
+/*
+ * Device probe and attach routines for the following
+ * Advanced Systems Inc. SCSI controllers:
+ *
+ * Connectivity Products:
+ * ABP510/5150 - Bus-Master ISA (240 CDB) *
+ * ABP5140 - Bus-Master ISA PnP (16 CDB) * **
+ * ABP5142 - Bus-Master ISA PnP with floppy (16 CDB) ***
+ *
+ * Single Channel Products:
+ * ABP542 - Bus-Master ISA with floppy (240 CDB)
+ * ABP842 - Bus-Master VL (240 CDB)
+ *
+ * Dual Channel Products:
+ * ABP852 - Dual Channel Bus-Master VL (240 CDB Per Channel)
+ *
+ * * This board has been shipped by HP with the 4020i CD-R drive.
+ * The board has no BIOS so it cannot control a boot device, but
+ * it can control any secondary SCSI device.
+ * ** This board has been sold by SIIG as the i540 SpeedMaster.
+ * *** This board has been sold by SIIG as the i542 SpeedMaster.
+ *
+ * Copyright (c) 1996, 1997 Justin T. Gibbs.
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <isa/isavar.h>
+
+#include <dev/advansys/advansys.h>
+
+#include <cam/scsi/scsi_all.h>
+
+#define ADV_ISA_MAX_DMA_ADDR (0x00FFFFFFL)
+#define ADV_ISA_MAX_DMA_COUNT (0x00FFFFFFL)
+
+#define ADV_VL_MAX_DMA_ADDR (0x07FFFFFFL)
+#define ADV_VL_MAX_DMA_COUNT (0x07FFFFFFL)
+
+/*
+ * The overrun buffer shared amongst all ISA/VL adapters.
+ */
+static u_int8_t* overrun_buf;
+static bus_dma_tag_t overrun_dmat;
+static bus_dmamap_t overrun_dmamap;
+static bus_addr_t overrun_physbase;
+
+/* Possible port addresses an ISA or VL adapter can live at */
+static u_int16_t adv_isa_ioports[] =
+{
+ 0x100,
+ 0x110, /* First selection in BIOS setup */
+ 0x120,
+ 0x130, /* Second selection in BIOS setup */
+ 0x140,
+ 0x150, /* Third selection in BIOS setup */
+ 0x190, /* Fourth selection in BIOS setup */
+ 0x210, /* Fifth selection in BIOS setup */
+ 0x230, /* Sixth selection in BIOS setup */
+ 0x250, /* Seventh selection in BIOS setup */
+ 0x330 /* Eighth and default selection in BIOS setup */
+};
+
+#define MAX_ISA_IOPORT_INDEX (sizeof(adv_isa_ioports)/sizeof(u_int16_t) - 1)
+
+static int adv_isa_probe(device_t dev);
+static int adv_isa_attach(device_t dev);
+static void adv_set_isapnp_wait_for_key(void);
+static int adv_get_isa_dma_channel(struct adv_softc *adv);
+static int adv_set_isa_dma_settings(struct adv_softc *adv);
+
+static int
+adv_isa_probe(device_t dev)
+{
+ int port_index;
+ int max_port_index;
+ u_long iobase, iocount, irq;
+ int user_iobase = 0;
+ int rid = 0;
+ void *ih;
+ struct resource *iores, *irqres;
+
+ /*
+ * Default to scanning all possible device locations.
+ */
+ port_index = 0;
+ max_port_index = MAX_ISA_IOPORT_INDEX;
+
+ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, &iocount) == 0) {
+ user_iobase = 1;
+ for (;port_index <= max_port_index; port_index++)
+ if (iobase <= adv_isa_ioports[port_index])
+ break;
+ if ((port_index > max_port_index)
+ || (iobase != adv_isa_ioports[port_index])) {
+ if (bootverbose)
+ printf("adv%d: Invalid baseport of 0x%lx specified. "
+ "Nearest valid baseport is 0x%x. Failing "
+ "probe.\n", device_get_unit(dev), iobase,
+ (port_index <= max_port_index) ?
+ adv_isa_ioports[port_index] :
+ adv_isa_ioports[max_port_index]);
+ return ENXIO;
+ }
+ max_port_index = port_index;
+ }
+
+ /* Perform the actual probing */
+ adv_set_isapnp_wait_for_key();
+ for (;port_index <= max_port_index; port_index++) {
+ u_int16_t port_addr = adv_isa_ioports[port_index];
+ bus_size_t maxsegsz;
+ bus_size_t maxsize;
+ bus_addr_t lowaddr;
+ int error;
+ struct adv_softc *adv;
+
+ if (port_addr == 0)
+ /* Already been attached */
+ continue;
+
+ if (bus_set_resource(dev, SYS_RES_IOPORT, 0, port_addr, 1))
+ continue;
+
+ /* XXX what is the real portsize? */
+ iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (iores == NULL)
+ continue;
+
+ if (adv_find_signature(rman_get_bustag(iores),
+ rman_get_bushandle(iores)) == 0) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
+ continue;
+ }
+
+ /*
+ * Got one. Now allocate our softc
+ * and see if we can initialize the card.
+ */
+ adv = adv_alloc(dev, rman_get_bustag(iores),
+ rman_get_bushandle(iores));
+ if (adv == NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
+ break;
+ }
+
+ /*
+ * Stop the chip.
+ */
+ ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT);
+ ADV_OUTW(adv, ADV_CHIP_STATUS, 0);
+ /*
+ * Determine the chip version.
+ */
+ adv->chip_version = ADV_INB(adv, ADV_NONEISA_CHIP_REVISION);
+ if ((adv->chip_version >= ADV_CHIP_MIN_VER_VL)
+ && (adv->chip_version <= ADV_CHIP_MAX_VER_VL)) {
+ adv->type = ADV_VL;
+ maxsegsz = ADV_VL_MAX_DMA_COUNT;
+ maxsize = BUS_SPACE_MAXSIZE_32BIT;
+ lowaddr = ADV_VL_MAX_DMA_ADDR;
+ bus_delete_resource(dev, SYS_RES_DRQ, 0);
+ } else if ((adv->chip_version >= ADV_CHIP_MIN_VER_ISA)
+ && (adv->chip_version <= ADV_CHIP_MAX_VER_ISA)) {
+ if (adv->chip_version >= ADV_CHIP_MIN_VER_ISA_PNP) {
+ adv->type = ADV_ISAPNP;
+ ADV_OUTB(adv, ADV_REG_IFC,
+ ADV_IFC_INIT_DEFAULT);
+ } else {
+ adv->type = ADV_ISA;
+ }
+ maxsegsz = ADV_ISA_MAX_DMA_COUNT;
+ maxsize = BUS_SPACE_MAXSIZE_24BIT;
+ lowaddr = ADV_ISA_MAX_DMA_ADDR;
+ adv->isa_dma_speed = ADV_DEF_ISA_DMA_SPEED;
+ adv->isa_dma_channel = adv_get_isa_dma_channel(adv);
+ bus_set_resource(dev, SYS_RES_DRQ, 0,
+ adv->isa_dma_channel, 1);
+ } else {
+ panic("advisaprobe: Unknown card revision\n");
+ }
+
+ /*
+ * Allocate a parent dmatag for all tags created
+ * by the MI portions of the advansys driver
+ */
+ /* XXX Should be a child of the ISA bus dma tag */
+ error = bus_dma_tag_create(/*parent*/NULL,
+ /*alignemnt*/1,
+ /*boundary*/0,
+ lowaddr,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL,
+ /*filterarg*/NULL,
+ maxsize,
+ /*nsegs*/BUS_SPACE_UNRESTRICTED,
+ maxsegsz,
+ /*flags*/0,
+ &adv->parent_dmat);
+
+ if (error != 0) {
+ printf("%s: Could not allocate DMA tag - error %d\n",
+ adv_name(adv), error);
+ adv_free(adv);
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
+ break;
+ }
+
+ adv->init_level += 2;
+
+ if (overrun_buf == NULL) {
+ /* Need to allocate our overrun buffer */
+ if (bus_dma_tag_create(adv->parent_dmat,
+ /*alignment*/8,
+ /*boundary*/0,
+ ADV_ISA_MAX_DMA_ADDR,
+ BUS_SPACE_MAXADDR,
+ /*filter*/NULL,
+ /*filterarg*/NULL,
+ ADV_OVERRUN_BSIZE,
+ /*nsegments*/1,
+ BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0,
+ &overrun_dmat) != 0) {
+ adv_free(adv);
+ bus_release_resource(dev, SYS_RES_IOPORT, 0,
+ iores);
+ break;
+ }
+ if (bus_dmamem_alloc(overrun_dmat,
+ (void **)&overrun_buf,
+ BUS_DMA_NOWAIT,
+ &overrun_dmamap) != 0) {
+ bus_dma_tag_destroy(overrun_dmat);
+ adv_free(adv);
+ bus_release_resource(dev, SYS_RES_IOPORT, 0,
+ iores);
+ break;
+ }
+ /* And permanently map it in */
+ bus_dmamap_load(overrun_dmat, overrun_dmamap,
+ overrun_buf, ADV_OVERRUN_BSIZE,
+ adv_map, &overrun_physbase,
+ /*flags*/0);
+ }
+
+ adv->overrun_physbase = overrun_physbase;
+
+ if (adv_init(adv) != 0) {
+ bus_dmamap_unload(overrun_dmat, overrun_dmamap);
+ bus_dmamem_free(overrun_dmat, overrun_buf,
+ overrun_dmamap);
+ bus_dma_tag_destroy(overrun_dmat);
+ adv_free(adv);
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
+ break;
+ }
+
+ switch (adv->type) {
+ case ADV_ISAPNP:
+ if (adv->chip_version == ADV_CHIP_VER_ASYN_BUG) {
+ adv->bug_fix_control
+ |= ADV_BUG_FIX_ASYN_USE_SYN;
+ adv->fix_asyn_xfer = ~0;
+ }
+ /* Fall Through */
+ case ADV_ISA:
+ adv->max_dma_count = ADV_ISA_MAX_DMA_COUNT;
+ adv->max_dma_addr = ADV_ISA_MAX_DMA_ADDR;
+ adv_set_isa_dma_settings(adv);
+ break;
+
+ case ADV_VL:
+ adv->max_dma_count = ADV_VL_MAX_DMA_COUNT;
+ adv->max_dma_addr = ADV_VL_MAX_DMA_ADDR;
+ break;
+ default:
+ panic("advisaprobe: Invalid card type\n");
+ }
+
+ /* Determine our IRQ */
+ if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL))
+ bus_set_resource(dev, SYS_RES_IRQ, 0,
+ adv_get_chip_irq(adv), 1);
+ else
+ adv_set_chip_irq(adv, irq);
+
+ irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_ACTIVE);
+ if (irqres == NULL ||
+ bus_setup_intr(dev, irqres, INTR_TYPE_CAM|INTR_ENTROPY,
+ adv_intr, adv, &ih)) {
+ bus_dmamap_unload(overrun_dmat, overrun_dmamap);
+ bus_dmamem_free(overrun_dmat, overrun_buf,
+ overrun_dmamap);
+ bus_dma_tag_destroy(overrun_dmat);
+ adv_free(adv);
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, iores);
+ break;
+ }
+
+ /* Mark as probed */
+ adv_isa_ioports[port_index] = 0;
+ return 0;
+ }
+
+ if (user_iobase)
+ bus_set_resource(dev, SYS_RES_IOPORT, 0, iobase, iocount);
+ else
+ bus_delete_resource(dev, SYS_RES_IOPORT, 0);
+
+ return ENXIO;
+}
+
+static int
+adv_isa_attach(device_t dev)
+{
+ struct adv_softc *adv = device_get_softc(dev);
+
+ return (adv_attach(adv));
+}
+
+static int
+adv_get_isa_dma_channel(struct adv_softc *adv)
+{
+ int channel;
+
+ channel = ADV_INW(adv, ADV_CONFIG_LSW) & ADV_CFG_LSW_ISA_DMA_CHANNEL;
+ if (channel == 0x03)
+ return (0);
+ else if (channel == 0x00)
+ return (7);
+ return (channel + 4);
+}
+
+static int
+adv_set_isa_dma_settings(struct adv_softc *adv)
+{
+ u_int16_t cfg_lsw;
+ u_int8_t value;
+
+ if ((adv->isa_dma_channel >= 5) && (adv->isa_dma_channel <= 7)) {
+ if (adv->isa_dma_channel == 7)
+ value = 0x00;
+ else
+ value = adv->isa_dma_channel - 4;
+ cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW)
+ & ~ADV_CFG_LSW_ISA_DMA_CHANNEL;
+ cfg_lsw |= value;
+ ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
+
+ adv->isa_dma_speed &= 0x07;
+ adv_set_bank(adv, 1);
+ ADV_OUTB(adv, ADV_DMA_SPEED, adv->isa_dma_speed);
+ adv_set_bank(adv, 0);
+ isa_dmacascade(adv->isa_dma_channel);
+ }
+ return (0);
+}
+
+static void
+adv_set_isapnp_wait_for_key(void)
+{
+ static int isapnp_wait_set = 0;
+ if (isapnp_wait_set == 0) {
+ outb(ADV_ISA_PNP_PORT_ADDR, 0x02);
+ outb(ADV_ISA_PNP_PORT_WRITE, 0x02);
+ isapnp_wait_set++;
+ }
+}
+
+static device_method_t adv_isa_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, adv_isa_probe),
+ DEVMETHOD(device_attach, adv_isa_attach),
+ { 0, 0 }
+};
+
+static driver_t adv_isa_driver = {
+ "adv", adv_isa_methods, sizeof(struct adv_softc)
+};
+
+static devclass_t adv_isa_devclass;
+DRIVER_MODULE(adv, isa, adv_isa_driver, adv_isa_devclass, 0, 0);
OpenPOWER on IntegriCloud