diff options
Diffstat (limited to 'scsi/adapters/scsi_aha15_hdw.c')
-rw-r--r-- | scsi/adapters/scsi_aha15_hdw.c | 1467 |
1 files changed, 0 insertions, 1467 deletions
diff --git a/scsi/adapters/scsi_aha15_hdw.c b/scsi/adapters/scsi_aha15_hdw.c deleted file mode 100644 index 5514bc5a..00000000 --- a/scsi/adapters/scsi_aha15_hdw.c +++ /dev/null @@ -1,1467 +0,0 @@ -/* - * Mach Operating System - * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University - * All Rights Reserved. - * - * Permission to use, copy, modify and distribute this software and its - * documentation is hereby granted, provided that both the copyright - * notice and this permission notice appear in all copies of the - * software, derivative works or modified versions, and any portions - * thereof, and that both notices appear in supporting documentation. - * - * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" - * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR - * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. - * - * Carnegie Mellon requests users of this software to return to - * - * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU - * School of Computer Science - * Carnegie Mellon University - * Pittsburgh PA 15213-3890 - * - * any improvements or extensions that they make and grant Carnegie Mellon - * the rights to redistribute these changes. - */ -/* - * File: scsi_aha15_hdw.c - * Author: Alessandro Forin, Carnegie Mellon University - * Date: 6/91 - * - * Bottom layer of the SCSI driver: chip-dependent functions - * - * This file contains the code that is specific to the Adaptec - * AHA-15xx family of Intelligent SCSI Host Adapter boards: - * probing, start operation, and interrupt routine. - */ - -/* - * Since the board is "Intelligent" we do not need scripts like - * other simpler HBAs. Maybe. - */ -#include <cpus.h> -#include <platforms.h> - -#include <aha.h> -#if NAHA > 0 - -#include <mach/std_types.h> -#include <machine/machspl.h> -#include <sys/types.h> -#include <chips/busses.h> -#include <scsi/compat_30.h> - -/* #include <sys/syslog.h> */ - -#include <scsi/scsi.h> -#include <scsi/scsi2.h> -#include <scsi/scsi_defs.h> - -#include <scsi/adapters/scsi_aha15.h> - -#ifdef AT386 -#define MACHINE_PGBYTES I386_PGBYTES -#define MAPPABLE 0 -#define gimmeabreak() asm("int3") -#include <i386/pio.h> /* inlining of outb and inb */ -#endif /*AT386*/ - -#ifdef CBUS /* For the Corollary machine, physical */ -#include <i386at/mp/mp.h> -#include <cbus/cbus.h> - -#define aha_cbus_window transient_state.hba_dep[0] - /* must use windows for phys addresses */ - /* greater than 16 megs */ - -#define kvtoAT cbus_kvtoAT -#else /* CBUS */ -#define kvtoAT kvtophys -#endif /* CBUS */ - -#ifndef MACHINE_PGBYTES /* cross compile check */ -#define MACHINE_PGBYTES 0x1000 -#define MAPPABLE 1 -#define gimmeabreak() Debugger("gimmeabreak"); -#endif - -/* - * Data structures: ring, ccbs, a per target buffer - */ - -#define AHA_NMBOXES 2 /* no need for more, I think */ -struct aha_mb_ctl { - aha_mbox_t omb[AHA_NMBOXES]; - aha_mbox_t imb[AHA_NMBOXES]; - unsigned char iidx, oidx; /* roving ptrs into */ -}; -#define next_mbx_idx(i) ((((i)+1)==AHA_NMBOXES)?0:((i)+1)) - -#define AHA_NCCB 8 /* for now */ -struct aha_ccb_raw { - target_info_t *active_target; - aha_ccb_t ccb; - char buffer[256]; /* separate out this ? */ -}; -#define rccb_to_cmdptr(rccb) ((char*)&((rccb)->ccb.ccb_scsi_cmd)) - -/* forward decls */ -int aha_reset_scsibus(); -boolean_t aha_probe_target(); - -/* - * State descriptor for this layer. There is one such structure - * per (enabled) board - */ -struct aha_softc { - watchdog_t wd; - decl_simple_lock_data(, aha_lock) - unsigned int port; /* I/O port */ - - int ntargets; /* how many alive on this scsibus */ - - scsi_softc_t *sc; /* HBA-indep info */ - - struct aha_mb_ctl mb; /* mailbox structures */ - - /* This chicanery is for mapping back the phys address - of a CCB (which we get in an MBI) to its virtual */ - /* [we could use phystokv(), but it isn't standard] */ - vm_offset_t I_hold_my_phys_address; - struct aha_ccb_raw aha_ccbs[AHA_NCCB]; - -} aha_softc_data[NAHA]; - -typedef struct aha_softc *aha_softc_t; - -aha_softc_t aha_softc[NAHA]; - -struct aha_ccb_raw * -mb_to_rccb(aha, mbi) - aha_softc_t aha; - aha_mbox_t mbi; -{ - vm_offset_t addr; - - AHA_MB_GET_PTR(&mbi,addr); /* phys address of ccb */ - - /* make virtual */ - addr = ((vm_offset_t)&aha->I_hold_my_phys_address) + - (addr - aha->I_hold_my_phys_address); - - /* adjust by proper offset to get base */ - addr -= (vm_offset_t)&(((struct aha_ccb_raw *)0)->ccb); - - return (struct aha_ccb_raw *)addr; -} - -target_info_t * -aha_tgt_alloc(aha, id, sns_len, tgt) - aha_softc_t aha; - target_info_t *tgt; -{ - struct aha_ccb_raw *rccb; - - aha->ntargets++; - - if (tgt == 0) - tgt = scsi_slave_alloc(aha - aha_softc_data, id, aha); - - rccb = &(aha->aha_ccbs[id]); - rccb->ccb.ccb_reqsns_len = sns_len; - tgt->cmd_ptr = rccb_to_cmdptr(rccb); - tgt->dma_ptr = 0; -#ifdef CBUS - tgt->aha_cbus_window = 0; -#endif /* CBUS */ - return tgt; -} - -/* - * Synch xfer timing conversions - */ -#define aha_to_scsi_period(a) ((200 + ((a) * 50)) >> 2) -#define scsi_period_to_aha(p) ((((p) << 2) - 200) / 50) - -/* - * Definition of the controller for the auto-configuration program. - */ - -/* DOCUMENTATION */ -/* base ports can be: - 0x334, 0x330 (default), 0x234, 0x230, 0x134, 0x130 - possible interrupt channels are: - 9, 10, 11 (default), 12, 14, 15 - DMA channels can be: - 7, 6, 5 (default), 0 -/* DOCUMENTATION */ - -int aha_probe(), scsi_slave(), aha_go(), aha_intr(); -void scsi_attach(); - -vm_offset_t aha_std[NAHA] = { 0 }; -struct bus_device *aha_dinfo[NAHA*8]; -struct bus_ctlr *aha_minfo[NAHA]; -struct bus_driver aha_driver = - { aha_probe, scsi_slave, scsi_attach, aha_go, aha_std, "rz", aha_dinfo, - "ahac", aha_minfo, BUS_INTR_B4_PROBE}; - -#define DEBUG 1 -#if DEBUG - -#define PRINT(x) if (scsi_debug) printf x - -aha_state(port) -{ - register unsigned char st, intr; - - if (port == 0) - port = 0x330; - st = inb(AHA_STATUS_PORT(port)); - intr = inb(AHA_INTR_PORT(port)); - - printf("status %x intr %x\n", st, intr); - return 0; -} - -aha_target_state(tgt) - target_info_t *tgt; -{ - if (tgt == 0) - tgt = aha_softc[0]->sc->target[0]; - if (tgt == 0) - return 0; - printf("fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", - tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, - tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, - tgt->ior, tgt->done); - - return 0; -} - -aha_all_targets(unit) -{ - int i; - target_info_t *tgt; - for (i = 0; i < 8; i++) { - tgt = aha_softc[unit]->sc->target[i]; - if (tgt) - aha_target_state(tgt); - } -} - -#define TRMAX 200 -int tr[TRMAX+3]; -int trpt, trpthi; -#define TR(x) tr[trpt++] = x -#define TRWRAP trpthi = trpt; trpt = 0; -#define TRCHECK if (trpt > TRMAX) {TRWRAP} - -#define TRACE - -#ifdef TRACE - -#define LOGSIZE 256 -#define LOG_KERN 0<<3 /* from syslog.h */ - -int aha_logpt; -char aha_log[LOGSIZE]; - -#define MAXLOG_VALUE 0x1e -struct { - char *name; - unsigned int count; -} logtbl[MAXLOG_VALUE]; - -static LOG(e,f) - char *f; -{ - aha_log[aha_logpt++] = (e); - if (aha_logpt == LOGSIZE) aha_logpt = 0; - if ((e) < MAXLOG_VALUE) { - logtbl[(e)].name = (f); - logtbl[(e)].count++; - } -} - -aha_print_log(skip) - int skip; -{ - register int i, j; - register unsigned char c; - - for (i = 0, j = aha_logpt; i < LOGSIZE; i++) { - c = aha_log[j]; - if (++j == LOGSIZE) j = 0; - if (skip-- > 0) - continue; - if (c < MAXLOG_VALUE) - printf(" %s", logtbl[c].name); - else - printf("-%x", c & 0x7f); - } - return 0; -} - -aha_print_stat() -{ - register int i; - register char *p; - for (i = 0; i < MAXLOG_VALUE; i++) { - if (p = logtbl[i].name) - printf("%d %s\n", logtbl[i].count, p); - } -} - -#else /*TRACE*/ -#define LOG(e,f) -#define LOGSIZE -#endif /*TRACE*/ - -#else /*DEBUG*/ -#define PRINT(x) -#define LOG(e,f) -#define LOGSIZE -#define TRCHECK -#define TR(a) - -#endif /*DEBUG*/ - -/* Utility functions at end */ - - -/* - * Probe/Slave/Attach functions - */ - -int aha_dotarget = 1; /* somehow on some boards this is trouble */ - -/* - * Probe routine: - * Should find out (a) if the controller is - * present and (b) which/where slaves are present. - * - * Implementation: - * Just ask the board to do it - */ -aha_probe(port, ui) - register port; - struct bus_ctlr *ui; -{ - int unit = ui->unit; - aha_softc_t aha = &aha_softc_data[unit]; - int target_id; - scsi_softc_t *sc; - spl_t s; - boolean_t did_banner = FALSE; - struct aha_devs installed; - struct aha_conf conf; - - /* No interrupts yet */ - s = splbio(); - - /* - * We should be called with a sensible port, but you never know. - * Send an echo command and see that we get it back properly - */ - { - register unsigned char st; - - st = inb(AHA_STATUS_PORT(port)); - - /* - * There is no board reset in case of reboot with - * no power-on/power-off sequence. Test it and do - * the reset if necessary. - */ - - if (!(st & AHA_CSR_INIT_REQ)) { - outb(AHA_CONTROL_PORT(port), - AHA_CTL_SOFT_RESET|AHA_CTL_HARD_RESET); - while ((st = inb(AHA_STATUS_PORT(port))) & - AHA_CSR_SELF_TEST); - } - if ((st & AHA_CSR_DATAO_FULL) || - !(st & AHA_CSR_INIT_REQ)) - goto fail; - - outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO); - delay(1000);/*?*/ - st = inb(AHA_STATUS_PORT(port)); - if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL)) - goto fail; - - outb(AHA_COMMAND_PORT(port), 0x5e); - delay(1000); - - st = inb(AHA_STATUS_PORT(port)); - if ((st & AHA_CSR_CMD_ERR) || - ((st & AHA_CSR_DATAI_FULL) == 0)) - goto fail; - - st = inb(AHA_DATA_PORT(port)); - if (st != 0x5e) { -fail: splx(s); - return 0; - } - /* - * augment test with check for echoing inverse and with - * test for enhanced adapter with standard ports enabled. - */ - - /* Check that 0xa1 echoed as well as 0x5e */ - - outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO); - delay(1000);/*?*/ - st = inb(AHA_STATUS_PORT(port)); - if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL)) - goto fail; - - outb(AHA_COMMAND_PORT(port), 0xa1); - delay(1000); - - st = inb(AHA_STATUS_PORT(port)); - if ((st & AHA_CSR_CMD_ERR) || - ((st & AHA_CSR_DATAI_FULL) == 0)) - goto fail; - - st = inb(AHA_DATA_PORT(port)); - if (st != 0xa1) - goto fail ; - - { /* Check that port isn't 174x in enhanced mode - with standard mode ports enabled. This should be - ignored because it will be caught and correctly - handled by eaha_probe(). See TRM4-11..13. - dph - */ - unsigned z ; - static unsigned port_table[] = - {0,0,0x130,0x134,0x230,0x234,0x330,0x334}; - for (z= 0x1000; z<= 0xF000; z+= 0x1000) - if (inb(z+0xC80) == 0x04 && - inb(z+0xC81) == 0x90 && - inb(z+0xCC0) & 0x80 == 0x80 && - port_table [inb(z+0xCC0) & 0x07] == port) - goto fail ; - } - outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); - } - -#if MAPPABLE - /* Mappable version side */ - AHA_probe(port, ui); -#endif /*MAPPABLE*/ - - /* - * Initialize hw descriptor, cache some pointers - */ - aha_softc[unit] = aha; - aha->port = port; - - sc = scsi_master_alloc(unit, aha); - aha->sc = sc; - - simple_lock_init(&aha->aha_lock); - sc->go = aha_go; - sc->watchdog = scsi_watchdog; - sc->probe = aha_probe_target; - aha->wd.reset = aha_reset_scsibus; - - /* Stupid limitation, no way around it */ - sc->max_dma_data = (AHA_MAX_SEGLIST-1) * MACHINE_PGBYTES; - - - /* XXX - * I'm not sure how much use this bit of code is really. - * On the 1542CF we don't really want to try and initialize - * the mailboxes before unlocking them in any case, and - * resetting the card is done above. - */ -#if 0 -#if 0 - /* - * Reset board. - */ - aha_reset(port, TRUE); -#else - /* - * Initialize mailboxes - */ - aha_init_1(aha); -#endif -#endif - - /* - * Who are we ? - */ - { - struct aha_inq inq; - struct aha_extbios extbios; - char *id; - - aha_command(port, AHA_CMD_INQUIRY, 0, 0, &inq, sizeof(inq), TRUE); - - switch (inq.board_id) { - case AHA_BID_1540_B16: - case AHA_BID_1540_B64: - id = "1540"; break; - case AHA_BID_1540B: - id = "1540B/1542B"; break; - case AHA_BID_1640: - id = "1640"; break; - case AHA_BID_1740: - id = "1740 Unsupported!!"; break; - case AHA_BID_1542C: - id = "1542C"; aha_dotarget = 0; break; - case AHA_BID_1542CF: - id = "1542CF"; break; - default: - id = 0; break; - } - - printf("Adaptec %s [id %x], rev %c%c, options x%x\n", - id ? id : "Board", - inq.board_id, inq.frl_1, inq.frl_2, inq.options); - - /* - * If we are a 1542C or 1542CF disable the extended bios - * so that the mailbox interface is unlocked. - * No need to check the extended bios flags as some of the - * extensions that cause us problems are not flagged in - * that byte. - */ - if (inq.board_id == 0x44 || inq.board_id == 0x45) { - aha_command(port, AHA_EXT_BIOS, 0, 0, &extbios, - sizeof(extbios), TRUE); -#ifdef AHADEBUG - printf("aha: extended bios flags 0x%x\n", extbios.flags); - printf("aha: mailboxlock 0x%x\n", extbios.mblock); -#endif /* AHADEBUG */ - - printf("aha: 1542C/CF detected, unlocking mailbox\n"); - - /* XXX - This sends the mailboxlock code out to the - * controller. We need to output a 0, then the - * code...so since we don't care about the flags - * anyway, we just zero out that field and re-use - * the struct. - */ - extbios.flags = 0; - aha_command(port, AHA_MBX_ENABLE, &extbios, - sizeof(extbios), 0, 0, TRUE); - } - - } -doconf: - /* - * Readin conf data - */ - aha_command(port, AHA_CMD_GET_CONFIG, 0, 0, &conf, sizeof(conf), TRUE); - - { - unsigned char args; - - /* - * Change the bus on/off times to not clash with - * other dma users. - */ - args = 7; - aha_command(port, AHA_CMD_SET_BUSON, &args, 1, 0, 0, TRUE); - args = 5; - aha_command(port, AHA_CMD_SET_BUSOFF, &args, 1, 0, 0, TRUE); - } - - /* XXX - This is _REALLY_ sickening. */ - /* - * Set up the DMA channel we'll be using. - */ - { - register int d, i; - static struct { - unsigned char port; - unsigned char init_data; - } aha_dma_init[8][2] = { - {{0x0b,0x0c}, {0x0a,0x00}}, /* channel 0 */ - {{0,0},{0,0}}, - {{0,0},{0,0}}, - {{0,0},{0,0}}, - {{0,0},{0,0}}, - {{0xd6,0xc1}, {0xd4,0x01}}, /* channel 5 (def) */ - {{0xd6,0xc2}, {0xd4,0x02}}, /* channel 6 */ - {{0xd6,0xc3}, {0xd4,0x03}} /* channel 7 */ - }; - - - for (i = 0; i < 8; i++) - if ((1 << i) & conf.intr_ch) break; - i += 9; - -#if there_was_a_way - /* - * On second unit, avoid clashes with first - */ - if ((unit > 0) && (ui->sysdep1 != i)) { - printf("Reprogramming irq and dma ch..\n"); - .... - goto doconf; - } -#endif - - /* - * Initialize the DMA controller viz the channel we'll use - */ - for (d = 0; d < 8; d++) - if ((1 << d) & conf.dma_arbitration) break; - - outb(aha_dma_init[d][0].port, aha_dma_init[d][0].init_data); - outb(aha_dma_init[d][1].port, aha_dma_init[d][1].init_data); - - /* make mapping phys->virt possible for CCBs */ - aha->I_hold_my_phys_address = - kvtoAT((vm_offset_t)&aha->I_hold_my_phys_address); - - /* - * Our SCSI ID. (xxx) On some boards this is SW programmable. - */ - sc->initiator_id = conf.my_scsi_id; - - printf("%s%d: [dma ch %d intr ch %d] my SCSI id is %d", - ui->name, unit, d, i, sc->initiator_id); - - /* Interrupt vector setup */ - ui->sysdep1 = i; - take_ctlr_irq(ui); - } - - /* - * More initializations - */ - { - register target_info_t *tgt; - - aha_init(aha); - - /* allocate a desc for tgt mode role */ - tgt = aha_tgt_alloc(aha, sc->initiator_id, 1, 0); - sccpu_new_initiator(tgt, tgt); /* self */ - - } - - /* Now we could take interrupts, BUT we do not want to - be selected as targets by some other host just yet */ - - /* - * For all possible targets, see if there is one and allocate - * a descriptor for it if it is there. - * This includes ourselves, when acting as target - */ - aha_command( port, AHA_CMD_FIND_DEVICES, 0, 0, &installed, sizeof(installed), TRUE); - for (target_id = 0; target_id < 8; target_id++) { - - if (target_id == sc->initiator_id) /* done already */ - continue; - - if (installed.tgt_luns[target_id] == 0) - continue; - - printf(",%s%d", did_banner++ ? " " : " target(s) at ", - target_id); - - /* Normally, only LUN 0 */ - if (installed.tgt_luns[target_id] != 1) - printf("(%x)", installed.tgt_luns[target_id]); - /* - * Found a target - */ - (void) aha_tgt_alloc(aha, target_id, 1/*no REQSNS*/, 0); - - } - printf(".\n"); - splx(s); - - return 1; -} - -boolean_t -aha_probe_target(tgt, ior) - target_info_t *tgt; - io_req_t ior; -{ - aha_softc_t aha = aha_softc[tgt->masterno]; - boolean_t newlywed; - - newlywed = (tgt->cmd_ptr == 0); - if (newlywed) { - /* desc was allocated afresh */ - (void) aha_tgt_alloc(aha,tgt->target_id, 1/*no REQSNS*/, tgt); - } - - if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) - return FALSE; - - tgt->flags = TGT_ALIVE; - return TRUE; -} - -aha_reset(port, quick) -{ - register unsigned char st; - - /* - * Reset board and wait till done - */ - outb(AHA_CONTROL_PORT(port), AHA_CTL_SOFT_RESET); - do { - delay(25); - st = inb(AHA_STATUS_PORT(port)); - } while ((st & (AHA_CSR_IDLE|AHA_CSR_INIT_REQ)) == 0); - - if (quick) return; - - /* - * reset the scsi bus. Does NOT generate an interrupt (bozos) - */ - outb(AHA_CONTROL_PORT(port), AHA_CTL_SCSI_RST); -} - -aha_init_1(aha) - aha_softc_t aha; -{ - struct aha_init a; - vm_offset_t phys; - - bzero(&aha->mb, sizeof(aha->mb)); /* also means all free */ - a.mb_count = AHA_NMBOXES; - phys = kvtoAT((vm_offset_t)&aha->mb); - AHA_ADDRESS_SET(a.mb_ptr, phys); - aha_command(aha->port, AHA_CMD_INIT, &a, sizeof(a), 0, 0, TRUE); -} - -aha_init_2(port) -{ - unsigned char disable = AHA_MBO_DISABLE; - struct aha_tgt role; - - /* Disable MBO available interrupt */ - aha_command(port, AHA_CMD_MBO_IE, &disable, 1, 0,0, FALSE); - - if (aha_dotarget) { - /* Enable target mode role */ - role.enable = 1; - role.luns = 1; /* only LUN 0 */ - aha_command(port, AHA_CMD_ENB_TGT_MODE, &role, sizeof(role), 0, 0, TRUE); - } -} - -aha_init(aha) - aha_softc_t aha; -{ - aha_init_1(aha); - aha_init_2(aha->port); -} - -/* - * Operational functions - */ - -/* - * Start a SCSI command on a target - */ -aha_go(tgt, cmd_count, in_count, cmd_only) - target_info_t *tgt; - boolean_t cmd_only; -{ - aha_softc_t aha; - spl_t s; - struct aha_ccb_raw *rccb; - int len; - vm_offset_t virt, phys; - -#if CBUS - at386_io_lock_state(); -#endif - - LOG(1,"go"); - - aha = (aha_softc_t)tgt->hw_state; - -/* XXX delay the handling of the ccb till later */ - rccb = &(aha->aha_ccbs[tgt->target_id]); - rccb->active_target = tgt; - - /* - * We can do real DMA. - */ -/* tgt->transient_state.copy_count = 0; unused */ -/* tgt->transient_state.dma_offset = 0; unused */ - - tgt->transient_state.cmd_count = cmd_count; - - if ((tgt->cur_cmd == SCSI_CMD_WRITE) || - (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ - io_req_t ior = tgt->ior; - register int len = ior->io_count; - - tgt->transient_state.out_count = len; - - /* How do we avoid leaks here ? Trust the board - will do zero-padding, for now. XXX CHECKME */ -#if 0 - if (len < tgt->block_size) { - bzero(to + len, tgt->block_size - len); - len = tgt->block_size; - tgt->transient_state.out_count = len; - } -#endif - } else { - tgt->transient_state.out_count = 0; - } - - /* See above for in_count < block_size */ - tgt->transient_state.in_count = in_count; - - /* - * Setup CCB state - */ - tgt->done = SCSI_RET_IN_PROGRESS; - - switch (tgt->cur_cmd) { - case SCSI_CMD_READ: - case SCSI_CMD_LONG_READ: - LOG(9,"readop"); - virt = (vm_offset_t)tgt->ior->io_data; - len = tgt->transient_state.in_count; - rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0; - break; - case SCSI_CMD_WRITE: - case SCSI_CMD_LONG_WRITE: - LOG(0x1a,"writeop"); - virt = (vm_offset_t)tgt->ior->io_data; - len = tgt->transient_state.out_count; - rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1; - break; - case SCSI_CMD_INQUIRY: - case SCSI_CMD_REQUEST_SENSE: - case SCSI_CMD_MODE_SENSE: - case SCSI_CMD_RECEIVE_DIAG_RESULTS: - case SCSI_CMD_READ_CAPACITY: - case SCSI_CMD_READ_BLOCK_LIMITS: - case SCSI_CMD_READ_TOC: - case SCSI_CMD_READ_SUBCH: - case SCSI_CMD_READ_HEADER: - case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ - case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */ - case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */ - case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ - case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ - LOG(0x1c,"cmdop"); - LOG(0x80+tgt->cur_cmd,0); - virt = (vm_offset_t)tgt->cmd_ptr; - len = tgt->transient_state.in_count; - rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0; - break; - case SCSI_CMD_MODE_SELECT: - case SCSI_CMD_REASSIGN_BLOCKS: - case SCSI_CMD_FORMAT_UNIT: - case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ - { register int cs = sizeof_scsi_command(tgt->cur_cmd); - tgt->transient_state.cmd_count = cs; - len = - tgt->transient_state.out_count = cmd_count - cs; - virt = (vm_offset_t)tgt->cmd_ptr + cs; - rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1; - LOG(0x1c,"cmdop"); - LOG(0x80+tgt->cur_cmd,0); - } - break; - default: - LOG(0x1c,"cmdop"); - LOG(0x80+tgt->cur_cmd,0); - virt = 0; - len = 0; - rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 0; - } - -#if CBUS - at386_io_lock(MP_DEV_WAIT); -#endif - aha_prepare_rccb(tgt, rccb, virt, len); - - rccb->ccb.ccb_lun = tgt->lun; - rccb->ccb.ccb_scsi_id = tgt->target_id; - -/* AHA_LENGTH_SET(rccb->ccb.ccb_linkptr, 0); unused */ -/* rccb->ccb.ccb_linkid = 0; unused */ - -#if !CBUS - s = splbio(); -#endif - - LOG(3,"enqueue"); - - aha_start_scsi(aha, &rccb->ccb); - -#if CBUS - at386_io_unlock(); -#else - splx(s); -#endif -} - -aha_prepare_rccb(tgt, rccb, virt, len) - target_info_t *tgt; - struct aha_ccb_raw *rccb; - vm_offset_t virt; - vm_size_t len; -{ - vm_offset_t phys; -#ifdef CBUS - int cbus_window; -#endif /* CBUS */ - - rccb->ccb.ccb_cmd_len = tgt->transient_state.cmd_count; - - /* this opcode is refused, grrrr. */ -/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_R; /* default common case */ - rccb->ccb.ccb_code = AHA_CCB_I_CMD; /* default common case */ - AHA_LENGTH_SET(rccb->ccb.ccb_datalen, len);/* default common case */ - -#ifdef CBUS - if (tgt->aha_cbus_window == 0) - tgt->aha_cbus_window = cbus_alloc_win(AHA_MAX_SEGLIST+1); - cbus_window = tgt->aha_cbus_window; -#endif /* CBUS */ - - if (virt == 0) { - /* no xfers */ - AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, 0); - } else if (len <= MACHINE_PGBYTES) { -/* INCORRECT: what if across two pages :INCORRECT */ - /* simple xfer */ -#ifdef CBUS - phys = cbus_kvtoAT_ww(virt, cbus_window); -#else /* CBUS */ - phys = kvtophys(virt); -#endif /* CBUS */ - AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys); - } else { - /* messy xfer */ - aha_seglist_t *seglist; - vm_offset_t ph1, off; - vm_size_t l1; - - /* this opcode does not work, grrrrr */ -/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG_R;*/ - rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG; - - if (tgt->dma_ptr == 0) - aha_alloc_segment_list(tgt); - seglist = (aha_seglist_t *) tgt->dma_ptr; -#ifdef CBUS - phys = cbus_kvtoAT_ww(seglist, cbus_window); - cbus_window++; -#else /* CBUS */ - phys = kvtophys((vm_offset_t) seglist); -#endif /* CBUS */ - AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys); - - ph1 = /*i386_trunc_page*/ virt & ~(MACHINE_PGBYTES - 1); - off = virt & (MACHINE_PGBYTES - 1); -#ifdef CBUS - ph1 = cbus_kvtoAT_ww(ph1, cbus_window) + off; - cbus_window++; -#else /* CBUS */ - ph1 = kvtophys(ph1) + off; -#endif /* CBUS */ - l1 = MACHINE_PGBYTES - off; - - off = 1;/* now #pages */ - while (1) { - AHA_ADDRESS_SET(seglist->ptr, ph1); - AHA_LENGTH_SET(seglist->len, l1); - seglist++; - - if ((len -= l1) <= 0) - break; - virt += l1; off++; - -#ifdef CBUS - ph1 = cbus_kvtoAT_ww(virt, cbus_window); - cbus_window++; -#else /* CBUS */ - ph1 = kvtophys(virt); -#endif /* CBUS */ - l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len; - } - l1 = off * sizeof(*seglist); - AHA_LENGTH_SET(rccb->ccb.ccb_datalen, l1); - } -} - -aha_start_scsi(aha, ccb) - aha_softc_t aha; - aha_ccb_t *ccb; -{ - register aha_mbox_t *mb; - register idx; - vm_offset_t phys; - aha_mbox_t mbo; - spl_t s; - - LOG(4,"start"); - LOG(0x80+ccb->ccb_scsi_id,0); - - /* - * Get an MBO, spin if necessary (takes little time) - */ - s = splbio(); - phys = kvtoAT((vm_offset_t)ccb); - /* might cross pages, but should be ok (kernel is contig) */ - AHA_MB_SET_PTR(&mbo,phys); - mbo.mb.mb_cmd = AHA_MBO_START; - - simple_lock(&aha->aha_lock); - if (aha->wd.nactive++ == 0) - aha->wd.watchdog_state = SCSI_WD_ACTIVE; - idx = aha->mb.oidx; - aha->mb.oidx = next_mbx_idx(idx); - mb = &aha->mb.omb[idx]; - while (mb->mb.mb_status != AHA_MBO_FREE) - delay(1); - mb->bits = mbo.bits; - simple_unlock(&aha->aha_lock); - - /* - * Start the board going - */ - aha_command(aha->port, AHA_CMD_START, 0, 0, 0, 0, FALSE); - splx(s); -} - -/* - * Interrupt routine - * Take interrupts from the board - * - * Implementation: - * TBD - */ -aha_intr(unit) -{ - register aha_softc_t aha; - register port; - register csr, intr; -#if MAPPABLE - extern boolean_t rz_use_mapped_interface; - - if (rz_use_mapped_interface) - return AHA_intr(unit); -#endif /*MAPPABLE*/ - - aha = aha_softc[unit]; - port = aha->port; - - LOG(5,"\n\tintr"); -gotintr: - /* collect ephemeral information */ - csr = inb(AHA_STATUS_PORT(port)); - intr = inb(AHA_INTR_PORT(port)); - - /* - * Check for errors - */ - if (csr & (AHA_CSR_DIAG_FAIL|AHA_CSR_CMD_ERR)) { -/* XXX */ gimmeabreak(); - } - - /* drop spurious interrupts */ - if ((intr & AHA_INTR_PENDING) == 0) { - LOG(2,"SPURIOUS"); - return; - } - outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); - -TR(csr);TR(intr);TRCHECK - - if (intr & AHA_INTR_RST) - return aha_bus_reset(aha); - - /* we got an interrupt allright */ - if (aha->wd.nactive) - aha->wd.watchdog_state = SCSI_WD_ACTIVE; - - if (intr == AHA_INTR_DONE) { - /* csr & AHA_CSR_CMD_ERR --> with error */ - LOG(6,"done"); - return; - } - -/* if (intr & AHA_INTR_MBO_AVAIL) will not happen */ - - /* Some real work today ? */ - if (intr & AHA_INTR_MBI_FULL) { - register int idx; - register aha_mbox_t *mb; - int nscan = 0; - aha_mbox_t mbi; -rescan: - simple_lock(&aha->aha_lock); - idx = aha->mb.iidx; - aha->mb.iidx = next_mbx_idx(idx); - mb = &aha->mb.imb[idx]; - mbi.bits = mb->bits; - mb->mb.mb_status = AHA_MBI_FREE; - simple_unlock(&aha->aha_lock); - - nscan++; - - switch (mbi.mb.mb_status) { - - case AHA_MBI_FREE: - if (nscan >= AHA_NMBOXES) - return; - goto rescan; - break; - - case AHA_MBI_SUCCESS: - case AHA_MBI_ERROR: - aha_initiator_intr(aha, mbi); - break; - - case AHA_MBI_NEED_CCB: - aha_target_intr(aha, mbi); - break; - -/* case AHA_MBI_ABORTED: /* this we wont see */ -/* case AHA_MBI_NOT_FOUND: /* this we wont see */ - default: - log( LOG_KERN, - "aha%d: Bogus status (x%x) in MBI\n", - unit, mbi.mb.mb_status); - break; - } - - /* peek ahead */ - if (aha->mb.imb[aha->mb.iidx].mb.mb_status != AHA_MBI_FREE) - goto rescan; - } - - /* See if more work ready */ - if (inb(AHA_INTR_PORT(port)) & AHA_INTR_PENDING) { - LOG(7,"\n\tre-intr"); - goto gotintr; - } -} - -/* - * The interrupt routine turns to one of these two - * functions, depending on the incoming mbi's role - */ -aha_target_intr(aha, mbi) - aha_softc_t aha; - aha_mbox_t mbi; -{ - target_info_t *initiator; /* this is the caller */ - target_info_t *self; /* this is us */ - int len; - - if (mbi.mbt.mb_cmd != AHA_MBI_NEED_CCB) - gimmeabreak(); - - /* If we got here this is not zero .. */ - self = aha->sc->target[aha->sc->initiator_id]; - - initiator = aha->sc->target[mbi.mbt.mb_initiator_id]; - /* ..but initiators are not required to answer to our inquiry */ - if (initiator == 0) { - /* allocate */ - initiator = aha_tgt_alloc(aha, mbi.mbt.mb_initiator_id, - sizeof(scsi_sense_data_t) + 5, 0); - - /* We do not know here wether the host was down when - we inquired, or it refused the connection. Leave - the decision on how we will talk to it to higher - level code */ - LOG(0xC, "new_initiator"); - sccpu_new_initiator(self, initiator); - } - - /* The right thing to do would be build an ior - and call the self->dev_ops->strategy routine, - but we cannot allocate it at interrupt level. - Also note that we are now disconnected from the - initiator, no way to do anything else with it - but reconnect and do what it wants us to do */ - - /* obviously, this needs both spl and MP protection */ - self->dev_info.cpu.req_pending = TRUE; - self->dev_info.cpu.req_id = mbi.mbt.mb_initiator_id; - self->dev_info.cpu.req_lun = mbi.mbt.mb_lun; - self->dev_info.cpu.req_cmd = - mbi.mbt.mb_isa_send ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE; - len = (mbi.mbt.mb_data_len_msb << 16) | - (mbi.mbt.mb_data_len_mid << 8 ); - len += 0x100;/* truncation problem */ - self->dev_info.cpu.req_len = len; - - LOG(0xB,"tgt-mode-restart"); - (*self->dev_ops->restart)( self, FALSE); - - /* The call above has either prepared the data, - placing an ior on self, or it handled it some - other way */ - if (self->ior == 0) - return; /* I guess we'll do it later */ - - { - struct aha_ccb_raw *rccb; - - rccb = &(aha->aha_ccbs[initiator->target_id]); - rccb->active_target = initiator; - if (self->dev_info.cpu.req_cmd == SCSI_CMD_SEND) { - rccb->ccb.ccb_in = 1; - rccb->ccb.ccb_out = 0; - } else { - rccb->ccb.ccb_in = 0; - rccb->ccb.ccb_out = 1; - } - - aha_prepare_rccb(initiator, rccb, - (vm_offset_t)self->ior->io_data, self->ior->io_count); - rccb->ccb.ccb_code = AHA_CCB_T_CMD; - rccb->ccb.ccb_lun = initiator->lun; - rccb->ccb.ccb_scsi_id = initiator->target_id; - - simple_lock(&aha->aha_lock); - if (aha->wd.nactive++ == 0) - aha->wd.watchdog_state = SCSI_WD_ACTIVE; - simple_unlock(&aha->aha_lock); - - aha_start_scsi(aha, &rccb->ccb); - } -} - -aha_initiator_intr(aha, mbi) - aha_softc_t aha; - aha_mbox_t mbi; -{ - struct aha_ccb_raw *rccb; - scsi2_status_byte_t status; - target_info_t *tgt; - - rccb = mb_to_rccb(aha,mbi); - tgt = rccb->active_target; - rccb->active_target = 0; - - /* shortcut (sic!) */ - if (mbi.mb.mb_status == AHA_MBI_SUCCESS) - goto allok; - - switch (rccb->ccb.ccb_hstatus) { - case AHA_HST_SUCCESS: -allok: - status = rccb->ccb.ccb_status; - if (status.st.scsi_status_code != SCSI_ST_GOOD) { - scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0); - tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? - SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; - } else - tgt->done = SCSI_RET_SUCCESS; - break; - case AHA_HST_SEL_TIMEO: - if (tgt->flags & TGT_FULLY_PROBED) - tgt->flags = 0; /* went offline */ - tgt->done = SCSI_RET_DEVICE_DOWN; - break; - case AHA_HST_DATA_OVRUN: - /* BUT we don't know if this is an underrun. - It is ok if we get less data than we asked - for, in a number of cases. Most boards do not - seem to generate this anyways, but some do. */ - { register int cmd = tgt->cur_cmd; - switch (cmd) { - case SCSI_CMD_INQUIRY: - case SCSI_CMD_REQUEST_SENSE: - break; - default: - printf("%sx%x\n", - "aha: U/OVRUN on scsi command x%x\n", - cmd); - gimmeabreak(); - } - } - goto allok; - case AHA_HST_BAD_DISCONN: - printf("aha: bad disconnect\n"); - tgt->done = SCSI_RET_ABORTED; - break; - case AHA_HST_BAD_PHASE_SEQ: - /* we'll get an interrupt soon */ - printf("aha: bad PHASE sequencing\n"); - tgt->done = SCSI_RET_ABORTED; - break; - case AHA_HST_BAD_OPCODE: /* fall through */ - case AHA_HST_BAD_PARAM: -printf("aha: BADCCB\n");gimmeabreak(); - tgt->done = SCSI_RET_RETRY; - break; - case AHA_HST_BAD_LINK_LUN: /* these should not happen */ - case AHA_HST_INVALID_TDIR: - case AHA_HST_DUPLICATED_CCB: - printf("aha: bad hstatus (x%x)\n", rccb->ccb.ccb_hstatus); - tgt->done = SCSI_RET_ABORTED; - break; - } - - LOG(8,"end"); - - simple_lock(&aha->aha_lock); - if (aha->wd.nactive-- == 1) - aha->wd.watchdog_state = SCSI_WD_INACTIVE; - simple_unlock(&aha->aha_lock); - - if (tgt->ior) { - LOG(0xA,"ops->restart"); - (*tgt->dev_ops->restart)( tgt, TRUE); - } - - return FALSE; -} - -/* - * The bus was reset - */ -aha_bus_reset(aha) - register aha_softc_t aha; -{ - register port = aha->port; - - LOG(0x1d,"bus_reset"); - - /* - * Clear bus descriptor - */ - aha->wd.nactive = 0; - aha_reset(port, TRUE); - aha_init(aha); - - printf("aha: (%d) bus reset ", ++aha->wd.reset_count); - delay(scsi_delay_after_reset); /* some targets take long to reset */ - - if (aha->sc == 0) /* sanity */ - return; - - scsi_bus_was_reset(aha->sc); -} - -/* - * Watchdog - * - * We know that some (name withdrawn) disks get - * stuck in the middle of dma phases... - */ -aha_reset_scsibus(aha) - register aha_softc_t aha; -{ - register target_info_t *tgt; - register port = aha->port; - register int i; - - for (i = 0; i < AHA_NCCB; i++) { - tgt = aha->aha_ccbs[i].active_target; - if (/*scsi_debug &&*/ tgt) - printf("Target %d was active, cmd x%x in x%x out x%x\n", - tgt->target_id, tgt->cur_cmd, - tgt->transient_state.in_count, - tgt->transient_state.out_count); - } - aha_reset(port, FALSE); - delay(35); - /* no interrupt will come */ - aha_bus_reset(aha); -} - -/* - * Utilities - */ - -/* - * Send a command to the board along with some - * optional parameters, optionally receive the - * results at command completion, returns how - * many bytes we did NOT get back. - */ -aha_command(port, cmd, outp, outc, inp, inc, clear_interrupt) - unsigned char *outp, *inp; -{ - register unsigned char st; - boolean_t failed = TRUE; - - do { - st = inb(AHA_STATUS_PORT(port)); - } while (st & AHA_CSR_DATAO_FULL); - - /* Output command and any data */ - outb(AHA_COMMAND_PORT(port), cmd); - while (outc--) { - do { - st = inb(AHA_STATUS_PORT(port)); - if (st & AHA_CSR_CMD_ERR) goto out; - } while (st & AHA_CSR_DATAO_FULL); - - outb(AHA_COMMAND_PORT(port), *outp++); - } - - /* get any data */ - while (inc--) { - do { - st = inb(AHA_STATUS_PORT(port)); - if (st & AHA_CSR_CMD_ERR) goto out; - } while ((st & AHA_CSR_DATAI_FULL) == 0); - - *inp++ = inb(AHA_DATA_PORT(port)); - } - ++inc; - failed = FALSE; - - /* wait command complete */ - if (clear_interrupt) do { - delay(1); - st = inb(AHA_INTR_PORT(port)); - } while ((st & AHA_INTR_DONE) == 0); - -out: - if (clear_interrupt) - outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); - if (failed) - printf("aha_command: error on (%x %x %x %x %x %x), status %x\n", - port, cmd, outp, outc, inp, inc, st); - return inc; -} - -#include <vm/vm_kern.h> - -/* - * Allocate dynamically segment lists to - * targets (for scatter/gather) - * Its a max of 17*6=102 bytes per target. - */ -vm_offset_t aha_seglist_next, aha_seglist_end; - -aha_alloc_segment_list(tgt) - target_info_t *tgt; -{ -#define ALLOC_SIZE (AHA_MAX_SEGLIST * sizeof(aha_seglist_t)) - -/* XXX locking */ - if ((aha_seglist_next + ALLOC_SIZE) > aha_seglist_end) { - (void) kmem_alloc_wired(kernel_map, &aha_seglist_next, PAGE_SIZE); - aha_seglist_end = aha_seglist_next + PAGE_SIZE; - } - tgt->dma_ptr = (char *)aha_seglist_next; - aha_seglist_next += ALLOC_SIZE; -/* XXX locking */ -} - -#endif /* NAHA > 0 */ - |