summaryrefslogtreecommitdiff
path: root/sys/arch/mvme88k/dev
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-02-14 17:41:43 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-02-14 17:41:43 +0000
commit7f4a328769d19185d1b10dea05e35d4adabc7bac (patch)
treec2f5ba24c3bf504f4724d0bccecbf18fbf3bb639 /sys/arch/mvme88k/dev
parent6bb1f9f28218a81cec05e8039eac5289b9004965 (diff)
Add vsbic(4), a drive for the MVME327A SCSI and floppy controller, only for
the SCSI part so far.
Diffstat (limited to 'sys/arch/mvme88k/dev')
-rw-r--r--sys/arch/mvme88k/dev/bpp.c34
-rw-r--r--sys/arch/mvme88k/dev/bppvar.h20
-rw-r--r--sys/arch/mvme88k/dev/vsbic.c1553
3 files changed, 1580 insertions, 27 deletions
diff --git a/sys/arch/mvme88k/dev/bpp.c b/sys/arch/mvme88k/dev/bpp.c
index 05e0fe4cbc1..88f141b3a47 100644
--- a/sys/arch/mvme88k/dev/bpp.c
+++ b/sys/arch/mvme88k/dev/bpp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bpp.c,v 1.1 2009/02/12 22:03:47 miod Exp $ */
+/* $OpenBSD: bpp.c,v 1.2 2009/02/14 17:41:42 miod Exp $ */
/*
* Copyright (c) 2008, 2009 Miodrag Vallat.
@@ -31,12 +31,8 @@
int bpp_csr_command(struct bpp_softc *, uint, bus_addr_t);
void
-bpp_attach(struct bpp_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh,
- int ipl, int vec)
+bpp_attach(struct bpp_softc *sc, bus_space_tag_t iot, bus_space_handle_t ioh)
{
- sc->sc_ipl = ipl;
- sc->sc_vec = vec;
-
sc->sc_iot = iot;
sc->sc_ioh = ioh;
}
@@ -147,6 +143,18 @@ bpp_csr_command(struct bpp_softc *sc, uint command, bus_addr_t addr)
bus_space_read_2(sc->sc_iot, sc->sc_ioh, CSR_TAS));
bpp_attention(sc);
+ /* wait for the TAS bit to clear */
+ for (tmo = 1000; tmo != 0; tmo--) {
+ tas = bus_space_read_2(sc->sc_iot, sc->sc_ioh, CSR_TAS);
+ if (!ISSET(tas, TAS_TAS))
+ break;
+ delay(100);
+ }
+#ifdef BPP_DEBUG
+ if (tmo == 0)
+ printf("warning: TAS failed to clear\n");
+#endif
+
return (stat);
}
@@ -193,7 +201,8 @@ bpp_reset(struct bpp_softc *sc)
*/
int
-bpp_create_channel(struct bpp_softc *sc, struct bpp_chan *chan, int pri)
+bpp_create_channel(struct bpp_softc *sc, struct bpp_chan *chan, int pri,
+ int ipl, int vec)
{
struct bpp_channel *channel = chan->ch;
struct bpp_envelope *envcmd, *envsts;
@@ -221,8 +230,8 @@ bpp_create_channel(struct bpp_softc *sc, struct bpp_chan *chan, int pri)
htobe32((*sc->bpp_env_pa)(sc, envcmd));
channel->ch_status_head = channel->ch_status_tail =
htobe32((*sc->bpp_env_pa)(sc, envsts));
- channel->ch_ipl = sc->sc_ipl;
- channel->ch_ivec = sc->sc_vec;
+ channel->ch_ipl = ipl;
+ channel->ch_ivec = vec;
channel->ch_priority = pri;
channel->ch_addrmod = ADRM_EXT_S_D;
channel->ch_buswidth = MEMT_D32;
@@ -286,7 +295,7 @@ bpp_put_envelope(struct bpp_softc *sc, struct bpp_envelope *env)
*/
void
bpp_queue_envelope(struct bpp_softc *sc, struct bpp_chan *chan,
- struct bpp_envelope *tail, paddr_t cmdpa, uint32_t cmdextra)
+ struct bpp_envelope *tail, paddr_t cmdpa)
{
struct bpp_envelope *env;
struct bpp_channel *channel;
@@ -304,7 +313,6 @@ bpp_queue_envelope(struct bpp_softc *sc, struct bpp_chan *chan,
env = chan->envcmd;
env->env_link = htobe32(tailpa);
env->env_pkt = htobe32(cmdpa);
- env->env_extra = cmdextra;
(*sc->bpp_env_sync)(sc, env, BUS_DMASYNC_PREWRITE); /* paranoia */
env->env_valid = ENVELOPE_VALID;
(*sc->bpp_env_sync)(sc, env, BUS_DMASYNC_PREWRITE);
@@ -330,7 +338,7 @@ bpp_queue_envelope(struct bpp_softc *sc, struct bpp_chan *chan,
*/
int
bpp_dequeue_envelope(struct bpp_softc *sc, struct bpp_chan *chan,
- paddr_t *cmdpa, uint32_t *cmdextra)
+ paddr_t *cmdpa)
{
struct bpp_envelope *env;
struct bpp_channel *channel;
@@ -354,8 +362,6 @@ bpp_dequeue_envelope(struct bpp_softc *sc, struct bpp_chan *chan,
headpa = betoh32(env->env_link);
if (cmdpa != NULL)
*cmdpa = betoh32(env->env_pkt);
- if (cmdextra != NULL)
- *cmdextra = env->env_extra;
chan->envsts = (*sc->bpp_env_va)(sc, headpa);
channel = chan->ch;
diff --git a/sys/arch/mvme88k/dev/bppvar.h b/sys/arch/mvme88k/dev/bppvar.h
index 88bb95eba75..ddbfe012598 100644
--- a/sys/arch/mvme88k/dev/bppvar.h
+++ b/sys/arch/mvme88k/dev/bppvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bppvar.h,v 1.1 2009/02/12 22:03:47 miod Exp $ */
+/* $OpenBSD: bppvar.h,v 1.2 2009/02/14 17:41:42 miod Exp $ */
/*
* Copyright (c) 2008, 2009 Miodrag Vallat.
@@ -106,10 +106,8 @@ struct bpp_envelope {
* The following is not part of the envelope as defined by the
* firmware, but is used to page the envelope to a 68040/88200
* cache line boundary.
- * Plus this allows us to associate extra information to the
- * envelope.
*/
- uint32_t env_extra;
+ uint32_t pad;
/* total size 0x10 bytes */
};
@@ -162,9 +160,6 @@ struct bpp_softc {
bus_space_tag_t sc_iot; /* CSR registers access */
bus_space_handle_t sc_ioh;
- int sc_vec; /* interrupt vector */
- int sc_ipl; /* interrupt level */
-
struct bpp_envelope *sc_env_free; /* head of free envelope list */
/* channel function pointers */
@@ -177,17 +172,16 @@ struct bpp_softc {
void (*bpp_env_sync)(struct bpp_softc *, struct bpp_envelope *, int);
};
-void bpp_attach(struct bpp_softc *, bus_space_tag_t, bus_space_handle_t,
- int, int);
+void bpp_attach(struct bpp_softc *, bus_space_tag_t, bus_space_handle_t);
void bpp_attention(struct bpp_softc *);
-int bpp_create_channel(struct bpp_softc *, struct bpp_chan *, int);
-int bpp_dequeue_envelope(struct bpp_softc *, struct bpp_chan *, paddr_t *,
- uint32_t *);
+int bpp_create_channel(struct bpp_softc *, struct bpp_chan *,
+ int, int, int);
+int bpp_dequeue_envelope(struct bpp_softc *, struct bpp_chan *, paddr_t *);
struct bpp_envelope *
bpp_get_envelope(struct bpp_softc *);
void bpp_initialize_envelopes(struct bpp_softc *, struct bpp_envelope *,
uint);
void bpp_put_envelope(struct bpp_softc *, struct bpp_envelope *);
void bpp_queue_envelope(struct bpp_softc *, struct bpp_chan *,
- struct bpp_envelope *, paddr_t, uint32_t);
+ struct bpp_envelope *, paddr_t);
int bpp_reset(struct bpp_softc *);
diff --git a/sys/arch/mvme88k/dev/vsbic.c b/sys/arch/mvme88k/dev/vsbic.c
new file mode 100644
index 00000000000..6443759aeaf
--- /dev/null
+++ b/sys/arch/mvme88k/dev/vsbic.c
@@ -0,0 +1,1553 @@
+/* $OpenBSD: vsbic.c,v 1.1 2009/02/14 17:41:42 miod Exp $ */
+
+/*
+ * Copyright (c) 2008, 2009 Miodrag Vallat.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies. And
+ * I won't mind if you keep the disclaimer below.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * MVME327A SCSI and floppy controller driver
+ *
+ * This driver is currently limited to the SCSI part of the board, which
+ * is messy enough already.
+ */
+
+/* This card lives in an A24/D16 world, but is A32/D32 capable */
+#define __BUS_SPACE_RESTRICT_D16__
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/buf.h> /* minphys */
+#include <sys/malloc.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <scsi/scsi_message.h>
+
+#include <mvme88k/dev/vme.h>
+#include <mvme88k/dev/bppvar.h>
+
+/*
+ * Channel packet structure
+ */
+
+struct vsbic_pkt {
+ /* Command fields, read by firmware */
+ uint8_t cmd_cmd;
+ uint8_t cmd_cmd_ctrl;
+ uint8_t cmd_device;
+#define VSBIC_DEVICE_FLOPPY 0x01
+#define VSBIC_DEVICE_SCSI 0x05
+#define VSBIC_DEVICE_HOST 0x0f
+ uint8_t cmd_unit;
+#define VSBIC_UNIT(tgt,lun) (((tgt) << 4) | (lun))
+ uint16_t reserved;
+ uint8_t cmd_addrmod;
+ uint8_t cmd_buswidth;
+ uint32_t cmd_addr1;
+ uint32_t cmd_addr2;
+#define cmd_link cmd_addr2 /* used for free pkt linking */
+ uint32_t cmd_xfer_count;
+ uint16_t cmd_sg_count;
+ uint16_t cmd_parm1;
+ uint16_t cmd_parm2;
+ uint16_t cmd_parm3;
+
+ /* Status fields, written by firmware */
+ uint8_t sts_error;
+ uint8_t sts_recovered; /* error recovered by fw */
+ uint16_t sts_status;
+#define sts_sense sts_status
+ uint8_t sts_retries; /* # of retries performed */
+ uint8_t reserved2;
+ uint16_t sts_addr_high; /* not aligned... */
+ uint16_t sts_addr_low;
+ uint16_t sts_xfer_high; /* not aligned... */
+ uint16_t sts_xfer_low;
+ uint16_t sts_parm1;
+ uint16_t sts_parm2;
+ uint16_t sts_parm3;
+ /* total size 0x30 bytes */
+};
+
+/*
+ * Command values (cmd_cmd)
+ */
+
+#define CMD_BPP_TEST 0x00
+#define CMD_READ 0x01
+#define CMD_WRITE 0x02
+#define CMD_READ_DESCRIPTOR 0x03
+#define CMD_WRITE_DESCRIPTOR 0x04
+#define CMD_FORMAT 0x05
+#define CMD_FIX_BAD_SPOT 0x06
+#define CMD_INQUIRY 0x09
+#define CMD_READ_STATUS 0x10
+#define CMD_LOAD_UNLOAD_RETENSION 0x11
+#define CMD_WRITE_FILEMARK 0x12
+#define CMD_REWIND 0x13
+#define CMD_ERASE 0x14
+#define CMD_SPACE 0x15
+#define CMD_ENABLE_TARGET 0x20
+#define CMD_DISABLE_TARGET 0x21
+#define CMD_RESERVE_UNIT 0x22
+#define CMD_RELEASE_UNIT 0x23
+#define CMD_RESET 0x25
+#define CMD_SCSI 0x26
+#define CMD_SELF_TEST 0x27
+#define CMD_TARGET_WAIT 0x28
+#define CMD_TARGET_EXECUTE 0x29
+#define CMD_DOWNLOAD 0x2a
+#define CMD_SET_SCSI_ADDRESS 0x2b
+#define CMD_OPEN 0x2d
+
+/*
+ * Error values (sts_error, sts_status)
+ */
+
+#define ERR_OK 0x00
+/* command parameter errors */
+#define ERR_BAD_DESCRIPTOR 0x01
+#define ERR_BAD_COMMAND 0x02
+#define ERR_UNIMPLEMENTED_COMMAND 0x03
+#define ERR_BAD_DRIVE 0x04
+#define ERR_BAD_LOGICAL_ADDRESS 0x05
+#define ERR_BAD_SG_TABLE 0x06
+#define ERR_UNIMPLEMENTED_DEVICE 0x07
+#define ERR_UNIT_NOT_INITIALIZED 0x08
+/* media errors */
+#define ERR_NO_ID 0x10
+#define ERR_SEEK 0x11
+#define ERR_RELOCATED_TRACK 0x12
+#define ERR_BAD_ID 0x13
+#define ERR_DATA_SYNC_FAULT 0x14
+#define ERR_ECC 0x15
+#define ERR_RECORD_NOT_FOUND 0x16
+#define ERR_MEDIA 0x17
+/* drive errors */
+#define ERR_DRIVE_FAULT 0x20
+#define ERR_WRITE_PROTECTED_MEDIA 0x21
+#define ERR_MOTOR_OFF 0x22
+#define ERR_DOOR_OPEN 0x23
+#define ERR_DRIVE_NOT_READY 0x24
+#define ERR_DRIVE_BUSY 0x25
+/* VME DMA errors */
+#define ERR_BUS 0x30
+#define ERR_ALIGNMENT 0x31
+#define ERR_BUS_TIMEOUT 0x32
+#define ERR_INVALID_XFER_COUNT 0x33
+/* disk format error */
+#define ERR_NOT_ENOUGH_ALTERNATES 0x40
+#define ERR_FORMAT_FAILED 0x41
+#define ERR_VERIFY 0x42
+#define ERR_BAD_FORMAT_PARAMETERS 0x43
+#define ERR_CANNOT_FIX_BAD_SPOT 0x44
+#define ERR_TOO_MANY_DEFECTS 0x45
+/* MVME327A specific errors */
+#define ERR_SCSI 0x80 /* additional status available*/
+#define ERR_INDETERMINATE_MEDIA 0x81 /* no additional status */
+#define ERR_INDETERMINATE_HARDWARE 0x82
+#define ERR_BLANK_CHECK 0x83
+#define ERR_INCOMPLETE_EXTENDED_MESSAGE 0x84
+#define ERR_INVALID_RESELECTION 0x85
+#define ERR_NO_STATUS_RETURNED 0x86
+#define ERR_MESSAGE_OUT_NOT_TRANSFERRED 0x87
+#define ERR_MESSAGE_IN_NOT_RECEIVED 0x88
+#define ERR_INCOMPLETE_DATA_READ 0x89
+#define ERR_INCOMPLETE_DATA_WRITE 0x8a
+#define ERR_INCORRECT_CDB_SIZE 0x8b
+#define ERR_UNDEFINED_SCSI_PHASE 0x8c
+#define ERR_SELECT_TIMEOUT 0x8d
+#define ERR_BUS_RESET 0x8e
+#define ERR_INVALID_MESSAGE_RECEIVED 0x8f
+#define ERR_COMMAND_NOT_RECEIVED 0x90
+#define ERR_UNEXPECTED_STATUS_PHASE 0x91
+#define ERR_SCSI_SCRIPT_MISMATCH 0x92
+#define ERR_UNEXPECTED_DISCONNECT 0x93
+#define ERR_REQUEST_SENSE_FAILED 0x94
+#define ERR_NO_WRITE_DESCRIPTOR 0x95
+#define ERR_INCOMPLETE_DATA_TRANSFER 0x96
+#define ERR_OUT_OF_LOCAL_RESOURCES 0x97
+#define ERR_LOCAL_MEMORY_RESOURCES_LOST 0x98
+#define ERR_CHANNEL_RESERVED 0x99
+#define ERR_DEVICE_RESERVED 0x9a
+#define ERR_ALREADY_ENABLED 0x9b
+#define ERR_TARGET_NOT_ENABLED 0x9c
+#define ERR_UNSUPPORTED_CONTROLLER_TYPE 0x9d
+#define ERR_UNSUPPORTED_DEVICE_TYPE 0x9e
+#define ERR_BLOCK_SIZE_MISMATCH 0x9f
+#define ERR_INVALID_CYL_IN_DEFECT_LIST 0xa0
+#define ERR_INVALID_HEAD_IN_DEFECT_LIST 0xa1
+#define ERR_BLOCK_SIZE_MISMATCH_NF 0xa2 /* non fatal */
+#define ERR_SCSI_ID_UNCHANGED 0xa3
+#define ERR_SCSI_ID_CHANGED 0xa4
+#define ERR_NO_TARGET_ENABLE 0xa5
+#define ERR_CANNOT_DO_D32 0xa6
+#define ERR_CANNOT_DO_DMA 0xa7
+#define ERR_INVALID_BLOCK_SIZE 0xa8
+#define ERR_SPT_MISMATCH 0xa9
+#define ERR_HEAD_MISMATCH 0xaa
+#define ERR_CYL_MISMATCH 0xab
+#define ERR_INVALID_FLOPPY_PARAMETERS 0xac
+#define ERR_ALREADY_RESERVED 0xad
+#define ERR_WAS_NOT_RESERVED 0xae
+#define ERR_INVALID_SECTOR_NUMBER 0xaf
+#define ERR_SELFTEST_FAILED 0xcc
+
+/*
+ * SCSI specific command packet
+ */
+
+struct vsbic_scsi {
+ uint32_t link;
+ uint16_t ctrl;
+#define SCSI_CTRL_DMA 0x8000 /* allow DMA operation */
+#define SCSI_CTRL_SYNC 0x4000 /* synchronous phase */
+#define SCSI_CTRL_PAR 0x2000 /* enable parity checking */
+#define SCSI_CTRL_SCHK 0x1000 /* manual status checking */
+#define SCSI_CTRL_D32 0x0800 /* use D32 mode for data transfer */
+#define SCSI_CTRL_BYTESWAP 0x0400 /* byteswap D16 words */
+#define SCSI_CTRL_SG 0x0200 /* use scatter/gather transfer list */
+#define SCSI_CTRL_LINK 0x0100 /* linked scsi command */
+#define SCSI_CTRL_NO_ATN 0x0080 /* do not assert ATN during select */
+ uint8_t cmdlen;
+ uint8_t reserved;
+ uint8_t cdb[12];
+ uint32_t datalen;
+ uint32_t dataptr;
+ uint8_t status;
+ uint8_t initiator;
+ uint8_t msgin_flag;
+ uint8_t msgout_flag;
+#define SCSI_MSG_INTERNAL 0x00 /* bytes in structure */
+#define SCSI_MSG_EXTERNAL 0xff /* bytes pointed to by ptr */
+ uint16_t msgin_len;
+ uint16_t msgin_ptr_high; /* not aligned... */
+ uint16_t msgin_ptr_low;
+ uint8_t msgin[6];
+ uint16_t msgout_len;
+ uint16_t msgout_ptr_high; /* not aligned... */
+ uint16_t msgout_ptr_low;
+ uint8_t msgout[6];
+ uint8_t script[8]; /* script phases */
+#define SCRIPT_DATA_OUT 0x00
+#define SCRIPT_DATA_IN 0x01
+#define SCRIPT_COMMAND 0x02
+#define SCRIPT_STATUS 0x03
+#define SCRIPT_MSG_OUT 0x06
+#define SCRIPT_MSG_IN 0x07
+#define SCRIPT_END 0x08
+#define SCRIPT_END_LINKED 0x09
+ /* total size 0x40 bytes */
+};
+
+/*
+ * Scatter/gather list element
+ */
+
+struct vsbic_sg {
+ uint32_t addr;
+ uint32_t size;
+};
+
+#define VSBIC_MAXSG (MAXPHYS / PAGE_SIZE)
+
+/*
+ * Complete SCSI command structure: packet, SCSI specific structure, S/G list.
+ * Nothing needs them to be contiguous but it will make our life simpler
+ * this way.
+ */
+struct vsbic_cmd {
+ struct vsbic_pkt pkt;
+ struct vsbic_scsi scsi;
+ struct vsbic_sg sg[VSBIC_MAXSG];
+};
+
+/*
+ * The host firmware addresses the VME bus with A23 set to 1, and uses
+ * an extension address register to provide the top 9 bits of the A32
+ * address. We thus need to make sure that none of the structures we
+ * share with the firmware cross a 8MB boundary.
+ */
+
+#define VSBIC_MEM_BOUNDARY (1 << 23)
+
+/*
+ * A note on resource usage:
+ *
+ * - this driver uses 9 channels; one per SCSI target, one for polled
+ * commands (which does not cause interrupts); and a 9th one to issue
+ * reset commands when necessary, with a higher priority than the other
+ * channels.
+ *
+ * - for each target channel, there numopenings commands (packet, scsi
+ * specific packet and s/g list) allocated.
+ * - for the polling channel, there is only one command.
+ * - for the reset channel, there is one command (packet) per target.
+ *
+ * - for each channel, there are as many envelopes as possible commands
+ * on the channel, plus two (one NULL envelope per pipe) per channel.
+ */
+
+#define VSBIC_NUMOPENINGS 2
+#define VSBIC_NTARGETS 7
+#define VSBIC_NCHANNELS (VSBIC_NTARGETS + 2)
+
+#define VSBIC_TARGET_CHANNEL(sc,tgt) (tgt) /* 0..7 */
+#define VSBIC_RESET_CHANNEL(sc) (sc->sc_id) /* within 0..7 */
+#define VSBIC_POLLING_CHANNEL(sc) (8)
+
+#define VSBIC_NPACKETS \
+ ((VSBIC_NTARGETS * VSBIC_NUMOPENINGS) + VSBIC_NTARGETS + 1)
+#define VSBIC_NENVELOPES (VSBIC_NPACKETS + 2 * VSBIC_NCHANNELS)
+
+#define VSBIC_NCCBS (VSBIC_NTARGETS * VSBIC_NUMOPENINGS)
+
+/*
+ * Per command information
+ */
+struct vsbic_ccb {
+ struct vsbic_ccb *ccb_next;
+ struct vsbic_cmd *ccb_cmd; /* associated command */
+ struct scsi_xfer *ccb_xs; /* associated request */
+
+ bus_dmamap_t ccb_dmamap; /* DMA map for data transfer */
+ bus_size_t ccb_dmalen;
+};
+
+struct vsbic_softc {
+ struct bpp_softc sc_bpp;
+ bus_dma_tag_t sc_dmat;
+
+ struct scsi_link sc_link;
+ uint sc_id; /* host adapter ID */
+
+ int sc_vec; /* interrupt vector */
+ int sc_ipl; /* interrupt level */
+ struct intrhand sc_ih;
+
+ bus_dmamap_t sc_chanmap; /* channel pool */
+ bus_dma_segment_t sc_chanseg;
+#define sc_chanpa sc_chanseg.ds_addr
+ vaddr_t sc_chanva;
+
+ bus_dmamap_t sc_envmap; /* envelope pool */
+ bus_dma_segment_t sc_envseg;
+#define sc_envpa sc_envseg.ds_addr
+ vaddr_t sc_envva;
+
+ bus_dmamap_t sc_cmdmap; /* packet pool */
+ bus_dma_segment_t sc_cmdseg;
+#define sc_cmdpa sc_cmdseg.ds_addr
+ vaddr_t sc_cmdva;
+
+ struct vsbic_cmd *sc_cmd_free; /* head of free cmd list */
+ struct vsbic_ccb *sc_ccb_free; /* head of free ccb list */
+ struct vsbic_ccb *sc_ccb_active; /* head of active ccb list */
+
+ /* bpp channel array */
+ struct bpp_chan sc_chan[VSBIC_NCHANNELS];
+};
+
+#define DEVNAME(sc) ((sc)->sc_bpp.sc_dev.dv_xname)
+
+void vsbic_activate_ccb(struct vsbic_softc *, struct vsbic_ccb *);
+int vsbic_alloc_physical(struct vsbic_softc *, bus_dmamap_t *,
+ bus_dma_segment_t *, vaddr_t *, bus_size_t, const char *);
+void vsbic_attach(struct device *, struct device *, void *);
+int vsbic_channel_intr(struct vsbic_softc *, struct bpp_chan *);
+paddr_t vsbic_chan_pa(struct bpp_softc *, struct bpp_channel *);
+void vsbic_chan_sync(struct bpp_softc *, struct bpp_channel *, int);
+struct vsbic_ccb *
+ vsbic_cmd_ccb(struct vsbic_softc *, struct vsbic_cmd *);
+int vsbic_create_ccbs(struct vsbic_softc *, uint);
+int vsbic_create_channels(struct vsbic_softc *, uint);
+int vsbic_create_cmds(struct vsbic_softc *, uint);
+struct vsbic_cmd *
+ vsbic_dequeue_cmd(struct vsbic_softc *, struct bpp_chan *);
+paddr_t vsbic_env_pa(struct bpp_softc *, struct bpp_envelope *);
+void vsbic_env_sync(struct bpp_softc *, struct bpp_envelope *, int);
+struct bpp_envelope *
+ vsbic_env_va(struct bpp_softc *, paddr_t);
+void vsbic_free_ccb(struct vsbic_softc *, struct vsbic_ccb *);
+struct vsbic_ccb *
+ vsbic_get_ccb(struct vsbic_softc *);
+struct vsbic_cmd *
+ vsbic_get_cmd(struct vsbic_softc *);
+int vsbic_intr(void *);
+int vsbic_load_command(struct vsbic_softc *, struct vsbic_ccb *,
+ struct vsbic_cmd *, struct scsi_link *, int, struct scsi_generic *,
+ int, uint8_t *, int);
+int vsbic_match(struct device *, void *, void *);
+void vsbic_poll(struct vsbic_softc *, struct vsbic_ccb *);
+void vsbic_put_cmd(struct vsbic_softc *, struct vsbic_cmd *);
+void vsbic_queue_cmd(struct vsbic_softc *, struct bpp_chan *,
+ struct bpp_envelope *, struct vsbic_cmd *);
+void vsbic_reset_command(struct vsbic_softc *, struct vsbic_cmd *,
+ struct scsi_link *);
+int vsbic_scsicmd(struct scsi_xfer *);
+int vsbic_scsireset(struct vsbic_softc *, struct scsi_xfer *);
+void vsbic_timeout(void *);
+void vsbic_wrapup(struct vsbic_softc *, struct vsbic_ccb *);
+
+const struct cfattach vsbic_ca = {
+ sizeof(struct vsbic_softc), vsbic_match, vsbic_attach
+};
+
+struct cfdriver vsbic_cd = {
+ NULL, "vsbic", DV_DULL
+};
+
+struct scsi_adapter vsbic_swtch = {
+ vsbic_scsicmd,
+ minphys
+};
+
+struct scsi_device vsbic_scsidev = {
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+#define MVME327_CSR_ID 0xff
+#define MVME327_CSR_SIZE 0x100
+
+int
+vsbic_match(struct device *device, void *cf, void *args)
+{
+ struct confargs *ca = args;
+ bus_space_tag_t iot = ca->ca_iot;
+ bus_space_handle_t ioh;
+ int rc;
+ uint8_t id;
+
+ if (bus_space_map(iot, ca->ca_paddr, MVME327_CSR_SIZE, 0, &ioh) != 0)
+ return 0;
+
+ rc = badaddr((vaddr_t)bus_space_vaddr(iot, ioh) + MVME327_CSR_ID, 1);
+ if (rc == 0) {
+ /* Check the SCSI ID is sane */
+ id = bus_space_read_1(iot, ioh, MVME327_CSR_ID);
+ if (id & ~0x07)
+ rc = 1;
+ }
+ bus_space_unmap(iot, ioh, MVME327_CSR_SIZE);
+
+ return (rc == 0);
+}
+
+void
+vsbic_attach(struct device *parent, struct device *self, void *args)
+{
+ struct confargs *ca = args;
+ struct vsbic_softc *sc = (struct vsbic_softc *)self;
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ struct scsibus_attach_args saa;
+ bus_space_handle_t ioh;
+ int tmp;
+
+ if (ca->ca_ipl < IPL_BIO)
+ ca->ca_ipl = IPL_BIO;
+
+ printf("\n");
+
+ if (bus_space_map(ca->ca_iot, ca->ca_paddr, MVME327_CSR_SIZE,
+ BUS_SPACE_MAP_LINEAR, &ioh) != 0) {
+ printf("%s: can't map registers!\n", DEVNAME(sc));
+ return;
+ }
+
+ bpp_attach(bsc, ca->ca_iot, ioh);
+ sc->sc_ipl = ca->ca_ipl;
+ sc->sc_vec = ca->ca_vec;
+ sc->sc_dmat = ca->ca_dmat;
+
+ /*
+ * Setup envelope and channel memory utility functions.
+ *
+ * Since the MVME327 shares no memory with the host cpu, all the
+ * resources will be allocated in physical memory, and will need
+ * explicit cache operations.
+ */
+ bsc->bpp_chan_pa = vsbic_chan_pa;
+ bsc->bpp_chan_sync = vsbic_chan_sync;
+ bsc->bpp_env_pa = vsbic_env_pa;
+ bsc->bpp_env_va = vsbic_env_va;
+ bsc->bpp_env_sync = vsbic_env_sync;
+
+ sc->sc_id = bus_space_read_1(bsc->sc_iot, bsc->sc_ioh, MVME327_CSR_ID);
+
+ if (bpp_reset(bsc) != 0) {
+ printf("%s: reset failed\n", DEVNAME(sc));
+ return;
+ }
+
+ sc->sc_ih.ih_fn = vsbic_intr;
+ sc->sc_ih.ih_arg = sc;
+ sc->sc_ih.ih_wantframe = 0;
+ sc->sc_ih.ih_ipl = ca->ca_ipl;
+
+ /*
+ * Allocate memory for the channel headers, envelopes and packets.
+ */
+
+ if (vsbic_alloc_physical(sc, &sc->sc_envmap, &sc->sc_envseg,
+ &sc->sc_envva, VSBIC_NENVELOPES * sizeof(struct bpp_envelope),
+ "envelope") != 0)
+ return;
+
+ bpp_initialize_envelopes(bsc, (struct bpp_envelope *)sc->sc_envva,
+ VSBIC_NENVELOPES);
+
+ if (vsbic_create_cmds(sc, VSBIC_NPACKETS) != STATUS_OK) {
+ /* XXX free resources */
+ return;
+ }
+
+ if (vsbic_create_ccbs(sc, VSBIC_NCCBS) != 0) {
+ /* XXX free resources */
+ return;
+ }
+
+ if (vsbic_create_channels(sc, VSBIC_NCHANNELS) != STATUS_OK) {
+ /* XXX free resources */
+ return;
+ }
+
+ vmeintr_establish(sc->sc_vec, &sc->sc_ih, DEVNAME(sc));
+
+ sc->sc_link.adapter = &vsbic_swtch;
+ sc->sc_link.adapter_buswidth = 8;
+ sc->sc_link.adapter_softc = sc;
+ sc->sc_link.adapter_target = sc->sc_id;
+ sc->sc_link.device = &vsbic_scsidev;
+ sc->sc_link.openings = VSBIC_NUMOPENINGS;
+
+ bzero(&saa, sizeof saa);
+ saa.saa_sc_link = &sc->sc_link;
+
+ tmp = bootpart;
+ if (ca->ca_paddr != bootaddr)
+ bootpart = -1;
+ config_found(self, &saa, scsiprint);
+ bootpart = tmp;
+}
+
+/*
+ * Various simple routines to get the addresses of the various structures
+ * shared with the MVME327, as well as to copyback caches.
+ */
+
+paddr_t
+vsbic_chan_pa(struct bpp_softc *bsc, struct bpp_channel *va)
+{
+ struct vsbic_softc *sc = (struct vsbic_softc *)bsc;
+ paddr_t pa = sc->sc_chanpa + ((vaddr_t)va - sc->sc_chanva);
+
+#ifdef VSBIC_DEBUG
+ printf("chan: va %p -> pa %p\n", va, pa);
+#endif
+
+ return pa;
+}
+
+void
+vsbic_chan_sync(struct bpp_softc *bsc, struct bpp_channel *va, int fl)
+{
+ struct vsbic_softc *sc = (struct vsbic_softc *)bsc;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_chanmap,
+ (vaddr_t)va - sc->sc_chanva, sizeof(struct bpp_channel), fl);
+}
+
+paddr_t
+vsbic_env_pa(struct bpp_softc *bsc, struct bpp_envelope *va)
+{
+ struct vsbic_softc *sc = (struct vsbic_softc *)bsc;
+ paddr_t pa = sc->sc_envpa + ((vaddr_t)va - sc->sc_envva);
+
+#ifdef VSBIC_DEBUG
+ printf("env: va %p -> pa %p\n", va, pa);
+#endif
+
+ return pa;
+}
+
+struct bpp_envelope *
+vsbic_env_va(struct bpp_softc *bsc, paddr_t pa)
+{
+ struct vsbic_softc *sc = (struct vsbic_softc *)bsc;
+ vaddr_t va = sc->sc_envva + (pa - sc->sc_envpa);
+
+#ifdef VSBIC_DEBUG
+ printf("env: pa %p -> va %p\n", pa, va);
+#endif
+
+ return (struct bpp_envelope *)va;
+}
+
+void
+vsbic_env_sync(struct bpp_softc *bsc, struct bpp_envelope *va, int fl)
+{
+ struct vsbic_softc *sc = (struct vsbic_softc *)bsc;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_envmap,
+ (vaddr_t)va - sc->sc_envva, sizeof(struct bpp_envelope), fl);
+}
+
+static inline
+paddr_t vsbic_cmd_pa(struct vsbic_softc *sc, struct vsbic_cmd *va)
+{
+ return sc->sc_cmdpa + ((vaddr_t)va - sc->sc_cmdva);
+}
+
+static inline
+struct vsbic_cmd *vsbic_cmd_va(struct vsbic_softc *sc, paddr_t pa)
+{
+ return (struct vsbic_cmd *)(sc->sc_cmdva + (pa - sc->sc_cmdpa));
+}
+
+static inline
+void vsbic_cmd_sync(struct vsbic_softc *sc, struct vsbic_cmd *va, int fl)
+{
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_cmdmap,
+ (vaddr_t)va - sc->sc_cmdva, sizeof(struct vsbic_cmd), fl);
+}
+
+/*
+ * Allocate contiguous physical memory, not crossing 8 MB boundaries.
+ */
+int
+vsbic_alloc_physical(struct vsbic_softc *sc, bus_dmamap_t *dmamap,
+ bus_dma_segment_t *dmaseg, vaddr_t *va, bus_size_t len, const char *what)
+{
+ int nseg;
+ int rc;
+
+ len = round_page(len);
+
+ rc = bus_dmamem_alloc(sc->sc_dmat, len, 0, 0, dmaseg, 1, &nseg,
+ BUS_DMA_NOWAIT);
+ if (rc != 0) {
+ printf("%s: unable to allocate %s memory: error %d\n",
+ DEVNAME(sc), what, rc);
+ goto fail1;
+ }
+
+ rc = bus_dmamem_map(sc->sc_dmat, dmaseg, nseg, len,
+ (caddr_t *)va, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
+ if (rc != 0) {
+ printf("%s: unable to map %s memory: error %d\n",
+ DEVNAME(sc), what, rc);
+ goto fail2;
+ }
+
+ rc = bus_dmamap_create(sc->sc_dmat, len, 1, len, VSBIC_MEM_BOUNDARY,
+ BUS_DMA_NOWAIT /* | BUS_DMA_ALLOCNOW */, dmamap);
+ if (rc != 0) {
+ printf("%s: unable to create %s dma map: error %d\n",
+ DEVNAME(sc), what, rc);
+ goto fail3;
+ }
+
+ rc = bus_dmamap_load(sc->sc_dmat, *dmamap, (void *)*va, len, NULL,
+ BUS_DMA_NOWAIT);
+ if (rc != 0) {
+ printf("%s: unable to load %s dma map: error %d\n",
+ DEVNAME(sc), what, rc);
+ goto fail4;
+ }
+
+ return 0;
+
+fail4:
+ bus_dmamap_destroy(sc->sc_dmat, *dmamap);
+fail3:
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)*va, PAGE_SIZE);
+fail2:
+ bus_dmamem_free(sc->sc_dmat, dmaseg, 1);
+fail1:
+ return rc;
+}
+
+/*
+ * CSR operations
+ */
+
+int
+vsbic_create_channels(struct vsbic_softc *sc, uint cnt)
+{
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ struct bpp_chan *chan;
+ struct bpp_channel *channel;
+ int priority, ipl, vec;
+ uint i;
+ int rc;
+
+ rc = vsbic_alloc_physical(sc, &sc->sc_chanmap, &sc->sc_chanseg,
+ &sc->sc_chanva, cnt * sizeof(struct bpp_channel), "channel");
+ if (rc != 0)
+ return STATUS_ERRNO + rc;
+
+ chan = sc->sc_chan;
+ channel = (struct bpp_channel *)sc->sc_chanva;
+ for (i = 0; i < cnt; i++, chan++, channel++) {
+ chan->ch = channel;
+
+ if (i == VSBIC_RESET_CHANNEL(sc))
+ priority = BPP_PRIORITY_HIGHEST + 0x20;
+ else
+ priority = BPP_PRIORITY_HIGHEST + 0x40;
+ if (i == VSBIC_POLLING_CHANNEL(sc)) {
+ ipl = 0;
+ vec = 0;
+ } else {
+ ipl = sc->sc_ipl;
+ vec = sc->sc_vec;
+ }
+ rc = bpp_create_channel(bsc, chan, priority, ipl, vec);
+
+ if (rc != STATUS_OK) {
+ printf("%s: error creating channel for target %d: %x\n",
+ DEVNAME(sc), i, rc);
+ return rc;
+ }
+ }
+
+ return STATUS_OK;
+}
+
+/*
+ * Command (packet) management
+ */
+
+/*
+ * Create a set of commands, and put them on a free list.
+ */
+int
+vsbic_create_cmds(struct vsbic_softc *sc, uint cnt)
+{
+ struct vsbic_cmd *cmd;
+ int rc;
+
+ rc = vsbic_alloc_physical(sc, &sc->sc_cmdmap, &sc->sc_cmdseg,
+ &sc->sc_cmdva, cnt * sizeof(struct vsbic_cmd), "packet");
+ if (rc != 0)
+ return STATUS_ERRNO + rc;
+
+ sc->sc_cmd_free = NULL;
+ cmd = (struct vsbic_cmd *)sc->sc_cmdva;
+ while (cnt-- != 0)
+ vsbic_put_cmd(sc, cmd++);
+
+ return STATUS_OK;
+}
+
+/*
+ * Get a new command from the free list.
+ */
+struct vsbic_cmd *
+vsbic_get_cmd(struct vsbic_softc *sc)
+{
+ struct vsbic_cmd *cmd;
+
+ cmd = sc->sc_cmd_free;
+ if (cmd != NULL) {
+ sc->sc_cmd_free = (struct vsbic_cmd *)cmd->pkt.cmd_link;
+ memset(cmd, 0, sizeof(*cmd));
+ }
+
+ return cmd;
+}
+
+/*
+ * Put a command into the free list.
+ */
+void
+vsbic_put_cmd(struct vsbic_softc *sc, struct vsbic_cmd *cmd)
+{
+ cmd->pkt.cmd_link = (uint32_t)sc->sc_cmd_free;
+ sc->sc_cmd_free = cmd;
+}
+
+/*
+ * Dequeue an envelope from the status pipe, and return the command it
+ * was carrying. The envelope itself is returned to the free list.
+ */
+struct vsbic_cmd *
+vsbic_dequeue_cmd(struct vsbic_softc *sc, struct bpp_chan *chan)
+{
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ struct vsbic_cmd *cmd;
+ paddr_t cmdpa;
+
+ splassert(IPL_BIO);
+
+ if (bpp_dequeue_envelope(bsc, chan, &cmdpa) != 0)
+ return NULL;
+
+ cmd = vsbic_cmd_va(sc, cmdpa);
+ vsbic_cmd_sync(sc, cmd, BUS_DMASYNC_POSTREAD);
+
+#ifdef VSBIC_DEBUG
+ printf("%s: %s() -> %p\n", DEVNAME(sc), __func__, cmd);
+#endif
+ return cmd;
+}
+
+/*
+ * Send a command packet.
+ */
+void
+vsbic_queue_cmd(struct vsbic_softc *sc, struct bpp_chan *chan,
+ struct bpp_envelope *tail, struct vsbic_cmd *cmd)
+{
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ paddr_t cmdpa;
+
+ vsbic_cmd_sync(sc, cmd, BUS_DMASYNC_PREWRITE);
+ cmdpa = vsbic_cmd_pa(sc, cmd);
+
+#ifdef VSBIC_DEBUG
+ printf("%s: %s(%p)\n", DEVNAME(sc), __func__, cmd);
+#endif
+
+ bpp_queue_envelope(bsc, chan, tail, cmdpa);
+}
+
+/*
+ * CCB management
+ */
+
+/*
+ * Create a set of ccb, and put them on a free list.
+ */
+int
+vsbic_create_ccbs(struct vsbic_softc *sc, uint cnt)
+{
+ struct vsbic_ccb *ccb;
+ int rc;
+
+ ccb = (struct vsbic_ccb *)malloc(cnt * sizeof(struct vsbic_ccb),
+ M_DEVBUF, M_ZERO | M_NOWAIT);
+ if (ccb == NULL) {
+ printf("%s: unable to allocate CCB memory\n", DEVNAME(sc));
+ return ENOMEM;
+ }
+
+ sc->sc_ccb_free = NULL;
+ sc->sc_ccb_active = NULL;
+ while (cnt-- != 0) {
+ /*
+ * Create a DMA map for data transfers.
+ */
+ rc = bus_dmamap_create(sc->sc_dmat, MAXPHYS, VSBIC_MAXSG,
+ MAXPHYS, VSBIC_MEM_BOUNDARY,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ccb_dmamap);
+ if (rc != 0) {
+ printf("%s: unable to create CCB data dma map"
+ ": error %d\n", DEVNAME(sc), rc);
+ return rc;
+ }
+
+ vsbic_free_ccb(sc, ccb++);
+ }
+
+ return 0;
+}
+
+/*
+ * Get a new ccb from the free list.
+ */
+struct vsbic_ccb *
+vsbic_get_ccb(struct vsbic_softc *sc)
+{
+ struct vsbic_ccb *ccb;
+
+ ccb = sc->sc_ccb_free;
+ if (ccb != NULL) {
+ sc->sc_ccb_free = ccb->ccb_next;
+ ccb->ccb_next = NULL;
+ ccb->ccb_cmd = NULL;
+ }
+
+ return ccb;
+}
+
+/*
+ * Put a ccb into the free list.
+ */
+void
+vsbic_free_ccb(struct vsbic_softc *sc, struct vsbic_ccb *ccb)
+{
+ ccb->ccb_next = sc->sc_ccb_free;
+ sc->sc_ccb_free = ccb;
+}
+
+/*
+ * Retrieve the active ccb associated to a command, and remove it from
+ * the active list.
+ * Since there won't be many currently active commands, storing them
+ * in a list is acceptable.
+ */
+struct vsbic_ccb *
+vsbic_cmd_ccb(struct vsbic_softc *sc, struct vsbic_cmd *cmd)
+{
+ struct vsbic_ccb *ccb, *prev;
+
+ splassert(IPL_BIO);
+
+ for (prev = NULL, ccb = sc->sc_ccb_active; ccb != NULL; prev = ccb++) {
+ if (ccb->ccb_cmd == cmd) {
+ if (prev == NULL)
+ sc->sc_ccb_active = ccb->ccb_next;
+ else
+ prev->ccb_next = ccb->ccb_next;
+ break;
+ }
+ }
+
+ return ccb;
+}
+
+/*
+ * Put a ccb into the active list.
+ */
+void
+vsbic_activate_ccb(struct vsbic_softc *sc, struct vsbic_ccb *ccb)
+{
+ struct vsbic_ccb *tmp;
+
+ splassert(IPL_BIO);
+
+ /* insert at end of list */
+ if (sc->sc_ccb_active == NULL)
+ sc->sc_ccb_active = ccb;
+ else {
+ for (tmp = sc->sc_ccb_active; tmp->ccb_next != NULL;
+ tmp = tmp->ccb_next)
+ ;
+ tmp->ccb_next = ccb;
+ }
+}
+
+/*
+ * SCSI Layer Interface
+ */
+
+/*
+ * Setup a command packet according to the SCSI command to send
+ */
+int
+vsbic_load_command(struct vsbic_softc *sc, struct vsbic_ccb *ccb,
+ struct vsbic_cmd *c, struct scsi_link *sl, int flags,
+ struct scsi_generic *cmd, int cmdlen, uint8_t *data, int datalen)
+{
+ bus_dma_segment_t *seg;
+ struct vsbic_sg *sgelem;
+ int nsegs, segno;
+ uint ctrl;
+ uint8_t *msgout;
+ uint8_t *script;
+ int rc;
+
+#ifdef VSBIC_DEBUG
+ printf("%s: command %02x len %d data %d tgt %d:%d\n",
+ DEVNAME(sc), cmd->opcode, cmdlen, datalen,
+ sl->target, sl->lun);
+#endif
+
+ /*
+ * Setup DMA map for data transfer.
+ */
+
+ if (ISSET(flags, SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ ccb->ccb_dmalen = (bus_size_t)datalen;
+ rc = bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap, data,
+ ccb->ccb_dmalen, NULL, BUS_DMA_STREAMING |
+ (ISSET(flags,SCSI_NOSLEEP) ?
+ BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
+ (ISSET(flags, SCSI_DATA_IN) ?
+ BUS_DMA_READ : BUS_DMA_WRITE));
+ if (rc != 0)
+ return rc;
+
+ bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
+ ccb->ccb_dmalen, ISSET(flags, SCSI_DATA_IN) ?
+ BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+ nsegs = ccb->ccb_dmamap->dm_nsegs;
+ } else
+ nsegs = 0;
+
+ /*
+ * Setup command packet.
+ */
+
+ if (cmd->opcode == INQUIRY && nsegs == 1)
+ c->pkt.cmd_cmd = CMD_INQUIRY;
+ else
+ c->pkt.cmd_cmd = CMD_SCSI;
+ c->pkt.cmd_device = VSBIC_DEVICE_SCSI;
+ c->pkt.cmd_unit = VSBIC_UNIT(sl->target, sl->lun);
+ c->pkt.cmd_addrmod = ADRM_EXT_S_D;
+ c->pkt.cmd_buswidth = MEMT_D32;
+
+ if (c->pkt.cmd_cmd == CMD_INQUIRY) {
+ c->pkt.cmd_cmd_ctrl = 0;
+ seg = ccb->ccb_dmamap->dm_segs;
+ c->pkt.cmd_addr2 = htobe32(seg->ds_addr);
+ c->pkt.cmd_sg_count = 0;
+ c->pkt.cmd_parm2 = htobe16(datalen >> 16);
+ c->pkt.cmd_parm3 = htobe16(datalen & 0xffff);
+ } else {
+ c->pkt.cmd_addr1 = htobe32(vsbic_cmd_pa(sc, c) +
+ offsetof(struct vsbic_cmd, scsi));
+
+ /*
+ * Setup SCSI specific packet.
+ */
+
+ ctrl = SCSI_CTRL_PAR | SCSI_CTRL_SCHK;
+ if (nsegs != 0) {
+ ctrl |= SCSI_CTRL_D32;
+ /* manual recommands PIO for small transfers */
+ if (datalen > 0x100)
+ ctrl |= SCSI_CTRL_DMA;
+ }
+ if (cmd->opcode == REQUEST_SENSE)
+ ctrl |= SCSI_CTRL_NO_ATN;
+ c->scsi.cmdlen = cmdlen;
+ memcpy(c->scsi.cdb, cmd, cmdlen);
+ c->scsi.datalen = htobe32(datalen);
+ c->scsi.msgin_flag = SCSI_MSG_INTERNAL;
+ c->scsi.msgout_flag = SCSI_MSG_INTERNAL;
+
+ /*
+ * Setup scatter/gather information.
+ */
+
+ if (nsegs != 0) {
+ seg = ccb->ccb_dmamap->dm_segs;
+ if (nsegs == 1) {
+ c->scsi.dataptr = htobe32(seg->ds_addr);
+ } else {
+ sgelem = c->sg;
+ ctrl |= SCSI_CTRL_SG;
+ for (segno = 0; segno < nsegs; seg++, segno++) {
+ sgelem->addr = htobe32(seg->ds_addr);
+ sgelem->size = htobe32(seg->ds_len);
+ sgelem++;
+ }
+ c->pkt.cmd_sg_count = htobe16(nsegs);
+ c->scsi.dataptr = htobe32(vsbic_cmd_pa(sc, c) +
+ offsetof(struct vsbic_cmd, sg));
+ }
+ c->pkt.cmd_addr2 = c->scsi.dataptr;
+ }
+
+ c->scsi.ctrl = ctrl;
+
+ /*
+ * Setup SCSI ``script'' - really the list of phases to expect,
+ * as well as the message bytes to send during the message out
+ * phase if necessary.
+ *
+ * Since this driver doesn't support synchronous transfers yet,
+ * this is really easy to build.
+ */
+
+ msgout = c->scsi.msgout;
+ script = c->scsi.script;
+
+ /* always message out unless the command is request sense */
+ if (cmd->opcode != REQUEST_SENSE) {
+ *script++ = SCRIPT_MSG_OUT;
+ *msgout++ = MSG_IDENTIFY(sl->lun, 1);
+ }
+ *script++ = SCRIPT_COMMAND;
+ if (ISSET(flags, SCSI_DATA_IN))
+ *script++ = SCRIPT_DATA_IN;
+ else if (ISSET(flags, SCSI_DATA_OUT))
+ *script++ = SCRIPT_DATA_OUT;
+ *script++ = SCRIPT_STATUS;
+ *script++ = SCRIPT_MSG_IN;
+ *script++ = SCRIPT_END;
+
+ c->scsi.msgout_len = msgout - c->scsi.msgout;
+ }
+
+ return 0;
+}
+
+/*
+ * Setup a reset command
+ */
+void
+vsbic_reset_command(struct vsbic_softc *sc, struct vsbic_cmd *cmd,
+ struct scsi_link *sl)
+{
+ cmd->pkt.cmd_cmd = CMD_RESET;
+ cmd->pkt.cmd_device = VSBIC_DEVICE_SCSI;
+ if (sl != NULL) {
+ /* the lun is ignored for reset commands */
+ cmd->pkt.cmd_unit = VSBIC_UNIT(sl->target, 0 /* sl->lun */);
+ } else {
+ /* reset whole bus */
+ cmd->pkt.cmd_unit = VSBIC_UNIT(8, 0);
+ }
+}
+
+/*
+ * Try and send a command to the board.
+ */
+int
+vsbic_scsicmd(struct scsi_xfer *xs)
+{
+ struct scsi_link *sl = xs->sc_link;
+ struct vsbic_softc *sc = (struct vsbic_softc *)sl->adapter_softc;
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ struct vsbic_ccb *ccb;
+ struct bpp_envelope *env;
+ struct vsbic_cmd *cmd;
+ uint ch;
+ int rc;
+ int s;
+
+ s = splbio();
+
+#ifdef DIAGNOSTIC
+ /*
+ * We shouldn't receive commands on the host adapter ID, this
+ * channel is reserved for reset commands.
+ */
+ if (sl->target == sc->sc_id) {
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ splx(s);
+ return COMPLETE;
+ }
+#endif
+
+ /*
+ * CDB larger than 12 bytes are not supported, as well as
+ * odd-sized data transfers.
+ * Sense data borrowed from gdt(4).
+ */
+ if (xs->cmdlen > 12 ||
+ (ISSET(xs->flags, SCSI_DATA_IN | SCSI_DATA_OUT) &&
+ (xs->datalen & 1) != 0)) {
+#ifdef VSBIC_DEBUG
+ printf("%s: can't issue command, cdb len %d data len %x\n",
+ DEVNAME(sc), xs->cmdlen, xs->datalen);
+#endif
+ memset(&xs->sense, 0, sizeof(xs->sense));
+ xs->sense.error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT;
+ xs->sense.flags = SKEY_ILLEGAL_REQUEST;
+ /* 0x20 illcmd, 0x24 illfield */
+ xs->sense.add_sense_code = xs->cmdlen > 12 ? 0x20 : 0x24;
+ xs->error = XS_SENSE;
+ scsi_done(xs);
+ splx(s);
+ return COMPLETE;
+ }
+
+ /*
+ * Get a CCB, an envelope and a command packet.
+ */
+
+ ccb = vsbic_get_ccb(sc);
+ if (ccb == NULL) {
+#ifdef VSBIC_DEBUG
+ printf("%s: no free CCB\n", DEVNAME(sc));
+#endif
+ splx(s);
+ return NO_CCB;
+ }
+
+ env = bpp_get_envelope(bsc);
+ if (env == NULL) {
+#ifdef VSBIC_DEBUG
+ printf("%s: no free envelope\n", DEVNAME(sc));
+#endif
+ vsbic_free_ccb(sc, ccb);
+ splx(s);
+ return NO_CCB;
+ }
+ cmd = vsbic_get_cmd(sc);
+ if (cmd == NULL) {
+#ifdef VSBIC_DEBUG
+ printf("%s: no free command\n", DEVNAME(sc));
+#endif
+ bpp_put_envelope(bsc, env);
+ vsbic_free_ccb(sc, ccb);
+ splx(s);
+ return NO_CCB;
+ }
+
+ ccb->ccb_xs = xs;
+ timeout_set(&xs->stimeout, vsbic_timeout, ccb);
+
+ /*
+ * Build command script.
+ */
+
+ rc = vsbic_load_command(sc, ccb, cmd, sl, xs->flags, xs->cmd,
+ xs->cmdlen, xs->data, xs->datalen);
+ if (rc != 0) {
+ printf("%s: unable to load DMA map: error %d\n",
+ DEVNAME(sc), rc);
+ vsbic_put_cmd(sc, cmd);
+ bpp_put_envelope(bsc, env);
+ vsbic_free_ccb(sc, ccb);
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ splx(s);
+ return (COMPLETE);
+ }
+
+ /*
+ * Send the command to the hardware.
+ */
+
+ ccb->ccb_cmd = cmd;
+ vsbic_activate_ccb(sc, ccb);
+ if (ISSET(xs->flags, SCSI_POLL))
+ ch = VSBIC_POLLING_CHANNEL(sc);
+ else
+ ch = VSBIC_TARGET_CHANNEL(sc, sl->target);
+ vsbic_queue_cmd(sc, &sc->sc_chan[ch], env, cmd);
+
+ if (ISSET(xs->flags, SCSI_POLL)) {
+ splx(s);
+ vsbic_poll(sc, ccb);
+ return (COMPLETE);
+ } else {
+ timeout_add_msec(&xs->stimeout, xs->timeout);
+ splx(s);
+ return (SUCCESSFULLY_QUEUED);
+ }
+}
+
+/*
+ * Reset a target
+ */
+int
+vsbic_scsireset(struct vsbic_softc *sc, struct scsi_xfer *xs)
+{
+ struct bpp_softc *bsc = &sc->sc_bpp;
+ struct bpp_chan *chan;
+ struct scsi_link *sl;
+ struct bpp_envelope *env;
+ struct vsbic_cmd *cmd;
+
+ splassert(IPL_BIO);
+
+ /*
+ * Get an envelope and a command packet.
+ */
+
+ env = bpp_get_envelope(bsc);
+ if (env == NULL) {
+#ifdef VSBIC_DEBUG
+ printf("%s: no free envelope\n", DEVNAME(sc));
+#endif
+ return EAGAIN;
+ }
+ cmd = vsbic_get_cmd(sc);
+ if (cmd == NULL) {
+#ifdef VSBIC_DEBUG
+ printf("%s: no free command\n", DEVNAME(sc));
+#endif
+ bpp_put_envelope(bsc, env);
+ return EAGAIN;
+ }
+
+ if (xs == NULL)
+ sl = NULL;
+ else
+ sl = xs->sc_link;
+
+ vsbic_reset_command(sc, cmd, sl);
+
+ chan = &sc->sc_chan[VSBIC_RESET_CHANNEL(sc)];
+ vsbic_queue_cmd(sc, chan, env, cmd);
+
+ return 0;
+}
+
+/*
+ * Wrapup task after a command has completed (or failed).
+ */
+void
+vsbic_wrapup(struct vsbic_softc *sc, struct vsbic_ccb *ccb)
+{
+ struct scsi_xfer *xs = ccb->ccb_xs;
+
+ splassert(IPL_BIO);
+
+ if (xs->error == XS_NOERROR) {
+ switch (xs->status) {
+ case SCSI_OK:
+ xs->error = XS_NOERROR;
+ break;
+ case SCSI_CHECK:
+ /* TBD send a request sense command */
+ xs->error = XS_SENSE;
+ break;
+ default:
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ }
+
+ vsbic_free_ccb(sc, ccb);
+ timeout_del(&xs->stimeout);
+ SET(xs->flags, ITSDONE);
+ scsi_done(xs);
+}
+
+
+/*
+ * Polled command operation.
+ */
+
+void
+vsbic_poll(struct vsbic_softc *sc, struct vsbic_ccb *ccb)
+{
+ struct bpp_chan *chan = &sc->sc_chan[VSBIC_POLLING_CHANNEL(sc)];
+ struct scsi_xfer *xs = ccb->ccb_xs;
+ int s;
+ int tmo;
+
+ tmo = ccb->ccb_xs->timeout;
+ for (;;) {
+ s = splbio();
+ if (vsbic_channel_intr(sc, chan) != 0)
+ if (ISSET(xs->flags, ITSDONE))
+ break;
+
+ splx(s);
+ delay(1000);
+ tmo--;
+ if (tmo == 0) {
+ s = splbio();
+ break;
+ }
+ }
+
+ if (tmo == 0) {
+ vsbic_timeout(ccb);
+ if (!ISSET(xs->flags, ITSDONE)) {
+ /*
+ * We have sent a reset command. Poll for its
+ * completion.
+ */
+ chan = &sc->sc_chan[VSBIC_RESET_CHANNEL(sc)];
+ for (;;) {
+ if (vsbic_channel_intr(sc, chan) != 0)
+ if (ISSET(xs->flags, ITSDONE))
+ break;
+
+ delay(10000); /* 10ms */
+ }
+ }
+ }
+
+ /*
+ * vsbic_intr() does not invoke vsbic_wrapup for polled
+ * commands, since we need the scsi_xfer to remain valid
+ * after the commands completes.
+ */
+ vsbic_wrapup(sc, ccb);
+
+ splx(s);
+}
+
+void
+vsbic_timeout(void *v)
+{
+ struct vsbic_ccb *ccb = (struct vsbic_ccb *)v;
+ struct scsi_xfer *xs = ccb->ccb_xs;
+ struct scsi_link *sl = xs->sc_link;
+ struct vsbic_softc *sc = (struct vsbic_softc *)sl->adapter_softc;
+ int s;
+
+ sc_print_addr(sl);
+ printf("SCSI command 0x%x timeout\n", xs->cmd->opcode);
+
+ s = splbio();
+
+ if (vsbic_scsireset(sc, xs) != 0) {
+ sc_print_addr(sl);
+ printf("unable to reset SCSI bus\n");
+
+ xs->error = XS_TIMEOUT;
+ xs->status = SCSI_TERMINATED;
+ if (ISSET(xs->flags, SCSI_POLL)) {
+ SET(xs->flags, ITSDONE);
+ /* caller will invoke vsbic_wrapup() later */
+ } else
+ vsbic_wrapup(sc, ccb);
+
+ /*
+ * This is very likely to hose at least this device,
+ * until another SCSI bus reset is tried, since the
+ * command will never complete...
+ *
+ * We could remember this situation and issue a bus
+ * reset as soon as an envelope is available, but
+ * the initial envelope number computation is supposed
+ * to prevent this from occuring.
+ */
+ }
+
+ /*
+ * If we have been able to send the reset command, make
+ * sure we are forcing an error condition, to report the
+ * correct error.
+ */
+ xs->error = XS_TIMEOUT;
+ xs->status = SCSI_TERMINATED;
+
+ splx(s);
+}
+
+/*
+ * Interrupt Handler
+ */
+
+int
+vsbic_intr(void *arg)
+{
+ struct vsbic_softc *sc = arg;
+ struct bpp_chan *chan;
+ uint ch;
+
+ /*
+ * There is no easy way to know which channel caused the interrupt
+ * (unless we register as many different interrupts as channels...),
+ * so check all of them.
+ *
+ * This means that we will sometimes dequeue commands before
+ * receiving their channel interrupts... therefore, we always
+ * claim the interrupt for the kernel not to be confused.
+ */
+
+ /*
+ * Check the reset channel first, then check all real target channels.
+ */
+
+ chan = &sc->sc_chan[VSBIC_RESET_CHANNEL(sc)];
+ (void)vsbic_channel_intr(sc, chan);
+
+ for (ch = 0; ch < 8; ch++) {
+ if (ch == VSBIC_RESET_CHANNEL(sc))
+ continue;
+ chan = &sc->sc_chan[ch];
+ (void)vsbic_channel_intr(sc, chan);
+ }
+
+ return 1;
+}
+
+int
+vsbic_channel_intr(struct vsbic_softc *sc, struct bpp_chan *chan)
+{
+ struct vsbic_cmd *cmd;
+ struct vsbic_ccb *ccb;
+ struct scsi_xfer *xs;
+ uint32_t xferlen;
+ uint error, status;
+
+ cmd = vsbic_dequeue_cmd(sc, chan);
+ if (cmd == NULL)
+ return 0;
+
+ /* retrieve associated ccb, if any */
+ ccb = vsbic_cmd_ccb(sc, cmd);
+
+ xferlen = (betoh16(cmd->pkt.sts_xfer_high) << 16) |
+ betoh16(cmd->pkt.sts_xfer_low);
+ error = cmd->pkt.sts_error;
+ status = betoh16(cmd->pkt.sts_status);
+
+#ifdef VSBIC_DEBUG
+ printf("%p: ccb %p xfer %x error %02x status %04x sts %02x parm3 %x\n",
+ cmd, ccb, xferlen, error, status, cmd->scsi.status,
+ cmd->pkt.sts_parm3);
+ if (error == ERR_OK && cmd->pkt.sts_recovered != ERR_OK)
+ printf("%recovered error %02x tries %d\n",
+ cmd->pkt.sts_recovered, cmd->pkt.sts_retries);
+#endif
+
+ if (ccb != NULL) {
+ xs = ccb->ccb_xs;
+ xs->resid = xs->datalen - xferlen;
+
+ if (ISSET(xs->flags, SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
+ ccb->ccb_dmalen, ISSET(xs->flags, SCSI_DATA_IN) ?
+ BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
+ }
+
+ if (xs->error == XS_NOERROR)
+ switch (error) {
+ case ERR_SELECT_TIMEOUT:
+ xs->error = XS_SELTIMEOUT;
+ break;
+ case ERR_BUS_RESET:
+ xs->error = XS_RESET;
+ break;
+ case ERR_REQUEST_SENSE_FAILED:
+ xs->error = XS_SHORTSENSE;
+ break;
+ case ERR_INVALID_XFER_COUNT:
+ if (status == ERR_INCOMPLETE_DATA_TRANSFER)
+ xs->error = XS_NOERROR;
+ else
+ xs->error = XS_DRIVER_STUFFUP; /* XXX */
+ break;
+ case ERR_OK:
+ xs->error = XS_NOERROR;
+ xs->status = cmd->scsi.status;
+ break;
+ default:
+ xs->error = XS_DRIVER_STUFFUP; /* XXX */
+ break;
+ }
+ }
+
+ vsbic_put_cmd(sc, cmd);
+
+ if (ccb != NULL) {
+ if (ISSET(xs->flags, SCSI_POLL)) {
+ SET(xs->flags, ITSDONE);
+ /* caller will invoke vsbic_wrapup() later */
+ } else
+ vsbic_wrapup(sc, ccb);
+ }
+
+ return 1;
+}