From a950a7fffa3c4fad091962bcc7d90c3c2354577e Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Mon, 3 Jul 2006 21:28:22 +0000 Subject: [PATCH] * Added buslogic SCSI driver * Added 54c8xx SCSI driver Both drivers originated from the BeOS sample code. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18018 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/busses/scsi/53c8xx/53c8xx.c | 1449 +++++++++++++++++ .../kernel/busses/scsi/53c8xx/53c8xx.h | 389 +++++ src/add-ons/kernel/busses/scsi/53c8xx/Jamfile | 15 + src/add-ons/kernel/busses/scsi/53c8xx/LICENSE | 31 + .../kernel/busses/scsi/53c8xx/scripts.c | 279 ++++ .../kernel/busses/scsi/53c8xx/scripts.ss | 186 +++ .../kernel/busses/scsi/53c8xx/symbios.h | 167 ++ src/add-ons/kernel/busses/scsi/Jamfile | 2 + .../kernel/busses/scsi/buslogic/Jamfile | 15 + .../kernel/busses/scsi/buslogic/LICENSE | 31 + .../kernel/busses/scsi/buslogic/buslogic.c | 1000 ++++++++++++ .../kernel/busses/scsi/buslogic/buslogic.h | 167 ++ 12 files changed, 3731 insertions(+) create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.c create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.h create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/Jamfile create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/LICENSE create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/scripts.c create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/scripts.ss create mode 100644 src/add-ons/kernel/busses/scsi/53c8xx/symbios.h create mode 100644 src/add-ons/kernel/busses/scsi/buslogic/Jamfile create mode 100644 src/add-ons/kernel/busses/scsi/buslogic/LICENSE create mode 100644 src/add-ons/kernel/busses/scsi/buslogic/buslogic.c create mode 100644 src/add-ons/kernel/busses/scsi/buslogic/buslogic.h diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.c b/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.c new file mode 100644 index 0000000000..6188542fa7 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.c @@ -0,0 +1,1449 @@ +/* + Copyright 1998-1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +/* +** 53c8xx.c - Symbios 53c8xx SIM +*/ + +#define DEBUG_SYMBIOS 1 /* Print Debugging Messages */ +#define DEBUG_ISR 0 /* messages in ISR... leave off */ +#define DEBUG_PM 0 /* messages when Phase Mismatch occurs... leave off */ +#define DEBUG_SAFETY 0 /* don't load driver if serial debug off */ + +#define USE_STATFS 0 + +/* +** Debugging Macros +*/ +#if DEBUG_SYMBIOS +#define d_printf dprintf +#else +#define d_printf(x...) +#endif + +#include +#include +#include +#include +#include + +/* shorthand byteswapping macros */ +#define LE(n) B_HOST_TO_LENDIAN_INT32(n) +#define HE(n) B_LENDIAN_TO_HOST_INT32(n) + +#include +#include +#include + +#include +#include + +#include "53c8xx.h" + +#include "symbios.h" + +#if USE_STATFS +#include + +static int32 +stat_controller(void *stats, char **buf) +{ + Symbios *s = (Symbios *) stats; + + if(*buf = (char *) malloc(256)){ + sprintf(*buf, + "Chipset: %s\n" + "IO Base: 0x%08x\n" + "SRAM Base: 0x%08x\n" + "IRQ Line: %d\n" + "SCSI Bus: %s\n", + s->name, s->iobase, s->sram_phys, s->irq, + s->max_targ_id > 7 ? "Wide" : "Narrow"); + return strlen(*buf); + } else { + return 0; + } +} + +static int32 +stat_target(void *stats, char **buf) +{ + SymTarg *st = (SymTarg *) stats; + + if(st->flags & tf_ignore){ + *buf = NULL; + return 0; + } + + if(*buf = (char *) malloc(256)){ + if(st->offset){ + sprintf(*buf, + "Width: %s\n" + "Transfer: Sync\n" + "Period: %d ns\n" + "Offset: %d\n", + st->wide ? "Wide" : "Narrow", + st->period, + st->offset + ); + } else { + sprintf(*buf, + "Width: %s\n" + "Transfer: Async\n", + st->wide ? "Wide" : "Narrow"); + } + return strlen(*buf); + } else { + return 0; + } +} + + +static void register_stats(Symbios *s) +{ + char buf[128]; + stat_module_info_t *stats; + int i; + + if(s->registered) return; + + if(get_module("generic/stat_module", (module_info**) &stats) == B_OK){ + sprintf(buf,"scsi/53c8xx/%d/info",s->num); + stats->register_statistics(buf, s, stat_controller); + for(i=0;i<=s->max_targ_id;i++){ + sprintf(buf,"scsi/53c8xx/%d/targ_%x",s->num,i); + stats->register_statistics(buf, &(s->targ[i]), stat_target); + } + s->registered = 1; + } else { + dprintf("symbios: cannot find stats module...\n"); + } +} + +#else +#define register_stats(x) + +#endif + +/* +** Constants for the SIM +*/ +#define SIM_VERSION 0x01 +#define HBA_VERSION 0x01 + +static char sim_vendor_name[] = "Be, Inc."; +static char hba_vendor_name[] = "Symbios"; + +static pci_module_info *pci; +static cam_for_sim_module_info *cam; + +static char cam_name[] = B_CAM_FOR_SIM_MODULE_NAME; +static char pci_name[] = B_PCI_MODULE_NAME; + +/* +** Supported Device / Device Attributes table +*/ +#define symf_sram 0x0001 /* on board SCRIPTS ram */ +#define symf_doubler 0x0002 /* SCLK doubler available */ +#define symf_quadrupler 0x0004 /* SCLK quadrupler available */ +#define symf_untested 0x1000 /* never actually tested one of these */ +#define symf_wide 0x0008 /* supports WIDE bus */ +#define symf_short 0x0010 /* short max period (8) */ + +static struct { + uint32 id; + uint32 rev; + char *name; + int flags; +} devinfo[] = { + { 0x0001, 0x10, "53c810a", symf_short }, + { 0x0001, 0x00, "53c810", symf_short }, + { 0x0006, 0x00, "53c860", symf_wide | symf_short | symf_untested }, + { 0x0004, 0x00, "53c815", symf_short | symf_untested }, + { 0x0002, 0x00, "53c820", symf_wide | symf_short | symf_untested }, + { 0x0003, 0x10, "53c825a", symf_wide | symf_short |symf_sram | symf_untested }, + { 0x0003, 0x00, "53c825", symf_wide | symf_short | symf_untested }, + { 0x000f, 0x02, "53c875", symf_wide | symf_sram | symf_doubler }, + { 0x000f, 0x00, "53c875", symf_wide | symf_sram }, + { 0x008f, 0x00, "53c875j", symf_wide | symf_sram | symf_doubler }, + { 0x000d, 0x00, "53c885", symf_wide | symf_sram | symf_untested }, + { 0x000c, 0x00, "53c895", symf_wide | symf_sram | symf_quadrupler | symf_untested }, + { 0x000b, 0x00, "53c896", symf_wide | symf_sram | symf_untested }, + { 0, 0, NULL, 0 } +}; + +#include "scripts.c" + +static void +setparams(SymTarg *t, uint period, uint offset, uint wide) +{ + Symbios *s = t->adapter; + + if(wide){ + if(!t->wide) kprintf("symbios%ld: target %ld wide\n",s->num,t->id); + t->wide = 1; + } else { + t->wide = 0; + } + + if(period){ + int i; + for(i=0;isyncsize;i++){ + if(period <= s->syncinfo[i].period){ + t->period = s->syncinfo[i].period; + t->offset = offset; + + t->device[3] = s->syncinfo[i].scntl3; + if(t->wide) t->device[3] |= 0x08; + t->device[2] = t->id; + t->device[1] = s->syncinfo[i].sxfer | (offset & 0x0f); + t->device[0] = 0; + kprintf("symbios%ld: target %ld sync period=%ld, offset=%d\n", + s->num, t->id, t->period, offset); + return; + } + } + } + + t->period = 0; + t->offset = 0; + + t->device[3] = s->scntl3; /* scntl3 - clock divisor */ + if(t->wide) t->device[3] |= 0x08; + t->device[2] = t->id; /* dest id */ + t->device[1] = 0; /* sync xfer */ + t->device[0] = 0; /* reserved */ +} + +static long init_symbios(Symbios *s, int restarting); + +/* +** IO Macros +*/ + +/* XXX - fix me bjs */ +#define inb(p) (*pci->read_io_8)(s->iobase + p) +#define outb(p,v) (*pci->write_io_8)(s->iobase + p,v) +#define inw(p) (*pci->read_io_16)(s->iobase + p) +#define outw(p,v) (*pci->write_io_16)(s->iobase + p,v) +#define in32(p) (*pci->read_io_32)(s->iobase + p) +#define out32(p,v) (*pci->write_io_32)(s->iobase + p,v) + + +/* patch in an external symbol */ +#define RESOLV(sname,value) \ +{ int i; \ + d_printf("symbios%d: relocting %d instances of %s to 0x%08x\n", \ + s->num,sizeof(E_##sname##_Used)/4,#sname,value); \ + for(i=0;i<(sizeof(E_##sname##_Used)/4);i++) { \ + scr[E_##sname##_Used[i]] = ((uint32) value); \ + } \ +} + +/* calc phys addr of a ptr inside the sram area */ +#define PHADDR(lvar) ((s->sram_phys) + (((uint32) &(lvar)) - ((uint32) s->script))) + +/* calc phys addr of a ptr inside the priv area */ +/*#define PPHADDR(ptr) (st->priv_phys + (((uint32) ptr) - ((uint32) st->priv)))*/ +#define PPHADDR(ptr) (phys + (((uint32) ptr) - ((uint32) sp))) + +/* prepare a Targ's scripts indirect table for one or more exec_io's */ +static void prep_io(SymPriv *sp, uint32 phys) +{ + sp->syncmsg.address = LE(PPHADDR(sp->_syncmsg)); + sp->syncmsg.count = LE(3); + + sp->widemsg.address = LE(PPHADDR(sp->_widemsg)); + sp->widemsg.count = LE(2); + + sp->sendmsg.address = LE(PPHADDR(sp->_sendmsg)); + + sp->recvmsg.address = LE(PPHADDR(sp->_recvmsg)); + sp->recvmsg.count = LE(1); + + sp->status.address = LE(PPHADDR(sp->_status)); + sp->status.count = LE(1); + + sp->extdmsg.address = LE(PPHADDR(sp->_extdmsg)); + sp->extdmsg.count = LE(1); + + sp->command.address = LE(PPHADDR(sp->_command)); +} + +/* + * actually execute an io transaction via SCRIPTS + * you MUST hold st->sem_targ before calling this + * + */ +static void exec_io(SymTarg *st, void *cmd, int cmdlen, void *msg, int msglen, + void *data, int datalen, int sg) +{ + cpu_status former; + Symbios *s = st->adapter; + + memcpy((void *) &(st->priv->device.count), st->device, 4); + + st->priv->sendmsg.count = LE(msglen); + memcpy(st->priv->_sendmsg, msg, msglen); + st->priv->command.count = LE(cmdlen); + memcpy(st->priv->_command, cmd, cmdlen); + + st->table_phys = st->priv_phys + ADJUST_PRIV_TO_TABLE; + + if(datalen){ + int i,sgcount; + uint32 opcode; + SymInd *t = st->priv->table; + physical_entry *pe = (physical_entry *) &(st->priv->table[1]); + + if(st->inbound){ + opcode = s->op_in; + st->datain_phys = st->table_phys; + st->dataout_phys = s->sram_phys + Ent_phase_dataerr; + } else { + opcode = s->op_out; + st->dataout_phys = st->table_phys; + st->datain_phys = s->sram_phys + Ent_phase_dataerr; + } + + if(sg) { + iovec *vec = (iovec *) data; + for(sgcount=0,i=0;idatain_phys = s->sram_phys + Ent_phase_dataerr; + st->dataout_phys = s->sram_phys + Ent_phase_dataerr; + } + +// dprintf("sym: pp = %08x di = %08x do = %08x\n",st->priv_phys,st->datain_phys,st->dataout_phys); + + st->status = status_queued; + +/* dprintf("symbios: enqueueing %02x %02x %02x ... for %d (%d bytes %s)\n", + ((uchar *)cmd)[0],((uchar *)cmd)[1],((uchar *)cmd)[2], + st->device[2],datalen,st->inbound?"IN":"OUT"); +*/ + former = disable_interrupts(); + acquire_spinlock(&(s->hwlock)); + + /* enqueue the request */ + if(s->startqueuetail){ + s->startqueuetail->next = st; + } else { + s->startqueue = st; + } + st->next = NULL; + s->startqueuetail = st; + + /* If the adapter is idle, signal it so that this request may be started */ + if(s->status == IDLE) outb(sym_istat, sym_istat_sigp); + + release_spinlock(&(s->hwlock)); + restore_interrupts(former); + + /* wait for completion */ + acquire_sem(st->sem_done); + +#if 0 + if(acquire_sem_etc(st->sem_done, 1, B_TIMEOUT, 10*1000000) != B_OK){ + kprintf("sym: targ %d never finished, argh...\n",st->device[2]); + init_symbios(st->adapter,1); + st->state = sTIMEOUT; + return; + } +#endif +} + + +#if DEBUG_ISR +#define kp kprintf +#else +#define kp(x...) +#endif + +static int32 +scsi_int_dispatch(void *data) +{ + Symbios *s = (Symbios *) data; + int reselected = 0; + uchar istat; + + if(s->reset) return B_UNHANDLED_INTERRUPT; + + acquire_spinlock(&(s->hwlock)); + istat = inb(sym_istat); + + if(istat & sym_istat_dip){ + uchar dstat = inb(sym_dstat); + + if(dstat & sym_dstat_sir){ + /* Handle and interrupt from the SCRIPTS program */ + uint32 status = HE(in32(sym_dsps)); + // kprintf("<%02x>",status); + + switch(status){ + case status_ready: + kp("sym: ready\n"); + break; + + case status_iocomplete: + kp("sym: done %08x\n",s->active); + if(s->active){ + /* io is complete -- any more io is an error */ + s->active->datain_phys = s->sram_phys + Ent_phase_dataerr; + s->active->dataout_phys = s->sram_phys + Ent_phase_dataerr; + } + break; + + case status_reselected:{ + uint32 id = inb(sym_ssid); + if(id & sym_ssid_val) { + s->active = &(s->targ[id & s->idmask]); + kp("sym: resel %08x\n",s->active); + if(s->active->status != status_waiting){ + s->active = NULL; + kprintf("symbios: bad reselect %ld\n",id & sym_ssid_encid); + } else { + reselected = 1; + } + } else { + kprintf("symbios: invalid reselection!?\n"); + } + break; + } + + case status_timeout: + /* inform the unlucky party and dequeue it */ + kp("sym: timeout %08lx\n",s->startqueue); + if(s->startqueue){ + s->startqueue->status = status_timeout; + release_sem_etc(s->startqueue->sem_done, 1, B_DO_NOT_RESCHEDULE); + if(!(s->startqueue = s->startqueue->next)){ + s->startqueuetail = NULL; + } + } + break; + + case status_selected: + /* selection succeeded. Remove from start queue and make active */ + kp("sym: selected %08lx\n",s->startqueue); + if(s->startqueue){ + s->active = s->startqueue; + s->active->status = status_active; + if(!(s->startqueue = s->startqueue->next)){ + s->startqueuetail = NULL; + } + } + break; + + case status_syncin: + setparams(s->active, + s->active->priv->_syncmsg[1]*4, + s->active->priv->_syncmsg[2], + s->active->wide); + break; + + case status_widein: + setparams(s->active, s->active->period, s->active->offset, + s->active->priv->_widemsg[1]); + break; + + case status_ignore_residue: + kprintf("ignore residue 0x%02x\n",s->active->priv->_extdmsg[0]); + break; + + case status_disconnect: + kp("sym: disc %08lx\n",s->active); + /* device disconnected. make inactive */ + if(s->active){ + s->active->status = status_waiting; + s->active = NULL; + } + break; + + case status_badmsg: + kp("sym: badmsg %02x\n",s->active->priv->_recvmsg[0]); + + case status_complete: + case status_badstatus: + case status_overrun: + case status_underrun: + case status_badphase: + case status_badextmsg: + kp("sym: error %08lx / %02x\n",s->active,status); + /* transaction completed successfully or in error. report our status. */ + if(s->active){ + s->active->status = status; + release_sem_etc(s->active->sem_done, 1, B_DO_NOT_RESCHEDULE); + s->active = NULL; + } + break; + + case status_selftest: + /* signal a response to the selftest ... don't actually start up the + SCRIPTS like we normally do */ + s->status = OFFLINE; + goto done; + break; + + default: + kp("sym: int 0x%08lx ...\n",status); + } + goto reschedule; + } else { + kprintf("symbios: weird error, dstat = %02x\n",dstat); + } + } + + if(istat & sym_istat_sip){ + uchar sist0; + sist0 = inb(sym_sist1); + + if(sist0 & sym_sist1_sbmc){ + kprintf("sym: SBMC %02x!\n",inb(sym_stest4) & 0xc0); + } + + if(sist0 & sym_sist1_sto){ + /* select timeout */ + kp("sym: Timeout %08lx\n",s->startqueue); + /* inform the unlucky party and dequeue it */ + if(s->startqueue){ + s->startqueue->status = status_timeout; + release_sem_etc(s->startqueue->sem_done, 1, B_DO_NOT_RESCHEDULE); + if(!(s->startqueue = s->startqueue->next)){ + s->startqueuetail = NULL; + } + } else { + kprintf("symbios: ghost target timed out\n"); + } + inb(sym_sist0); // apparently we MUST read sist0 as well + goto reschedule; + } + + sist0 = inb(sym_sist0) & 0x8f; + + if(sist0 && s->active){ + if(sist0 & sym_sist0_ma){ + /* phase mismatch -- we experienced a disconnect while in + a DataIn or DataOut... gotta figure out how much we + transferred, update the sgtable, take the FIFOs into + account, etc (see 9-9 in Symbios PCI-SCSI Programming Guide) */ + SymInd *t; + uint32 dfifo_val, bytesleft, dbc; + uint32 dsp = HE(in32(sym_dsp)); + uint32 n = (dsp - s->active->table_phys) / 8 - 1; + + if((dsp < s->active->priv_phys) || (n > 129)) { + /* we mismatched during some other phase ?! */ + kprintf("Phase Mismatch (dsp = 0x%08lx)\n",dsp); + goto reschedule; + } + + t = &(s->active->priv->table[n]); + +#if 0 + t->count = HE(t->count); + t->address = HE(t->count); +#endif + /* dbc initially = table[n].count, counts down */ + dbc = (uint32) HE(in32(sym_dbc)) & 0x00ffffffL; + + t->count &= 0xffffff; +#if DEBUG_PM + kprintf("PM(%s) dbc=0x%08x, n=%02d, a=0x%08x, l=0x%08x\n", + s->active->inbound ? " in" : "out", dbc, n, t->address, t->count); +#endif + if(s->active->inbound){ + /* data in is easy... flush happens automatically */ + t->address += t->count - dbc; + t->count = dbc; +#if DEBUG_PM + kprintf(" a=0x%08x, l=0x%08x\n", + t->address, t->count); +#endif + s->active->datain_phys = s->active->table_phys + 8*(t->count ? n : n+1); + t->count |= s->op_in; +#if 0 + t->count = LE(t->count); + t->address = LE(t->address); +#endif + goto reschedule; + } else { + if(inb(sym_ctest5) & 0x20){ + /* wide FIFO */ + dfifo_val = ((inb(sym_ctest5) & 0x03) << 8) | inb(sym_dfifo); + bytesleft = (dfifo_val - (dbc & 0x3ff)) & 0x3ff; + } else { + dfifo_val = (inb(sym_dfifo) & 0x7f); + bytesleft = (dfifo_val - (dbc & 0x7f)) & 0x7f; + } + if(inb(sym_sstat0) & 0x20) bytesleft++; + if(inb(sym_sstat2) & 0x20) bytesleft++; + if(inb(sym_sstat0) & 0x40) bytesleft++; + if(inb(sym_sstat2) & 0x40) bytesleft++; + + /* clear fifo */ + outb(sym_ctest3, 0x04); + + t->address += t->count - dbc; + t->count = dbc; + + /* adjust for data that didn't make it to the target */ + t->address -= bytesleft; + t->count += bytesleft; +#if DEBUG_PM + kprintf(" a=0x%08x, l=0x%08x\n", + t->address, t->count); +#endif + s->active->dataout_phys = s->active->table_phys + 8*(t->count ? n : n+1); + t->count |= s->op_out; +#if 0 + t->count = LE(t->count); + t->address = LE(t->address); +#endif + spin(10); + goto reschedule; + } + } + + if(sist0 & sym_sist0_udc){ + kprintf("symbios: Unexpected Disconnect (dsp = 0x%08lx)\n", in32(sym_dsp)); + } + + if(sist0 & sym_sist0_sge){ + kprintf("symbios: SCSI Gross Error\n"); + } + + if(sist0 & sym_sist0_rst){ + kprintf("symbios: SCSI Reset\n"); + } + + if(sist0 & sym_sist0_par){ + kprintf("symbios: Parity Error\n"); + } + + s->active->status = status_badphase; + release_sem_etc(s->active->sem_done, 1, B_DO_NOT_RESCHEDULE); + s->active = NULL; + + goto reschedule; + } + } else { + /* nothing happened... must be somebody else's problem */ + release_spinlock(&(s->hwlock)); + return B_UNHANDLED_INTERRUPT; + } + +reschedule: + /* start the SCRIPTS processor at one of three places, depending on state + ** + ** 1. If there is an active transaction, insure that the script is patched + ** correctly, the DSA is loaded, and start up at "switch". + ** + ** 2. If there is a transaction at the head of the startqueue, set the DSA + ** and start up at "start" to try to select the target and start the + ** transaction + ** + ** 3. If there is nothing else to do, go to "idle" and wait for signal or + ** reselection + */ + + if(s->active){ + out32(sym_dsa, s->active->priv_phys + ADJUST_PRIV_TO_DSA); + s->script[PATCH_DATAIN] = LE(s->active->datain_phys); + s->script[PATCH_DATAOUT] = LE(s->active->dataout_phys); + + s->active->status = status_active; + s->status = ACTIVE; + if(reselected){ + out32(sym_dsp, LE(s->sram_phys + Ent_switch_resel)); + //kp("sym: restart @ %08x / reselected\n",Ent_switch_resel); + } else { + out32(sym_dsp, LE(s->sram_phys + Ent_switch)); + //kp("sym: restart @ %08x / selected\n", Ent_switch); + } + } else { + if(s->startqueue){ + out32(sym_dsa, LE(s->startqueue->priv_phys + ADJUST_PRIV_TO_DSA)); + + s->startqueue->status = status_selecting; + s->status = START; + out32(sym_dsp, LE(s->sram_phys + Ent_start)); + //kp("sym: restart @ %08x / started\n", Ent_start); + } else { + s->status = IDLE; + out32(sym_dsp, LE(s->sram_phys + Ent_idle)); + //kp("sym: restart @ %08x / idle\n", Ent_idle); + } + } + +done: + release_spinlock(&(s->hwlock)); + return B_HANDLED_INTERRUPT; +} + + +/* init the adapter... if restarting, no bus reset or whathaveyou */ +static long init_symbios(Symbios *s, int restarting) +{ + d_printf("symbios%ld: init_symbios()\n",s->num); + + if(restarting){ + s->reset = 1; + } else { + s->reset = 0; + } + + if(!restarting){ + /* reset the SCSI bus */ + dprintf("symbios%ld: scsi bus reset\n",s->num); + outb(sym_scntl1, sym_scntl1_rst); + spin(25); + outb(sym_scntl1, 0); + spin(250000); + + /* clear ints */ + inb(sym_istat); + inb(sym_sist0); + inb(sym_sist1); + inb(sym_dstat); + + install_io_interrupt_handler(s->irq, scsi_int_dispatch, s, 0); + d_printf("symbios%ld: registered interrupt handler for irq %ld\n",s->num,s->irq); + } + + /* enable irqs, prefetch, no 53c700 compat */ + outb(sym_dcntl, sym_dcntl_com); + + /* enable all DMA ints */ + outb(sym_dien, sym_dien_sir | sym_dien_mdpe | sym_dien_bf | sym_dien_abrt + | sym_dien_iid); + /* enable all fatal SCSI ints */ + outb(sym_sien0, sym_sien0_ma | sym_sien0_sge | sym_sien0_udc | sym_sien0_rst | + sym_sien0_par); + outb(sym_sien1, sym_sien1_sto | sym_sien1_sbmc); // XXX + + /* sel / hth timeouts */ + outb(sym_stime0, 0xbb); + + /* clear ints */ + inb(sym_istat); + inb(sym_sist0); + inb(sym_sist1); + inb(sym_dstat); + + /* clear ints */ + inb(sym_sist0); + inb(sym_sist1); + inb(sym_dstat); + + if(restarting){ + s->reset = 0; + } else { + int i; + s->status = TEST; + + dprintf("symbios%ld: selftest ",s->num); + out32(sym_dsp, LE(s->sram_phys + Ent_test)); + for(i=0;(s->status == TEST) && i<10;i++) { + dprintf("."); + spin(10000); + } + if(s->status == TEST){ + dprintf("FAIL\n"); + return B_ERROR; //XXX teardown + } else { + dprintf("PASS\n"); + } + } + + s->status = IDLE; + out32(sym_dsp, LE(s->sram_phys + Ent_idle)); + d_printf("symbios%ld: started script\n",s->num); + + return B_NO_ERROR; +} + +/* When an inquiry succeeds the negotiator gets the option to attempt to +** request a better transfer agreement with the target. +*/ +static void negotiator(Symbios *s, SymTarg *targ, uchar *ident, uchar *msg) +{ + if(ident[7] & 0x20){ /* wide supported */ + if(targ->flags & tf_ask_wide){ + uchar cmd[6] = { 0, 0, 0, 0, 0, 0 }; + targ->flags &= (~tf_ask_wide); /* only ask once */ + + dprintf("symbios%ld: negotiating wide xfer with target %ld\n", + s->num,targ->id); + + msg[1] = 0x01; /* extended message */ + msg[2] = 0x02; /* length */ + msg[3] = 0x03; /* sync negotiate */ + msg[4] = 0x01; /* 16 bit wide */ + + exec_io(targ, cmd, 6, msg, 5, NULL, 0, 0); + } + } + + if(ident[7] & 0x10){ /* sync supported */ + if(targ->flags & tf_ask_sync) { + uchar cmd[6] = { 0, 0, 0, 0, 0, 0 }; + + targ->flags &= (~tf_ask_sync); /* only ask once */ + + dprintf("symbios%ld: negotiating sync xfer with target %ld\n", + s->num,targ->id); + + msg[1] = 0x01; /* extended message */ + msg[2] = 0x03; /* length */ + msg[3] = 0x01; /* sync negotiate */ + msg[4] = s->syncinfo[0].period / 4; /* sync period / 4 */ + msg[5] = s->maxoffset; + + exec_io(targ, cmd, 6, msg, 6, NULL, 0, 0); + } + } +} + +/* Convert a CCB_SCSIIO into a BL_CCB32 and (possibly SG array). +** +*/ +static long sim_execute_scsi_io(Symbios *s, CCB_HEADER *ccbh) +{ + CCB_SCSIIO *ccb = (CCB_SCSIIO *) ccbh; + uchar *cdb; + physical_entry pe[2]; + SymTarg *targ; + uchar msg[8]; + + targ = s->targ + ccb->cam_ch.cam_target_id; + + if(targ->flags & tf_ignore){ + ccbh->cam_status = CAM_SEL_TIMEOUT; + return B_OK; + } + + if(ccb->cam_ch.cam_flags & CAM_CDB_POINTER) { + cdb = ccb->cam_cdb_io.cam_cdb_ptr; + } else { + cdb = ccb->cam_cdb_io.cam_cdb_bytes; + } + + get_memory_map((void*) (ccb->cam_sim_priv), 1536, pe, 2); + + /* identify message */ + msg[0] = 0xC0 | (ccb->cam_ch.cam_target_lun & 0x07); + + /* fill out table */ + prep_io((SymPriv *) ccb->cam_sim_priv, (uint32) pe[0].address); + + /* insure only one transaction at a time for any given target */ + acquire_sem(targ->sem_targ); + + targ->priv = (SymPriv *) ccb->cam_sim_priv;; + targ->priv_phys = (uint32 ) pe[0].address; + + targ->inbound = (ccb->cam_ch.cam_flags & CAM_DIR_IN) ? 1 : 0; + + if(ccb->cam_ch.cam_flags & CAM_SCATTER_VALID){ + exec_io(targ, cdb, ccb->cam_cdb_len, msg, 1, + ccb->cam_data_ptr, ccb->cam_sglist_cnt, 1); + } else { + exec_io(targ, cdb, ccb->cam_cdb_len, msg, 1, + ccb->cam_data_ptr, ccb->cam_dxfer_len, 0); + } + +/* dprintf("symbios%d: state = 0x%02x, status = 0x%02x\n", + s->num,targ->state,targ->priv->status[0]);*/ + + /* decode status */ + switch(targ->status){ + case status_complete: + if((ccb->cam_scsi_status=targ->priv->_status[0]) != 0) { + ccbh->cam_status = CAM_REQ_CMP_ERR; + + /* nonzero status is an error ... 0x02 = check condition */ + if((ccb->cam_scsi_status == 0x02) && + !(ccb->cam_ch.cam_flags & CAM_DIS_AUTOSENSE) && + ccb->cam_sense_ptr && ccb->cam_sense_len){ + uchar command[6]; + + command[0] = 0x03; /* request_sense */ + command[1] = ccb->cam_ch.cam_target_lun << 5; + command[2] = 0; + command[3] = 0; + command[4] = ccb->cam_sense_len; + command[5] = 0; + + targ->inbound = 1; + exec_io(targ, command, 6, msg, 1, + ccb->cam_sense_ptr, ccb->cam_sense_len, 0); + + if(targ->priv->_status[0]){ + ccb->cam_ch.cam_status |= CAM_AUTOSENSE_FAIL; + } else { + ccb->cam_ch.cam_status |= CAM_AUTOSNS_VALID; + } + } + } else { + ccbh->cam_status = CAM_REQ_CMP; + + if(cdb[0] == 0x12) { + /* inquiry just succeeded ... is it non SG and with enough data to + snoop the support bits? */ + if(!(ccb->cam_ch.cam_flags & CAM_SCATTER_VALID) && (ccb->cam_dxfer_len>7)){ + negotiator(s, targ, ccb->cam_data_ptr, msg); + } + } + } + break; + + case status_timeout: + ccbh->cam_status = CAM_SEL_TIMEOUT; + break; + + default: // XXX + ccbh->cam_status = CAM_SEL_TIMEOUT; + } + + targ->status = status_inactive; +// dprintf("symbios%d: releasing targ @ 0x%08x\n",s->num,targ); + release_sem(targ->sem_targ); + return B_OK; +} + +/* +** sim_path_inquiry returns info on the target/lun. +*/ +static long sim_path_inquiry(Symbios *s, CCB_HEADER *ccbh) +{ + CCB_PATHINQ *ccb; + ccb = (CCB_PATHINQ *) ccbh; + ccb->cam_version_num = SIM_VERSION; + ccb->cam_target_sprt = 0; + ccb->cam_hba_eng_cnt = 0; + memset (ccb->cam_vuhba_flags, 0, VUHBA); + ccb->cam_sim_priv = SIM_PRIV; + ccb->cam_async_flags = 0; + ccb->cam_initiator_id = s->host_targ_id; + ccb->cam_hba_inquiry = s->max_targ_id > 7 ? PI_WIDE_16 : 0 ; + strncpy (ccb->cam_sim_vid, sim_vendor_name, SIM_ID); + strncpy (ccb->cam_hba_vid, hba_vendor_name, HBA_ID); + ccb->cam_osd_usage = 0; + ccbh->cam_status = CAM_REQ_CMP; + register_stats(s); + return 0; +} + + +/* +** sim_extended_path_inquiry returns info on the target/lun. +*/ +static long sim_extended_path_inquiry(Symbios *s, CCB_HEADER *ccbh) +{ + CCB_EXTENDED_PATHINQ *ccb; + + sim_path_inquiry(s, ccbh); + ccb = (CCB_EXTENDED_PATHINQ *) ccbh; + sprintf(ccb->cam_sim_version, "%d.0", SIM_VERSION); + sprintf(ccb->cam_hba_version, "%d.0", HBA_VERSION); + strncpy(ccb->cam_controller_family, "Symbios", FAM_ID); + strncpy(ccb->cam_controller_type, s->name, TYPE_ID); + return 0; +} + +/* +** scsi_sim_action performes the scsi i/o command embedded in the +** passed ccb. +** +** The target/lun ids are assumed to be in range. +*/ +static long sim_action(Symbios *s, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INPROG; + switch(ccbh->cam_func_code){ + case XPT_SCSI_IO: + return sim_execute_scsi_io(s,ccbh); + case XPT_PATH_INQ: + return sim_path_inquiry(s,ccbh); + case XPT_EXTENDED_PATH_INQ: + return sim_extended_path_inquiry(s, ccbh); + default: + ccbh->cam_status = CAM_REQ_INVALID; + return -1; + } +} + +static void reloc_script(Symbios *s) +{ + int i; + ulong *scr = s->script; + + memcpy(scr, SCRIPT, sizeof(SCRIPT)); + for(i=0;isram_phys; + } + d_printf("symbios%ld: loaded %ld byte SCRIPT, relocated %ld labels\n", + s->num, sizeof(SCRIPT), PATCHES); + + /* disable scsi ints */ + outb(sym_scratcha, 0x42); + outb(sym_scratcha+1, 0x00); + outb(sym_scratchb, 0x04); + outb(sym_sien0, 0); + outb(sym_sien1, 0); + outb(sym_dien, sym_dien_sir); + + /* clear ints */ + inb(sym_sist0); + inb(sym_sist1); + inb(sym_dstat); + + outb(sym_dmode, ( sym_dmode_diom | sym_dmode_siom, 0 )); /* FIXME: ??? */ +} + +static uint32 sym_readclock(Symbios *s) +{ + uint32 ms,a,i; + bigtime_t t0,t1; + + outw(sym_sien0 , 0); /* mask all scsi interrupts */ + outb(sym_dien , 0); /* mask all dma interrupts */ + inw(sym_sist0); /* clear pending scsi interrupts */ + outb(sym_scntl3, 4); /* set pre-scaler to divide by 3 */ + + for(a=0,i=0;i<5;i++){ + ms = 0; + outb(sym_stime1, 0); /* disable general purpose timer */ + spin(10000); /* let it all settle for 10ms */ + inw(sym_sist0); /* another one, just to be sure :) */ + + t0 = system_time(); + outb(sym_stime1, 11); /* delay of 128ms */ + while (!(inb(sym_sist1) & sym_sist1_gen)) snooze(250); + t1 = system_time(); + ms = (t1-t0)/1000 + 10; /* we seem to be off by 10ms typically */ + + a += ((1 << 11) * 4400) / ms; + } + + outb(sym_stime1, 0); /* disable general purpose timer */ + return a / 5; +} + +static uchar id_bits[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + +/* +** Allocate the actual memory for the cardinfo object +*/ +static Symbios *create_cardinfo(int num, pci_info *pi, int flags) +{ + char name[32]; + Symbios *s; + int i,scf; + area_id aid; + uint32 stest2,stest4; + + if((pi->u.h0.interrupt_line == 0) || (pi->u.h0.interrupt_line > 128)) { + return NULL; /* invalid IRQ */ + } + + if(!(s = (Symbios *) malloc(sizeof(Symbios)))) return NULL; + + s->num = num; + s->iobase = pi->u.h0.base_registers[0]; + s->irq = pi->u.h0.interrupt_line; + s->hwlock = 0; + s->startqueue = NULL; + s->startqueuetail = NULL; + s->active = NULL; + + sprintf(name,"sym%d:sram",num); + if(flags & symf_sram){ + unsigned char *c; + s->sram_phys = pi->u.h0.base_registers[2]; + if((aid=map_physical_memory(name, (void *) s->sram_phys, 4096, + B_ANY_KERNEL_ADDRESS, B_READ_AREA + B_WRITE_AREA, + (void **) &(s->script))) < 0){ + free(s); + return NULL; + } + /* memory io test */ + c = (unsigned char *) s->script; + for(i=0;i<4096;i++) c[i] = (255 - (i & 0xff)); + for(i=0;i<4096;i++) { + if(c[i] != (255 - (i & 0xff))) { + d_printf("symbios%d: scripts ram io error @ %d\n",num,i); + goto err; + } + } + } else { + uchar *a; + physical_entry entries[2]; + aid = create_area(name, (void **)&a, B_ANY_KERNEL_ADDRESS, 4096*5, + B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if(aid == B_ERROR || aid == B_BAD_VALUE || aid == B_NO_MEMORY){ + free(s); + return NULL; + } + get_memory_map(a, 4096, entries, 2); + s->sram_phys = (uint32) entries[0].address; + s->script = (uint32 *) a; + } + + d_printf("symbios%d: scripts ram @ 0x%08lx, mapped to 0x%08lx (%s)\n", + num, s->sram_phys, (uint32) s->script, + flags & symf_sram ? "onboard" : "offboard" ); + + /* what are we set at now? */ + s->host_targ_id = inb(sym_scid) & 0x07; + dprintf("symbios%ld: host id %ld\n",s->num,s->host_targ_id); + + s->host_targ_id = 7; /* XXX figure this out somehow... */ + s->max_targ_id = (flags & symf_wide) ? 15 : 7; + + stest2 = inb(sym_stest2); + stest4 = inb(sym_stest4); + + /* software reset */ + outb(sym_istat, sym_istat_srst); + spin(10000); + outb(sym_istat, 0); + spin(10000); + + /* initiator mode, full arbitration */ + outb(sym_scntl0, sym_scntl0_arb0 | sym_scntl0_arb1); + + outb(sym_scntl1, 0); + outb(sym_scntl2, 0); + + /* initiator id=7, respond to reselection */ + /* respond to reselect of id 7 */ + outb(sym_respid, id_bits[s->host_targ_id]); + outb(sym_scid, sym_scid_rre | s->host_targ_id); + + outb(sym_dmode, 0); + + dprintf("symbios%ld: stest2 = 0x%02lx, stest4 = 0x%02lx\n",s->num,stest2,stest4); + + /* no differential, no loopback, no hiz, no always-wide, no filter, no lowlevel */ + outb(sym_stest2, 0); // save diff bit + outb(sym_stest3, 0); + +// if(flags & symf_quadrupler){ +// outb(sym_stest4, sym_stest4_lvd); +// } + + outb(sym_stest1, 0); /* make sure clock doubler is OFF */ + + s->sclk = sym_readclock(s); + dprintf("symbios%ld: clock is %ldKHz\n",s->num,s->sclk); + + if(flags & symf_doubler){ + /* if we have a doubler and we don't already have an 80MHz clock */ + if((s->sclk > 35000) && (s->sclk < 45000)){ + dprintf("symbios%ld: enabling clock doubler...\n",s->num); + outb(sym_stest1, 0x08); /* enable doubler */ + spin(200); /* wait 20us */ + outb(sym_stest3, 0xa0); /* halt sclk, enable TolerANT*/ + outb(sym_scntl3, 0x05); /* SCLK/4 */ + outb(sym_stest1, 0x0c); /* engage doubler */ + outb(sym_stest3, 0x80); /* reenable sclk, leave TolerANT on */ + + spin(3000); + + s->sclk = sym_readclock(s); + dprintf("symbios%ld: clock is %ldKHz\n",s->num,s->sclk); + } + } + if(flags & symf_quadrupler){ + if((s->sclk > 35000) && (s->sclk < 45000)){ + dprintf("symbios%ld: enabling clock quadrupler...\n",s->num); + outb(sym_stest1, 0x08); /* enable doubler */ + spin(200); /* wait 20us */ + outb(sym_stest3, 0xa0); /* halt sclk, enable TolerANT*/ + outb(sym_scntl3, 0x05); /* SCLK/4 */ + outb(sym_stest1, 0x0c); /* engage doubler */ + outb(sym_stest3, 0x80); /* reenable sclk, leave TolerANT on */ + + spin(3000); + + s->sclk = sym_readclock(s); + dprintf("symbios%ld: clock is %ldKHz\n",s->num,s->sclk); + s->sclk = 160000; + } + } + outb(sym_stest3, 0x80); /* leave TolerANT on */ + + scf = 0; + /* set CCF / SCF according to specs */ + if(s->sclk < 25010) { + dprintf("symbios%ld: unsupported clock frequency\n",s->num); + goto err; // s->scntl3 = 0x01; + } else if(s->sclk < 37510){ + dprintf("symbios%ld: unsupported clock frequency\n",s->num); + goto err; // s->scntl3 = 0x02; + } else if(s->sclk < 50010){ + /* 40MHz - divide by 1, 2 */ + scf = 0x10; + s->scntl3 = 0x03; + } else if(s->sclk < 75010){ + dprintf("symbios%ld: unsupported clock frequency\n",s->num); + goto err; // s->scntl3 = 0x04; + } else if(s->sclk < 85000){ + /* 80 MHz - divide by 2, 4*/ + scf = 0x30; + s->scntl3 = 0x05; + } else { + /* 160 MHz - divide by 4, 8 */ + scf = 0x50; + s->scntl3 = 0x07; + } + + + s->maxoffset = (flags & symf_short) ? 8 : 15 ; + s->syncsize = 0; + + if(scf == 0x50){ + /* calculate values for 160MHz clock */ + for(i=0;i<4;i++){ + s->syncinfo[s->syncsize].sxfer = i << 5; + s->syncinfo[s->syncsize].scntl3 = s->scntl3 | 0x90; /* /2, Ultra2 */ + s->syncinfo[s->syncsize].period_ns = (625 * (i+4)) / 100; + s->syncinfo[s->syncsize].period = 4 * (s->syncinfo[s->syncsize].period_ns / 4); + s->syncsize++; + } + } + + if(scf >= 0x30){ + /* calculate values for 80MHz clock */ + for(i=0;i<4;i++){ + s->syncinfo[s->syncsize].sxfer = i << 5; + if(scf == 0x30){ + s->syncinfo[s->syncsize].scntl3 = s->scntl3 | 0x90; /* /2, Ultra */ + } else { + s->syncinfo[s->syncsize].scntl3 = s->scntl3 | 0xb0; /* /4, Ultra2 */ + } + + s->syncinfo[s->syncsize].period_ns = (125 * (i+4)) / 10; + s->syncinfo[s->syncsize].period = 4 * (s->syncinfo[s->syncsize].period_ns / 4); + s->syncsize++; + } + } + + /* calculate values for 40MHz clock */ + for(i=0;i<8;i++){ + s->syncinfo[s->syncsize].sxfer = i << 5; + s->syncinfo[s->syncsize].scntl3 = s->scntl3 | scf; + s->syncinfo[s->syncsize].period_ns = 25 * (i+4); + s->syncinfo[s->syncsize].period = 4 * (s->syncinfo[s->syncsize].period_ns / 4); + s->syncsize++; + } + + for(i=0;isyncsize;i++){ + dprintf("symbios%ld: syncinfo[%d] = { %02x, %02x, %d ns, %d ns }\n", + s->num, i, + s->syncinfo[i].sxfer, s->syncinfo[i].scntl3, + s->syncinfo[i].period_ns, s->syncinfo[i].period); + } + + for(i=0;i<16;i++){ + s->targ[i].id = i; + s->targ[i].adapter = s; + s->targ[i].wide = 0; + s->targ[i].offset = 0; + s->targ[i].status = status_inactive; + + if((i == s->host_targ_id) || (i > s->max_targ_id)){ + s->targ[i].flags = tf_ignore; + } else { + s->targ[i].flags = tf_ask_sync; + if(flags & symf_wide) s->targ[i].flags |= tf_ask_wide; +// s->targ[i].flags = 0; + + setparams(s->targ + i, 0, 0, 0); + + sprintf(name,"sym%ld:%02d:lock",s->num,i); + s->targ[i].sem_targ = create_sem(1,name); + + sprintf(name,"sym%ld:%02d:done",s->num,i); + s->targ[i].sem_done = create_sem(0,name); + } + } + + if(flags & symf_wide){ + s->idmask = 15; + s->op_in = OP_WDATA_IN; + s->op_out = OP_WDATA_OUT; + } else { + s->idmask = 7; + s->op_in = OP_NDATA_IN; + s->op_out = OP_NDATA_OUT; + } + + reloc_script(s); + return s; + +err: + free(s); + delete_area(aid); + return NULL; +} + + +/* +** Multiple Card Cruft +*/ +#define MAXCARDS 4 + +static Symbios *cardinfo[MAXCARDS] = { NULL, NULL, NULL, NULL }; + +static long sim_init0(void) { return init_symbios(cardinfo[0],0); } +static long sim_init1(void) { return init_symbios(cardinfo[1],0); } +static long sim_init2(void) { return init_symbios(cardinfo[2],0); } +static long sim_init3(void) { return init_symbios(cardinfo[3],0); } +static long sim_action0(CCB_HEADER *ccbh) { return sim_action(cardinfo[0],ccbh); } +static long sim_action1(CCB_HEADER *ccbh) { return sim_action(cardinfo[1],ccbh); } +static long sim_action2(CCB_HEADER *ccbh) { return sim_action(cardinfo[2],ccbh); } +static long sim_action3(CCB_HEADER *ccbh) { return sim_action(cardinfo[3],ccbh); } + +static long (*sim_init_funcs[MAXCARDS])(void) = { + sim_init0, sim_init1, sim_init2, sim_init3 +}; + +static long (*sim_action_funcs[MAXCARDS])(CCB_HEADER *) = { + sim_action0, sim_action1, sim_action2, sim_action3 +}; + + +/* +** Detect the controller and register the SIM with the CAM layer. +** returns the number of controllers installed... +*/ +static int +sim_install_symbios(void) +{ + int i, j, iobase, irq; + int cardcount = 0; + pci_info h; + CAM_SIM_ENTRY entry; + + dprintf("symbios: sim_install()\n"); + + for (i = 0; ; i++) { + if ((*pci->get_nth_pci_info) (i, &h) != B_NO_ERROR) { + /*if(!cardcount) d_printf("symbios: no controller found\n");*/ + break; + } + +// d_printf("scan: %04x %04x %02x\n", h.device_id, h.vendor_id, h.revision); + +#define PCI_VENDOR_SYMBIOS 0x1000 + + if(h.vendor_id == PCI_VENDOR_SYMBIOS) { + for(j=0;devinfo[j].id;j++){ + if((devinfo[j].id == h.device_id) && (h.revision >= devinfo[j].rev)){ + iobase = h.u.h0.base_registers[0]; + irq = h.u.h0.interrupt_line; + d_printf("symbios%d: %s controller @ 0x%08x, irq %d\n", + cardcount, devinfo[j].name, iobase, irq); + + if(cardcount == MAXCARDS){ + d_printf("symbios: too many controllers!\n"); + return cardcount; + } + + if((cardinfo[cardcount]=create_cardinfo(cardcount,&h,devinfo[j].flags)) != NULL){ + cardinfo[cardcount]->name = devinfo[j].name; + entry.sim_init = sim_init_funcs[cardcount]; + entry.sim_action = sim_action_funcs[cardcount]; + cardinfo[cardcount]->registered = 0; + register_stats(cardinfo[cardcount]); + + (*cam->xpt_bus_register)(&entry); + cardcount++; + } else { + d_printf("symbios%d: cannot allocate cardinfo\n",cardcount); + } + break; + } + } + } + } + + return cardcount; +} + +static status_t std_ops(int32 op, ...) +{ + switch(op) { + case B_MODULE_INIT: +#if DEBUG_SAFETY + set_dprintf_enabled(true); +#endif + load_driver_symbols("53c8xx"); + + if (get_module(pci_name, (module_info **) &pci) != B_OK) + return B_ERROR; + + if (get_module(cam_name, (module_info **) &cam) != B_OK) { + put_module(pci_name); + return B_ERROR; + } + + if(sim_install_symbios()){ + return B_OK; + } + + put_module(pci_name); + put_module(cam_name); + return B_ERROR; + + case B_MODULE_UNINIT: + put_module(pci_name); + put_module(cam_name); + return B_OK; + + default: + return B_ERROR; + } +} + +static +sim_module_info sim_symbios_module = { + { "busses/scsi/53c8xx/v1", 0, &std_ops } +}; + +_EXPORT +module_info *modules[] = +{ + (module_info *) &sim_symbios_module, + NULL +}; + diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.h b/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.h new file mode 100644 index 0000000000..b2d4d4b764 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/53c8xx.h @@ -0,0 +1,389 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _SYM53C8XX_H +#define _SYM53C8XX_H + +/*======================================================================*/ +/* operating registers definitions */ + +#define sym_scntl0 0x00 /* scsi control 0 */ +#define sym_scntl1 0x01 /* scsi control 1 */ +#define sym_scntl2 0x02 /* scsi control 2 */ +#define sym_scntl3 0x03 /* scsi control 3 */ +#define sym_scid 0x04 /* scsi chip ID */ +#define sym_sxfer 0x05 /* scsi transfer */ +#define sym_sdid 0x06 /* scsi destination ID */ +#define sym_gpreg 0x07 /* general purpose bits */ +#define sym_sfbr 0x08 /* scsi first byte received */ +#define sym_socl 0x09 /* scsi output control latch */ +#define sym_ssid 0x0a /* scsi selector ID */ +#define sym_sbcl 0x0b /* scsi bus control lines */ +#define sym_dstat 0x0c /* dma status */ +#define sym_sstat0 0x0d /* scsi status 0 */ +#define sym_sstat1 0x0e /* scsi status 1 */ +#define sym_sstat2 0x0f /* scsi status 2 */ +#define sym_dsa 0x10 /* data structure address [4 bytes] */ +#define sym_istat 0x14 /* interrupt status */ +#define sym_ctest0 0x18 /* chip test 0 */ +#define sym_ctest1 0x19 /* chip test 1 */ +#define sym_ctest2 0x1a /* chip test 2 */ +#define sym_ctest3 0x1b /* chip test 3 */ +#define sym_temp 0x1c /* temporary stack [4 bytes] */ +#define sym_dfifo 0x20 /* dma fifo */ +#define sym_ctest4 0x21 /* chip test 4 */ +#define sym_ctest5 0x22 /* chip test 5 */ +#define sym_ctest6 0x23 /* chip test 6 */ +#define sym_dbc 0x24 /* dma byte counter [3 bytes] */ +#define sym_dcmd 0x27 /* dma command */ +#define sym_dnad 0x28 /* dma next address for data [4 bytes] */ +#define sym_dsp 0x2c /* dma scripts pointer [4 bytes] */ +#define sym_dsps 0x30 /* dma scripts pointer save [4 bytes] */ +#define sym_scratcha 0x34 /* general purpose scratch pad a [4 bytes] */ +#define sym_dmode 0x38 /* dma mode */ +#define sym_dien 0x39 /* dma interrupt enable */ +#define sym_dwt 0x3a /* dma watchdog timer */ +#define sym_dcntl 0x3b /* dma control */ +#define sym_adder 0x3c /* sum output of internal adder [4 bytes] */ +#define sym_sien0 0x40 /* scsi interrupt enable 0 */ +#define sym_sien1 0x41 /* scsi interrupt enable 1 */ +#define sym_sist0 0x42 /* scsi interrupt status 0 */ +#define sym_sist1 0x43 /* scsi interrupt status 1 */ +#define sym_slpar 0x44 /* scsi longitudinal parity */ +#define sym_macntl 0x46 /* memory access control */ +#define sym_gpcntl 0x47 /* general purpose control */ +#define sym_stime0 0x48 /* scsi timer 0 */ +#define sym_stime1 0x49 /* scsi timer 1 */ +#define sym_respid 0x4a /* responce ID */ +#define sym_stest0 0x4c /* scsi test 0 */ +#define sym_stest1 0x4d /* scsi test 1 */ +#define sym_stest2 0x4e /* scsi test 2 */ +#define sym_stest3 0x4f /* scsi test 3 */ +#define sym_sidl 0x50 /* scsi input data latch */ +#define sym_stest4 0x52 /* new with 895/896 */ +#define sym_sodl 0x54 /* scsi output data latch */ +#define sym_sbdl 0x58 /* scsi bus data lines */ +#define sym_scratchb 0x5c /* general purpose scratch pad b [4 bytes] */ + + +/*----------------------------------------------------------------------*/ +/* operating registers bit definitions */ + +/* 0x00 - scntl0 (scsi control 0) bit definitions */ + +#define sym_scntl0_trg 0x01 /* target mode [1 = target, 0 = initiator */ +#define sym_scntl0_aap 0x02 /* assert SATN on parity */ +#define sym_scntl0_epc 0x08 /* enable parity checking */ +#define sym_scntl0_watn 0x10 /* select with SATN on a start sequence */ +#define sym_scntl0_start 0x20 /* start sequence */ +#define sym_scntl0_arb0 0x40 /* arbitration mode bit 0 */ +#define sym_scntl0_arb1 0x80 /* arbitration mode bit 1 */ + + +/* 0x01 - scntl1 (scsi control 1) bit definitions */ + +#define sym_scntl1_sst 0x01 /* start scsi transfer */ +#define sym_scntl1_iarb 0x02 /* immediate arbitration */ +#define sym_scntl1_aesp 0x04 /* assert even scsi parity (force bad parity) */ +#define sym_scntl1_rst 0x08 /* assert scsi RST signal */ +#define sym_scntl1_con 0x10 /* connected */ +#define sym_scntl1_dhp 0x20 /* disable halt on parity error or ATN */ +#define sym_scntl1_adb 0x40 /* assert scsi data bus */ +#define sym_scntl1_exc 0x80 /* extra clock cycle of data setup */ + + +/* 0x02 - scntl2 (scsi control 2) bit definitions */ + +#define sym_scntl2_sdu 0x80 /* scsi disconnect unexpected */ + + +/* 0x03 - scntl3 (scsi control 3) bit definitions */ + +#define sym_scntl3_ccf 0x07 /* clock conversion factor [mask] */ +#define sym_scntl3_scf 0x70 /* synchronous clock conversion factor [mask] */ + + +/* 0x04 - scid (scsi chip id) bit definitions */ + +#define sym_scid_enc 0x07 /* encoded scsi id [mask] */ +#define sym_scid_sre 0x20 /* enable responce to selection */ +#define sym_scid_rre 0x40 /* enable responce to reselection */ + + +/* 0x05 - sxfer (scsi transfer) bit definitions */ + +#define sym_sxfer_mo 0x0f /* max scsi synchronous offset [mask] */ +#define sym_sxfer_tp 0xe0 /* scsi synchronous transfer period [mask] */ + + +/* 0x06 - sdid (scsi destination id) bit definitions */ + +#define sym_sdid_enc 0x07 /* encoded destination scsi id [mask] */ + + +/* 0x07 - gpreg (general purpose) bit definitions */ + +#define sym_gpreg_gpio0 0x01 /* general purpose */ +#define sym_gpreg_gpio1 0x02 /* general purpose */ + + +/* 0x09 - socl (scsi output control latch) bit definitions */ + +#define sym_socl_io 0x01 /* assert i/o signal */ +#define sym_socl_cd 0x02 /* assert c/d signal */ +#define sym_socl_msg 0x04 /* assert msg signal */ +#define sym_socl_atn 0x08 /* assert atn signal */ +#define sym_socl_sel 0x10 /* assert sel signal */ +#define sym_socl_bsy 0x20 /* assert bsy signal */ +#define sym_socl_ack 0x40 /* assert ack signal */ +#define sym_socl_req 0x80 /* assert req signal */ + + +/* 0x0a - ssid (scsi selector id) bit definitions */ + +#define sym_ssid_encid 0x07 /* encoded destination scsi id [mask] */ +#define sym_ssid_val 0x80 /* scsi valid bit */ + + +/* 0x0b - sbcl (scsi bus control lines) bit definitions */ + +#define sym_sbcl_io 0x01 /* i/o status */ +#define sym_sbcl_cd 0x02 /* c/d status */ +#define sym_sbcl_msg 0x04 /* msg status */ +#define sym_sbcl_atn 0x08 /* atn status */ +#define sym_sbcl_sel 0x10 /* sel status */ +#define sym_sbcl_bsy 0x20 /* bsy status */ +#define sym_sbcl_ack 0x40 /* ack status */ +#define sym_sbcl_req 0x80 /* req status */ + + +/* 0x0c - dstat (dma status) bit definitions */ + +#define sym_dstat_iid 0x01 /* illegal instruction detected */ +#define sym_dstat_sir 0x04 /* scripts interrupt instruction received */ +#define sym_dstat_ssi 0x08 /* single step interrupt */ +#define sym_dstat_abrt 0x10 /* aborted */ +#define sym_dstat_bf 0x20 /* bus fault */ +#define sym_dstat_mdpe 0x40 /* master data parity error */ +#define sym_dstat_dfe 0x80 /* dma fifo empty */ + + +/* 0x0d - sstat0 (scsi status 0) bit definitions */ + +#define sym_sstat0_sdp 0x01 /* scsi parity signal */ +#define sym_sstat0_rst 0x02 /* scsi reset signal */ +#define sym_sstat0_woa 0x04 /* won arbitration */ +#define sym_sstat0_loa 0x08 /* lost arbitration */ +#define sym_sstat0_aip 0x10 /* arbitration in progress */ +#define sym_sstat0_olf 0x20 /* scsi output data latch full */ +#define sym_sstat0_orf 0x40 /* scsi output data register full */ +#define sym_sstat0_ilf 0x80 /* scsi input data latch full */ + + +/* 0x0e - sstat1 (scsi status 1) bit definitions */ + +#define sym_sstat1_io 0x01 /* scsi i/o signal */ +#define sym_sstat1_cd 0x02 /* scsi c/d signal */ +#define sym_sstat1_msg 0x04 /* scsi msg signal */ +#define sym_sstat1_sdpl 0x08 /* latched scsi parity */ +#define sym_sstat1_fifo 0xf0 /* mask for fifo flags [mask] */ + + +/* 0x0f - sstat2 (scsi status 2) bit definitions */ + +#define sym_sstat2_ldsc 0x02 /* last disconnect */ + + +/* 0x14 - istat (interrupt status) bit definitions */ + +#define sym_istat_dip 0x01 /* dma interrupt pending */ +#define sym_istat_sip 0x02 /* scsi interrupt pending */ +#define sym_istat_intf 0x04 /* interrupt on the fly */ +#define sym_istat_con 0x08 /* connected */ +#define sym_istat_sem 0x10 /* semaphore */ +#define sym_istat_sigp 0x20 /* signal process */ +#define sym_istat_srst 0x40 /* software reset */ +#define sym_istat_abrt 0x80 /* abort operation */ + + +/* 0x19 - ctest1 (chip test 1) bit definitions */ + +#define sym_ctest1_ffl 0x0f /* byte empty in dma fifo [mask] */ +#define sym_ctest1_fmt 0xf0 /* byte full in dma fifo [mask] */ + + +/* 0x1a - ctest2 (chip test 2) bit definitions */ + +#define sym_ctest2_dack 0x01 /* data acknowledge status */ +#define sym_ctest2_dreq 0x02 /* data request status */ +#define sym_ctest2_teop 0x04 /* scsi true end of process */ +#define sym_ctest2_cm 0x10 /* configured as memory */ +#define sym_ctest2_cio 0x20 /* configured as i/o */ +#define sym_ctest2_sigp 0x40 /* signal process */ +#define sym_ctest2_ddir 0x80 /* data transfer direction */ + + +/* 0x1b - ctest3 (chip test 3) bit definitions */ + +#define sym_ctest3_fm 0x02 /* fetch pin mode */ +#define sym_ctest3_clf 0x04 /* clear dma fifo */ +#define sym_ctest3_flf 0x08 /* flush dma fifo */ +#define sym_ctest3_v 0xf0 /* chip revision level [mask] */ + + +/* 0x20 - dfifo (dma fifo) bit definitions */ + +#define sym_dfifo_bo 0x7f /* byte offset counter */ + + +/* 0x21 - ctest4 (chip test 4) bit definitions */ + +#define sym_ctest4_fbl 0x07 /* fifo byte control [mask] */ +#define sym_ctest4_mpee 0x08 /* master parity error enable */ +#define sym_ctest4_srtm 0x10 /* shadow register test mode */ +#define sym_ctest4_zsd 0x20 /* scsi data high impedance */ +#define sym_ctest4_zmod 0x40 /* high impedance mode */ +#define sym_ctest4_bdis 0x80 /* burst disable */ + +/* 0x22 - ctest5 (chip test 5) bit definitions */ + +#define sym_ctest5_ddir 0x08 /* dma direction */ +#define sym_ctest5_masr 0x10 /* master control for set or reset pulses */ +#define sym_ctest5_bbck 0x40 /* clock byte counter */ +#define sym_ctest5_adck 0x80 /* clock address incrementor */ + + +/* 0x38 - dmode (dma mode) bit definitions */ + +#define sym_dmode_man 0x01 /* manual start mode */ +#define sym_dmode_erl 0x08 /* enable read line */ +#define sym_dmode_diom 0x10 /* destination i/o memory enable */ +#define sym_dmode_siom 0x20 /* source i/o memory enable */ +#define sym_dmode_bl 0xc0 /* burst length */ + + +/* 0x39 - dien (dma interrupt enable) bit definitions */ + +#define sym_dien_iid 0x01 /* illegal instruction detected */ +#define sym_dien_sir 0x04 /* scripts interrupt instruction received */ +#define sym_dien_ssi 0x08 /* single step instruction */ +#define sym_dien_abrt 0x10 /* aborted */ +#define sym_dien_bf 0x20 /* bus fault */ +#define sym_dien_mdpe 0x40 /* master data parity error */ + + +/* 0x3b - dcntl (dma control) bit definitions */ + +#define sym_dcntl_com 0x01 /* 53c700 compatibility */ +#define sym_dcntl_std 0x04 /* start dma operation */ +#define sym_dcntl_irqm 0x08 /* irq mode */ +#define sym_dcntl_ssm 0x10 /* single step mode */ + + +/* 0x40 - sien0 (scsi interrupt enable 0) bit definitions */ + +#define sym_sien0_par 0x01 /* scsi parity error */ +#define sym_sien0_rst 0x02 /* scsi reset condition */ +#define sym_sien0_udc 0x04 /* unexpected disconnect */ +#define sym_sien0_sge 0x08 /* scsi gross error */ +#define sym_sien0_rsl 0x10 /* reselected */ +#define sym_sien0_sel 0x20 /* selected */ +#define sym_sien0_cmp 0x40 /* function complete */ +#define sym_sien0_ma 0x80 /* scsi phase mismatch or scsi atn condition */ + + +/* 0x41 - sien1 (scsi interrupt enable 1) bit definitions */ + +#define sym_sien1_hth 0x01 /* handshake to handshake timer expired */ +#define sym_sien1_gen 0x02 /* general purpose timer expired */ +#define sym_sien1_sto 0x04 /* selection or reselection timeout */ +#define sym_sien1_sbmc 0x10 /* SCSI Bus Mode Change */ + +/* 0x42 - sist0 (scsi interrupt status 0) bit definitions */ + +#define sym_sist0_par 0x01 /* parity error */ +#define sym_sist0_rst 0x02 /* scsi rst/received */ +#define sym_sist0_udc 0x04 /* unexpected disconnect */ +#define sym_sist0_sge 0x08 /* scsi gross error */ +#define sym_sist0_rsl 0x10 /* reselected */ +#define sym_sist0_sel 0x20 /* selected */ +#define sym_sist0_cmp 0x40 /* function complete */ +#define sym_sist0_ma 0x80 /* scsi phase mismatch or scsi atn condition */ + + +/* 0x43 - sist1 (scsi interrupt status 1) bit definitions */ + +#define sym_sist1_hth 0x01 /* handshake to handshake timer expired */ +#define sym_sist1_gen 0x02 /* general purpose timer expired */ +#define sym_sist1_sto 0x04 /* selection or reselection timeout */ +#define sym_sist1_sbmc 0x10 /* SCSI Bus Mode Change */ + +/* 0x46 - macntl (memory access control) bit definitions */ + +#define sym_macntl_scpts 0x01 /* scripts */ +#define sym_macntl_pscpt 0x02 /* pointer scripts */ +#define sym_macntl_drd 0x04 /* data read */ +#define sym_macntl_dwr 0x08 /* data write */ +#define sym_macntl_typ 0xf0 /* chip type [mask] */ + + +/* 0x47 - gpcntl (general purpose pin control) bit definitions */ + +#define sym_gpcntl_gpio0 0x01 /* gpio 0 */ +#define sym_gpcntl_gpio1 0x02 /* gpio 1 */ +#define sym_gpcntl_fe 0x40 /* fetch enable */ +#define sym_gpcntl_me 0x80 /* master enable */ + + +/* 0x48 - stime0 (scsi timer 0) bit definitions */ + +#define sym_stime0_sel 0x07 /* selection time-out [mask] */ +#define sym_stime0_hth 0xf8 /* handshake to handshake timer period [mask] */ + + +/* 0x49 - stime1 (scsi timer 1) bit definitions */ + +#define sym_stime1_gen 0x0f /* general purpose timer period [mask] */ + + +/* 0x4c - stest0 (scsi test 0) bit definitions */ + +#define sym_stest0_som 0x01 /* scsi synchronous offset maximum */ +#define sym_stest0_soz 0x02 /* scsi synchronous offset zero */ +#define sym_stest0_art 0x04 /* arbitration priority encoder test */ +#define sym_stest0_slt 0x08 /* selection response logic test */ + + +/* 0x4d - stest1 (scsi test 1) bit definitions */ + +#define sym_stest1_sclk 0x80 /* sclk */ + + +/* 0x4e - stest2 (scsi test 2) bit definitions */ + +#define sym_stest2_low 0x01 /* scsi low level mode */ +#define sym_stest2_ext 0x02 /* extend sreq/sack filtering */ +#define sym_stest2_szm 0x08 /* scsi high-impedance mode */ +#define sym_stest2_slb 0x10 /* scsi loopback mode */ +#define sym_stest2_rof 0x40 /* reset scsi offset */ +#define sym_stest2_sce 0x80 /* scsi control enable */ + + +/* 0x4f - stest3 (scsi test 3) bit definitions */ + +#define sym_stest3_stw 0x01 /* scsi fifo test write */ +#define sym_stest3_csf 0x02 /* clear scsi fifo */ +#define sym_stest3_ttm 0x04 /* timer test mode */ +#define sym_stest3_dsi 0x10 /* disable single initiator response */ +#define sym_stest3_hsc 0x20 /* halt scsi clock */ +#define sym_stest3_str 0x40 /* scsi fifo test read */ +#define sym_stest3_te 0x80 /* tolerant enable */ + +#define sym_stest4_hvd 0x40 /* high voltage diff */ +#define sym_stest4_se 0x80 /* single ended */ +#define sym_stest4_lvd 0xc0 /* low voltage diff */ +#define sym_stest4_lock 0x20 /* clock quadrupler locker */ + +#endif diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/Jamfile b/src/add-ons/kernel/busses/scsi/53c8xx/Jamfile new file mode 100644 index 0000000000..127da95e20 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src add-ons kernel busses scsi symbios ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +SubDirCcFlags -DBUILD_LOADABLE ; + +KernelAddon 53c8xx : kernel busses scsi : + 53c8xx.c +; + +Package haiku-53c8xx-cvs + : + 53c8xx + : + boot home config add-ons kernel busses scsi ; diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/LICENSE b/src/add-ons/kernel/busses/scsi/53c8xx/LICENSE new file mode 100644 index 0000000000..86a4268fa9 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/LICENSE @@ -0,0 +1,31 @@ +---------------------- +Be Sample Code License +---------------------- + +Copyright 1991-1999, Be Incorporated. +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. 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 TITLE, NON-INFRINGEMENT, 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. diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/scripts.c b/src/add-ons/kernel/busses/scsi/53c8xx/scripts.c new file mode 100644 index 0000000000..ccd4b01609 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/scripts.c @@ -0,0 +1,279 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +typedef unsigned long ULONG; + +ULONG SCRIPT[] = { + 0x50000000L, 0x00000010L, + 0x98080000L, 0x00000011L, + 0x721A0000L, 0x00000000L, + 0x98080000L, 0x00000010L, + 0x43000000L, 0x00000040L, + 0x860B0000L, 0x00000038L, + 0x98080000L, 0x00000012L, + 0x98080000L, 0x00000013L, + 0x98080000L, 0x00000011L, + 0x42000000L, 0x00000050L, + 0x870B0000L, 0x00000088L, + 0x860A0000L, 0x000001D8L, + 0x820A0000L, 0x000001E8L, + 0x810A0000L, 0x000001F8L, + 0x800A0000L, 0x000001F8L, + 0x830A0000L, 0x00000200L, + 0x98080000L, 0x00000019L, + 0x1F000000L, 0x00000010L, + 0x800C0001L, 0x00000170L, + 0x800C0004L, 0x00000238L, + 0x800C0023L, 0x00000150L, + 0x60000040L, 0x00000000L, + 0x800C0002L, 0x00000050L, + 0x800C0007L, 0x00000050L, + 0x800C0003L, 0x00000050L, + 0x800C0080L, 0x00000050L, + 0x800C0081L, 0x00000050L, + 0x800C0082L, 0x00000050L, + 0x800C0083L, 0x00000050L, + 0x800C0084L, 0x00000050L, + 0x800C0085L, 0x00000050L, + 0x800C0086L, 0x00000050L, + 0x800C0087L, 0x00000050L, + 0x800C00C0L, 0x00000050L, + 0x800C00C1L, 0x00000050L, + 0x800C00C2L, 0x00000050L, + 0x800C00C3L, 0x00000050L, + 0x800C00C4L, 0x00000050L, + 0x800C00C5L, 0x00000050L, + 0x800C00C6L, 0x00000050L, + 0x800C00C7L, 0x00000050L, + 0x98080000L, 0x0000001AL, + 0x60000040L, 0x00000000L, + 0x1F000000L, 0x00000018L, + 0x60000040L, 0x00000000L, + 0x80080000L, 0x00000050L, + 0x60000040L, 0x00000000L, + 0x1F000000L, 0x00000018L, + 0x800C0003L, 0x000001B8L, + 0x800C0002L, 0x00000198L, + 0x98080000L, 0x0000001BL, + 0x60000040L, 0x00000000L, + 0x1F000000L, 0x00000038L, + 0x60000040L, 0x00000000L, + 0x98080000L, 0x0000001FL, + 0x60000040L, 0x00000000L, + 0x1F000000L, 0x00000020L, + 0x60000040L, 0x00000000L, + 0x98080000L, 0x0000001EL, + 0x1E000000L, 0x00000008L, + 0x80080000L, 0x00000050L, + 0x1A000000L, 0x00000030L, + 0x80080000L, 0x00000050L, + 0x98080000L, 0x00000017L, + 0x1B000000L, 0x00000028L, + 0x9F030000L, 0x00000016L, + 0x1F000000L, 0x00000010L, + 0x7C027F00L, 0x00000000L, + 0x60000040L, 0x00000000L, + 0x48000000L, 0x00000000L, + 0x98080000L, 0x00000014L, + 0x7C027F00L, 0x00000000L, + 0x60000040L, 0x00000000L, + 0x48000000L, 0x00000000L, + 0x98080000L, 0x00000015L, + 0x98080000L, 0x0000001CL + +}; + +#define Abs_Count 22 +char *Absolute_Names[Abs_Count] = { + "ctxt_command", + "ctxt_extdmsg", + "ctxt_recvmsg", + "ctxt_sendmsg", + "ctxt_device", + "ctxt_status", + "ctxt_syncmsg", + "ctxt_widemsg", + "status_badextmsg", + "status_badmsg", + "status_badphase", + "status_badstatus", + "status_disconnect", + "status_complete", + "status_overrun", + "status_ready", + "status_reselected", + "status_selected", + "status_selftest", + "status_syncin", + "status_timeout", + "status_widein" +}; + +#define A_ctxt_device 0x00000000L +ULONG A_ctxt_device_Used[] = { + 0x00000008L, + 0x00000012L +}; + +#define A_ctxt_sendmsg 0x00000008L +ULONG A_ctxt_sendmsg_Used[] = { + 0x00000077L +}; + +#define A_ctxt_recvmsg 0x00000010L +ULONG A_ctxt_recvmsg_Used[] = { + 0x00000023L, + 0x00000085L +}; + +#define A_status_ready 0x00000010L +ULONG A_status_ready_Used[] = { + 0x00000007L +}; + +#define A_status_reselected 0x00000011L +ULONG A_status_reselected_Used[] = { + 0x00000003L, + 0x00000011L +}; + +#define A_status_timeout 0x00000012L +ULONG A_status_timeout_Used[] = { + 0x0000000DL +}; + +#define A_status_selected 0x00000013L +ULONG A_status_selected_Used[] = { + 0x0000000FL +}; + +#define A_status_complete 0x00000014L +ULONG A_status_complete_Used[] = { + 0x0000008DL +}; + +#define A_status_disconnect 0x00000015L +ULONG A_status_disconnect_Used[] = { + 0x00000095L +}; + +#define A_status_badstatus 0x00000016L +ULONG A_status_badstatus_Used[] = { + 0x00000083L +}; + +#define A_status_overrun 0x00000017L +ULONG A_status_overrun_Used[] = { + 0x0000007FL +}; + +#define A_ctxt_extdmsg 0x00000018L +ULONG A_ctxt_extdmsg_Used[] = { + 0x00000057L, + 0x0000005FL +}; + +#define A_status_badphase 0x00000019L +ULONG A_status_badphase_Used[] = { + 0x00000021L +}; + +#define A_status_badmsg 0x0000001AL +ULONG A_status_badmsg_Used[] = { + 0x00000053L +}; + +#define A_status_badextmsg 0x0000001BL +ULONG A_status_badextmsg_Used[] = { + 0x00000065L +}; + +#define A_status_selftest 0x0000001CL +ULONG A_status_selftest_Used[] = { + 0x00000097L +}; + +#define A_status_syncin 0x0000001EL +ULONG A_status_syncin_Used[] = { + 0x00000075L +}; + +#define A_status_widein 0x0000001FL +ULONG A_status_widein_Used[] = { + 0x0000006DL +}; + +#define A_ctxt_syncmsg 0x00000020L +ULONG A_ctxt_syncmsg_Used[] = { + 0x00000071L +}; + +#define A_ctxt_status 0x00000028L +ULONG A_ctxt_status_Used[] = { + 0x00000081L +}; + +#define A_ctxt_command 0x00000030L +ULONG A_ctxt_command_Used[] = { + 0x0000007BL +}; + +#define A_ctxt_widemsg 0x00000038L +ULONG A_ctxt_widemsg_Used[] = { + 0x00000069L +}; + +#define Ent_do_dataout 0x00000070L +#define Ent_do_datain 0x00000068L +#define Ent_idle 0x00000000L +#define Ent_phase_dataerr 0x000001F8L +#define Ent_start 0x00000020L +#define Ent_switch 0x00000050L +#define Ent_switch_resel 0x00000048L +#define Ent_test 0x00000258L + + +ULONG LABELPATCHES[] = { + 0x00000001L, + 0x00000009L, + 0x0000000BL, + 0x00000013L, + 0x00000015L, + 0x00000017L, + 0x00000019L, + 0x0000001BL, + 0x0000001DL, + 0x0000001FL, + 0x00000025L, + 0x00000027L, + 0x00000029L, + 0x0000002DL, + 0x0000002FL, + 0x00000031L, + 0x00000033L, + 0x00000035L, + 0x00000037L, + 0x00000039L, + 0x0000003BL, + 0x0000003DL, + 0x0000003FL, + 0x00000041L, + 0x00000043L, + 0x00000045L, + 0x00000047L, + 0x00000049L, + 0x0000004BL, + 0x0000004DL, + 0x0000004FL, + 0x00000051L, + 0x0000005BL, + 0x00000061L, + 0x00000063L, + 0x00000079L, + 0x0000007DL +}; + +ULONG INSTRUCTIONS = 0x0000004CL; +ULONG PATCHES = 0x00000025L; diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/scripts.ss b/src/add-ons/kernel/busses/scsi/53c8xx/scripts.ss new file mode 100644 index 0000000000..541d3de337 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/scripts.ss @@ -0,0 +1,186 @@ +; +; BeOS 53c8xx SCRIPTS +; +ARCH 810 + +; Interrupt codes used to signal driver from SCRIPTS +; +ABSOLUTE status_ready = 0x10 ; idle loop interrupted by driver +ABSOLUTE status_reselected = 0x11 ; select or idle interrupted by reselection +ABSOLUTE status_timeout = 0x12 ; select timed out +ABSOLUTE status_selected = 0x13 ; select succeeded +ABSOLUTE status_complete = 0x14 ; transaction completed +ABSOLUTE status_disconnect = 0x15 ; device disconnected in the middle +ABSOLUTE status_badstatus = 0x16 ; snafu in the status phase +ABSOLUTE status_overrun = 0x17 ; data overrun occurred +ABSOLUTE status_underrun = 0x18 ; data underrun occurred +ABSOLUTE status_badphase = 0x19 ; weird phase transition occurred +ABSOLUTE status_badmsg = 0x1a ; bad msg received +ABSOLUTE status_badextmsg = 0x1b ; bad extended msg received +ABSOLUTE status_selftest = 0x1c ; used by selftest stub +ABSOLUTE status_iocomplete = 0x1d ; used by driver +ABSOLUTE status_syncin = 0x1e ; inbound sync msg +ABSOLUTE status_widein = 0x1f ; inbound wdtr msg +ABSOLUTE status_ignore_residue = 0x20 ; ignore wide residue bytes + +; DSA-offset data table +; +ABSOLUTE ctxt_device = 0x00 ; targ id, sync params, etc +ABSOLUTE ctxt_sendmsg = 0x08 ; outgoing (ID) msg(1) pointer +ABSOLUTE ctxt_recvmsg = 0x10 ; incoming msg(1) pointer +ABSOLUTE ctxt_extdmsg = 0x18 ; extdmsg(1) pointer +ABSOLUTE ctxt_syncmsg = 0x20 ; sync(2) pointer +ABSOLUTE ctxt_status = 0x28 ; status(1) pointer +ABSOLUTE ctxt_command = 0x30 ; command(6,10,12) pointer +ABSOLUTE ctxt_widemsg = 0x38 ; wide(2) pointer + +ENTRY start +ENTRY idle +ENTRY switch +ENTRY switch_resel +ENTRY phase_dataerr +ENTRY test + +ENTRY do_datain +ENTRY do_dataout + +; ------------------------------------------------------------------------------------ +; Idle loop -- when no new requests are pending wait for a reselect +; or interrupt from the driver +; +idle: + WAIT RESELECT interrupted + INT status_reselected + +interrupted: + MOVE CTEST2 TO SFBR ; read sigp to clear it + INT status_ready + + +; ------------------------------------------------------------------------------------ +; Driver must load DSA and jump here to attempt to select a target, beginning a new +; transaction. Interrupt will indicate success, timeout, or reselection +; +start: + SELECT ATN FROM ctxt_device, resel ; try to select the dev for the req + JUMP selected, WHEN MSG_OUT ; force wait for timeout + INT status_timeout + +selected: + INT status_selected + +resel: + INT status_reselected + + +; ------------------------------------------------------------------------------------ +; If we're entering the main dispatcher after a reselection, we must insure that +; the registers for SYNC/WIDE transfers are properly loaded. This is the solution +; suggested in 9-18 of the Symbios programming guide. +; +switch_resel: + SELECT FROM ctxt_device, switch ; force the load of SXFER/SCNTL3, etc + +; ------------------------------------------------------------------------------------ +; Main activity entry -- driver must set DSA and patch do_datain and do_dataout +; before starting this dispatch function. +; +switch: + JUMP phase_msgin, WHEN MSG_IN + JUMP phase_msgout, IF MSG_OUT + JUMP phase_command, IF CMD +do_datain: ; Patched by driver + JUMP phase_dataerr, IF DATA_IN +do_dataout: ; Patched by driver + JUMP phase_dataerr, IF DATA_OUT + JUMP phase_status, IF STATUS + INT status_badphase + +phase_msgin: + MOVE FROM ctxt_recvmsg, WHEN MSG_IN + JUMP phase_msgin_ext, IF 0x01 + JUMP disc, IF 0x04 + JUMP ignore, IF 0x23 ; /* wide ignore residue */ + CLEAR ACK + JUMP switch, IF 0x02 ; ignore save data pointers + JUMP switch, IF 0x07 ; ignore message reject :) + JUMP switch, IF 0x03 ; ignore restore data pointers + JUMP switch, IF 0x80 ; ignore ident after reselect + JUMP switch, IF 0x81 ; ignore ident after reselect + JUMP switch, IF 0x82 ; ignore ident after reselect + JUMP switch, IF 0x83 ; ignore ident after reselect + JUMP switch, IF 0x84 ; ignore ident after reselect + JUMP switch, IF 0x85 ; ignore ident after reselect + JUMP switch, IF 0x86 ; ignore ident after reselect + JUMP switch, IF 0x87 ; ignore ident after reselect + JUMP switch, IF 0xC0 ; ignore ident after reselect + JUMP switch, IF 0xC1 ; ignore ident after reselect + JUMP switch, IF 0xC2 ; ignore ident after reselect + JUMP switch, IF 0xC3 ; ignore ident after reselect + JUMP switch, IF 0xC4 ; ignore ident after reselect + JUMP switch, IF 0xC5 ; ignore ident after reselect + JUMP switch, IF 0xC6 ; ignore ident after reselect + JUMP switch, IF 0xC7 ; ignore ident after reselect + INT status_badmsg + +ignore: + CLEAR ACK + MOVE FROM ctxt_extdmsg, WHEN MSG_IN ; read status byte + CLEAR ACK + JUMP switch +; INT status_ignore_residue + +phase_msgin_ext: + CLEAR ACK + MOVE FROM ctxt_extdmsg, WHEN MSG_IN ; read extended message length + JUMP phase_msgin_sync, IF 0x03 + JUMP phase_msgin_wide, IF 0x02 + INT status_badextmsg + +phase_msgin_wide: + CLEAR ACK + MOVE FROM ctxt_widemsg, WHEN MSG_IN + CLEAR ACK + INT status_widein + +phase_msgin_sync: + CLEAR ACK + MOVE FROM ctxt_syncmsg, WHEN MSG_IN + CLEAR ACK + INT status_syncin + +phase_msgout: + MOVE FROM ctxt_sendmsg, WHEN MSG_OUT + JUMP switch + +phase_command: + MOVE FROM ctxt_command, WHEN CMD + JUMP switch + +phase_dataerr: + INT status_overrun + +phase_status: + MOVE FROM ctxt_status, WHEN STATUS + INT status_badstatus, WHEN NOT MSG_IN + MOVE FROM ctxt_recvmsg, WHEN MSG_IN + INT status_badmsg, IF NOT 0x00 ; not disconnect?! + MOVE SCNTL2 & 0x7f TO SCNTL2 + CLEAR ACK + WAIT DISCONNECT + INT status_complete + +disc: + MOVE SCNTL2 & 0x7f to SCNTL2 ; expect disconnect + CLEAR ACK + WAIT DISCONNECT + INT status_disconnect + +; ------------------------------------------------------------------------------------ +; Self-test snippet +; +test: + INT status_selftest + + + \ No newline at end of file diff --git a/src/add-ons/kernel/busses/scsi/53c8xx/symbios.h b/src/add-ons/kernel/busses/scsi/53c8xx/symbios.h new file mode 100644 index 0000000000..847be72c79 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/53c8xx/symbios.h @@ -0,0 +1,167 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +/* +** 53c8xx Driver - Data structures and shared constants +*/ + +/* status codes signaled from SCRIPTS to driver */ +#define status_ready 0x10 // idle loop interrupted by driver +#define status_reselected 0x11 // select or idle interrupted by reselection +#define status_timeout 0x12 // select timed out +#define status_selected 0x13 // select succeeded +#define status_complete 0x14 // transaction completed +#define status_disconnect 0x15 // device disconnected in the middle +#define status_badstatus 0x16 // snafu in the status phase +#define status_overrun 0x17 // data overrun occurred +#define status_underrun 0x18 // data underrun occurred +#define status_badphase 0x19 // weird phase transition occurred +#define status_badmsg 0x1a // bad msg received +#define status_badextmsg 0x1b // bad extended msg received +#define status_selftest 0x1c // used by selftest stub +#define status_iocomplete 0x1d +#define status_syncin 0x1e +#define status_widein 0x1f +#define status_ignore_residue 0x20 + +/* status codes private to driver */ +#define status_inactive 0x00 // no request pending +#define status_queued 0x01 // start request is in the startqueue +#define status_selecting 0x02 // attempting to select +#define status_active 0x03 // SCRIPTS is handling it +#define status_waiting 0x04 // Waiting for reselection + +#define OP_NDATA_IN 0x09000000L +#define OP_NDATA_OUT 0x08000000L +#define OP_WDATA_IN 0x01000000L +#define OP_WDATA_OUT 0x00000000L + +#define OP_END 0x98080000L +#define ARG_END (status_iocomplete) + +typedef struct +{ + uint32 count; + uint32 address; +} SymInd; + +#define PATCH_DATAIN ((Ent_do_datain/4) + 1) +#define PATCH_DATAOUT ((Ent_do_dataout/4) + 1) + + +#define ctxt_device 0 +#define ctxt_sendmsg 1 +#define ctxt_recvmsg 2 +#define ctxt_extdmsg 3 +#define ctxt_syncmsg 4 +#define ctxt_status 5 +#define ctxt_command 6 +#define ctxt_widemsg 7 +#define ctxt_program 8 + +typedef struct +{ + uchar _command[12]; /* 0 - 11 */ + uchar _syncmsg[2]; /* 12 - 13 */ + uchar _widemsg[2]; /* 14 - 15 */ + uchar _sendmsg[8]; /* 16 - 23 */ + uchar _recvmsg[1]; /* 24 */ + uchar _extdmsg[1]; /* 25 */ + uchar _status[1]; /* 26 */ + uchar _padding[1]; /* 27 */ + + SymInd device; /* 28 */ + SymInd sendmsg; /* 36 */ + SymInd recvmsg; /* 44 */ + SymInd extdmsg; /* 52 */ + SymInd syncmsg; /* 60 */ + SymInd status; /* 68 */ + SymInd command; /* 76 */ + SymInd widemsg; /* 84 */ + +/* MUST be dword aligned! */ + SymInd table[131]; /* 92 --- 129 entries, 1 eot, 1 scratch */ +} SymPriv; + +#define ADJUST_PRIV_TO_DSA 28 +#define ADJUST_PRIV_TO_TABLE 92 + +typedef struct _SymTarg +{ + struct _Symbios *adapter; + struct _SymTarg *next; + + uchar device[4]; /* symbios register defs for the device */ + int sem_targ; /* mutex allowing only one req per target */ + int sem_done; /* notification semaphore */ + CCB_SCSIIO *ccb; /* ccb for the current request for this target or NULL */ + + SymPriv *priv; /* priv data area within ccb */ + uint32 priv_phys; /* physical address of priv */ + uint32 table_phys; /* physical address of sgtable */ + uint32 datain_phys; + uint32 dataout_phys; + + int inbound; /* read data from device */ + + uint32 period; /* sync period */ + uint32 offset; /* sync offset */ + uint32 wide; + + uint32 flags; + uint32 status; + uint32 id; +} SymTarg; + +#define tf_ask_sync 0x0001 +#define tf_ask_wide 0x0002 +#define tf_is_sync 0x0010 +#define tf_is_wide 0x0020 +#define tf_ignore 0x0100 + +typedef struct _Symbios +{ + uint32 num; /* card number */ + uint32 iobase; /* io base address */ + uint32 irq; /* assigned irq */ + + char *name; /* device type name */ + uint32 host_targ_id; + uint32 max_targ_id; + int reset; + + int registered; + + uint32 *script; /* 1 page of on/offboard scripts ram */ + uint32 sram_phys; /* physical address thereof */ + + SymTarg targ[16]; /* one targ descriptor per target */ + spinlock hwlock; /* lock protecting register access */ + + SymTarg *startqueue; /* target being started */ + SymTarg *startqueuetail; + SymTarg *active; /* target currently being interacted with */ + /* null if IDLE, == startqueue if starting */ + + enum { + OFFLINE, IDLE, START, ACTIVE, TEST + } status; + + struct { + uint period; /* negotiated period */ + uint period_ns; /* configured period in ns */ + uchar scntl3; /* values for scntl3 SCF and CCF bits */ + uchar sxfer; /* values for xfer TP2-0 bits */ + } syncinfo[16]; + uint32 syncsize; /* number of syncinfo entries to look at */ + uint32 idmask; + + uint32 scntl3; + uint32 sclk; /* SCLK in KHz */ + uint32 maxoffset; + + uint32 op_in; + uint32 op_out; +} Symbios; diff --git a/src/add-ons/kernel/busses/scsi/Jamfile b/src/add-ons/kernel/busses/scsi/Jamfile index f9cf48f6c7..fe10b8bab7 100644 --- a/src/add-ons/kernel/busses/scsi/Jamfile +++ b/src/add-ons/kernel/busses/scsi/Jamfile @@ -1,3 +1,5 @@ SubDir HAIKU_TOP src add-ons kernel busses scsi ; SubInclude HAIKU_TOP src add-ons kernel busses scsi usb ; +SubInclude HAIKU_TOP src add-ons kernel busses scsi buslogic ; +SubInclude HAIKU_TOP src add-ons kernel busses scsi 53c8xx ; diff --git a/src/add-ons/kernel/busses/scsi/buslogic/Jamfile b/src/add-ons/kernel/busses/scsi/buslogic/Jamfile new file mode 100644 index 0000000000..007f44eae1 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/buslogic/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src add-ons kernel busses scsi buslogic ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +SubDirCcFlags -DBUILD_LOADABLE ; + +KernelAddon buslogic : kernel busses scsi : + buslogic.c +; + +Package haiku-buslogic-cvs + : + buslogic + : + boot home config add-ons kernel busses scsi ; diff --git a/src/add-ons/kernel/busses/scsi/buslogic/LICENSE b/src/add-ons/kernel/busses/scsi/buslogic/LICENSE new file mode 100644 index 0000000000..86a4268fa9 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/buslogic/LICENSE @@ -0,0 +1,31 @@ +---------------------- +Be Sample Code License +---------------------- + +Copyright 1991-1999, Be Incorporated. +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. 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 TITLE, NON-INFRINGEMENT, 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. diff --git a/src/add-ons/kernel/busses/scsi/buslogic/buslogic.c b/src/add-ons/kernel/busses/scsi/buslogic/buslogic.c new file mode 100644 index 0000000000..e49b189ea8 --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/buslogic/buslogic.c @@ -0,0 +1,1000 @@ +/* +** $Id: sim_buslogic.c,v 1.8 1998/04/21 00:54:52 swetland Exp $ +** +** SCSI Interface Module for BusLogic MultiMaster Controllers +** Copyright 1998, Brian J. Swetland +** +** Portions Copyright (c) 1994 by Be Incorporated. +** This file may be used under the terms of the Be Sample Code License. +*/ + + +#include +#include +#include + +/* +** Debug options: +*/ +#define DEBUG_BUSLOGIC /* Print Debugging Messages */ +#define xDEBUG_TRANSACTIONS +#define xSERIALIZE_REQS /* only allow one req to the controller at a time */ +#define xVERBOSE_IRQ /* enable kprintf()s in the IRQ handler */ + +#include +/* +** Macros for virt <-> phys translation +*/ +#define PhysToVirt(n) ((void *) (((uint32) n) + bl->phys_to_virt)) +#define VirtToPhys(n) (((uint32) n) + bl->virt_to_phys) + +/* +** Debugging Macros +*/ +#ifdef DEBUG_BUSLOGIC +#define d_printf dprintf +#ifdef DEBUG_TRANSACTIONS +#define dt_printf dprintf +#else +#define dt_printf(x...) +#endif +#else +#define d_printf(x...) +#endif + +/* TODO: +** +** - endian issues: the MultiMaster is little endian, should use swap() +** macro for PPC compatibility whenever exchanging addresses with it (DONE) +** - wrap phys addrs with ram_address() +** - support scatter-gather in the ccb_scsiio struct (DONE) +** - allocte bl_ccb_## semaphores on-demand (1/2 - only use 32) +** - error checking for *_sem functions... we're without a net right now +** - sync? (DONE - card handles this) +** - tagged queuing? +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "buslogic.h" + +/* +** Constants for the SIM +*/ +#define SIM_VERSION 0x01 +#define HBA_VERSION 0x01 + +static char sim_vendor_name[] = "Be, Inc."; +static char hba_vendor_name[] = "BusLogic"; +static char controller_family[] = "MultiMaster PCI"; + +static pci_module_info *pci; +static cam_for_sim_module_info *cam; + +static char cam_name[] = B_CAM_FOR_SIM_MODULE_NAME; +static char pci_name[] = B_PCI_MODULE_NAME; + +/* +** IO Macros +*/ + +/* XXX - fix me bjs */ +#define inb(p) (*pci->read_io_8)(p) +#define outb(p,v) (*pci->write_io_8)(p,v) + +#ifdef __INTEL__ +#define toLE(x) (x) +#define unLE(x) (x) +#else +#define toLE(x) B_HOST_TO_LENDIAN_INT32(x) +#define unLE(x) B_LENDIAN_TO_HOST_INT32(x) +#endif + + +static int32 +scsi_int_dispatch(void *data) +{ + BusLogic *bl = (BusLogic *) data; + BL_CCB32 *bl_ccb; + uchar intstat = inb(BL_INT_REG); + +#if BOOT +#else + if(!(intstat & BL_INT_INTV)) return B_UNHANDLED_INTERRUPT; +#endif + if(intstat & BL_INT_CMDC) { + bl->done = 1; +#ifdef VERBOSE_IRQ + kprintf("buslogic_irq: Command Complete\n"); +#endif + } + + if(intstat & BL_INT_RSTS){ + kprintf("buslogic_irq: BUS RESET\n"); + } + + /* have we got mail? */ + if(intstat & BL_INT_IMBL){ + while(bl->in_boxes[bl->in_nextbox].completion_code){ + bl_ccb = (BL_CCB32 *) + PhysToVirt(unLE(bl->in_boxes[bl->in_nextbox].ccb_phys)); + +#ifdef VERBOSE_IRQ + kprintf("buslogic_irq: CCB %08x (%08x) done, cc=0x%02x\n", + unLE(bl->in_boxes[bl->in_nextbox].ccb_phys), (uint32) bl_ccb, + bl->in_boxes[bl->in_nextbox].completion_code); +#endif + release_sem_etc(bl_ccb->done, 1, B_DO_NOT_RESCHEDULE); + + bl->in_boxes[bl->in_nextbox].completion_code = 0; + bl->in_nextbox++; + if(bl->in_nextbox == bl->box_count) bl->in_nextbox = 0; + } + } + + /* acknowledge the irq */ + outb(BL_CONTROL_REG, BL_CONTROL_RINT); + + return B_HANDLED_INTERRUPT; +} + + +/* Execute a command, with optional params send (in[in_len]) and +** results received (out[out_len]) +*/ +static int bl_execute(BusLogic *bl, uchar command, + void *in, int in_len, + void *out, int out_len) +{ + uchar status; + uchar *_in, *_out; +#ifdef TIMEOUT + int timeout; +#endif + + _in = (uchar *) in; + _out = (uchar *) out; + + if(!(inb(BL_STATUS_REG) & BL_STATUS_HARDY)) { + d_printf("buslogic: command 0x%02x %d/%d, not ready\n", + command, in_len, out_len); + return 1; + } + + outb(BL_COMMAND_REG, command); + +#ifdef TIMEOUT + timeout = 100; +#endif + while(in_len){ + status = inb(BL_STATUS_REG); + if(status & BL_STATUS_CMDINV) { + d_printf("buslogic: command 0x%02x %d/%d invalid\n", + command, in_len, out_len); + return 1; + } + if(status & BL_STATUS_CPRBSY) { +#ifdef TIMEOUT + timeout--; + if(!timeout) { + d_printf("buslogic: command 0x%02 timed out (a)\n",command); + return 1; + } + spin(100); +#endif + continue; + } + outb(BL_COMMAND_REG, *_in); + _in++; + in_len--; + } + +#ifdef TIMEOUT + timeout = 100; +#endif + while(out_len){ + status = inb(BL_STATUS_REG); + if(status & BL_STATUS_CMDINV) { + d_printf("buslogic: command 0x%02x %d/%d invalid\n", + command, in_len, out_len); + return 1; + } + if(status & BL_STATUS_DIRRDY) { + *_out = inb(BL_DATA_REG); + _out++; + out_len--; + } else { +#ifdef TIMEOUT + timeout--; + if(!timeout) { + d_printf("buslogic: command 0x%02 timed out (b)\n",command); + return 1; + } + spin(100); +#endif + } + } + +#ifdef TIMEOUT + timeout = 100; +#endif + while(!(inb(BL_STATUS_REG) & BL_STATUS_HARDY)) { +#ifdef TIMEOUT + timeout--; + if(!timeout) { + d_printf("buslogic: command 0x%02 timed out (c)\n",command); + return 1; + } + spin(100); +#endif + } + + return 0; +} + +/* Initialize the BT-9X8 and confirm that it is operating as expected +*/ +static long init_buslogic(BusLogic *bl) +{ + uchar status; + uchar id[16]; + int i; + char *str = bl->productname; + + d_printf("buslogic: init_buslogic()\n"); + + dprintf("buslogic: reset: "); + outb(BL_CONTROL_REG, BL_CONTROL_RHARD); + spin(10000); /* give the controller some time to settle down from reset */ + + for(i=0;i<1000;i++){ + spin(100000); + status = inb(BL_STATUS_REG); + if(status & BL_STATUS_DACT){ + dprintf("."); + continue; + } + if(status & BL_STATUS_DFAIL){ + dprintf(" FAILED\n"); + return -1; + } + if(status & BL_STATUS_INREQ){ + dprintf(" OKAY\n"); + break; + } + if(status & BL_STATUS_HARDY) { + dprintf(" READY\n"); + break; + } + } + if(i==100) { + dprintf(" TIMEOUT\n"); + return -1; + } + + if(bl_execute(bl, 0x04, NULL, 0, id, 4)){ + d_printf("buslogic: can't id?\n"); + return B_ERROR; + } + d_printf("buslogic: Firmware Rev %c.%c\n",id[2],id[3]); + + id[0]=14; + id[14]=id[2]; + + if(bl_execute(bl, 0x8d, id, 1, id, 14)){ + d_printf("buslogic: cannot read extended config\n"); + return B_ERROR; + } + + d_printf("buslogic: rev = %c.%c%c%c mb = %d, sgmax = %d, flags = 0x%02x\n", + id[14], id[10], id[11], id[12], id[4], id[2] | (id[3]<<8), id[13]); + if(id[13] & 0x01) bl->wide = 1; + else bl->wide = 0; + + if(id[14] == '5'){ + *str++ = 'B'; + *str++ = 'T'; + *str++ = '-'; + *str++ = '9'; + *str++ = bl->wide ? '5' : '4'; + *str++ = '8'; + if(id[13] & 0x02) *str++ = 'D'; + *str++ = ' '; + *str++ = 'v'; + *str++ = id[14]; + *str++ = '.'; + *str++ = id[10]; + *str++ = id[11]; + *str++ = id[12]; + *str++ = 0; + } else { + strcpy(str,"unknown"); + } + if(bl_execute(bl, 0x0B, NULL, 0, id, 3)){ + d_printf("buslogic: cannot read config\n"); + return B_ERROR; + } + bl->scsi_id = id[2]; + d_printf("buslogic: Adapter SCSI ID = %d\n",bl->scsi_id); + + if(install_io_interrupt_handler(bl->irq, scsi_int_dispatch, bl, 0) + == B_ERROR) d_printf("buslogic: can't install irq handler\n"); + + /* are we getting INTs? */ + bl->done = 0; + spin(10000); + outb(BL_COMMAND_REG, 0x00); + spin(1000000); + if(bl->done) { + d_printf("buslogic: interrupt test passed\n"); + } else { + d_printf("buslogic: interrupt test failed\n"); + return B_ERROR; + } + + /* strict round-robin on */ + id[0] = 0; + if(bl_execute(bl,0x8F, id, 1, NULL, 0)){ + d_printf("buslogic: cannot enable strict round-robin mode\n"); + return B_ERROR; + } + + + id[0] = bl->box_count; +{ int mbaddr = toLE(bl->phys_mailboxes); + memcpy(id + 1, &(mbaddr),4); +} + if(bl_execute(bl, 0x81, id, 5, NULL, 0)){ + d_printf("buslogic: cannot init mailboxes\n"); + return B_ERROR; + } + d_printf("buslogic: %d mailboxes @ 0x%08xv/0x%08lxp\n", + bl->box_count, (uint) bl->out_boxes, bl->phys_mailboxes); + + return B_NO_ERROR; +} + + + +/* sim_invalid() +** +** Generic invalid XPT command response +*/ +static long sim_invalid(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + d_printf("sim_invalid\n"); + return B_ERROR; +} + + +/* Convert a CCB_SCSIIO into a BL_CCB32 and (possibly SG array). +** +** +*/ + +static long sim_execute_scsi_io(BusLogic *bl, CCB_HEADER *ccbh) +{ + CCB_SCSIIO *ccb; + int cdb_len; + BL_CCB32 *bl_ccb; + BL_PRIV *priv; + uint32 priv_phys; + uint32 bl_ccb_phys; + physical_entry entries[2]; + physical_entry *scratch; + uint32 tmp; + int i,t,req; + + ccb = (CCB_SCSIIO *) ccbh; + +#ifdef DEBUG_BUSLOGIC + req = atomic_add(&(bl->reqid),1); +#endif + + /* valid cdb len? */ + cdb_len = ccb->cam_cdb_len; + if (cdb_len != 6 && cdb_len != 10 && cdb_len != 12) { + ccb->cam_ch.cam_status = CAM_REQ_INVALID; + return B_ERROR; + } + + /* acquire a CCB32 block */ + acquire_sem(bl->ccb_count); + + /* protect the freelist and unchain the CCB32 from it */ + acquire_sem(bl->ccb_lock); + bl_ccb = bl->first_ccb; + bl->first_ccb = bl_ccb->next; + release_sem(bl->ccb_lock); + + bl_ccb_phys = VirtToPhys(bl_ccb); + + + /* get contiguous area for bl_ccb in the private data area */ + get_memory_map((void *)ccb->cam_sim_priv, 4096, entries, 2); + + priv_phys = (uint32) entries[0].address; + priv = (BL_PRIV *) ccb->cam_sim_priv; + + /* copy over the CDB */ + if(ccb->cam_ch.cam_flags & CAM_CDB_POINTER) { + memcpy(bl_ccb->cdb, ccb->cam_cdb_io.cam_cdb_ptr, cdb_len); + } else { + memcpy(bl_ccb->cdb, ccb->cam_cdb_io.cam_cdb_bytes, cdb_len); + } + + /* fill out the ccb header */ + bl_ccb->direction = BL_CCB_DIR_DEFAULT; + bl_ccb->length_cdb = cdb_len; + bl_ccb->length_sense = ccb->cam_sense_len; + bl_ccb->_reserved1 = bl_ccb->_reserved2 = 0; + bl_ccb->target_id = ccb->cam_ch.cam_target_id; + bl_ccb->lun_tag = ccb->cam_ch.cam_target_lun & 0x07; + bl_ccb->ccb_control = 0; + bl_ccb->link_id = 0; + bl_ccb->link = 0; + bl_ccb->sense = toLE(priv_phys); + + /* okay, this is really disgusting and could potentially + break if physical_entry{} changes format... we use the + sg list as a scratchpad. Disgusting, but a start */ + + scratch = (physical_entry *) priv->sg; + + + if(ccb->cam_ch.cam_flags & CAM_SCATTER_VALID){ + /* we're using scatter gather -- things just got trickier */ + iovec *iov = (iovec *) ccb->cam_data_ptr; + int j,sgcount = 0; + + /* dprintf("buslogic: sg count = %d\n",ccb->cam_sglist_cnt);*/ + /* multiple entries, use SG */ + bl_ccb->opcode = BL_CCB_OP_INITIATE_RETLEN_SG; + bl_ccb->data = toLE(priv_phys + 256); + + /* for each entry in the sglist we were given ... */ + for(t=0,i=0;icam_sglist_cnt;i++){ + /* map it ... */ + get_memory_map(iov[i].iov_base, iov[i].iov_len, &(scratch[sgcount]), + MAX_SCATTER - sgcount); + + /* and make a bl sgentry for each chunk ... */ + for(j=sgcount;scratch[j].size && jsg[j].length; + priv->sg[j].length = toLE(priv->sg[j].phys); + priv->sg[j].phys = toLE(tmp); + } + + if(scratch[j].size) panic("egads! sgseg overrun in BusLogic SIM"); + } + if(t != ccb->cam_dxfer_len){ + dt_printf("buslogic/%d: error, %d != %d\n",req,t,ccb->cam_dxfer_len); + ccb->cam_ch.cam_status = CAM_REQ_INVALID; + + /* put the CCB32 back on the freelist and release our lock */ + acquire_sem(bl->ccb_lock); + bl_ccb->next = bl->first_ccb; + bl->first_ccb = bl_ccb; + release_sem(bl->ccb_lock); + release_sem(bl->ccb_count); + return B_ERROR; + } + /* total bytes in DataSegList */ + bl_ccb->length_data = toLE(sgcount * 8); + } else { + get_memory_map((void *)ccb->cam_data_ptr, ccb->cam_dxfer_len, scratch, + MAX_SCATTER); + + if(scratch[1].size){ + /* multiple entries, use SG */ + bl_ccb->opcode = BL_CCB_OP_INITIATE_RETLEN_SG; + bl_ccb->data = toLE(priv_phys + 256); + for(t=0,i=0;scratch[i].size && isg[i].length; + priv->sg[i].length = toLE(priv->sg[i].phys); + priv->sg[i].phys = toLE(tmp); + } + if(t != ccb->cam_dxfer_len){ + dt_printf("buslogic/%d: error, %d != %d\n",req,t,ccb->cam_dxfer_len); + ccb->cam_ch.cam_status = CAM_REQ_INVALID; + + /* put the CCB32 back on the freelist and release our lock */ + acquire_sem(bl->ccb_lock); + bl_ccb->next = bl->first_ccb; + bl->first_ccb = bl_ccb; + release_sem(bl->ccb_lock); + release_sem(bl->ccb_count); + return B_ERROR; + } + /* total bytes in DataSegList */ + bl_ccb->length_data = toLE(i * 8); + + } else { + bl_ccb->opcode = BL_CCB_OP_INITIATE_RETLEN; + /* single entry, use direct */ + t = bl_ccb->length_data = toLE(ccb->cam_dxfer_len); + bl_ccb->data = toLE((uint32) scratch[0].address); + } + } + + dt_printf("buslogic/%d: targ %d, dxfr %d, scsi op = 0x%02x\n",req, + bl_ccb->target_id, t, bl_ccb->cdb[0]); + + acquire_sem(bl->hw_lock); + + /* check for box in use state XXX */ + bl->out_boxes[bl->out_nextbox].ccb_phys = toLE(bl_ccb_phys); + bl->out_boxes[bl->out_nextbox].action_code = BL_ActionCode_Start; + bl->out_nextbox++; + if(bl->out_nextbox == bl->box_count) bl->out_nextbox = 0; + outb(BL_COMMAND_REG, 0x02); + +#ifndef SERIALIZE_REQS + release_sem(bl->hw_lock); +#endif +/* d_printf("buslogic/%d: CCB %08x (%08xv) waiting on done\n", + req, bl_ccb_phys, (uint32) bl_ccb);*/ + acquire_sem(bl_ccb->done); +/* d_printf("buslogic/%d: CCB %08x (%08xv) done\n", + req, bl_ccb_phys, (uint32) bl_ccb);*/ + +#ifdef SERIALIZE_REQS + release_sem(bl->hw_lock); +#endif + + if(bl_ccb->btstat){ + /* XXX - error state xlat goes here */ + switch(bl_ccb->btstat){ + case 0x11: + ccb->cam_ch.cam_status = CAM_SEL_TIMEOUT; + break; + case 0x12: + ccb->cam_ch.cam_status = CAM_DATA_RUN_ERR; + break; + case 0x13: + ccb->cam_ch.cam_status = CAM_UNEXP_BUSFREE; + break; + case 0x22: + case 0x23: + ccb->cam_ch.cam_status = CAM_SCSI_BUS_RESET; + break; + case 0x34: + ccb->cam_ch.cam_status = CAM_UNCOR_PARITY; + break; + default: + ccb->cam_ch.cam_status = CAM_REQ_INVALID; + } + dt_printf("buslogic/%d: error stat %02x\n",req,bl_ccb->btstat); + } else { + dt_printf("buslogic/%d: data %d/%d, sense %d/%d\n", req, + bl_ccb->length_data, ccb->cam_dxfer_len, + bl_ccb->length_sense, ccb->cam_sense_len); + + ccb->cam_resid = bl_ccb->length_data; + + /* under what condition should we do this? */ + memcpy(ccb->cam_sense_ptr, priv->sensedata, ccb->cam_sense_len); + + ccb->cam_scsi_status = bl_ccb->sdstat; + + if(bl_ccb->sdstat == 02){ + ccb->cam_ch.cam_status = CAM_REQ_CMP_ERR | CAM_AUTOSNS_VALID; + ccb->cam_sense_resid = 0; + dt_printf("buslogic/%d: error scsi\n",req); + } else { + ccb->cam_ch.cam_status = CAM_REQ_CMP; + ccb->cam_sense_resid = bl_ccb->length_sense; + dt_printf("buslogic/%d: success scsi\n",req); + /* put the CCB32 back on the freelist and release our lock */ + acquire_sem(bl->ccb_lock); + bl_ccb->next = bl->first_ccb; + bl->first_ccb = bl_ccb; + release_sem(bl->ccb_lock); + release_sem(bl->ccb_count); + return 0; + + } + } + + /* put the CCB32 back on the freelist and release our lock */ + acquire_sem(bl->ccb_lock); + bl_ccb->next = bl->first_ccb; + bl->first_ccb = bl_ccb; + release_sem(bl->ccb_lock); + release_sem(bl->ccb_count); + return B_ERROR; +} + + +/* +** sim_path_inquiry returns info on the target/lun. +*/ +static long sim_path_inquiry(BusLogic *bl, CCB_HEADER *ccbh) +{ + CCB_PATHINQ *ccb; + d_printf("buslogic: sim_path_inquiry()\n"); + + ccb = (CCB_PATHINQ *) ccbh; + + ccb->cam_version_num = SIM_VERSION; + ccb->cam_target_sprt = 0; + ccb->cam_hba_eng_cnt = 0; + memset (ccb->cam_vuhba_flags, 0, VUHBA); + ccb->cam_sim_priv = SIM_PRIV; + ccb->cam_async_flags = 0; + ccb->cam_initiator_id = bl->scsi_id; + ccb->cam_hba_inquiry = bl->wide ? PI_WIDE_16 : 0; + strncpy (ccb->cam_sim_vid, sim_vendor_name, SIM_ID); + strncpy (ccb->cam_hba_vid, hba_vendor_name, HBA_ID); + ccb->cam_osd_usage = 0; + ccbh->cam_status = CAM_REQ_CMP; + return 0; +} + + +/* +** sim_extended_path_inquiry returns info on the target/lun. +*/ +static long sim_extended_path_inquiry(BusLogic *bl, CCB_HEADER *ccbh) +{ + CCB_EXTENDED_PATHINQ *ccb; + + sim_path_inquiry(bl, ccbh); + ccb = (CCB_EXTENDED_PATHINQ *) ccbh; + sprintf(ccb->cam_sim_version, "%d.0", SIM_VERSION); + sprintf(ccb->cam_hba_version, "%d.0", HBA_VERSION); + strncpy(ccb->cam_controller_family, controller_family, FAM_ID); + strncpy(ccb->cam_controller_type, bl->productname, TYPE_ID); + return 0; +} + + +/* +** sim_release_queue unfreezes the target/lun's request queue. +*/ +static long sim_sim_release_queue(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + return B_ERROR; +} + + +/* +** sim_set_async_callback registers a callback routine for the +** target/lun; +*/ +static long sim_set_async_callback(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + return B_ERROR; +} + + +/* +** sim_abort aborts a pending or queued scsi operation. +*/ +static long sim_abort(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + return B_ERROR; +} + + +/* +** sim_reset_bus resets the scsi bus. +*/ +static long sim_reset_bus(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_CMP; + return 0; +} + + +/* +** sim_reset_device resets the target/lun with the scsi "bus +** device reset" command. +*/ +static long sim_reset_device(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + return B_ERROR; +} + + +/* +** sim_terminate_process terminates the scsi i/o request without +** corrupting the medium. It is used to stop lengthy requests +** when a higher priority request is available. +** +** Not yet implemented. +*/ +static long sim_terminate_process(BusLogic *bl, CCB_HEADER *ccbh) +{ + ccbh->cam_status = CAM_REQ_INVALID; + return B_ERROR; +} + + +/* +** scsi_sim_action performes the scsi i/o command embedded in the +** passed ccb. +** +** The target/lun ids are assumed to be in range. +*/ +static long sim_action(BusLogic *bl, CCB_HEADER *ccbh) +{ + static long (*sim_functions[])(BusLogic *, CCB_HEADER *) = { + sim_invalid, /* do nothing */ + sim_execute_scsi_io, /* execute a scsi i/o command */ + sim_invalid, /* get device type info */ + sim_path_inquiry, /* path inquiry */ + sim_sim_release_queue, /* release a frozen SIM queue */ + sim_set_async_callback, /* set async callback parameters */ + sim_invalid, /* set device type info */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_invalid, /* invalid function code */ + sim_abort, /* abort the selected CCB */ + sim_reset_bus, /* reset a SCSI bus */ + sim_reset_device, /* reset a SCSI device */ + sim_terminate_process /* terminate an i/o process */ + }; + uchar op; + + /* d_printf("buslogic: sim_execute(), op = %d\n", ccbh->cam_func_code); */ + + /* check for function codes out of range of dispatch table */ + op = ccbh->cam_func_code; + if ((op >= sizeof (sim_functions) / sizeof (long (*)())) && + (op != XPT_EXTENDED_PATH_INQ)) { + /* check for our vendor-uniques (if any) here... */ + ccbh->cam_status = CAM_REQ_INVALID; + return -1; + } + + ccbh->cam_status = CAM_REQ_INPROG; + if (op == XPT_EXTENDED_PATH_INQ) { + return sim_extended_path_inquiry(bl, ccbh); + } else { + return (*sim_functions [op])(bl, ccbh); + } +} + + +static char *hextab = "0123456789ABCDEF"; + +/* +** Allocate the actual memory for the cardinfo object +*/ +static BusLogic *create_cardinfo(int num, int iobase, int irq) +{ + uchar *a; + area_id aid; + int i; + physical_entry entries[5]; + char name[9] = { 'b', 'l', '_', 'c', 'c', 'b', '0', '0', 0 }; + + BusLogic *bl = (BusLogic *) malloc(sizeof(BusLogic)); + +#ifndef __INTEL__ + i = map_physical_memory("bl_regs",(void*) iobase, 4096, + B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, &a); + iobase = (uint32) a; + if(i < 0) { + dprintf("buslogic: can't map registers...\n"); + } +#endif + + bl->id = num; + bl->iobase = iobase; + bl->irq = irq; + bl->out_nextbox = bl->in_nextbox = 0; + + /* create our 20k workspace. First 4k goes to 510 mailboxes. + ** remaining 16k is used for up to 256 CCB32's + */ + a = NULL; + +#if BOOT + /* life in the bootstrap is a bit different... */ + /* can't be sure of getting contig pages -- scale + stuff down so we can live in just one page */ + bl->box_count = 4; + if(!(a = malloc(4096*2))) return NULL; + a = (uchar *) ((((uint32) a) & 0xFFFFF000) + 0x1000); + get_memory_map(a, 4096, entries, 2); +#else + bl->box_count = MAX_CCB_COUNT; + aid = create_area("bl_workspace", (void **)&a, B_ANY_KERNEL_ADDRESS, 4096*5, + B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if(aid == B_ERROR || aid == B_BAD_VALUE || aid == B_NO_MEMORY) + return NULL; + get_memory_map(a, 4096*5, entries, 2); +#endif + + /* figure virtual <-> physical translations */ + bl->phys_to_virt = ((uint) a) - ((uint) entries[0].address); + bl->virt_to_phys = (((uint) entries[0].address - (uint) a)); + bl->phys_mailboxes = (uint) entries[0].address; + + /* initialize all mailboxes to empty */ + bl->out_boxes = (BL_Out_Mailbox32 *) a; + bl->in_boxes = (BL_In_Mailbox32 *) (a + (8 * bl->box_count)); + for(i=0;ibox_count;i++){ + bl->out_boxes[i].action_code = BL_ActionCode_NotInUse; + bl->in_boxes[i].completion_code = BL_CompletionCode_NotInUse; + } + + /* setup the CCB32 cache */ +#if BOOT + bl->ccb = (BL_CCB32 *) (((uchar *)a) + 1024); +#else + bl->ccb = (BL_CCB32 *) (((uchar *)a) + 4096); +#endif + bl->first_ccb = NULL; + for(i=0;ibox_count;i++){ + name[6] = hextab[(i & 0xF0) >> 4]; + name[7] = hextab[i & 0x0F]; + bl->ccb[i].done = create_sem(0, name); + bl->ccb[i].next = bl->first_ccb; + bl->first_ccb = &(bl->ccb[i]); + } + + bl->hw_lock = create_sem(1, "bl_hw_lock"); + bl->ccb_lock = create_sem(1, "bl_ccb_lock"); + bl->ccb_count = create_sem(MAX_CCB_COUNT, "bl_ccb_count"); + bl->reqid = 0; + + return bl; +} + + +/* +** Multiple Card Cruft +*/ +#define MAXCARDS 4 + +static BusLogic *cardinfo[MAXCARDS] = { NULL, NULL, NULL, NULL }; + +static long sim_init0(void) { return init_buslogic(cardinfo[0]); } +static long sim_init1(void) { return init_buslogic(cardinfo[1]); } +static long sim_init2(void) { return init_buslogic(cardinfo[2]); } +static long sim_init3(void) { return init_buslogic(cardinfo[3]); } +static long sim_action0(CCB_HEADER *ccbh) { return sim_action(cardinfo[0],ccbh); } +static long sim_action1(CCB_HEADER *ccbh) { return sim_action(cardinfo[1],ccbh); } +static long sim_action2(CCB_HEADER *ccbh) { return sim_action(cardinfo[2],ccbh); } +static long sim_action3(CCB_HEADER *ccbh) { return sim_action(cardinfo[3],ccbh); } + + +static long (*sim_init_funcs[MAXCARDS])(void) = { + sim_init0, sim_init1, sim_init2, sim_init3 +}; + +static long (*sim_action_funcs[MAXCARDS])(CCB_HEADER *) = { + sim_action0, sim_action1, sim_action2, sim_action3 +}; + + +/* +** Detect the controller and register the SIM with the CAM layer. +** returns the number of controllers installed... +*/ +static int +sim_install_buslogic(void) +{ + int i, iobase, irq; + int cardcount = 0; + pci_info h; + CAM_SIM_ENTRY entry; + + /* d_printf("buslogic: sim_install()\n"); */ + + for (i = 0; ; i++) { + if ((*pci->get_nth_pci_info) (i, &h) != B_NO_ERROR) { + /* if(!cardcount) d_printf("buslogic: no controller found\n"); */ + break; + } + + if ((h.vendor_id == PCI_VENDOR_BUSLOGIC) && + (h.device_id == PCI_DEVICE_MULTIMASTER)) { + +#ifdef __INTEL__ + iobase = h.u.h0.base_registers[0]; +#else + iobase = h.u.h0.base_registers[1]; +#endif + irq = h.u.h0.interrupt_line; + + d_printf("buslogic%d: controller @ 0x%08x, irq %d\n", + cardcount, iobase, irq); + if((irq == 0) || (irq > 128)) { + dprintf("buslogic%d: bad irq %d\n",cardcount,irq); + continue; + } + + if(cardcount == MAXCARDS){ + d_printf("buslogic: too many controllers!\n"); + return cardcount; + } + + if((cardinfo[cardcount] = create_cardinfo(cardcount,iobase,irq))){ + entry.sim_init = sim_init_funcs[cardcount]; + entry.sim_action = sim_action_funcs[cardcount]; + (*cam->xpt_bus_register)(&entry); + cardcount++; + } else { + d_printf("buslogic: cannot allocate cardinfo\n"); + } + } + } + + return cardcount; +} + +static status_t std_ops(int32 op, ...) +{ + switch(op) { + case B_MODULE_INIT: + if (get_module(pci_name, (module_info **) &pci) != B_OK) + return B_ERROR; + + if (get_module(cam_name, (module_info **) &cam) != B_OK) { + put_module(pci_name); + return B_ERROR; + } + + if(sim_install_buslogic()){ + return B_OK; + } + + put_module(pci_name); + put_module(cam_name); + return B_ERROR; + + case B_MODULE_UNINIT: + put_module(pci_name); + put_module(cam_name); + return B_OK; + + default: + return B_ERROR; + } +} + +#if BUILD_LOADABLE +static +#endif +sim_module_info sim_buslogic_module = { + { "busses/scsi/buslogic/v1", 0, &std_ops } +}; + +#if BUILD_LOADABLE +_EXPORT module_info *modules[] = +{ + (module_info *) &sim_buslogic_module, + NULL +}; +#endif diff --git a/src/add-ons/kernel/busses/scsi/buslogic/buslogic.h b/src/add-ons/kernel/busses/scsi/buslogic/buslogic.h new file mode 100644 index 0000000000..524c36613a --- /dev/null +++ b/src/add-ons/kernel/busses/scsi/buslogic/buslogic.h @@ -0,0 +1,167 @@ +/* +** $Id: buslogic.h,v 1.2 1998/04/21 00:51:57 swetland Exp $ +** +** Constants and Structures for BusLogic MultiMaster Controllers +** Copyright 1998, Brian J. Swetland +** +** This file may be used under the terms of the Be Sample Code License. +*/ + +#define PCI_VENDOR_BUSLOGIC 0x104b +#define PCI_DEVICE_MULTIMASTER 0x1040 + + +/* BusLogic MultiMaster Register Definitions +** cf. Technical Reference Manual, pp. 1-10 - 1-17 +*/ +#define BL_CONTROL_REG (bl->iobase) +#define BL_CONTROL_RHARD 0x80 /* Controller Hard Reset */ +#define BL_CONTROL_RSOFT 0x40 /* Controller Soft Reset */ +#define BL_CONTROL_RINT 0x20 /* Reset (Acknowledge) Interrupts */ +#define BL_CONTROL_RSBUS 0x10 /* Reset SCSI Bus */ + +#define BL_STATUS_REG (bl->iobase) +#define BL_STATUS_DACT 0x80 /* Diagnostic Active */ +#define BL_STATUS_DFAIL 0x40 /* Diagnostic Failure */ +#define BL_STATUS_INREQ 0x20 /* Initialization Required */ +#define BL_STATUS_HARDY 0x10 /* Host Adapter Ready */ +#define BL_STATUS_CPRBSY 0x08 /* Command/Param Out Register Busy */ +#define BL_STATUS_DIRRDY 0x04 /* Data In Register Ready */ +#define BL_STATUS_CMDINV 0x01 /* Command Invalid */ + +#define BL_COMMAND_REG (bl->iobase + 1) +#define BL_DATA_REG (bl->iobase + 1) + +#define BL_INT_REG (bl->iobase + 2) +#define BL_INT_INTV 0x80 /* Interrupt Valid */ +#define BL_INT_RSTS 0x08 /* SCSI Reset State */ +#define BL_INT_CMDC 0x04 /* Command Complete */ +#define BL_INT_MBOR 0x02 /* Mailbox Out Ready */ +#define BL_INT_IMBL 0x01 /* Incoming Mailbox Loaded */ + + +#define MAX_SCATTER 130 + +typedef struct _bl_ccb32 +{ + /* buslogic ccb structure */ + uchar opcode; /* operation code - see CCB_OP_* below */ + uchar direction; /* data direction control - see CCB_DIR_* */ + uchar length_cdb; /* length of the cdb */ + uchar length_sense; /* length of sense data block */ + uint32 length_data; /* length of data block */ + uint32 data; /* 32bit physical pointer to data or s/g table */ + uchar _reserved1; /* set to zero */ + uchar _reserved2; /* set to zero */ + uchar btstat; /* Host Adapter Status Return */ + uchar sdstat; /* SCSI Device Status Return */ + uchar target_id; /* Target SCSI ID */ + uchar lun_tag; /* bits 0-2 = LUN, | with CCB_TAG_* */ + uchar cdb[12]; /* SCSI CDB area */ + uchar ccb_control; /* controle bits - see CCB_CONTROL_* */ + uchar link_id; /* id number for linked CCB's */ + uint32 link; /* 32bit physical pointer to a linked CCB */ + uint32 sense; /* 32bit physical pointer to the sense datablk */ + + /* used by the driver */ + + sem_id done; /* used by ISR for completion notification */ + int completion_code; /* completion code storage from mailbox */ + struct _bl_ccb32 *next; /* chain pointer for CCB32 freelist */ + uint _reserved[3]; /* padding */ +} BL_CCB32; + +typedef struct +{ + /* used by the driver */ + uchar sensedata[256]; /* data area for sense data return */ + + struct { + uint length; /* length of this SG segment (bytes) */ + uint phys; /* physical address of this SG segment */ + } sg[MAX_SCATTER]; /* scatter/gather table */ +} BL_PRIV; + +#define BL_CCB_TAG_SIMPLE 0x20 +#define BL_CCB_TAG_HEAD 0x60 +#define BL_CCB_TAG_ORDERED 0xA0 + +#define BL_CCB_OP_INITIATE 0x00 +#define BL_CCB_OP_INITIATE_SG 0x02 + +/* returns dif between req/actual xmit bytecount */ +#define BL_CCB_OP_INITIATE_RETLEN 0x03 +#define BL_CCB_OP_INITIATE_RETLEN_SG 0x04 +#define BL_CCB_OP_SCSI_BUS_RESET 0x81 + +#define BL_CCB_DIR_DEFAULT 0x00 /* transfer in direction native to scsi */ +#define BL_CCB_DIR_INBOUND 0x08 +#define BL_CCB_DIR_OUTBOUND 0x10 +#define BL_CCB_DIR_NO_XFER 0x18 + +typedef struct +{ + uint32 ccb_phys; + uchar _reserved1; + uchar _reserved2; + uchar _reserved3; + uchar action_code; +} BL_Out_Mailbox32; + +#define BL_ActionCode_NotInUse 0x00 +#define BL_ActionCode_Start 0x01 +#define BL_ActionCode_Abort 0x02 + + + +typedef struct +{ + uint32 ccb_phys; + uchar btstat; + uchar sdstat; + uchar _reserved1; + uchar completion_code; +} BL_In_Mailbox32; + +#define BL_CompletionCode_NotInUse 0x00 +#define BL_CompletionCode_NoError 0x01 +#define BL_CompletionCode_HostAbort 0x02 +#define BL_CompletionCode_NotFound 0x03 +#define BL_CompletionCode_Error 0x04 + +#define MAX_CCB_COUNT 32 + +/* Host Adapter State Structure +** +*/ +typedef struct +{ + int id; /* board id 0, 1, ... */ + int done; /* command complete from ISR */ + int irq; /* board's irq */ + int iobase; /* base io address */ + int scsi_id; /* board's SCSI id */ + int wide; /* wide target id's allowed */ + long reqid; /* request counter for debugging */ + + char productname[16]; + + uint32 phys_to_virt; /* adjustment for mapping BL_CCB32's */ + uint32 virt_to_phys; /* between virt and phys addrs */ + + uint32 phys_mailboxes; /* phys addr of mailboxes */ + + sem_id hw_lock; /* lock for hardware and outbox access */ + sem_id ccb_lock; /* lock protecting the ccb chain */ + sem_id ccb_count; /* counting sem protecting the ccb chain */ + + int box_count; + int out_nextbox; + int in_nextbox; + BL_Out_Mailbox32 *out_boxes; + BL_In_Mailbox32 *in_boxes; + + BL_CCB32 *ccb; /* table of MAX_CCB_COUNT CCB's */ + BL_CCB32 *first_ccb; /* head of ccb freelist */ +} BusLogic; +