diff options
author | Justin T. Gibbs <gibbs@FreeBSD.org> | 1996-01-03 06:34:10 +0000 |
---|---|---|
committer | Justin T. Gibbs <gibbs@FreeBSD.org> | 1996-01-03 06:34:10 +0000 |
commit | c5753242cfb0031ff4af6e9cac55736a526ed741 (patch) | |
tree | 54c0518019fe26bf1ee4ebb4f259a1722ab87877 /sys/pci/aic7870.c | |
parent | 6cb248359ebca3a22e049d93087816237c81f170 (diff) |
Notes
Diffstat (limited to 'sys/pci/aic7870.c')
-rw-r--r-- | sys/pci/aic7870.c | 382 |
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 *)≻ + 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 */ |