aboutsummaryrefslogtreecommitdiff
path: root/sys/pci/aic7870.c
diff options
context:
space:
mode:
authorJustin T. Gibbs <gibbs@FreeBSD.org>1996-01-03 06:34:10 +0000
committerJustin T. Gibbs <gibbs@FreeBSD.org>1996-01-03 06:34:10 +0000
commitc5753242cfb0031ff4af6e9cac55736a526ed741 (patch)
tree54c0518019fe26bf1ee4ebb4f259a1722ab87877 /sys/pci/aic7870.c
parent6cb248359ebca3a22e049d93087816237c81f170 (diff)
Notes
Diffstat (limited to 'sys/pci/aic7870.c')
-rw-r--r--sys/pci/aic7870.c382
1 files changed, 374 insertions, 8 deletions
diff --git a/sys/pci/aic7870.c b/sys/pci/aic7870.c
index 0182e5fa4842..73ab551e4f05 100644
--- a/sys/pci/aic7870.c
+++ b/sys/pci/aic7870.c
@@ -19,7 +19,7 @@
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
- * $Id: aic7870.c,v 1.19 1995/11/07 05:33:27 gibbs Exp $
+ * $Id: aic7870.c,v 1.20 1995/12/14 09:53:55 phk Exp $
*/
#include <pci.h>
@@ -28,16 +28,26 @@
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
+
#include <pci/pcireg.h>
#include <pci/pcivar.h>
+
+#include <machine/clock.h>
+
#include <i386/scsi/aic7xxx.h>
+#include <i386/scsi/93cx6.h>
+
+#include <dev/aic7xxx/aic7xxx_reg.h>
#define PCI_BASEADR0 PCI_MAP_REG_START
#define PCI_DEVICE_ID_ADAPTEC_3940U 0x82789004ul
+#define PCI_DEVICE_ID_ADAPTEC_2944U 0x84789004ul
#define PCI_DEVICE_ID_ADAPTEC_2940U 0x81789004ul
#define PCI_DEVICE_ID_ADAPTEC_3940 0x72789004ul
+#define PCI_DEVICE_ID_ADAPTEC_2944 0x74789004ul
#define PCI_DEVICE_ID_ADAPTEC_2940 0x71789004ul
#define PCI_DEVICE_ID_ADAPTEC_AIC7880 0x80789004ul
#define PCI_DEVICE_ID_ADAPTEC_AIC7870 0x70789004ul
@@ -47,7 +57,7 @@
#define MPORTMODE 0x00000400ul /* aic7870 only */
#define RAMPSM 0x00000200ul /* aic7870 only */
#define VOLSENSE 0x00000100ul
-#define RAMENB 0x00000080ul
+#define SCBRAMSEL 0x00000080ul
#define MRDCEN 0x00000040ul
#define EXTSCBTIME 0x00000020ul /* aic7870 only */
#define EXTSCBPEN 0x00000010ul /* aic7870 only */
@@ -56,8 +66,86 @@
#define STPWLEVEL 0x00000002ul
#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x0000003ful /* only 5 bits */
+#define LATTIME 0x0000ff00ul
+
+/*
+ * Define the format of the aic78X0 SEEPROM registers (16 bits).
+ *
+ */
+
+struct seeprom_config {
+
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device */
+/* UNUSED 0x00C0 */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+/* UNUSED 0xf800 */
+ unsigned short device_flags[16]; /* words 0-15 */
+
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+/* UNUSED 0x0060 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xff00 */
+ unsigned short bios_control; /* word 16 */
+
+/*
+ * Host Adapter Control Bits
+ */
+/* UNUSED 0x0001 */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */
+#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
+#define CFSPARITY 0x0010 /* SCSI parity */
+/* UNUSED 0x0020 */
+#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */
+/* UNUSED 0xff80 */
+ unsigned short adapter_control; /* word 17 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time */
+ unsigned short brtime_id; /* word 18 */
+
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG 0x00ff /* maximum targets */
+/* UNUSED 0xff00 */
+ unsigned short max_targets; /* word 19 */
+
+ unsigned short res_1[11]; /* words 20-30 */
+ unsigned short checksum; /* word 31 */
+
+};
+
static char* aic7870_probe __P((pcici_t tag, pcidi_t type));
static void aic7870_attach __P((pcici_t config_id, int unit));
+static int load_seeprom __P((struct ahc_data *ahc));
+static int acquire_seeprom __P((u_long offset, u_short CS, u_short CK,
+ u_short DO, u_short DI, u_short RDY,
+ u_short MS));
+static void release_seeprom __P((u_long offset, u_short CS, u_short CK,
+ u_short DO, u_short DI, u_short RDY,
+ u_short MS));
static u_char aic3940_count;
@@ -81,9 +169,15 @@ aic7870_probe (pcici_t tag, pcidi_t type)
case PCI_DEVICE_ID_ADAPTEC_3940:
return ("Adaptec 3940 SCSI host adapter");
break;
+ case PCI_DEVICE_ID_ADAPTEC_2944U:
+ return ("Adaptec 2944 Ultra SCSI host adapter");
+ break;
case PCI_DEVICE_ID_ADAPTEC_2940U:
return ("Adaptec 2940 Ultra SCSI host adapter");
break;
+ case PCI_DEVICE_ID_ADAPTEC_2944:
+ return ("Adaptec 2944 SCSI host adapter");
+ break;
case PCI_DEVICE_ID_ADAPTEC_2940:
return ("Adaptec 2940 SCSI host adapter");
break;
@@ -137,9 +231,11 @@ aic7870_attach(config_id, unit)
/* Even though it doesn't turn on RAMPS, it has them */
ahc_f |= AHC_EXTSCB;
break;
+ case PCI_DEVICE_ID_ADAPTEC_2944U:
case PCI_DEVICE_ID_ADAPTEC_2940U:
ahc_t = AHC_294U;
break;
+ case PCI_DEVICE_ID_ADAPTEC_2944:
case PCI_DEVICE_ID_ADAPTEC_2940:
ahc_t = AHC_294;
break;
@@ -156,19 +252,59 @@ aic7870_attach(config_id, unit)
break;
}
+ ahc_reset(io_port);
+
if(ahc_t & AHC_AIC7870){
u_long devconfig = pci_conf_read(config_id, DEVCONFIG);
- if(devconfig & (RAMPSM|RAMENB)) {
+ if(devconfig & (RAMPSM)) {
/*
* External SRAM present. Have the probe walk
* the SCBs to see how much SRAM we have and set
- * the number of SCBs accordingly.
+ * the number of SCBs accordingly. We have to
+ * turn off SCBRAMSEL to access the external
+ * SCB SRAM.
+ *
+ * It seems that early versions of the aic7870
+ * didn't use these bits, hence the hack for the
+ * 3940 above. I would guess that recent 3940s
+ * using later aic7870 or aic7880 chips do
+ * actually set RAMPSM.
+ *
+ * The documentation isn't clear, but it sounds
+ * like the value written to devconfig must not
+ * have RAMPSM set. The second sixteen bits of
+ * the register are R/O anyway, so it shouldn't
+ * affect RAMPSM either way.
*/
+ devconfig &= ~(RAMPSM|SCBRAMSEL);
+ pci_conf_write(config_id, DEVCONFIG, devconfig);
+
ahc_f |= AHC_EXTSCB;
}
}
- ahc_reset(io_port);
+ /*
+ * Ensure that we are using good values for the PCI burst size
+ * and latency timer.
+ */
+ {
+ u_long csize_lattime = pci_conf_read(config_id, CSIZE_LATTIME);
+ if((csize_lattime & CACHESIZE) == 0) {
+ /* default to 8DWDs. What's the PCI define for this? */
+ csize_lattime |= 8;
+ }
+ if((csize_lattime & LATTIME) == 0) {
+ /* Default to 64 PCLKS (is this a good value?) */
+ /* This may also be availble in the SEEPROM?? */
+ csize_lattime |= (64 << 8);
+ }
+ if(bootverbose)
+ printf("ahc%d: BurstLen = %dDWDs, "
+ "Latency Timer = %dPCLKS\n",
+ csize_lattime & CACHESIZE,
+ (csize_lattime >> 8) & 0xff);
+
+ }
if(!(ahc = ahc_alloc(unit, io_port, ahc_t, ahc_f)))
return; /* XXX PCI code should take return status */
@@ -183,15 +319,245 @@ aic7870_attach(config_id, unit)
*/
opri = splbio();
- if(ahc_init(unit)){
+ /*
+ * Do aic7870/aic7880/aic7850 specific initialization
+ */
+ {
+ u_char sblkctl;
+ char *id_string;
+ u_long iobase = ahc->baseport;
+
+ switch(ahc->type) {
+ case AHC_394U:
+ case AHC_294U:
+ case AHC_AIC7880:
+ {
+ id_string = "aic7880 ";
+ load_seeprom(ahc);
+ ahc->maxscbs = 16;
+ break;
+ }
+ case AHC_394:
+ case AHC_294:
+ case AHC_AIC7870:
+ {
+ id_string = "aic7870 ";
+ load_seeprom(ahc);
+ ahc->maxscbs = 16;
+ break;
+ }
+ case AHC_AIC7850:
+ {
+ id_string = "aic7850 ";
+ ahc->maxscbs = 3;
+ /* Assume there is no BIOS for these cards? */
+ ahc->flags |= AHC_USEDEFAULTS;
+ break;
+ }
+ default:
+ {
+ printf("ahc: Unknown controller type. Ignoring.\n");
+ return;
+ break;
+ }
+ }
+
+ printf("ahc%d: %s", unit, id_string);
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = inb(SBLKCTL + iobase);
+ outb(SBLKCTL + iobase, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+ /*
+ * I don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100%.
+ */
+ outb(DSPCISTATUS + iobase, DFTHRSH_100);
+
+ if(ahc->flags & AHC_USEDEFAULTS) {
+ /*
+ * PCI Adapter default setup
+ * Should only be used if the adapter does not have
+ * an SEEPROM and we don't think a BIOS was installed.
+ */
+ /* Set the host ID */
+ outb(SCSICONF + iobase, 7);
+ /* In case we are a wide card */
+ outb(SCSICONF + 1 + iobase, 7);
+ }
+
+ if(ahc->flags & AHC_EXTSCB) {
+ /*
+ * This adapter has external SCB memory.
+ * Walk the SCBs to determine how many there are.
+ */
+ int i;
+
+ for(i = 0; i < AHC_SCB_MAX; i++) {
+ outb(SCBPTR + iobase, i);
+ outb(SCBARRAY + iobase, 0xaa);
+ if(inb(SCBARRAY + iobase) == 0xaa){
+ outb(SCBARRAY + iobase, 0x55);
+ if(inb(SCBARRAY + iobase) == 0x55) {
+ continue;
+ }
+ }
+ break;
+ }
+ ahc->maxscbs = i;
+ }
+ }
+
+ if(ahc_init(ahc)){
ahc_free(ahc);
splx(opri);
return; /* XXX PCI code should take return status */
}
-
- ahc_attach(unit);
splx(opri);
+
+ ahc_attach(ahc);
return;
}
+/*
+ * Read the SEEPROM. Return 0 on failure
+ */
+int
+load_seeprom(ahc)
+ struct ahc_data *ahc;
+{
+ struct seeprom_config sc;
+ u_short *scarray = (u_short *)&sc;
+ u_short checksum = 0;
+ u_long iobase = ahc->baseport;
+ u_char host_id;
+ int have_seeprom, retval;
+
+ if(bootverbose)
+ printf("ahc%d: Reading SEEPROM...", ahc->unit);
+ have_seeprom = acquire_seeprom(iobase + SEECTL, SEECS,
+ SEECK, SEEDO, SEEDI, SEERDY, SEEMS);
+ if (have_seeprom) {
+ have_seeprom = read_seeprom(iobase + SEECTL,
+ (u_short *)&sc,
+ ahc->flags & AHC_CHNLB,
+ sizeof(sc)/2, SEECS, SEECK, SEEDO,
+ SEEDI, SEERDY, SEEMS);
+ release_seeprom(iobase + SEECTL, SEECS, SEECK, SEEDO,
+ SEEDI, SEERDY, SEEMS);
+ if (have_seeprom) {
+ /* Check checksum */
+ int i;
+
+ for (i = 0;i < (sizeof(sc)/2 - 1);i = i + 1)
+ checksum = checksum + scarray[i];
+ if (checksum != sc.checksum) {
+ printf ("checksum error");
+ have_seeprom = 0;
+ }
+ else {
+ if(bootverbose)
+ printf("done.\n");
+ host_id = (sc.brtime_id & CFSCSIID);
+ }
+ }
+ }
+ if (!have_seeprom) {
+ printf("\nahc%d: SEEPROM read failed, "
+ "using leftover BIOS values\n", ahc->unit);
+ retval = 0;
+
+ host_id = 0x7; /* Assume a default */
+ /*
+ * If we happen to be an ULTRA card,
+ * default to non-ultra mode.
+ */
+ ahc->type &= ~AHC_ULTRA;
+ }
+ else {
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ = sc.max_targets & CFMAXTARG;
+
+ for(i = 0; i <= max_targ; i++){
+ u_char 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);
+ outb(TARG_SCRATCH+i+iobase, target_settings);
+ }
+ outb(DISC_DSB + iobase, ~(ahc->discenable & 0xff));
+ outb(DISC_DSB + iobase + 1, ~((ahc->discenable >> 8) & 0xff));
+
+ host_id = sc.brtime_id & CFSCSIID;
+
+ if(ahc->type & AHC_ULTRA) {
+ /* Should we enable Ultra mode? */
+ if(!(sc.adapter_control & CFULTRAEN))
+ /* Treat us as a non-ultra card */
+ ahc->type &= ~AHC_ULTRA;
+ }
+ retval = 1;
+ }
+ /* Set the host ID */
+ outb(SCSICONF + iobase, host_id);
+ /* In case we are a wide card */
+ outb(SCSICONF + 1 + iobase, host_id);
+
+ return(retval);
+}
+
+static int
+acquire_seeprom(offset, CS, CK, DO, DI, RDY, MS)
+ u_long offset;
+ u_short CS; /* chip select */
+ u_short CK; /* clock */
+ u_short DO; /* data out */
+ u_short DI; /* data in */
+ u_short RDY; /* ready */
+ u_short MS; /* mode select */
+{
+ int wait;
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the chip reset, there
+ * should be no contention.
+ */
+ outb(offset, MS);
+ wait = 1000; /* 1 second timeout in msec */
+ while (--wait && ((inb(offset) & RDY) == 0)) {
+ DELAY (1000); /* delay 1 msec */
+ }
+ if ((inb(offset) & RDY) == 0) {
+ outb (offset, 0);
+ return (0);
+ }
+ return(1);
+}
+
+static void
+release_seeprom(offset, CS, CK, DO, DI, RDY, MS)
+ u_long offset;
+ u_short CS; /* chip select */
+ u_short CK; /* clock */
+ u_short DO; /* data out */
+ u_short DI; /* data in */
+ u_short RDY; /* ready */
+ u_short MS; /* mode select */
+{
+ /* Release access to the memory port and the serial EEPROM. */
+ outb(offset, 0);
+}
+
#endif /* NPCI > 0 */