/* * Product specific probe and attach routines for: * 27/284X and aic7770 motherboard SCSI controllers * * Copyright (c) 1994, 1995, 1996, 1997, 1998 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 immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 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. * * $Id: aic7770.c,v 1.40 1997/07/20 06:31:08 bde Exp $ */ #include "eisa.h" #if NEISA > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #define EISA_DEVICE_ID_ADAPTEC_AIC7770 0x04907770 #define EISA_DEVICE_ID_ADAPTEC_274x 0x04907771 #define EISA_DEVICE_ID_ADAPTEC_284xB 0x04907756 /* BIOS enabled */ #define EISA_DEVICE_ID_ADAPTEC_284x 0x04907757 /* BIOS disabled*/ #define AHC_EISA_SLOT_OFFSET 0xc00 #define AHC_EISA_IOSIZE 0x100 #define INTDEF 0x5cul /* Interrupt Definition Register */ static int aic7770probe(void); static int aic7770_attach(struct eisa_device *e_dev); static void aha2840_load_seeprom(struct ahc_softc *ahc); static struct eisa_driver ahc_eisa_driver = { "ahc", aic7770probe, aic7770_attach, /*shutdown*/NULL, &ahc_unit }; DATA_SET (eisadriver_set, ahc_eisa_driver); static const char *aic7770_match(eisa_id_t type); static const char* aic7770_match(type) eisa_id_t type; { switch (type) { case EISA_DEVICE_ID_ADAPTEC_AIC7770: return ("Adaptec aic7770 SCSI host adapter"); break; case EISA_DEVICE_ID_ADAPTEC_274x: return ("Adaptec 274X SCSI host adapter"); break; case EISA_DEVICE_ID_ADAPTEC_284xB: case EISA_DEVICE_ID_ADAPTEC_284x: return ("Adaptec 284X SCSI host adapter"); break; default: break; } return (NULL); } static int aic7770probe(void) { u_int32_t iobase; u_int32_t irq; u_int8_t intdef; u_int8_t hcntrl; struct eisa_device *e_dev; int count; e_dev = NULL; count = 0; while ((e_dev = eisa_match_dev(e_dev, aic7770_match))) { iobase = (e_dev->ioconf.slot * EISA_SLOT_SIZE) + AHC_EISA_SLOT_OFFSET; /* Pause the card preseving the IRQ type */ hcntrl = inb(iobase + HCNTRL) & IRQMS; outb(iobase + HCNTRL, hcntrl | PAUSE); eisa_add_iospace(e_dev, iobase, AHC_EISA_IOSIZE, RESVADDR_NONE); intdef = inb(INTDEF + iobase); irq = intdef & 0xf; switch (irq) { case 9: case 10: case 11: case 12: case 14: case 15: break; default: printf("aic7770 at slot %d: illegal " "irq setting %d\n", e_dev->ioconf.slot, intdef); irq = 0; break; } if (irq == 0) continue; eisa_add_intr(e_dev, irq); eisa_registerdev(e_dev, &ahc_eisa_driver); count++; } return count; } static int aic7770_attach(struct eisa_device *e_dev) { ahc_chip chip; struct ahc_softc *ahc; resvaddr_t *iospace; int unit = e_dev->unit; int irq; int error; if (TAILQ_FIRST(&e_dev->ioconf.irqs) == NULL) return (-1); irq = TAILQ_FIRST(&e_dev->ioconf.irqs)->irq_no; iospace = e_dev->ioconf.ioaddrs.lh_first; if (!iospace) return -1; switch (e_dev->id) { case EISA_DEVICE_ID_ADAPTEC_274x: case EISA_DEVICE_ID_ADAPTEC_AIC7770: chip = AHC_AIC7770|AHC_EISA; break; case EISA_DEVICE_ID_ADAPTEC_284xB: case EISA_DEVICE_ID_ADAPTEC_284x: chip = AHC_AIC7770|AHC_VL; break; default: printf("aic7770_attach: Unknown device type!\n"); return -1; break; } if (!(ahc = ahc_alloc(unit, iospace->addr, NULL, chip, AHC_AIC7770_FE, AHC_FNONE, NULL))) return -1; ahc->channel = 'A'; ahc->channel_b = 'B'; /* XXX Should be a child of the EISA bus dma tag */ error = bus_dma_tag_create(/*parent*/NULL, /*alignment*/0, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG, /*maxsegsz*/AHC_MAXTRANSFER_SIZE, /*flags*/BUS_DMA_ALLOCNOW, &ahc->dmat); if (error != 0) { printf("%s: Could not allocate DMA tag - error %d\n", ahc_name(ahc), error); ahc_free(ahc); return -1; } eisa_reg_start(e_dev); if (eisa_reg_iospace(e_dev, iospace)) { ahc_free(ahc); return -1; } if (ahc_reset(ahc) != 0) { ahc_free(ahc); return -1; } /* * The IRQMS bit enables level sensitive interrupts. Only allow * IRQ sharing if it's set. */ if (eisa_reg_intr(e_dev, irq, ahc_intr, (void *)ahc, &cam_imask, /*shared ==*/ahc->pause & IRQMS)) { ahc_free(ahc); return -1; } eisa_reg_end(e_dev); /* * Tell the user what type of interrupts we're using. * usefull for debugging irq problems */ if (bootverbose) { printf("%s: Using %s Interrupts\n", ahc_name(ahc), ahc->pause & IRQMS ? "Level Sensitive" : "Edge Triggered"); } /* * Now that we know we own the resources we need, do the * card initialization. * * First, the aic7770 card specific setup. */ switch (chip & (AHC_EISA|AHC_VL)) { case AHC_EISA: { u_int8_t biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL); #if 0 for (i = TARG_SCSIRATE; i <= HA_274_BIOSCTRL; i+=8) { printf("0x%x, 0x%x, 0x%x, 0x%x, " "0x%x, 0x%x, 0x%x, 0x%x\n", ahc_inb(ahc, i), ahc_inb(ahc, i+1), ahc_inb(ahc, i+2), ahc_inb(ahc, i+3), ahc_inb(ahc, i+4), ahc_inb(ahc, i+5), ahc_inb(ahc, i+6), ahc_inb(ahc, i+7)); } #endif /* Get the primary channel information */ ahc->flags |= (biosctrl & CHANNEL_B_PRIMARY); if ((biosctrl & BIOSMODE) == BIOSDISABLED) { ahc->flags |= AHC_USEDEFAULTS; } else { if ((ahc->features & AHC_WIDE) != 0) { ahc->our_id = ahc_inb(ahc, SCSICONF + 1) & HWSCSIID; } else { ahc->our_id = ahc_inb(ahc, SCSICONF) & HSCSIID; ahc->our_id_b = ahc_inb(ahc, SCSICONF) & HSCSIID; } } break; } case AHC_VL: { aha2840_load_seeprom(ahc); break; } default: break; } /* * See if we have a Rev E or higher aic7770. Anything below a * Rev E will have a R/O autoflush disable configuration bit. */ { char *id_string; u_int8_t sblkctl; u_int8_t sblkctl_orig; sblkctl_orig = ahc_inb(ahc, SBLKCTL); sblkctl = sblkctl_orig ^ AUTOFLUSHDIS; ahc_outb(ahc, SBLKCTL, sblkctl); sblkctl = ahc_inb(ahc, SBLKCTL); if (sblkctl != sblkctl_orig) { id_string = "aic7770 >= Rev E, "; /* * Ensure autoflush is enabled */ sblkctl &= ~AUTOFLUSHDIS; ahc_outb(ahc, SBLKCTL, sblkctl); } else id_string = "aic7770 <= Rev C, "; printf("%s: %s", ahc_name(ahc), id_string); } /* Setup the FIFO threshold and the bus off time */ { u_int8_t hostconf = ahc_inb(ahc, HOSTCONF); ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH); ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF); } /* * Generic aic7xxx initialization. */ if (ahc_init(ahc)) { ahc_free(ahc); /* * The board's IRQ line is not yet enabled so it's safe * to release the irq. */ eisa_release_intr(e_dev, irq, ahc_intr); return -1; } /* * Enable the board's BUS drivers */ ahc_outb(ahc, BCTL, ENABLE); /* * Enable our interrupt handler. */ if (eisa_enable_intr(e_dev, irq)) { ahc_free(ahc); eisa_release_intr(e_dev, irq, ahc_intr); return -1; } /* Attach sub-devices - always succeeds */ ahc_attach(ahc); return 0; } /* * Read the 284x SEEPROM. */ static void aha2840_load_seeprom(struct ahc_softc *ahc) { struct seeprom_descriptor sd; struct seeprom_config sc; u_int16_t checksum = 0; u_int8_t scsi_conf; u_int8_t sxfrctl1; int have_seeprom; sd.sd_tag = ahc->tag; sd.sd_bsh = ahc->bsh; sd.sd_control_offset = SEECTL_2840; sd.sd_status_offset = STATUS_2840; sd.sd_dataout_offset = STATUS_2840; sd.sd_chip = C46; sd.sd_MS = 0; sd.sd_RDY = EEPROM_TF; sd.sd_CS = CS_2840; sd.sd_CK = CK_2840; sd.sd_DO = DO_2840; sd.sd_DI = DI_2840; if (bootverbose) printf("%s: Reading SEEPROM...", ahc_name(ahc)); have_seeprom = read_seeprom(&sd, (u_int16_t *)&sc, /*start_addr*/0, sizeof(sc)/2); if (have_seeprom) { /* Check checksum */ int i; int maxaddr = (sizeof(sc)/2) - 1; u_int16_t *scarray = (u_int16_t *)≻ for (i = 0; i < maxaddr; i++) checksum = checksum + scarray[i]; if (checksum != sc.checksum) { if(bootverbose) printf ("checksum error\n"); have_seeprom = 0; } else if (bootverbose) { printf("done.\n"); } } if (!have_seeprom) { if (bootverbose) printf("%s: No SEEPROM available\n", ahc_name(ahc)); ahc->flags |= AHC_USEDEFAULTS; } else { /* * Put the data we've collected down into SRAM * where ahc_init will find it. */ int i; int max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8; for (i = 0; i < max_targ; i++){ u_int8_t target_settings; target_settings = (sc.device_flags[i] & CFXFER) << 4; if (sc.device_flags[i] & CFSYNCH) target_settings |= SOFS; if (sc.device_flags[i] & CFWIDEB) target_settings |= WIDEXFER; if (sc.device_flags[i] & CFDISC) ahc->discenable |= (0x01 << i); ahc_outb(ahc, TARG_SCSIRATE + i, target_settings); } ahc_outb(ahc, DISC_DSB, ~(ahc->discenable & 0xff)); ahc_outb(ahc, DISC_DSB + 1, ~((ahc->discenable >> 8) & 0xff)); ahc->our_id = sc.brtime_id & CFSCSIID; scsi_conf = (ahc->our_id & 0x7); if (sc.adapter_control & CFSPARITY) scsi_conf |= ENSPCHK; if (sc.adapter_control & CFRESETB) scsi_conf |= RESET_SCSI; if (sc.bios_control & CF284XEXTEND) ahc->flags |= AHC_EXTENDED_TRANS_A; /* Set SCSICONF info */ ahc_outb(ahc, SCSICONF, scsi_conf); sxfrctl1 = ahc_inb(ahc, SXFRCTL1); if (sc.adapter_control & CF284XSTERM) { sxfrctl1 |= STPWEN; } else { sxfrctl1 &= ~STPWEN; } ahc_outb(ahc, SXFRCTL1, sxfrctl1); } } #endif /* NEISA > 0 */