2001-12-16 07:17:02 +03:00
|
|
|
/* $NetBSD: trm.c,v 1.6 2001/12/16 04:18:42 tsutsui Exp $ */
|
2001-11-03 20:01:16 +03:00
|
|
|
/*
|
|
|
|
* Device Driver for Tekram DC395U/UW/F, DC315/U
|
|
|
|
* PCI SCSI Bus Master Host Adapter
|
|
|
|
* (SCSI chip set used Tekram ASIC TRM-S1040)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2001 Rui-Xiang Guo
|
|
|
|
* 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 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.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Ported from
|
|
|
|
* dc395x_trm.c
|
|
|
|
*
|
|
|
|
* Written for NetBSD 1.4.x by
|
|
|
|
* Erich Chen (erich@tekram.com.tw)
|
|
|
|
*
|
|
|
|
* Provided by
|
|
|
|
* (C)Copyright 1995-1999 Tekram Technology Co., Ltd. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:48:40 +03:00
|
|
|
#include <sys/cdefs.h>
|
2001-12-16 07:17:02 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: trm.c,v 1.6 2001/12/16 04:18:42 tsutsui Exp $");
|
2001-11-13 10:48:40 +03:00
|
|
|
|
2001-11-04 20:17:22 +03:00
|
|
|
/* #define TRM_DEBUG */
|
2001-11-03 20:01:16 +03:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/intr.h>
|
|
|
|
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
|
|
|
#include <dev/scsipi/scsi_all.h>
|
|
|
|
#include <dev/scsipi/scsi_message.h>
|
|
|
|
#include <dev/scsipi/scsipi_all.h>
|
|
|
|
#include <dev/scsipi/scsiconf.h>
|
|
|
|
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <dev/pci/trmreg.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* feature of chip set MAX value
|
|
|
|
*/
|
|
|
|
#define TRM_MAX_TARGETS 16
|
|
|
|
#define TRM_MAX_SG_ENTRIES (MAXPHYS / PAGE_SIZE + 1)
|
|
|
|
#define TRM_MAX_SRB 32
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Segment Entry
|
|
|
|
*/
|
|
|
|
struct trm_sg_entry {
|
|
|
|
u_int32_t address;
|
|
|
|
u_int32_t length;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TRM_SG_SIZE (sizeof(struct trm_sg_entry) * TRM_MAX_SG_ENTRIES)
|
|
|
|
|
|
|
|
/*
|
|
|
|
**********************************************************************
|
|
|
|
* The SEEPROM structure for TRM_S1040
|
|
|
|
**********************************************************************
|
|
|
|
*/
|
|
|
|
struct nvram_target {
|
|
|
|
u_int8_t config0; /* Target configuration byte 0 */
|
|
|
|
#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */
|
|
|
|
#define NTC_DO_TAG_QUEUING 0x10 /* Enable SCSI tag queuing */
|
|
|
|
#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */
|
|
|
|
#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */
|
|
|
|
#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */
|
2001-11-18 16:02:30 +03:00
|
|
|
#define NTC_DO_PARITY_CHK 0x01 /* Parity check enable */
|
2001-11-03 20:01:16 +03:00
|
|
|
u_int8_t period; /* Target period */
|
|
|
|
u_int8_t config2; /* Target configuration byte 2 */
|
|
|
|
u_int8_t config3; /* Target configuration byte 3 */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct trm_nvram {
|
|
|
|
u_int8_t subvendor_id[2]; /* 0,1 Sub Vendor ID */
|
|
|
|
u_int8_t subsys_id[2]; /* 2,3 Sub System ID */
|
|
|
|
u_int8_t subclass; /* 4 Sub Class */
|
|
|
|
u_int8_t vendor_id[2]; /* 5,6 Vendor ID */
|
|
|
|
u_int8_t device_id[2]; /* 7,8 Device ID */
|
|
|
|
u_int8_t reserved0; /* 9 Reserved */
|
|
|
|
struct nvram_target target[TRM_MAX_TARGETS];
|
|
|
|
/* 10,11,12,13
|
|
|
|
* 14,15,16,17
|
|
|
|
* ....
|
|
|
|
* 70,71,72,73 */
|
|
|
|
u_int8_t scsi_id; /* 74 Host Adapter SCSI ID */
|
|
|
|
u_int8_t channel_cfg; /* 75 Channel configuration */
|
|
|
|
#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */
|
2001-11-11 08:28:55 +03:00
|
|
|
#define NAC_DO_PARITY_CHK 0x08 /* Parity check enable */
|
2001-11-03 20:01:16 +03:00
|
|
|
#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */
|
|
|
|
#define NAC_GREATER_1G 0x02 /* > 1G support enable */
|
|
|
|
#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */
|
|
|
|
u_int8_t delay_time; /* 76 Power on delay time */
|
|
|
|
u_int8_t max_tag; /* 77 Maximum tags */
|
|
|
|
u_int8_t reserved1; /* 78 */
|
|
|
|
u_int8_t boot_target; /* 79 */
|
|
|
|
u_int8_t boot_lun; /* 80 */
|
|
|
|
u_int8_t reserved2; /* 81 */
|
|
|
|
u_int8_t reserved3[44]; /* 82,..125 */
|
|
|
|
u_int8_t checksum0; /* 126 */
|
|
|
|
u_int8_t checksum1; /* 127 */
|
|
|
|
#define TRM_NVRAM_CKSUM 0x1234
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Nvram Initiater bits definition */
|
|
|
|
#define MORE2_DRV 0x00000001
|
|
|
|
#define GREATER_1G 0x00000002
|
|
|
|
#define RST_SCSI_BUS 0x00000004
|
|
|
|
#define ACTIVE_NEGATION 0x00000008
|
|
|
|
#define NO_SEEK 0x00000010
|
|
|
|
#define LUN_CHECK 0x00000020
|
|
|
|
|
|
|
|
#define trm_wait_30us() DELAY(30)
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
* SCSI Request Block
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
struct trm_srb {
|
|
|
|
struct trm_srb *next;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
|
|
|
|
struct trm_sg_entry *sgentry;
|
2001-11-18 16:02:30 +03:00
|
|
|
struct scsipi_xfer *xs; /* scsipi_xfer for this cmd */
|
2001-11-03 20:01:16 +03:00
|
|
|
bus_dmamap_t dmap;
|
|
|
|
bus_size_t sgoffset; /* Xfer buf offset */
|
|
|
|
|
|
|
|
u_int32_t buflen; /* Total xfer length */
|
|
|
|
u_int32_t sgaddr; /* SGList physical starting address */
|
|
|
|
|
|
|
|
u_int state; /* SRB State */
|
|
|
|
#define SRB_FREE 0x0000
|
|
|
|
#define SRB_WAIT 0x0001
|
|
|
|
#define SRB_READY 0x0002
|
|
|
|
#define SRB_MSGOUT 0x0004 /* arbitration+msg_out 1st byte */
|
|
|
|
#define SRB_MSGIN 0x0008
|
|
|
|
#define SRB_EXTEND_MSGIN 0x0010
|
|
|
|
#define SRB_COMMAND 0x0020
|
|
|
|
#define SRB_START_ 0x0040 /* arbitration+msg_out+command_out */
|
|
|
|
#define SRB_DISCONNECT 0x0080
|
|
|
|
#define SRB_DATA_XFER 0x0100
|
|
|
|
#define SRB_XFERPAD 0x0200
|
|
|
|
#define SRB_STATUS 0x0400
|
|
|
|
#define SRB_COMPLETED 0x0800
|
|
|
|
#define SRB_ABORT_SENT 0x1000
|
|
|
|
#define SRB_DO_SYNC_NEGO 0x2000
|
|
|
|
#define SRB_DO_WIDE_NEGO 0x4000
|
|
|
|
#define SRB_UNEXPECT_RESEL 0x8000
|
|
|
|
u_int8_t *msg;
|
|
|
|
|
|
|
|
int sgcnt;
|
|
|
|
int sgindex;
|
|
|
|
|
|
|
|
int phase; /* SCSI phase */
|
|
|
|
int hastat; /* Host Adapter Status */
|
|
|
|
#define H_STATUS_GOOD 0x00
|
|
|
|
#define H_SEL_TIMEOUT 0x11
|
|
|
|
#define H_OVER_UNDER_RUN 0x12
|
|
|
|
#define H_UNEXP_BUS_FREE 0x13
|
|
|
|
#define H_TARGET_PHASE_F 0x14
|
|
|
|
#define H_INVALID_CCB_OP 0x16
|
|
|
|
#define H_LINK_CCB_BAD 0x17
|
|
|
|
#define H_BAD_TARGET_DIR 0x18
|
|
|
|
#define H_DUPLICATE_CCB 0x19
|
|
|
|
#define H_BAD_CCB_OR_SG 0x1A
|
|
|
|
#define H_ABORT 0xFF
|
|
|
|
int tastat; /* Target SCSI Status Byte */
|
|
|
|
int flag; /* SRBFlag */
|
|
|
|
#define DATAOUT 0x0080
|
|
|
|
#define DATAIN 0x0040
|
|
|
|
#define RESIDUAL_VALID 0x0020
|
|
|
|
#define ENABLE_TIMER 0x0010
|
|
|
|
#define RESET_DEV0 0x0004
|
|
|
|
#define ABORT_DEV 0x0002
|
|
|
|
#define AUTO_REQSENSE 0x0001
|
|
|
|
int srbstat; /* SRB Status */
|
|
|
|
#define SRB_OK 0x01
|
|
|
|
#define ABORTION 0x02
|
|
|
|
#define OVER_RUN 0x04
|
|
|
|
#define UNDER_RUN 0x08
|
|
|
|
#define PARITY_ERROR 0x10
|
|
|
|
#define SRB_ERROR 0x20
|
|
|
|
int tagnum; /* Tag number */
|
|
|
|
int msgcnt;
|
|
|
|
|
|
|
|
int cmdlen; /* SCSI command length */
|
|
|
|
u_int8_t cmd[12]; /* SCSI command */
|
|
|
|
|
|
|
|
u_int8_t msgin[6];
|
|
|
|
u_int8_t msgout[6];
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
* Device Control Block
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
struct trm_dcb {
|
|
|
|
struct trm_dcb *next;
|
|
|
|
|
|
|
|
struct trm_srb *waitsrb;
|
|
|
|
struct trm_srb *last_waitsrb;
|
|
|
|
|
|
|
|
struct trm_srb *gosrb;
|
|
|
|
struct trm_srb *last_gosrb;
|
|
|
|
|
|
|
|
struct trm_srb *actsrb;
|
|
|
|
|
|
|
|
int gosrb_cnt;
|
|
|
|
u_int maxcmd; /* Max command */
|
|
|
|
|
|
|
|
int id; /* SCSI Target ID (SCSI Only) */
|
|
|
|
int lun; /* SCSI Log. Unit (SCSI Only) */
|
|
|
|
|
|
|
|
u_int8_t tagmask; /* Tag mask */
|
|
|
|
|
|
|
|
u_int8_t tacfg; /* Target Config */
|
|
|
|
u_int8_t idmsg; /* Identify Msg */
|
|
|
|
u_int8_t period; /* Max Period for nego. */
|
|
|
|
|
|
|
|
u_int8_t synctl; /* Sync control for reg. */
|
|
|
|
u_int8_t offset; /* Sync offset for reg. and nego.(low nibble) */
|
|
|
|
u_int8_t mode; /* Sync mode ? (1 sync):(0 async) */
|
|
|
|
#define SYNC_NEGO_ENABLE 0x01
|
|
|
|
#define SYNC_NEGO_DONE 0x02
|
|
|
|
#define WIDE_NEGO_ENABLE 0x04
|
|
|
|
#define WIDE_NEGO_DONE 0x08
|
|
|
|
#define EN_TAG_QUEUING 0x10
|
|
|
|
#define EN_ATN_STOP 0x20
|
|
|
|
#define SYNC_NEGO_OFFSET 15
|
|
|
|
u_int8_t flag;
|
|
|
|
#define ABORT_DEV_ 0x01
|
|
|
|
#define SHOW_MESSAGE_ 0x02
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
* Adapter Control Block
|
|
|
|
*-----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
struct trm_softc {
|
|
|
|
struct device sc_dev;
|
|
|
|
|
|
|
|
bus_space_tag_t sc_iot;
|
|
|
|
bus_space_handle_t sc_ioh;
|
|
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
bus_dmamap_t sc_dmamap; /* Map the control structures */
|
|
|
|
|
|
|
|
struct trm_dcb *sc_linkdcb;
|
|
|
|
struct trm_dcb *sc_roundcb;
|
|
|
|
|
|
|
|
struct trm_dcb *sc_actdcb;
|
|
|
|
struct trm_dcb *sc_dcb[TRM_MAX_TARGETS][8];
|
|
|
|
|
|
|
|
struct trm_srb *sc_freesrb;
|
|
|
|
struct trm_srb *sc_srb; /* SRB array */
|
2001-11-04 20:17:22 +03:00
|
|
|
struct trm_srb sc_tempsrb;
|
2001-11-03 20:01:16 +03:00
|
|
|
|
|
|
|
struct trm_sg_entry *sc_sglist;
|
|
|
|
|
|
|
|
int maxid;
|
|
|
|
int maxtag; /* Max Tag number */
|
|
|
|
/*
|
|
|
|
* Link to the generic SCSI driver
|
|
|
|
*/
|
|
|
|
struct scsipi_channel sc_channel;
|
|
|
|
struct scsipi_adapter sc_adapter;
|
|
|
|
|
|
|
|
int sc_id; /* Adapter SCSI Target ID */
|
|
|
|
|
|
|
|
int devcnt; /* Device Count */
|
|
|
|
|
|
|
|
int devflag[TRM_MAX_TARGETS][8]; /* flag of initDCB for device */
|
|
|
|
|
|
|
|
int devscan[TRM_MAX_TARGETS][8];
|
|
|
|
int devscan_end;
|
|
|
|
int cur_offset; /* Current Sync offset */
|
|
|
|
|
|
|
|
struct trm_nvram sc_eeprom;
|
|
|
|
int sc_config;
|
|
|
|
#define HCC_WIDE_CARD 0x20
|
|
|
|
#define HCC_SCSI_RESET 0x10
|
|
|
|
#define HCC_PARITY 0x08
|
|
|
|
#define HCC_AUTOTERM 0x04
|
|
|
|
#define HCC_LOW8TERM 0x02
|
|
|
|
#define HCC_UP8TERM 0x01
|
|
|
|
int sc_flag;
|
|
|
|
#define RESET_DEV 0x01
|
|
|
|
#define RESET_DETECT 0x02
|
|
|
|
#define RESET_DONE 0x04
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI Status codes not defined in scsi_all.h
|
|
|
|
*/
|
|
|
|
#define SCSI_COND_MET 0x04 /* Condition Met */
|
|
|
|
#define SCSI_INTERM_COND_MET 0x14 /* Intermediate condition met */
|
|
|
|
#define SCSI_UNEXP_BUS_FREE 0xFD /* Unexpect Bus Free */
|
|
|
|
#define SCSI_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */
|
|
|
|
#define SCSI_SEL_TIMEOUT 0xFF /* Selection Time out */
|
|
|
|
|
|
|
|
static void trm_rewait_srb(struct trm_dcb *, struct trm_srb *);
|
|
|
|
static void trm_wait_srb(struct trm_softc *);
|
|
|
|
static void trm_reset_device(struct trm_softc *);
|
|
|
|
static void trm_recover_srb(struct trm_softc *);
|
|
|
|
static int trm_start_scsi(struct trm_softc *, struct trm_dcb *,
|
|
|
|
struct trm_srb *);
|
|
|
|
static int trm_intr(void *);
|
|
|
|
|
|
|
|
static void trm_dataout_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_datain_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_command_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_status_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_msgout_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_msgin_phase0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_dataout_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_datain_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_command_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_status_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_msgout_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_msgin_phase1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_nop0(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
static void trm_nop1(struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
|
|
|
|
static void trm_set_xfer_rate(struct trm_softc *, struct trm_srb *,
|
|
|
|
struct trm_dcb *);
|
|
|
|
static void trm_dataio_xfer(struct trm_softc *, struct trm_srb *, int);
|
|
|
|
static void trm_disconnect(struct trm_softc *);
|
|
|
|
static void trm_reselect(struct trm_softc *);
|
|
|
|
static void trm_srb_done(struct trm_softc *, struct trm_dcb *,
|
|
|
|
struct trm_srb *);
|
|
|
|
static void trm_doing_srb_done(struct trm_softc *);
|
|
|
|
static void trm_scsi_reset_detect(struct trm_softc *);
|
|
|
|
static void trm_reset_scsi_bus(struct trm_softc *);
|
2001-11-18 16:02:30 +03:00
|
|
|
static int trm_request_sense(struct trm_softc *, struct trm_dcb *,
|
2001-11-03 20:01:16 +03:00
|
|
|
struct trm_srb *);
|
|
|
|
static void trm_msgout_abort(struct trm_softc *, struct trm_srb *);
|
|
|
|
static void trm_timeout(void *);
|
|
|
|
static void trm_reset(struct trm_softc *);
|
|
|
|
static void trm_send_srb(struct scsipi_xfer *, struct trm_softc *,
|
|
|
|
struct trm_srb *);
|
|
|
|
static int trm_init(struct trm_softc *);
|
|
|
|
static void trm_init_adapter(struct trm_softc *);
|
|
|
|
static void trm_init_dcb(struct trm_softc *, struct trm_dcb *,
|
|
|
|
struct scsipi_xfer *);
|
|
|
|
static void trm_link_srb(struct trm_softc *);
|
|
|
|
static void trm_init_sc(struct trm_softc *);
|
|
|
|
static void trm_check_eeprom(struct trm_softc *, struct trm_nvram *);
|
|
|
|
static void trm_release_srb(struct trm_softc *, struct trm_dcb *,
|
|
|
|
struct trm_srb *);
|
|
|
|
void trm_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t, void *);
|
|
|
|
|
|
|
|
static void trm_eeprom_read_all(struct trm_softc *, struct trm_nvram *);
|
|
|
|
static void trm_eeprom_write_all(struct trm_softc *, struct trm_nvram *);
|
|
|
|
static void trm_eeprom_set_data(struct trm_softc *, u_int8_t, u_int8_t);
|
|
|
|
static void trm_eeprom_write_cmd(struct trm_softc *, u_int8_t, u_int8_t);
|
|
|
|
static u_int8_t trm_eeprom_get_data(struct trm_softc *, u_int8_t);
|
|
|
|
|
|
|
|
static int trm_probe(struct device *, struct cfdata *, void *);
|
|
|
|
static void trm_attach(struct device *, struct device *, void *);
|
|
|
|
|
|
|
|
struct cfattach trm_ca = {
|
|
|
|
sizeof(struct trm_softc), trm_probe, trm_attach
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* state_v = (void *) trm_scsi_phase0[phase]
|
|
|
|
*/
|
|
|
|
static void *trm_scsi_phase0[] = {
|
|
|
|
trm_dataout_phase0, /* phase:0 */
|
|
|
|
trm_datain_phase0, /* phase:1 */
|
|
|
|
trm_command_phase0, /* phase:2 */
|
|
|
|
trm_status_phase0, /* phase:3 */
|
|
|
|
trm_nop0, /* phase:4 */
|
|
|
|
trm_nop1, /* phase:5 */
|
|
|
|
trm_msgout_phase0, /* phase:6 */
|
|
|
|
trm_msgin_phase0, /* phase:7 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* state_v = (void *) trm_scsi_phase1[phase]
|
|
|
|
*/
|
|
|
|
static void *trm_scsi_phase1[] = {
|
|
|
|
trm_dataout_phase1, /* phase:0 */
|
|
|
|
trm_datain_phase1, /* phase:1 */
|
|
|
|
trm_command_phase1, /* phase:2 */
|
|
|
|
trm_status_phase1, /* phase:3 */
|
|
|
|
trm_nop0, /* phase:4 */
|
|
|
|
trm_nop1, /* phase:5 */
|
|
|
|
trm_msgout_phase1, /* phase:6 */
|
|
|
|
trm_msgin_phase1, /* phase:7 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* real period: */
|
|
|
|
static const u_int8_t trm_clock_period[] = {
|
2001-11-18 16:02:30 +03:00
|
|
|
12, /* 48 ns 20.0 MB/sec */
|
2001-11-03 20:01:16 +03:00
|
|
|
18, /* 72 ns 13.3 MB/sec */
|
|
|
|
25, /* 100 ns 10.0 MB/sec */
|
|
|
|
31, /* 124 ns 8.0 MB/sec */
|
|
|
|
37, /* 148 ns 6.6 MB/sec */
|
|
|
|
43, /* 172 ns 5.7 MB/sec */
|
|
|
|
50, /* 200 ns 5.0 MB/sec */
|
|
|
|
62 /* 248 ns 4.0 MB/sec */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Q back to pending Q
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_rewait_srb(dcb, srb)
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
struct trm_srb *psrb1;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
dcb->gosrb_cnt--;
|
|
|
|
psrb1 = dcb->gosrb;
|
|
|
|
if (srb == psrb1)
|
|
|
|
dcb->gosrb = psrb1->next;
|
|
|
|
else {
|
|
|
|
while (srb != psrb1->next)
|
|
|
|
psrb1 = psrb1->next;
|
|
|
|
|
|
|
|
psrb1->next = srb->next;
|
|
|
|
if (srb == dcb->last_gosrb)
|
|
|
|
dcb->last_gosrb = psrb1;
|
|
|
|
}
|
|
|
|
if (dcb->waitsrb) {
|
|
|
|
srb->next = dcb->waitsrb;
|
|
|
|
dcb->waitsrb = srb;
|
|
|
|
} else {
|
|
|
|
srb->next = NULL;
|
|
|
|
dcb->waitsrb = srb;
|
|
|
|
dcb->last_waitsrb = srb;
|
|
|
|
}
|
|
|
|
dcb->tagmask &= ~(1 << srb->tagnum); /* Free TAG number */
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_wait_srb(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
struct trm_dcb *ptr, *ptr1;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
if (sc->sc_actdcb == NULL &&
|
|
|
|
(sc->sc_flag & (RESET_DETECT | RESET_DONE | RESET_DEV)) == 0) {
|
|
|
|
ptr = sc->sc_roundcb;
|
|
|
|
if (ptr == NULL) {
|
|
|
|
ptr = sc->sc_linkdcb;
|
|
|
|
sc->sc_roundcb = ptr;
|
|
|
|
}
|
|
|
|
for (ptr1 = ptr; ptr1 != NULL;) {
|
|
|
|
sc->sc_roundcb = ptr1->next;
|
|
|
|
if (ptr1->maxcmd <= ptr1->gosrb_cnt ||
|
|
|
|
(srb = ptr1->waitsrb) == NULL) {
|
|
|
|
if (sc->sc_roundcb == ptr)
|
|
|
|
break;
|
|
|
|
ptr1 = ptr1->next;
|
|
|
|
} else {
|
|
|
|
if (trm_start_scsi(sc, ptr1, srb) == 0) {
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 0 :
|
|
|
|
* current interrupt status is
|
|
|
|
* interrupt enable. It's said that
|
|
|
|
* SCSI processor is unoccupied
|
|
|
|
*/
|
|
|
|
ptr1->gosrb_cnt++;
|
|
|
|
if (ptr1->last_waitsrb == srb) {
|
|
|
|
ptr1->waitsrb = NULL;
|
|
|
|
ptr1->last_waitsrb = NULL;
|
|
|
|
} else
|
|
|
|
ptr1->waitsrb = srb->next;
|
|
|
|
|
|
|
|
srb->next = NULL;
|
|
|
|
if (ptr1->gosrb != NULL)
|
|
|
|
ptr1->last_gosrb->next = srb;
|
|
|
|
else
|
|
|
|
ptr1->gosrb = srb;
|
|
|
|
|
|
|
|
ptr1->last_gosrb = srb;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_send_srb(xs, sc, srb)
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_send_srb..........\n");
|
|
|
|
#endif
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* now get the DCB from upper layer( OS )
|
|
|
|
*/
|
|
|
|
dcb = srb->dcb;
|
|
|
|
|
|
|
|
if (dcb->maxcmd <= dcb->gosrb_cnt ||
|
|
|
|
sc->sc_actdcb != NULL ||
|
|
|
|
(sc->sc_flag & (RESET_DETECT | RESET_DONE | RESET_DEV))) {
|
|
|
|
if (dcb->waitsrb != NULL) {
|
|
|
|
dcb->last_waitsrb->next = srb;
|
|
|
|
dcb->last_waitsrb = srb;
|
|
|
|
srb->next = NULL;
|
|
|
|
} else {
|
|
|
|
dcb->waitsrb = srb;
|
|
|
|
dcb->last_waitsrb = srb;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (dcb->waitsrb != NULL) {
|
|
|
|
dcb->last_waitsrb->next = srb;
|
|
|
|
dcb->last_waitsrb = srb;
|
|
|
|
srb->next = NULL;
|
|
|
|
/* srb = GetWaitingSRB(dcb); */
|
|
|
|
srb = dcb->waitsrb;
|
|
|
|
dcb->waitsrb = srb->next;
|
|
|
|
srb->next = NULL;
|
|
|
|
}
|
|
|
|
if (trm_start_scsi(sc, dcb, srb) == 0) {
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 0: current interrupt status
|
|
|
|
* is interrupt enable. It's said that SCSI processor is
|
|
|
|
* unoccupied.
|
|
|
|
*/
|
|
|
|
dcb->gosrb_cnt++; /* stack waiting SRB */
|
|
|
|
if (dcb->gosrb != NULL) {
|
|
|
|
dcb->last_gosrb->next = srb;
|
|
|
|
dcb->last_gosrb = srb;
|
|
|
|
} else {
|
|
|
|
dcb->gosrb = srb;
|
|
|
|
dcb->last_gosrb = srb;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 1: current interrupt status
|
|
|
|
* is interrupt disreenable. It's said that SCSI processor
|
|
|
|
* has more one SRB need to do we need reQ back SRB.
|
|
|
|
*/
|
|
|
|
if (dcb->waitsrb != NULL) {
|
|
|
|
srb->next = dcb->waitsrb;
|
|
|
|
dcb->waitsrb = srb;
|
|
|
|
} else {
|
|
|
|
srb->next = NULL;
|
|
|
|
dcb->waitsrb = srb;
|
|
|
|
dcb->last_waitsrb = srb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by GENERIC SCSI driver
|
|
|
|
* enqueues a SCSI command
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
trm_scsipi_request(chan, req, arg)
|
|
|
|
struct scsipi_channel *chan;
|
|
|
|
scsipi_adapter_req_t req;
|
|
|
|
void *arg;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot;
|
|
|
|
bus_space_handle_t ioh;
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb = NULL;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
int error, i, id, lun, s;
|
|
|
|
|
|
|
|
sc = (struct trm_softc *)chan->chan_adapter->adapt_dev;
|
|
|
|
iot = sc->sc_iot;
|
|
|
|
ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
switch (req) {
|
|
|
|
case ADAPTER_REQ_RUN_XFER:
|
|
|
|
xs = arg;
|
|
|
|
id = xs->xs_periph->periph_target;
|
|
|
|
lun = xs->xs_periph->periph_lun;
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_scsipi_request.....\n");
|
|
|
|
printf("%s: id= %d lun= %d\n", sc->sc_dev.dv_xname, id, lun);
|
|
|
|
printf("sc->devscan[id][lun]= %d\n", sc->devscan[id][lun]);
|
|
|
|
#endif
|
|
|
|
if ((id > sc->maxid) || (lun > 7)) {
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dcb = sc->sc_dcb[id][lun];
|
|
|
|
if (sc->devscan[id][lun] != 0 && sc->devflag[id][lun] == 0) {
|
|
|
|
/*
|
|
|
|
* Scan SCSI BUS => trm_init_dcb
|
|
|
|
*/
|
|
|
|
if (sc->devcnt < TRM_MAX_TARGETS) {
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_init_dcb: dcb=%8x, ", (int) dcb);
|
|
|
|
printf("ID=%2x, LUN=%2x\n", id, lun);
|
|
|
|
#endif
|
|
|
|
sc->devflag[id][lun] = 1;
|
|
|
|
trm_init_dcb(sc, dcb, xs);
|
|
|
|
} else {
|
|
|
|
printf("%s: ", sc->sc_dev.dv_xname);
|
|
|
|
printf("sc->devcnt >= TRM_MAX_TARGETS\n");
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xs->xs_control & XS_CTL_RESET) {
|
|
|
|
trm_reset(sc);
|
|
|
|
xs->error = XS_NOERROR | XS_RESET;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (xs->xs_status & XS_STS_DONE) {
|
|
|
|
printf("%s: Is it done?\n", sc->sc_dev.dv_xname);
|
|
|
|
xs->xs_status &= ~XS_STS_DONE;
|
|
|
|
}
|
|
|
|
xs->error = 0;
|
|
|
|
xs->status = 0;
|
|
|
|
xs->resid = 0;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
/* Get SRB */
|
|
|
|
srb = sc->sc_freesrb;
|
|
|
|
if (srb != NULL) {
|
|
|
|
sc->sc_freesrb = srb->next;
|
|
|
|
srb->next = NULL;
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("srb = %8p sc->sc_freesrb= %8p\n",
|
|
|
|
srb, sc->sc_freesrb);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
xs->error = XS_RESOURCE_SHORTAGE;
|
|
|
|
scsipi_done(xs);
|
|
|
|
splx(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* XXX BuildSRB(srb ,dcb); XXX
|
|
|
|
*/
|
|
|
|
srb->dcb = dcb;
|
|
|
|
srb->xs = xs;
|
|
|
|
srb->cmdlen = xs->cmdlen;
|
|
|
|
/*
|
|
|
|
* Move layer of CAM command block to layer of SCSI
|
|
|
|
* Request Block for SCSI processor command doing.
|
|
|
|
*/
|
|
|
|
memcpy(srb->cmd, xs->cmd, xs->cmdlen);
|
2001-11-18 16:02:30 +03:00
|
|
|
if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
|
2001-11-03 20:01:16 +03:00
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("xs->datalen...\n");
|
|
|
|
printf("sc->sc_dmat=%x\n", (int) sc->sc_dmat);
|
|
|
|
printf("srb->dmap=%x\n", (int) srb->dmap);
|
|
|
|
printf("xs->data=%x\n", (int) xs->data);
|
|
|
|
printf("xs->datalen=%x\n", (int) xs->datalen);
|
|
|
|
#endif
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, srb->dmap,
|
|
|
|
xs->data, xs->datalen, NULL,
|
2001-11-18 16:02:30 +03:00
|
|
|
((xs->xs_control & XS_CTL_NOSLEEP) ?
|
|
|
|
BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
|
|
|
|
BUS_DMA_STREAMING |
|
|
|
|
((xs->xs_control & XS_CTL_DATA_IN) ?
|
|
|
|
BUS_DMA_READ : BUS_DMA_WRITE))) != 0) {
|
2001-11-03 20:01:16 +03:00
|
|
|
printf("%s: DMA transfer map unable to load, "
|
|
|
|
"error = %d\n", sc->sc_dev.dv_xname, error);
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
/*
|
|
|
|
* free SRB
|
|
|
|
*/
|
|
|
|
srb->next = sc->sc_freesrb;
|
|
|
|
sc->sc_freesrb = srb;
|
2001-11-11 08:28:55 +03:00
|
|
|
splx(s);
|
2001-11-03 20:01:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
|
|
|
|
srb->dmap->dm_mapsize,
|
|
|
|
(xs->xs_control & XS_CTL_DATA_IN) ?
|
|
|
|
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/* Set up the scatter gather list */
|
|
|
|
for (i = 0; i < srb->dmap->dm_nsegs; i++) {
|
|
|
|
srb->sgentry[i].address =
|
|
|
|
htole32(srb->dmap->dm_segs[i].ds_addr);
|
|
|
|
srb->sgentry[i].length =
|
|
|
|
htole32(srb->dmap->dm_segs[i].ds_len);
|
|
|
|
}
|
|
|
|
srb->buflen = xs->datalen;
|
|
|
|
srb->sgcnt = srb->dmap->dm_nsegs;
|
|
|
|
} else {
|
|
|
|
srb->sgentry[0].address = 0;
|
|
|
|
srb->sgentry[0].length = 0;
|
|
|
|
srb->buflen = 0;
|
|
|
|
srb->sgcnt = 0;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
|
|
srb->sgoffset, TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
srb->sgindex = 0;
|
|
|
|
srb->hastat = 0;
|
|
|
|
srb->tastat = 0;
|
|
|
|
srb->msgcnt = 0;
|
|
|
|
srb->srbstat = 0;
|
|
|
|
srb->flag = 0;
|
|
|
|
srb->state = 0;
|
|
|
|
srb->phase = PH_BUS_FREE; /* SCSI bus free Phase */
|
|
|
|
|
|
|
|
trm_send_srb(xs, sc, srb);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
if ((xs->xs_control & XS_CTL_POLL) == 0) {
|
|
|
|
int timeout = xs->timeout;
|
|
|
|
timeout = (timeout > 100000) ?
|
|
|
|
timeout / 1000 * hz : timeout * hz / 1000;
|
|
|
|
callout_reset(&xs->xs_callout, timeout,
|
|
|
|
trm_timeout, srb);
|
|
|
|
} else {
|
|
|
|
s = splbio();
|
|
|
|
do {
|
|
|
|
while (--xs->timeout) {
|
|
|
|
DELAY(1000);
|
|
|
|
if (bus_space_read_2(iot, ioh,
|
|
|
|
TRM_SCSI_STATUS) & SCSIINTERRUPT)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (xs->timeout == 0) {
|
|
|
|
trm_timeout(srb);
|
|
|
|
break;
|
|
|
|
} else
|
|
|
|
trm_intr(sc);
|
|
|
|
} while ((xs->xs_status & XS_STS_DONE) == 0);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ADAPTER_REQ_GROW_RESOURCES:
|
|
|
|
/* XXX Not supported. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
case ADAPTER_REQ_SET_XFER_MODE:
|
|
|
|
/* XXX XXX XXX */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_reset_device(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
struct trm_dcb *dcb, *pdcb;
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
dcb = sc->sc_linkdcb;
|
|
|
|
if (dcb == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pdcb = dcb;
|
|
|
|
do {
|
|
|
|
dcb->mode &= ~(SYNC_NEGO_DONE | WIDE_NEGO_DONE);
|
|
|
|
dcb->synctl = 0;
|
|
|
|
dcb->offset = 0;
|
|
|
|
eeprom = &sc->sc_eeprom;
|
|
|
|
dcb->tacfg = eeprom->target[dcb->id].config0;
|
|
|
|
index = eeprom->target[dcb->id].period & 0x07;
|
|
|
|
dcb->period = trm_clock_period[index];
|
|
|
|
if ((dcb->tacfg & NTC_DO_WIDE_NEGO) &&
|
|
|
|
(sc->sc_config & HCC_WIDE_CARD))
|
|
|
|
dcb->mode |= WIDE_NEGO_ENABLE;
|
|
|
|
|
|
|
|
dcb = dcb->next;
|
|
|
|
}
|
|
|
|
while (pdcb != dcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_recover_srb(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
struct trm_dcb *dcb, *pdcb;
|
|
|
|
struct trm_srb *psrb, *psrb2;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dcb = sc->sc_linkdcb;
|
|
|
|
if (dcb == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pdcb = dcb;
|
|
|
|
do {
|
|
|
|
psrb = pdcb->gosrb;
|
|
|
|
for (i = 0; i < pdcb->gosrb_cnt; i++) {
|
|
|
|
psrb2 = psrb;
|
|
|
|
psrb = psrb->next;
|
|
|
|
if (pdcb->waitsrb) {
|
|
|
|
psrb2->next = pdcb->waitsrb;
|
|
|
|
pdcb->waitsrb = psrb2;
|
|
|
|
} else {
|
|
|
|
pdcb->waitsrb = psrb2;
|
|
|
|
pdcb->last_waitsrb = psrb2;
|
|
|
|
psrb2->next = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pdcb->gosrb_cnt = 0;
|
|
|
|
pdcb->gosrb = NULL;
|
|
|
|
pdcb->tagmask = 0;
|
|
|
|
pdcb = pdcb->next;
|
|
|
|
}
|
|
|
|
while (pdcb != dcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* perform a hard reset on the SCSI bus (and TRM_S1040 chip).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_reset(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("%s: SCSI RESET.........", sc->sc_dev.dv_xname);
|
|
|
|
#endif
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
/* disable SCSI and DMA interrupt */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_INTEN, 0);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_INTEN, 0);
|
|
|
|
|
|
|
|
trm_reset_scsi_bus(sc);
|
|
|
|
DELAY(500000);
|
|
|
|
|
|
|
|
/* Enable SCSI interrupt */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_INTEN,
|
|
|
|
EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED |
|
|
|
|
EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE);
|
|
|
|
|
|
|
|
/* Enable DMA interrupt */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_INTEN, EN_SCSIINTR);
|
|
|
|
|
|
|
|
/* Clear DMA FIFO */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);
|
|
|
|
|
|
|
|
/* Clear SCSI FIFO */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
|
|
|
|
trm_reset_device(sc);
|
|
|
|
trm_doing_srb_done(sc);
|
|
|
|
sc->sc_actdcb = NULL;
|
|
|
|
sc->sc_flag = 0; /* RESET_DETECT, RESET_DONE, RESET_DEV */
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_timeout(arg)
|
|
|
|
void *arg;
|
|
|
|
{
|
|
|
|
struct trm_srb *srb = (struct trm_srb *)arg;
|
|
|
|
struct scsipi_xfer *xs = srb->xs;
|
|
|
|
struct scsipi_periph *periph = xs->xs_periph;
|
|
|
|
struct trm_softc *sc;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
if (xs == NULL)
|
|
|
|
printf("trm_timeout called with xs == NULL\n");
|
|
|
|
|
|
|
|
else {
|
|
|
|
scsipi_printaddr(xs->xs_periph);
|
|
|
|
printf("SCSI OpCode 0x%02x timed out\n", xs->cmd->opcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
sc = (void *)periph->periph_channel->chan_adapter->adapt_dev;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
trm_reset_scsi_bus(sc);
|
|
|
|
callout_stop(&xs->xs_callout);
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
trm_start_scsi(sc, dcb, srb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int tagnum;
|
|
|
|
u_int32_t tagmask;
|
|
|
|
u_int8_t scsicmd, idmsg;
|
|
|
|
|
|
|
|
srb->tagnum = 31;
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_TARGETID, dcb->id);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_SYNC, dcb->synctl);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, dcb->offset);
|
|
|
|
srb->phase = PH_BUS_FREE; /* initial phase */
|
|
|
|
/* Flush FIFO */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
|
|
|
|
idmsg = dcb->idmsg;
|
|
|
|
|
|
|
|
if ((srb->cmd[0] == INQUIRY) ||
|
2001-11-18 16:02:30 +03:00
|
|
|
(srb->cmd[0] == REQUEST_SENSE)) {
|
2001-11-03 20:01:16 +03:00
|
|
|
if (((dcb->mode & WIDE_NEGO_ENABLE) &&
|
|
|
|
(dcb->mode & WIDE_NEGO_DONE) == 0) ||
|
|
|
|
((dcb->mode & SYNC_NEGO_ENABLE) &&
|
|
|
|
(dcb->mode & SYNC_NEGO_DONE) == 0)) {
|
|
|
|
if ((dcb->idmsg & 7) == 0 || srb->cmd[0] != INQUIRY) {
|
|
|
|
scsicmd = SCMD_SEL_ATNSTOP;
|
|
|
|
srb->state = SRB_MSGOUT;
|
|
|
|
goto polling;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Send identify message */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
idmsg & ~MSG_IDENTIFY_DISCFLAG);
|
|
|
|
scsicmd = SCMD_SEL_ATN;
|
|
|
|
srb->state = SRB_START_;
|
|
|
|
} else { /* not inquiry,request sense,auto request sense */
|
|
|
|
/* Send identify message */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, idmsg);
|
|
|
|
DELAY(30);
|
|
|
|
scsicmd = SCMD_SEL_ATN;
|
|
|
|
srb->state = SRB_START_;
|
|
|
|
if (dcb->mode & EN_TAG_QUEUING) {
|
|
|
|
/* Send Tag message, get tag id */
|
|
|
|
tagmask = 1;
|
|
|
|
tagnum = 0;
|
|
|
|
while (tagmask & dcb->tagmask) {
|
|
|
|
tagmask = tagmask << 1;
|
|
|
|
tagnum++;
|
|
|
|
}
|
|
|
|
/* Send Tag id */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_SIMPLE_Q_TAG);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, tagnum);
|
|
|
|
|
|
|
|
dcb->tagmask |= tagmask;
|
|
|
|
srb->tagnum = tagnum;
|
|
|
|
|
|
|
|
scsicmd = SCMD_SEL_ATN3;
|
|
|
|
srb->state = SRB_START_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
polling:
|
|
|
|
/*
|
|
|
|
* Send CDB ..command block...
|
|
|
|
*/
|
2001-11-18 16:02:30 +03:00
|
|
|
bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO, srb->cmd, srb->cmdlen);
|
2001-11-03 20:01:16 +03:00
|
|
|
|
|
|
|
if (bus_space_read_2(iot, ioh, TRM_SCSI_STATUS) & SCSIINTERRUPT) {
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 1: current interrupt status
|
|
|
|
* is interrupt disreenable. It's said that SCSI processor
|
|
|
|
* has more one SRB need to do, SCSI processor has been
|
|
|
|
* occupied by one SRB.
|
|
|
|
*/
|
|
|
|
srb->state = SRB_READY;
|
|
|
|
dcb->tagmask &= ~(1 << srb->tagnum);
|
|
|
|
return (1);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 0: current interrupt status
|
|
|
|
* is interrupt enable. It's said that SCSI processor is
|
|
|
|
* unoccupied.
|
|
|
|
*/
|
|
|
|
srb->phase = PH_BUS_FREE; /* SCSI bus free Phase */
|
|
|
|
sc->sc_actdcb = dcb;
|
|
|
|
dcb->actsrb = srb;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_DATALATCH | DO_HWRESELECT);
|
|
|
|
/* it's important for atn stop */
|
|
|
|
/*
|
|
|
|
* SCSI command
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, scsicmd);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Catch an interrupt from the adapter
|
|
|
|
* Process pending device interrupts.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
trm_intr(vsc)
|
|
|
|
void *vsc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot;
|
|
|
|
bus_space_handle_t ioh;
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
void (*state_v) (struct trm_softc *, struct trm_srb *, int *);
|
|
|
|
int phase, intstat, stat = 0;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_intr......\n");
|
|
|
|
#endif
|
|
|
|
sc = (struct trm_softc *)vsc;
|
|
|
|
iot = sc->sc_iot;
|
|
|
|
ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
if (sc == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
stat = bus_space_read_2(iot, ioh, TRM_SCSI_STATUS);
|
|
|
|
if ((stat & SCSIINTERRUPT) == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("stat=%2x,", stat);
|
|
|
|
#endif
|
|
|
|
intstat = bus_space_read_1(iot, ioh, TRM_SCSI_INTSTATUS);
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("intstat=%2x,", intstat);
|
|
|
|
#endif
|
|
|
|
if (intstat & (INT_SELTIMEOUT | INT_DISCONNECT)) {
|
|
|
|
trm_disconnect(sc);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (intstat & INT_RESELECTED) {
|
|
|
|
trm_reselect(sc);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (intstat & INT_SCSIRESET) {
|
|
|
|
trm_scsi_reset_detect(sc);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (intstat & (INT_BUSSERVICE | INT_CMDDONE)) {
|
|
|
|
dcb = sc->sc_actdcb;
|
|
|
|
srb = dcb->actsrb;
|
|
|
|
if (dcb != NULL)
|
|
|
|
if (dcb->flag & ABORT_DEV_) {
|
|
|
|
srb->msgout[0] = MSG_ABORT;
|
|
|
|
trm_msgout_abort(sc, srb);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* software sequential machine
|
|
|
|
*/
|
|
|
|
phase = srb->phase; /* phase: */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 62037 or 62137 call trm_scsi_phase0[]... "phase
|
|
|
|
* entry" handle every phase before start transfer
|
|
|
|
*/
|
|
|
|
state_v = (void *)trm_scsi_phase0[phase];
|
|
|
|
state_v(sc, srb, &stat);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if there were any exception occured
|
|
|
|
* stat will be modify to bus free phase new
|
|
|
|
* stat transfer out from ... prvious state_v
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
/* phase:0,1,2,3,4,5,6,7 */
|
|
|
|
srb->phase = stat & PHASEMASK;
|
|
|
|
phase = stat & PHASEMASK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call trm_scsi_phase1[]... "phase entry" handle every
|
|
|
|
* phase do transfer
|
|
|
|
*/
|
|
|
|
state_v = (void *)trm_scsi_phase1[phase];
|
|
|
|
state_v(sc, srb, &stat);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_msgout_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
if (srb->state & (SRB_UNEXPECT_RESEL | SRB_ABORT_SENT))
|
|
|
|
*pstat = PH_BUS_FREE; /* .. initial phase */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_msgout_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
dcb = sc->sc_actdcb;
|
|
|
|
if ((srb->state & SRB_MSGOUT) == 0) {
|
|
|
|
if (srb->msgcnt > 0) {
|
|
|
|
bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
srb->msgout, srb->msgcnt);
|
|
|
|
srb->msgcnt = 0;
|
|
|
|
if ((dcb->flag & ABORT_DEV_) &&
|
|
|
|
(srb->msgout[0] == MSG_ABORT))
|
|
|
|
srb->state = SRB_ABORT_SENT;
|
|
|
|
} else {
|
|
|
|
if ((srb->cmd[0] == INQUIRY) ||
|
2001-11-18 16:02:30 +03:00
|
|
|
(srb->cmd[0] == REQUEST_SENSE))
|
2001-11-03 20:01:16 +03:00
|
|
|
if (dcb->mode & SYNC_NEGO_ENABLE)
|
|
|
|
goto mop1;
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, MSG_ABORT);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mop1: /* message out phase */
|
|
|
|
if ((srb->state & SRB_DO_WIDE_NEGO) == 0 &&
|
|
|
|
(dcb->mode & WIDE_NEGO_ENABLE)) {
|
|
|
|
/*
|
|
|
|
* WIDE DATA TRANSFER REQUEST code (03h)
|
|
|
|
*/
|
|
|
|
dcb->mode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
dcb->idmsg & ~MSG_IDENTIFY_DISCFLAG);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXTENDED); /* (01h) */
|
|
|
|
|
|
|
|
/* Message length (02h) */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXT_WDTR_LEN);
|
|
|
|
|
|
|
|
/* wide data xfer (03h) */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXT_WDTR);
|
|
|
|
|
|
|
|
/* width: 0(8bit), 1(16bit) ,2(32bit) */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXT_WDTR_BUS_16_BIT);
|
|
|
|
|
|
|
|
srb->state |= SRB_DO_WIDE_NEGO;
|
|
|
|
} else if ((srb->state & SRB_DO_SYNC_NEGO) == 0 &&
|
|
|
|
(dcb->mode & SYNC_NEGO_ENABLE)) {
|
|
|
|
/*
|
|
|
|
* SYNCHRONOUS DATA TRANSFER REQUEST code (01h)
|
|
|
|
*/
|
|
|
|
if ((dcb->mode & WIDE_NEGO_DONE) == 0)
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
dcb->idmsg & ~MSG_IDENTIFY_DISCFLAG);
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXTENDED); /* (01h) */
|
|
|
|
|
|
|
|
/* Message length (03h) */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXT_SDTR_LEN);
|
|
|
|
|
|
|
|
/* SYNCHRONOUS DATA TRANSFER REQUEST code (01h) */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
MSG_EXT_SDTR);
|
|
|
|
|
|
|
|
/* Transfer peeriod factor */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO, dcb->period);
|
|
|
|
|
|
|
|
/* REQ/ACK offset */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_FIFO,
|
|
|
|
SYNC_NEGO_OFFSET);
|
|
|
|
srb->state |= SRB_DO_SYNC_NEGO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_command_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_command_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRATN | DO_CLRFIFO);
|
2001-11-18 16:02:30 +03:00
|
|
|
bus_space_write_multi_1(iot, ioh, TRM_SCSI_FIFO, srb->cmd, srb->cmdlen);
|
2001-11-03 20:01:16 +03:00
|
|
|
|
|
|
|
srb->state = SRB_COMMAND;
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_dataout_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_sg_entry *sg;
|
|
|
|
int sgindex;
|
|
|
|
u_int32_t xferlen, leftcnt = 0;
|
|
|
|
|
|
|
|
dcb = srb->dcb;
|
|
|
|
|
|
|
|
if ((srb->state & SRB_XFERPAD) == 0) {
|
|
|
|
if (*pstat & PARITYERROR)
|
|
|
|
srb->srbstat |= PARITY_ERROR;
|
|
|
|
|
|
|
|
if ((*pstat & SCSIXFERDONE) == 0) {
|
|
|
|
/*
|
|
|
|
* when data transfer from DMA FIFO to SCSI FIFO
|
|
|
|
* if there was some data left in SCSI FIFO
|
|
|
|
*/
|
|
|
|
leftcnt = bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT) &
|
|
|
|
SCSI_FIFOCNT_MASK;
|
|
|
|
if (dcb->synctl & WIDE_SYNC)
|
|
|
|
/*
|
|
|
|
* if WIDE scsi SCSI FIFOCNT unit is word
|
|
|
|
* so need to * 2
|
|
|
|
*/
|
|
|
|
leftcnt <<= 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* caculate all the residue data that not yet tranfered
|
|
|
|
* SCSI transfer counter + left in SCSI FIFO data
|
|
|
|
*
|
|
|
|
* .....TRM_SCSI_XCNT (24bits)
|
|
|
|
* The counter always decrement by one for every SCSI
|
|
|
|
* byte transfer.
|
|
|
|
* .....TRM_SCSI_FIFOCNT ( 5bits)
|
|
|
|
* The counter is SCSI FIFO offset counter
|
|
|
|
*/
|
|
|
|
leftcnt += bus_space_read_4(iot, ioh, TRM_SCSI_XCNT);
|
|
|
|
if (leftcnt == 1) {
|
|
|
|
leftcnt = 0;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_CLRFIFO);
|
|
|
|
}
|
|
|
|
if ((leftcnt == 0) || (*pstat & SCSIXFERCNT_2_ZERO)) {
|
|
|
|
while ((bus_space_read_1(iot, ioh, TRM_DMA_STATUS) &
|
|
|
|
DMAXFERCOMP) == 0)
|
|
|
|
;
|
|
|
|
|
|
|
|
srb->buflen = 0;
|
|
|
|
} else { /* Update SG list */
|
|
|
|
/*
|
|
|
|
* if transfer not yet complete
|
|
|
|
* there were some data residue in SCSI FIFO or
|
|
|
|
* SCSI transfer counter not empty
|
|
|
|
*/
|
|
|
|
if (srb->buflen != leftcnt) {
|
|
|
|
/* data that had transferred length */
|
|
|
|
xferlen = srb->buflen - leftcnt;
|
|
|
|
|
|
|
|
/* next time to be transferred length */
|
|
|
|
srb->buflen = leftcnt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parsing from last time disconnect sgindex
|
|
|
|
*/
|
|
|
|
sg = srb->sgentry + srb->sgindex;
|
|
|
|
for (sgindex = srb->sgindex;
|
|
|
|
sgindex < srb->sgcnt;
|
|
|
|
sgindex++, sg++) {
|
|
|
|
/*
|
|
|
|
* find last time which SG transfer
|
|
|
|
* be disconnect
|
|
|
|
*/
|
|
|
|
if (xferlen >= le32toh(sg->length))
|
|
|
|
xferlen -= le32toh(sg->length);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* update last time
|
|
|
|
* disconnected SG list
|
|
|
|
*/
|
|
|
|
/* residue data length */
|
|
|
|
sg->length = htole32(
|
|
|
|
le32toh(sg->length)
|
|
|
|
- xferlen);
|
|
|
|
/* residue data pointer */
|
|
|
|
sg->address = htole32(
|
|
|
|
le32toh(sg->address)
|
|
|
|
+ xferlen);
|
|
|
|
srb->sgindex = sgindex;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
|
|
srb->sgoffset, TRM_SG_SIZE,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, STOPDMAXFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_dataout_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do prepare befor transfer when data out phase
|
|
|
|
*/
|
|
|
|
trm_dataio_xfer(sc, srb, XFERDATAOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_datain_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_sg_entry *sg;
|
|
|
|
int sgindex;
|
|
|
|
u_int32_t xferlen, leftcnt = 0;
|
|
|
|
|
|
|
|
if ((srb->state & SRB_XFERPAD) == 0) {
|
|
|
|
if (*pstat & PARITYERROR)
|
|
|
|
srb->srbstat |= PARITY_ERROR;
|
|
|
|
|
|
|
|
leftcnt += bus_space_read_4(iot, ioh, TRM_SCSI_XCNT);
|
|
|
|
if ((leftcnt == 0) || (*pstat & SCSIXFERCNT_2_ZERO)) {
|
|
|
|
while ((bus_space_read_1(iot, ioh, TRM_DMA_STATUS) &
|
|
|
|
DMAXFERCOMP) == 0)
|
|
|
|
;
|
|
|
|
|
|
|
|
srb->buflen = 0;
|
|
|
|
} else { /* phase changed */
|
|
|
|
/*
|
|
|
|
* parsing the case:
|
|
|
|
* when a transfer not yet complete
|
|
|
|
* but be disconnected by uper layer
|
|
|
|
* if transfer not yet complete
|
|
|
|
* there were some data residue in SCSI FIFO or
|
|
|
|
* SCSI transfer counter not empty
|
|
|
|
*/
|
|
|
|
if (srb->buflen != leftcnt) {
|
|
|
|
/*
|
|
|
|
* data that had transferred length
|
|
|
|
*/
|
|
|
|
xferlen = srb->buflen - leftcnt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* next time to be transferred length
|
|
|
|
*/
|
|
|
|
srb->buflen = leftcnt;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parsing from last time disconnect sgindex
|
|
|
|
*/
|
|
|
|
sg = srb->sgentry + srb->sgindex;
|
|
|
|
for (sgindex = srb->sgindex;
|
|
|
|
sgindex < srb->sgcnt;
|
|
|
|
sgindex++, sg++) {
|
|
|
|
/*
|
|
|
|
* find last time which SG transfer
|
|
|
|
* be disconnect
|
|
|
|
*/
|
|
|
|
if (xferlen >= le32toh(sg->length))
|
|
|
|
xferlen -= le32toh(sg->length);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* update last time
|
|
|
|
* disconnected SG list
|
|
|
|
*/
|
|
|
|
/* residue data length */
|
|
|
|
sg->length = htole32(
|
|
|
|
le32toh(sg->length)
|
|
|
|
- xferlen);
|
|
|
|
/* residue data pointer */
|
|
|
|
sg->address = htole32(
|
|
|
|
le32toh(sg->address)
|
|
|
|
+ xferlen);
|
|
|
|
srb->sgindex = sgindex;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
|
|
srb->sgoffset, TRM_SG_SIZE,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_datain_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do prepare befor transfer when data in phase
|
|
|
|
*/
|
|
|
|
trm_dataio_xfer(sc, srb, XFERDATAIN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_dataio_xfer(sc, srb, iodir)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int iodir;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb = srb->dcb;
|
|
|
|
|
|
|
|
if (srb->sgindex < srb->sgcnt) {
|
|
|
|
if (srb->buflen > 0) {
|
|
|
|
/*
|
|
|
|
* load what physical address of Scatter/Gather
|
|
|
|
* list table want to be transfer
|
|
|
|
*/
|
|
|
|
srb->state = SRB_DATA_XFER;
|
|
|
|
bus_space_write_4(iot, ioh, TRM_DMA_XHIGHADDR, 0);
|
|
|
|
bus_space_write_4(iot, ioh, TRM_DMA_XLOWADDR,
|
|
|
|
srb->sgaddr +
|
|
|
|
srb->sgindex * sizeof(struct trm_sg_entry));
|
|
|
|
/*
|
|
|
|
* load how many bytes in the Scatter/Gather list table
|
|
|
|
*/
|
|
|
|
bus_space_write_4(iot, ioh, TRM_DMA_XCNT,
|
|
|
|
(srb->sgcnt - srb->sgindex)
|
|
|
|
* sizeof(struct trm_sg_entry));
|
|
|
|
/*
|
|
|
|
* load total xfer length (24bits) max value 16Mbyte
|
|
|
|
*/
|
|
|
|
bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, srb->buflen);
|
|
|
|
/* Start DMA transfer */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_COMMAND,
|
|
|
|
iodir | SGXFER);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL,
|
|
|
|
STARTDMAXFER);
|
|
|
|
|
|
|
|
/* Start SCSI transfer */
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND,
|
|
|
|
(iodir == XFERDATAOUT) ?
|
|
|
|
SCMD_DMA_OUT : SCMD_DMA_IN);
|
|
|
|
} else { /* xfer pad */
|
|
|
|
if (srb->sgcnt) {
|
|
|
|
srb->hastat = H_OVER_UNDER_RUN;
|
|
|
|
srb->srbstat |= OVER_RUN;
|
|
|
|
}
|
|
|
|
bus_space_write_4(iot, ioh, TRM_SCSI_XCNT,
|
|
|
|
(dcb->synctl & WIDE_SYNC) ? 2 : 1);
|
|
|
|
|
|
|
|
if (iodir == XFERDATAOUT)
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_FIFO, 0);
|
|
|
|
else
|
|
|
|
bus_space_read_2(iot, ioh, TRM_SCSI_FIFO);
|
|
|
|
|
|
|
|
srb->state |= SRB_XFERPAD;
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND,
|
|
|
|
(iodir == XFERDATAOUT) ?
|
|
|
|
SCMD_FIFO_OUT : SCMD_FIFO_IN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_status_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
srb->tastat = bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
|
|
|
|
srb->state = SRB_COMPLETED;
|
|
|
|
*pstat = PH_BUS_FREE; /* .. initial phase */
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_status_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
if (bus_space_read_1(iot, ioh, TRM_DMA_COMMAND) & XFERDATAIN) {
|
|
|
|
if ((bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT)
|
|
|
|
& SCSI_FIFO_EMPTY) == 0)
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
if ((bus_space_read_1(iot, ioh, TRM_DMA_FIFOSTATUS)
|
|
|
|
& DMA_FIFO_EMPTY) == 0)
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);
|
|
|
|
} else {
|
|
|
|
if ((bus_space_read_1(iot, ioh, TRM_DMA_FIFOSTATUS)
|
|
|
|
& DMA_FIFO_EMPTY) == 0)
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, CLRXFIFO);
|
|
|
|
if ((bus_space_read_1(iot, ioh, TRM_SCSI_FIFOCNT)
|
|
|
|
& SCSI_FIFO_EMPTY) == 0)
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
}
|
|
|
|
srb->state = SRB_STATUS;
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_COMP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_msgin_phase0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb = sc->sc_actdcb;
|
|
|
|
struct trm_srb *tempsrb;
|
|
|
|
int syncxfer, tagid, index;
|
|
|
|
u_int8_t msgin_code;
|
|
|
|
|
|
|
|
msgin_code = bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
|
|
|
|
if ((srb->state & SRB_EXTEND_MSGIN) == 0) {
|
|
|
|
if (msgin_code == MSG_DISCONNECT) {
|
|
|
|
srb->state = SRB_DISCONNECT;
|
|
|
|
goto min6;
|
|
|
|
} else if (msgin_code == MSG_SAVEDATAPOINTER) {
|
|
|
|
goto min6;
|
|
|
|
} else if ((msgin_code == MSG_EXTENDED) ||
|
|
|
|
((msgin_code >= MSG_SIMPLE_Q_TAG) &&
|
|
|
|
(msgin_code <= MSG_ORDERED_Q_TAG))) {
|
|
|
|
srb->state |= SRB_EXTEND_MSGIN;
|
|
|
|
/* extended message (01h) */
|
|
|
|
srb->msgin[0] = msgin_code;
|
|
|
|
|
|
|
|
srb->msgcnt = 1;
|
|
|
|
/* extended message length (n) */
|
|
|
|
srb->msg = &srb->msgin[1];
|
|
|
|
|
|
|
|
goto min6;
|
|
|
|
} else if (msgin_code == MSG_MESSAGE_REJECT) {
|
|
|
|
/* Reject message */
|
|
|
|
/* do wide nego reject */
|
|
|
|
if (dcb->mode & WIDE_NEGO_ENABLE) {
|
|
|
|
dcb = srb->dcb;
|
|
|
|
dcb->mode |= WIDE_NEGO_DONE;
|
|
|
|
dcb->mode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP |
|
|
|
|
WIDE_NEGO_ENABLE);
|
|
|
|
srb->state &= ~(SRB_DO_WIDE_NEGO | SRB_MSGIN);
|
|
|
|
if ((dcb->mode & SYNC_NEGO_ENABLE) &&
|
|
|
|
(dcb->mode & SYNC_NEGO_DONE) == 0) {
|
|
|
|
/* Set ATN, in case ATN was clear */
|
|
|
|
srb->state |= SRB_MSGOUT;
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_SETATN);
|
|
|
|
} else
|
|
|
|
/* Clear ATN */
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_CLRATN);
|
|
|
|
} else if (dcb->mode & SYNC_NEGO_ENABLE) {
|
|
|
|
/* do sync nego reject */
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_CLRATN);
|
|
|
|
if (srb->state & SRB_DO_SYNC_NEGO) {
|
|
|
|
dcb = srb->dcb;
|
|
|
|
dcb->mode &= ~(SYNC_NEGO_ENABLE |
|
|
|
|
SYNC_NEGO_DONE);
|
|
|
|
dcb->synctl = 0;
|
|
|
|
dcb->offset = 0;
|
|
|
|
goto re_prog;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto min6;
|
|
|
|
} else if (msgin_code == MSG_IGN_WIDE_RESIDUE) {
|
|
|
|
bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, 1);
|
|
|
|
bus_space_read_1(iot, ioh, TRM_SCSI_FIFO);
|
|
|
|
goto min6;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Restore data pointer message
|
|
|
|
* Save data pointer message
|
|
|
|
* Completion message
|
|
|
|
* NOP message
|
|
|
|
*/
|
|
|
|
goto min6;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* when extend message in:srb->state = SRB_EXTEND_MSGIN
|
|
|
|
* Parsing incomming extented messages
|
|
|
|
*/
|
|
|
|
*srb->msg = msgin_code;
|
|
|
|
srb->msgcnt++;
|
|
|
|
srb->msg++;
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("srb->msgin[0]=%2x\n", srb->msgin[0]);
|
|
|
|
printf("srb->msgin[1]=%2x\n", srb->msgin[1]);
|
|
|
|
printf("srb->msgin[2]=%2x\n", srb->msgin[2]);
|
|
|
|
printf("srb->msgin[3]=%2x\n", srb->msgin[3]);
|
|
|
|
printf("srb->msgin[4]=%2x\n", srb->msgin[4]);
|
|
|
|
#endif
|
|
|
|
if ((srb->msgin[0] >= MSG_SIMPLE_Q_TAG) &&
|
|
|
|
(srb->msgin[0] <= MSG_ORDERED_Q_TAG)) {
|
|
|
|
/*
|
|
|
|
* is QUEUE tag message :
|
|
|
|
*
|
|
|
|
* byte 0:
|
|
|
|
* HEAD QUEUE TAG (20h)
|
|
|
|
* ORDERED QUEUE TAG (21h)
|
|
|
|
* SIMPLE QUEUE TAG (22h)
|
|
|
|
* byte 1:
|
|
|
|
* Queue tag (00h - FFh)
|
|
|
|
*/
|
|
|
|
if (srb->msgcnt == 2) {
|
|
|
|
srb->state = 0;
|
|
|
|
tagid = srb->msgin[1];
|
|
|
|
srb = dcb->gosrb;
|
|
|
|
tempsrb = dcb->last_gosrb;
|
|
|
|
if (srb) {
|
|
|
|
for (;;) {
|
|
|
|
if (srb->tagnum != tagid) {
|
|
|
|
if (srb == tempsrb)
|
|
|
|
goto mingx0;
|
|
|
|
|
|
|
|
srb = srb->next;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dcb->flag & ABORT_DEV_) {
|
|
|
|
srb->state = SRB_ABORT_SENT;
|
|
|
|
srb->msgout[0] = MSG_ABORT;
|
|
|
|
trm_msgout_abort(sc, srb);
|
|
|
|
}
|
|
|
|
if ((srb->state & SRB_DISCONNECT) == 0)
|
|
|
|
goto mingx0;
|
|
|
|
|
|
|
|
dcb->actsrb = srb;
|
|
|
|
srb->state = SRB_DATA_XFER;
|
|
|
|
} else {
|
|
|
|
mingx0:
|
2001-11-04 20:17:22 +03:00
|
|
|
srb = &sc->sc_tempsrb;
|
2001-11-03 20:01:16 +03:00
|
|
|
srb->state = SRB_UNEXPECT_RESEL;
|
|
|
|
dcb->actsrb = srb;
|
|
|
|
srb->msgout[0] = MSG_ABORT_TAG;
|
|
|
|
trm_msgout_abort(sc, srb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if ((srb->msgin[0] == MSG_EXTENDED) &&
|
|
|
|
(srb->msgin[2] == MSG_EXT_WDTR) &&
|
|
|
|
(srb->msgcnt == 4)) {
|
|
|
|
/*
|
|
|
|
* is Wide data xfer Extended message :
|
|
|
|
* ======================================
|
|
|
|
* WIDE DATA TRANSFER REQUEST
|
|
|
|
* ======================================
|
|
|
|
* byte 0 : Extended message (01h)
|
|
|
|
* byte 1 : Extended message length (02h)
|
|
|
|
* byte 2 : WIDE DATA TRANSFER code (03h)
|
|
|
|
* byte 3 : Transfer width exponent
|
|
|
|
*/
|
|
|
|
dcb = srb->dcb;
|
|
|
|
srb->state &= ~(SRB_EXTEND_MSGIN | SRB_DO_WIDE_NEGO);
|
|
|
|
if ((srb->msgin[1] != MSG_EXT_WDTR_LEN)) {
|
|
|
|
/* Length is wrong, reject it */
|
|
|
|
dcb->mode &=
|
|
|
|
~(WIDE_NEGO_ENABLE | WIDE_NEGO_DONE);
|
|
|
|
srb->msgcnt = 1;
|
|
|
|
srb->msgin[0] = MSG_MESSAGE_REJECT;
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_SETATN);
|
|
|
|
goto min6;
|
|
|
|
}
|
|
|
|
if (dcb->mode & WIDE_NEGO_ENABLE) {
|
|
|
|
/* Do wide negoniation */
|
|
|
|
if (srb->msgin[3] > MSG_EXT_WDTR_BUS_32_BIT) {
|
|
|
|
/* reject_msg: */
|
|
|
|
dcb->mode &= ~(WIDE_NEGO_ENABLE |
|
|
|
|
WIDE_NEGO_DONE);
|
|
|
|
srb->msgcnt = 1;
|
|
|
|
srb->msgin[0] = MSG_MESSAGE_REJECT;
|
|
|
|
bus_space_write_2(iot, ioh,
|
|
|
|
TRM_SCSI_CONTROL, DO_SETATN);
|
|
|
|
goto min6;
|
|
|
|
}
|
|
|
|
if (srb->msgin[3] == MSG_EXT_WDTR_BUS_32_BIT)
|
|
|
|
/* do 16 bits */
|
|
|
|
srb->msgin[3] = MSG_EXT_WDTR_BUS_16_BIT;
|
|
|
|
else {
|
|
|
|
if ((dcb->mode & WIDE_NEGO_DONE) == 0) {
|
|
|
|
srb->state &=
|
|
|
|
~(SRB_DO_WIDE_NEGO |
|
|
|
|
SRB_MSGIN);
|
|
|
|
dcb->mode |= WIDE_NEGO_DONE;
|
|
|
|
dcb->mode &=
|
|
|
|
~(SYNC_NEGO_DONE |
|
|
|
|
EN_ATN_STOP |
|
|
|
|
WIDE_NEGO_ENABLE);
|
|
|
|
if (srb->msgin[3] !=
|
|
|
|
MSG_EXT_WDTR_BUS_8_BIT)
|
|
|
|
/* is Wide data xfer */
|
|
|
|
dcb->synctl |=
|
|
|
|
WIDE_SYNC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
srb->msgin[3] = MSG_EXT_WDTR_BUS_8_BIT;
|
|
|
|
|
|
|
|
srb->state |= SRB_MSGOUT;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_SETATN);
|
|
|
|
goto min6;
|
|
|
|
} else if ((srb->msgin[0] == MSG_EXTENDED) &&
|
|
|
|
(srb->msgin[2] == MSG_EXT_SDTR) &&
|
|
|
|
(srb->msgcnt == 5)) {
|
|
|
|
/*
|
|
|
|
* is 8bit transfer Extended message :
|
|
|
|
* =================================
|
|
|
|
* SYNCHRONOUS DATA TRANSFER REQUEST
|
|
|
|
* =================================
|
|
|
|
* byte 0 : Extended message (01h)
|
|
|
|
* byte 1 : Extended message length (03)
|
|
|
|
* byte 2 : SYNCHRONOUS DATA TRANSFER code (01h)
|
|
|
|
* byte 3 : Transfer period factor
|
|
|
|
* byte 4 : REQ/ACK offset
|
|
|
|
*/
|
|
|
|
srb->state &= ~(SRB_EXTEND_MSGIN | SRB_DO_SYNC_NEGO);
|
|
|
|
if (srb->msgin[1] != MSG_EXT_SDTR_LEN) {
|
|
|
|
/* reject_msg: */
|
|
|
|
srb->msgcnt = 1;
|
|
|
|
srb->msgin[0] = MSG_MESSAGE_REJECT;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_SETATN);
|
|
|
|
} else if (srb->msgin[3] == 0 || srb->msgin[4] == 0) {
|
|
|
|
/* set async */
|
|
|
|
dcb = srb->dcb;
|
|
|
|
/* disable sync & sync nego */
|
|
|
|
dcb->mode &=
|
|
|
|
~(SYNC_NEGO_ENABLE | SYNC_NEGO_DONE);
|
|
|
|
dcb->synctl = 0;
|
|
|
|
dcb->offset = 0;
|
|
|
|
if (((dcb->flag & SHOW_MESSAGE_) == 0) &&
|
|
|
|
(dcb->lun == 0)) {
|
|
|
|
printf("%s: target %d, Sync period=0 "
|
|
|
|
"or Sync offset=0 to be "
|
|
|
|
"asynchronous transfer\n",
|
|
|
|
sc->sc_dev.dv_xname, dcb->id);
|
|
|
|
dcb->flag |= SHOW_MESSAGE_;
|
|
|
|
}
|
|
|
|
goto re_prog;
|
|
|
|
} else { /* set sync */
|
|
|
|
dcb = srb->dcb;
|
|
|
|
dcb->mode |= SYNC_NEGO_ENABLE | SYNC_NEGO_DONE;
|
|
|
|
|
|
|
|
/* Transfer period factor */
|
|
|
|
dcb->period = srb->msgin[3];
|
|
|
|
|
|
|
|
/* REQ/ACK offset */
|
|
|
|
dcb->offset = srb->msgin[4];
|
|
|
|
for (index = 0; index < 7; index++)
|
|
|
|
if (srb->msgin[3] <=
|
|
|
|
trm_clock_period[index])
|
|
|
|
break;
|
|
|
|
|
|
|
|
dcb->synctl |= (index | ALT_SYNC);
|
|
|
|
/*
|
|
|
|
* show negotiation message
|
|
|
|
*/
|
|
|
|
if (((dcb->flag & SHOW_MESSAGE_) == 0) &&
|
|
|
|
(dcb->lun == 0)) {
|
|
|
|
syncxfer = 100000 /
|
|
|
|
(trm_clock_period[index] * 4);
|
|
|
|
if (dcb->synctl & WIDE_SYNC) {
|
|
|
|
printf("%s: target %d, "
|
|
|
|
"16bits Wide transfer\n",
|
|
|
|
sc->sc_dev.dv_xname,
|
|
|
|
dcb->id);
|
|
|
|
syncxfer = syncxfer * 2;
|
|
|
|
} else
|
|
|
|
printf("%s: target %d, "
|
|
|
|
"8bits Narrow transfer\n",
|
|
|
|
sc->sc_dev.dv_xname,
|
|
|
|
dcb->id);
|
|
|
|
|
|
|
|
printf("%s: target %d, "
|
|
|
|
"Sync transfer %d.%01d MB/sec, "
|
|
|
|
"Offset %d\n", sc->sc_dev.dv_xname,
|
|
|
|
dcb->id, syncxfer / 100,
|
|
|
|
syncxfer % 100, dcb->offset);
|
|
|
|
dcb->flag |= SHOW_MESSAGE_;
|
|
|
|
}
|
|
|
|
re_prog:
|
|
|
|
/*
|
|
|
|
* program SCSI control register
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_SYNC,
|
|
|
|
dcb->synctl);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET,
|
|
|
|
dcb->offset);
|
|
|
|
trm_set_xfer_rate(sc, srb, dcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
min6:
|
|
|
|
*pstat = PH_BUS_FREE; /* .. initial phase */
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_msgin_phase1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
bus_space_write_4(iot, ioh, TRM_SCSI_XCNT, 1);
|
|
|
|
if ((srb->state & SRB_MSGIN) == 0) {
|
|
|
|
srb->state &= SRB_DISCONNECT;
|
|
|
|
srb->state |= SRB_MSGIN;
|
|
|
|
}
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_FIFO_IN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_nop0(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_nop1(sc, srb, pstat)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int *pstat;
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_set_xfer_rate(sc, srb, dcb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
{
|
|
|
|
struct trm_dcb *tempdcb;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set all lun device's (period, offset)
|
|
|
|
*/
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_set_xfer_rate............\n");
|
|
|
|
#endif
|
|
|
|
if ((dcb->idmsg & 0x07) == 0) {
|
|
|
|
if (sc->devscan_end == 0)
|
|
|
|
sc->cur_offset = dcb->offset;
|
|
|
|
else {
|
|
|
|
tempdcb = sc->sc_linkdcb;
|
|
|
|
for (i = 0; i < sc->devcnt; i++) {
|
|
|
|
/*
|
|
|
|
* different LUN but had same target ID
|
|
|
|
*/
|
|
|
|
if (tempdcb->id == dcb->id) {
|
|
|
|
tempdcb->synctl = dcb->synctl;
|
|
|
|
tempdcb->offset = dcb->offset;
|
|
|
|
tempdcb->mode = dcb->mode;
|
|
|
|
}
|
|
|
|
tempdcb = tempdcb->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_disconnect(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb, *psrb;
|
|
|
|
int i, s;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_disconnect...............\n");
|
|
|
|
#endif
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
dcb = sc->sc_actdcb;
|
|
|
|
if (dcb == NULL) {
|
|
|
|
DELAY(1000); /* 1 msec */
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_CLRFIFO | DO_HWRESELECT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
srb = dcb->actsrb;
|
|
|
|
sc->sc_actdcb = 0;
|
|
|
|
srb->phase = PH_BUS_FREE; /* SCSI bus free Phase */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL,
|
|
|
|
DO_CLRFIFO | DO_HWRESELECT);
|
|
|
|
DELAY(100);
|
|
|
|
if (srb->state & SRB_UNEXPECT_RESEL) {
|
|
|
|
srb->state = 0;
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
} else if (srb->state & SRB_ABORT_SENT) {
|
|
|
|
dcb->tagmask = 0;
|
|
|
|
dcb->flag &= ~ABORT_DEV_;
|
|
|
|
srb = dcb->gosrb;
|
|
|
|
for (i = 0; i < dcb->gosrb_cnt; i++) {
|
|
|
|
psrb = srb->next;
|
|
|
|
srb->next = sc->sc_freesrb;
|
|
|
|
sc->sc_freesrb = srb;
|
|
|
|
srb = psrb;
|
|
|
|
}
|
|
|
|
dcb->gosrb_cnt = 0;
|
|
|
|
dcb->gosrb = 0;
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
} else {
|
|
|
|
if ((srb->state & (SRB_START_ | SRB_MSGOUT)) ||
|
|
|
|
(srb->state & (SRB_DISCONNECT | SRB_COMPLETED)) == 0) {
|
|
|
|
/* Selection time out */
|
|
|
|
if (sc->devscan_end) {
|
|
|
|
srb->state = SRB_READY;
|
|
|
|
trm_rewait_srb(dcb, srb);
|
|
|
|
} else {
|
|
|
|
srb->tastat = SCSI_SEL_TIMEOUT;
|
|
|
|
goto disc1;
|
|
|
|
}
|
|
|
|
} else if (srb->state & SRB_DISCONNECT) {
|
|
|
|
/*
|
|
|
|
* SRB_DISCONNECT
|
|
|
|
*/
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
} else if (srb->state & SRB_COMPLETED) {
|
|
|
|
disc1:
|
|
|
|
/*
|
|
|
|
* SRB_COMPLETED
|
|
|
|
*/
|
|
|
|
if (dcb->maxcmd > 1) {
|
|
|
|
/* free tag mask */
|
|
|
|
dcb->tagmask &= ~(1 << srb->tagnum);
|
|
|
|
}
|
|
|
|
dcb->actsrb = 0;
|
|
|
|
srb->state = SRB_FREE;
|
|
|
|
trm_srb_done(sc, dcb, srb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_reselect(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int id, lun;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_reselect.................\n");
|
|
|
|
#endif
|
|
|
|
dcb = sc->sc_actdcb;
|
|
|
|
if (dcb != NULL) { /* Arbitration lost but Reselection win */
|
|
|
|
srb = dcb->actsrb;
|
|
|
|
srb->state = SRB_READY;
|
|
|
|
trm_rewait_srb(dcb, srb);
|
|
|
|
}
|
|
|
|
/* Read Reselected Target Id and LUN */
|
|
|
|
id = bus_space_read_1(iot, ioh, TRM_SCSI_TARGETID);
|
|
|
|
lun = bus_space_read_1(iot, ioh, TRM_SCSI_IDMSG) & 0x07;
|
|
|
|
dcb = sc->sc_linkdcb;
|
|
|
|
while (id != dcb->id && lun != dcb->lun)
|
|
|
|
/* get dcb of the reselect id */
|
|
|
|
dcb = dcb->next;
|
|
|
|
|
|
|
|
sc->sc_actdcb = dcb;
|
|
|
|
if (dcb->mode & EN_TAG_QUEUING) {
|
2001-11-04 20:17:22 +03:00
|
|
|
srb = &sc->sc_tempsrb;
|
2001-11-03 20:01:16 +03:00
|
|
|
dcb->actsrb = srb;
|
|
|
|
} else {
|
|
|
|
srb = dcb->actsrb;
|
|
|
|
if (srb == NULL || (srb->state & SRB_DISCONNECT) == 0) {
|
|
|
|
/*
|
|
|
|
* abort command
|
|
|
|
*/
|
2001-11-04 20:17:22 +03:00
|
|
|
srb = &sc->sc_tempsrb;
|
2001-11-03 20:01:16 +03:00
|
|
|
srb->state = SRB_UNEXPECT_RESEL;
|
|
|
|
dcb->actsrb = srb;
|
|
|
|
srb->msgout[0] = MSG_ABORT;
|
|
|
|
trm_msgout_abort(sc, srb);
|
|
|
|
} else {
|
|
|
|
if (dcb->flag & ABORT_DEV_) {
|
|
|
|
srb->state = SRB_ABORT_SENT;
|
|
|
|
srb->msgout[0] = MSG_ABORT;
|
|
|
|
trm_msgout_abort(sc, srb);
|
|
|
|
} else
|
|
|
|
srb->state = SRB_DATA_XFER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
srb->phase = PH_BUS_FREE; /* SCSI bus free Phase */
|
|
|
|
/*
|
|
|
|
* Program HA ID, target ID, period and offset
|
|
|
|
*/
|
|
|
|
/* target ID */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_TARGETID, id);
|
|
|
|
|
|
|
|
/* host ID */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);
|
|
|
|
|
|
|
|
/* period */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_SYNC, dcb->synctl);
|
|
|
|
|
|
|
|
/* offset */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, dcb->offset);
|
|
|
|
|
|
|
|
/* it's important for atn stop */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_DATALATCH);
|
|
|
|
DELAY(30);
|
|
|
|
/*
|
|
|
|
* SCSI cammand
|
|
|
|
*/
|
|
|
|
/* to rls the /ACK signal */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_COMMAND, SCMD_MSGACCEPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Complete execution of a SCSI command
|
|
|
|
* Signal completion to the generic SCSI driver
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_srb_done(sc, dcb, srb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
struct scsipi_xfer *xs = srb->xs;
|
|
|
|
struct scsipi_inquiry_data *ptr;
|
|
|
|
struct trm_dcb *tempdcb;
|
2001-11-18 16:02:30 +03:00
|
|
|
int i, j, id, lun, s;
|
2001-11-03 20:01:16 +03:00
|
|
|
u_int8_t bval;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_srb_done..................\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (xs == NULL)
|
|
|
|
return;
|
|
|
|
|
2001-11-18 16:02:30 +03:00
|
|
|
if ((xs->xs_control & XS_CTL_POLL) == 0)
|
|
|
|
callout_stop(&xs->xs_callout);
|
2001-11-03 20:01:16 +03:00
|
|
|
|
2001-11-18 16:02:30 +03:00
|
|
|
if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT) ||
|
|
|
|
srb->flag & AUTO_REQSENSE) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
|
|
|
|
srb->dmap->dm_mapsize,
|
|
|
|
((xs->xs_control & XS_CTL_DATA_IN) ||
|
|
|
|
(srb->flag & AUTO_REQSENSE)) ?
|
|
|
|
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, srb->dmap);
|
2001-11-03 20:01:16 +03:00
|
|
|
}
|
2001-11-18 16:02:30 +03:00
|
|
|
|
2001-11-03 20:01:16 +03:00
|
|
|
/*
|
|
|
|
* target status
|
|
|
|
*/
|
2001-11-18 16:02:30 +03:00
|
|
|
xs->status = srb->tastat;
|
|
|
|
|
|
|
|
switch (xs->status) {
|
|
|
|
case SCSI_OK:
|
2001-11-03 20:01:16 +03:00
|
|
|
/*
|
|
|
|
* process initiator status......
|
|
|
|
* Adapter (initiator) status
|
|
|
|
*/
|
|
|
|
if (srb->hastat & H_OVER_UNDER_RUN) {
|
|
|
|
srb->tastat = 0;
|
|
|
|
/* Illegal length (over/under run) */
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
} else if (srb->srbstat & PARITY_ERROR) {
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("%s: driver stuffup at %s %d\n",
|
|
|
|
sc->sc_dev.dv_xname, __FILE__, __LINE__);
|
|
|
|
#endif
|
|
|
|
/* Driver failed to perform operation */
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
2001-11-18 16:02:30 +03:00
|
|
|
} else {
|
|
|
|
/* No error */
|
2001-11-03 20:01:16 +03:00
|
|
|
srb->hastat = 0;
|
2001-11-18 16:02:30 +03:00
|
|
|
if (srb->flag & AUTO_REQSENSE) {
|
|
|
|
/* there is no error, (sense is invalid) */
|
|
|
|
xs->error = XS_SENSE;
|
|
|
|
} else {
|
|
|
|
srb->tastat = 0;
|
|
|
|
xs->error = XS_NOERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_CHECK:
|
|
|
|
if ((srb->flag & AUTO_REQSENSE) != 0 ||
|
|
|
|
trm_request_sense(sc, dcb, srb) != 0) {
|
|
|
|
printf("%s: request sense failed\n",
|
|
|
|
sc->sc_dev.dv_xname);
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
2001-11-03 20:01:16 +03:00
|
|
|
}
|
2001-11-18 16:02:30 +03:00
|
|
|
xs->error = XS_SENSE;
|
|
|
|
return;
|
|
|
|
case SCSI_QUEUE_FULL:
|
|
|
|
dcb->maxcmd = dcb->gosrb_cnt - 1;
|
|
|
|
trm_rewait_srb(dcb, srb);
|
|
|
|
srb->hastat = 0;
|
|
|
|
srb->tastat = 0;
|
|
|
|
break;
|
|
|
|
case SCSI_SEL_TIMEOUT:
|
|
|
|
srb->hastat = H_SEL_TIMEOUT;
|
|
|
|
srb->tastat = 0;
|
|
|
|
xs->error = XS_TIMEOUT;
|
|
|
|
break;
|
|
|
|
case SCSI_BUSY:
|
|
|
|
xs->error = XS_BUSY;
|
|
|
|
break;
|
|
|
|
case SCSI_RESV_CONFLICT:
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("%s: target reserved at ", sc->sc_dev.dv_xname);
|
|
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
|
|
#endif
|
|
|
|
xs->error = XS_BUSY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
srb->hastat = 0;
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("%s: driver stuffup at %s %d\n",
|
|
|
|
sc->sc_dev.dv_xname, __FILE__, __LINE__);
|
|
|
|
#endif
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
2001-11-03 20:01:16 +03:00
|
|
|
}
|
2001-11-18 16:02:30 +03:00
|
|
|
|
2001-11-03 20:01:16 +03:00
|
|
|
id = srb->xs->xs_periph->periph_target;
|
|
|
|
lun = srb->xs->xs_periph->periph_lun;
|
|
|
|
if (sc->devscan[id][lun]) {
|
|
|
|
/*
|
|
|
|
* if SCSI command in "scan devices" duty
|
2001-11-18 16:02:30 +03:00
|
|
|
* XXX XXX XXX should not be done here! XXX XXX XXX
|
2001-11-03 20:01:16 +03:00
|
|
|
*/
|
2001-11-18 16:02:30 +03:00
|
|
|
if (srb->cmd[0] == INQUIRY) {
|
2001-11-03 20:01:16 +03:00
|
|
|
/*
|
|
|
|
* SCSI command phase: inquiry scsi device data
|
|
|
|
* (type,capacity,manufacture....
|
|
|
|
*/
|
|
|
|
if (xs->error == XS_TIMEOUT)
|
|
|
|
goto NO_DEV;
|
|
|
|
|
|
|
|
ptr = (struct scsipi_inquiry_data *)xs->data;
|
|
|
|
bval = ptr->device & SID_TYPE;
|
2001-11-18 16:02:30 +03:00
|
|
|
|
2001-11-03 20:01:16 +03:00
|
|
|
if (bval == T_NODEVICE) {
|
|
|
|
NO_DEV:
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_srb_done NO Device: ");
|
|
|
|
printf("id= %d ,lun= %d\n", id, lun);
|
|
|
|
#endif
|
|
|
|
s = splbio();
|
|
|
|
/*
|
|
|
|
* dcb Q link
|
|
|
|
* move the head of DCB to temdcb
|
|
|
|
*/
|
|
|
|
tempdcb = sc->sc_linkdcb;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search current DCB for pass link
|
|
|
|
*/
|
|
|
|
while (tempdcb->next != dcb)
|
|
|
|
tempdcb = tempdcb->next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* when the current DCB been found
|
|
|
|
* than connect current DCB tail
|
|
|
|
* to the DCB tail that before current DCB
|
|
|
|
*/
|
|
|
|
tempdcb->next = dcb->next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if there was only one DCB ,connect his
|
|
|
|
* tail to his head
|
|
|
|
*/
|
|
|
|
if (sc->sc_linkdcb == dcb)
|
|
|
|
sc->sc_linkdcb = tempdcb->next;
|
|
|
|
|
|
|
|
if (sc->sc_roundcb == dcb)
|
|
|
|
sc->sc_roundcb = tempdcb->next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if no device than free this device DCB
|
|
|
|
* free( dcb, M_DEVBUF);
|
|
|
|
*/
|
|
|
|
sc->devcnt--;
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("sc->devcnt=%d\n", sc->devcnt);
|
|
|
|
#endif
|
|
|
|
if (sc->devcnt == 0) {
|
|
|
|
sc->sc_linkdcb = NULL;
|
|
|
|
sc->sc_roundcb = NULL;
|
|
|
|
}
|
|
|
|
/* no device set scan device flag=0 */
|
|
|
|
sc->devscan[id][lun] = 0;
|
|
|
|
i = 0;
|
|
|
|
j = 0;
|
|
|
|
while (i <= sc->maxid) {
|
|
|
|
while (j < 8) {
|
|
|
|
if (sc->devscan[i][j] == 1) {
|
|
|
|
sc->devscan_end = 0;
|
|
|
|
splx(s);
|
|
|
|
goto exit;
|
|
|
|
} else
|
|
|
|
sc->devscan_end = 1;
|
|
|
|
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
j = 0;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
} else {
|
|
|
|
if (bval == T_DIRECT || bval == T_OPTICAL) {
|
|
|
|
if ((((ptr->version & 0x07) >= 2) ||
|
|
|
|
((ptr->response_format & 0x0F)
|
|
|
|
== 2)) &&
|
|
|
|
(ptr->flags3 & SID_CmdQue) &&
|
|
|
|
(dcb->tacfg & NTC_DO_TAG_QUEUING) &&
|
|
|
|
(dcb->tacfg & NTC_DO_DISCONNECT)) {
|
|
|
|
dcb->maxcmd = sc->maxtag;
|
|
|
|
dcb->mode |= EN_TAG_QUEUING;
|
|
|
|
dcb->tagmask = 0;
|
|
|
|
} else
|
|
|
|
dcb->mode |= EN_ATN_STOP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
trm_release_srb(sc, dcb, srb);
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
xs->xs_status |= XS_STS_DONE;
|
|
|
|
/* Notify cmd done */
|
|
|
|
scsipi_done(xs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_release_srb(sc, dcb, srb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
struct trm_srb *psrb;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (srb == dcb->gosrb)
|
|
|
|
dcb->gosrb = srb->next;
|
|
|
|
else {
|
|
|
|
psrb = dcb->gosrb;
|
|
|
|
while (psrb->next != srb)
|
|
|
|
psrb = psrb->next;
|
|
|
|
|
|
|
|
psrb->next = srb->next;
|
|
|
|
if (srb == dcb->last_gosrb)
|
|
|
|
dcb->last_gosrb = psrb;
|
|
|
|
}
|
|
|
|
srb->next = sc->sc_freesrb;
|
|
|
|
sc->sc_freesrb = srb;
|
|
|
|
dcb->gosrb_cnt--;
|
|
|
|
splx(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_doing_srb_done(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
struct trm_dcb *dcb, *pdcb;
|
|
|
|
struct trm_srb *psrb, *psrb2;
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dcb = sc->sc_linkdcb;
|
|
|
|
if (dcb == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pdcb = dcb;
|
|
|
|
do {
|
|
|
|
psrb = pdcb->gosrb;
|
|
|
|
for (i = 0; i < pdcb->gosrb_cnt; i++) {
|
|
|
|
psrb2 = psrb->next;
|
|
|
|
xs = psrb->xs;
|
|
|
|
xs->error = XS_TIMEOUT;
|
|
|
|
/* ReleaseSRB( dcb, srb ); */
|
|
|
|
psrb->next = sc->sc_freesrb;
|
|
|
|
sc->sc_freesrb = psrb;
|
|
|
|
scsipi_done(xs);
|
|
|
|
psrb = psrb2;
|
|
|
|
}
|
|
|
|
pdcb->gosrb_cnt = 0;;
|
|
|
|
pdcb->gosrb = NULL;
|
|
|
|
pdcb->tagmask = 0;
|
|
|
|
pdcb = pdcb->next;
|
|
|
|
}
|
|
|
|
while (pdcb != dcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_reset_scsi_bus(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
sc->sc_flag |= RESET_DEV;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_RSTSCSI);
|
|
|
|
while ((bus_space_read_2(iot, ioh, TRM_SCSI_INTSTATUS) &
|
|
|
|
INT_SCSIRESET) == 0)
|
|
|
|
;
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_scsi_reset_detect(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("trm_scsi_reset_detect...............\n");
|
|
|
|
#endif
|
|
|
|
DELAY(1000000); /* delay 1 sec */
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_CONTROL, STOPDMAXFER);
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_CLRFIFO);
|
|
|
|
|
|
|
|
if (sc->sc_flag & RESET_DEV) {
|
|
|
|
sc->sc_flag |= RESET_DONE;
|
|
|
|
} else {
|
|
|
|
sc->sc_flag |= RESET_DETECT;
|
|
|
|
trm_reset_device(sc);
|
|
|
|
/* trm_doing_srb_done( sc ); ???? */
|
|
|
|
trm_recover_srb(sc);
|
|
|
|
sc->sc_actdcb = NULL;
|
|
|
|
sc->sc_flag = 0;
|
|
|
|
trm_wait_srb(sc);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2001-11-18 16:02:30 +03:00
|
|
|
static int
|
2001-11-03 20:01:16 +03:00
|
|
|
trm_request_sense(sc, dcb, srb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
struct scsipi_xfer *xs = srb->xs;
|
|
|
|
struct scsipi_sense *ss;
|
|
|
|
int error, lun = xs->xs_periph->periph_lun;
|
|
|
|
|
|
|
|
srb->flag |= AUTO_REQSENSE;
|
|
|
|
|
|
|
|
/* Status of initiator/target */
|
|
|
|
srb->hastat = 0;
|
|
|
|
srb->tastat = 0;
|
|
|
|
|
|
|
|
ss = (struct scsipi_sense *)srb->cmd;
|
|
|
|
ss->opcode = REQUEST_SENSE;
|
|
|
|
ss->byte2 = lun << SCSI_CMD_LUN_SHIFT;
|
|
|
|
ss->unused[0] = ss->unused[1] = 0;
|
|
|
|
ss->length = sizeof(struct scsipi_sense_data);
|
|
|
|
ss->control = 0;
|
|
|
|
|
|
|
|
srb->buflen = sizeof(struct scsipi_sense_data);
|
|
|
|
srb->sgcnt = 1;
|
|
|
|
srb->sgindex = 0;
|
|
|
|
srb->cmdlen = sizeof(struct scsipi_sense);
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, srb->dmap,
|
|
|
|
&xs->sense.scsi_sense, srb->buflen, NULL,
|
|
|
|
BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) {
|
2001-11-18 16:02:30 +03:00
|
|
|
return error;
|
2001-11-03 20:01:16 +03:00
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, srb->dmap, 0,
|
|
|
|
srb->buflen, BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
srb->sgentry[0].address = htole32(srb->dmap->dm_segs[0].ds_addr);
|
|
|
|
srb->sgentry[0].length = htole32(sizeof(struct scsipi_sense_data));
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, srb->sgoffset,
|
|
|
|
TRM_SG_SIZE, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
if (trm_start_scsi(sc, dcb, srb))
|
|
|
|
/*
|
|
|
|
* If trm_start_scsi return 1: current interrupt status
|
|
|
|
* is interrupt disreenable. It's said that SCSI processor
|
|
|
|
* has more one SRB need to do.
|
|
|
|
*/
|
|
|
|
trm_rewait_srb(dcb, srb);
|
2001-11-18 16:02:30 +03:00
|
|
|
return 0;
|
2001-11-03 20:01:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_msgout_abort(sc, srb)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_srb *srb;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
srb->msgcnt = 1;
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_SETATN);
|
|
|
|
srb->dcb->flag &= ~ABORT_DEV_;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the internal structures for a given DCB
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_init_dcb(sc, dcb, xs)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_dcb *dcb;
|
|
|
|
struct scsipi_xfer *xs;
|
|
|
|
{
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
struct trm_dcb *tempdcb;
|
|
|
|
int index, id, lun, s;
|
|
|
|
|
|
|
|
id = xs->xs_periph->periph_target;
|
|
|
|
lun = xs->xs_periph->periph_lun;
|
|
|
|
|
|
|
|
s = splbio();
|
|
|
|
if (sc->sc_linkdcb == 0) {
|
|
|
|
sc->sc_linkdcb = dcb;
|
|
|
|
/*
|
|
|
|
* RunRobin impersonate the role that let each device had
|
|
|
|
* good proportion about SCSI command proceeding.
|
|
|
|
*/
|
|
|
|
sc->sc_roundcb = dcb;
|
|
|
|
dcb->next = dcb;
|
|
|
|
} else {
|
|
|
|
tempdcb = sc->sc_linkdcb;
|
|
|
|
/* search the last nod of DCB link */
|
|
|
|
while (tempdcb->next != sc->sc_linkdcb)
|
|
|
|
tempdcb = tempdcb->next;
|
|
|
|
|
|
|
|
/* connect current DCB with last DCB tail */
|
|
|
|
tempdcb->next = dcb;
|
|
|
|
/* connect current DCB tail to this DCB Q head */
|
|
|
|
dcb->next = sc->sc_linkdcb;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
sc->devcnt++;
|
|
|
|
dcb->id = id;
|
|
|
|
dcb->lun = lun;
|
|
|
|
dcb->waitsrb = NULL;
|
|
|
|
dcb->gosrb = NULL;
|
|
|
|
dcb->gosrb_cnt = 0;
|
|
|
|
dcb->actsrb = NULL;
|
|
|
|
dcb->tagmask = 0;
|
|
|
|
dcb->maxcmd = 1;
|
|
|
|
dcb->flag = 0;
|
|
|
|
|
|
|
|
eeprom = &sc->sc_eeprom;
|
|
|
|
dcb->tacfg = eeprom->target[id].config0;
|
|
|
|
/*
|
|
|
|
* disconnect enable?
|
|
|
|
*/
|
|
|
|
dcb->idmsg = MSG_IDENTIFY(lun, dcb->tacfg & NTC_DO_DISCONNECT);
|
|
|
|
/*
|
|
|
|
* tag Qing enable?
|
|
|
|
* wide nego, sync nego enable?
|
|
|
|
*/
|
|
|
|
dcb->synctl = 0;
|
|
|
|
dcb->offset = 0;
|
|
|
|
index = eeprom->target[id].period & 0x07;
|
|
|
|
dcb->period = trm_clock_period[index];
|
|
|
|
dcb->mode = 0;
|
|
|
|
if ((dcb->tacfg & NTC_DO_WIDE_NEGO) && (sc->sc_config & HCC_WIDE_CARD))
|
|
|
|
/* enable wide nego */
|
|
|
|
dcb->mode |= WIDE_NEGO_ENABLE;
|
|
|
|
|
|
|
|
if ((dcb->tacfg & NTC_DO_SYNC_NEGO) && (lun == 0 || sc->cur_offset > 0))
|
|
|
|
/* enable sync nego */
|
|
|
|
dcb->mode |= SYNC_NEGO_ENABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
trm_link_srb(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
struct trm_srb *srb;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sc->sc_srb = malloc(sizeof(struct trm_srb) * TRM_MAX_SRB,
|
2001-12-16 07:17:02 +03:00
|
|
|
M_DEVBUF, M_NOWAIT|M_ZERO);
|
2001-11-03 20:01:16 +03:00
|
|
|
if (sc->sc_srb == NULL) {
|
|
|
|
printf("%s: can not allocate SRB\n", sc->sc_dev.dv_xname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, srb = sc->sc_srb; i < TRM_MAX_SRB; i++, srb++) {
|
|
|
|
srb->sgentry = sc->sc_sglist + TRM_MAX_SG_ENTRIES * i;
|
|
|
|
srb->sgoffset = TRM_SG_SIZE * i;
|
|
|
|
srb->sgaddr = sc->sc_dmamap->dm_segs[0].ds_addr + srb->sgoffset;
|
|
|
|
/*
|
|
|
|
* map all SRB space to SRB_array
|
|
|
|
*/
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat,
|
|
|
|
MAXPHYS, TRM_MAX_SG_ENTRIES, MAXPHYS, 0,
|
|
|
|
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &srb->dmap)) {
|
|
|
|
printf("%s: unable to create DMA transfer map...\n",
|
|
|
|
sc->sc_dev.dv_xname);
|
|
|
|
free(sc->sc_srb, M_DEVBUF);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (i != TRM_MAX_SRB - 1) {
|
|
|
|
/*
|
|
|
|
* link all SRB
|
|
|
|
*/
|
2001-11-04 20:17:22 +03:00
|
|
|
srb->next = srb + 1;
|
2001-11-03 20:01:16 +03:00
|
|
|
#ifdef TRM_DEBUG
|
2001-11-04 20:17:22 +03:00
|
|
|
printf("srb->next = %8x ", (int) (srb + 1));
|
2001-11-03 20:01:16 +03:00
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* load NULL to NextSRB of the last SRB
|
|
|
|
*/
|
|
|
|
srb->next = NULL;
|
|
|
|
}
|
|
|
|
#ifdef TRM_DEBUG
|
2001-11-04 20:17:22 +03:00
|
|
|
printf("srb = %8x\n", (int) srb);
|
2001-11-03 20:01:16 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the internal structures for a given SCSI host
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_init_sc(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
eeprom = &sc->sc_eeprom;
|
|
|
|
sc->maxid = 7;
|
|
|
|
sc->sc_config = HCC_AUTOTERM | HCC_PARITY;
|
|
|
|
if (bus_space_read_1(iot, ioh, TRM_GEN_STATUS) & WIDESCSI) {
|
|
|
|
sc->sc_config |= HCC_WIDE_CARD;
|
|
|
|
sc->maxid = 15;
|
|
|
|
}
|
|
|
|
if (eeprom->channel_cfg & NAC_POWERON_SCSI_RESET)
|
|
|
|
sc->sc_config |= HCC_SCSI_RESET;
|
|
|
|
|
|
|
|
sc->sc_linkdcb = NULL;
|
|
|
|
sc->sc_roundcb = NULL;
|
|
|
|
sc->sc_actdcb = NULL;
|
|
|
|
sc->sc_id = eeprom->scsi_id;
|
|
|
|
sc->devcnt = 0;
|
|
|
|
sc->maxtag = 2 << eeprom->max_tag;
|
|
|
|
sc->sc_flag = 0;
|
|
|
|
sc->devscan_end = 0;
|
|
|
|
/*
|
|
|
|
* link all device's SRB Q of this adapter
|
|
|
|
*/
|
|
|
|
trm_link_srb(sc);
|
|
|
|
sc->sc_freesrb = sc->sc_srb;
|
|
|
|
/* allocate DCB array for scan device */
|
|
|
|
for (i = 0; i <= sc->maxid; i++)
|
|
|
|
if (sc->sc_id != i)
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
sc->devscan[i][j] = 1;
|
|
|
|
sc->devflag[i][j] = 0;
|
|
|
|
sc->sc_dcb[i][j] =
|
|
|
|
malloc(sizeof(struct trm_dcb),
|
|
|
|
M_DEVBUF, M_WAITOK);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("sizeof(struct trm_dcb)= %8x\n", sizeof(struct trm_dcb));
|
|
|
|
printf("sizeof(struct trm_softc)= %8x\n", sizeof(struct trm_softc));
|
|
|
|
printf("sizeof(struct trm_srb)= %8x\n", sizeof(struct trm_srb));
|
|
|
|
#endif
|
|
|
|
sc->sc_adapter.adapt_dev = &sc->sc_dev;
|
|
|
|
sc->sc_adapter.adapt_nchannels = 1;
|
|
|
|
sc->sc_adapter.adapt_openings = TRM_MAX_SRB;
|
|
|
|
sc->sc_adapter.adapt_max_periph = TRM_MAX_SRB;
|
|
|
|
sc->sc_adapter.adapt_request = trm_scsipi_request;
|
|
|
|
sc->sc_adapter.adapt_minphys = minphys;
|
|
|
|
|
|
|
|
sc->sc_channel.chan_adapter = &sc->sc_adapter;
|
|
|
|
sc->sc_channel.chan_bustype = &scsi_bustype;
|
|
|
|
sc->sc_channel.chan_channel = 0;
|
|
|
|
sc->sc_channel.chan_ntargets = sc->maxid + 1;
|
|
|
|
sc->sc_channel.chan_nluns = 8;
|
|
|
|
sc->sc_channel.chan_id = sc->sc_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write sc_eeprom 128 bytes to seeprom
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_eeprom_write_all(sc, eeprom)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
u_int8_t *buf = (u_int8_t *)eeprom;
|
|
|
|
u_int8_t addr;
|
|
|
|
|
|
|
|
/* Enable SEEPROM */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
|
|
|
|
bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) | EN_EEPROM);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write enable
|
|
|
|
*/
|
|
|
|
trm_eeprom_write_cmd(sc, 0x04, 0xFF);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
|
|
|
|
trm_wait_30us();
|
|
|
|
|
|
|
|
for (addr = 0; addr < 128; addr++, buf++)
|
|
|
|
trm_eeprom_set_data(sc, addr, *buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write disable
|
|
|
|
*/
|
|
|
|
trm_eeprom_write_cmd(sc, 0x04, 0x00);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
|
|
|
|
trm_wait_30us();
|
|
|
|
|
|
|
|
/* Disable SEEPROM */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
|
|
|
|
bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) & ~EN_EEPROM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write one byte to seeprom
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_eeprom_set_data(sc, addr, data)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
u_int8_t addr;
|
|
|
|
u_int8_t data;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int i;
|
|
|
|
u_int8_t send;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send write command & address
|
|
|
|
*/
|
|
|
|
trm_eeprom_write_cmd(sc, 0x05, addr);
|
|
|
|
/*
|
|
|
|
* Write data
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 8; i++, data <<= 1) {
|
|
|
|
send = NVR_SELECT;
|
|
|
|
if (data & 0x80) /* Start from bit 7 */
|
|
|
|
send |= NVR_BITOUT;
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
|
|
|
|
trm_wait_30us();
|
|
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
|
|
|
|
trm_wait_30us();
|
|
|
|
/*
|
|
|
|
* Disable chip select
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
|
|
|
|
trm_wait_30us();
|
|
|
|
/*
|
|
|
|
* Wait for write ready
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM,
|
|
|
|
NVR_SELECT | NVR_CLOCK);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
|
|
|
|
trm_wait_30us();
|
|
|
|
if (bus_space_read_1(iot, ioh, TRM_GEN_NVRAM) & NVR_BITIN)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Disable chip select
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read seeprom 128 bytes to sc_eeprom
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_eeprom_read_all(sc, eeprom)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
u_int8_t *buf = (u_int8_t *)eeprom;
|
|
|
|
u_int8_t addr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable SEEPROM
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
|
|
|
|
bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) | EN_EEPROM);
|
|
|
|
|
|
|
|
for (addr = 0; addr < 128; addr++, buf++)
|
|
|
|
*buf = trm_eeprom_get_data(sc, addr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable SEEPROM
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_CONTROL,
|
|
|
|
bus_space_read_1(iot, ioh, TRM_GEN_CONTROL) & ~EN_EEPROM);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read one byte from seeprom
|
|
|
|
*/
|
|
|
|
static u_int8_t
|
|
|
|
trm_eeprom_get_data(sc, addr)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
u_int8_t addr;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int i;
|
|
|
|
u_int8_t read, data = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send read command & address
|
|
|
|
*/
|
|
|
|
trm_eeprom_write_cmd(sc, 0x06, addr);
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) { /* Read data */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM,
|
|
|
|
NVR_SELECT | NVR_CLOCK);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
|
|
|
|
/*
|
|
|
|
* Get data bit while falling edge
|
|
|
|
*/
|
|
|
|
read = bus_space_read_1(iot, ioh, TRM_GEN_NVRAM);
|
|
|
|
data <<= 1;
|
|
|
|
if (read & NVR_BITIN)
|
|
|
|
data |= 1;
|
|
|
|
|
|
|
|
trm_wait_30us();
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Disable chip select
|
|
|
|
*/
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, 0);
|
|
|
|
return (data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write SB and Op Code into seeprom
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_eeprom_write_cmd(sc, cmd, addr)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
u_int8_t cmd;
|
|
|
|
u_int8_t addr;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int i;
|
|
|
|
u_int8_t send;
|
|
|
|
|
|
|
|
/* Program SB+OP code */
|
|
|
|
for (i = 0; i < 3; i++, cmd <<= 1) {
|
|
|
|
send = NVR_SELECT;
|
|
|
|
if (cmd & 0x04) /* Start from bit 2 */
|
|
|
|
send |= NVR_BITOUT;
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
|
|
|
|
trm_wait_30us();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Program address */
|
|
|
|
for (i = 0; i < 7; i++, addr <<= 1) {
|
|
|
|
send = NVR_SELECT;
|
|
|
|
if (addr & 0x40) /* Start from bit 6 */
|
|
|
|
send |= NVR_BITOUT;
|
|
|
|
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send);
|
|
|
|
trm_wait_30us();
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, send | NVR_CLOCK);
|
|
|
|
trm_wait_30us();
|
|
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, TRM_GEN_NVRAM, NVR_SELECT);
|
|
|
|
trm_wait_30us();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* read seeprom 128 bytes to sc_eeprom and check checksum.
|
|
|
|
* If it is wrong, updated with default value.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_check_eeprom(sc, eeprom)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
struct trm_nvram *eeprom;
|
|
|
|
{
|
|
|
|
struct nvram_target *target;
|
|
|
|
u_int16_t *ep;
|
|
|
|
u_int16_t chksum;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("\n trm_check_eeprom......\n");
|
|
|
|
#endif
|
|
|
|
trm_eeprom_read_all(sc, eeprom);
|
|
|
|
ep = (u_int16_t *)eeprom;
|
|
|
|
chksum = 0;
|
|
|
|
for (i = 0; i < 64; i++)
|
|
|
|
chksum += le16toh(*ep++);
|
|
|
|
|
|
|
|
if (chksum != TRM_NVRAM_CKSUM) {
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("TRM_S1040 EEPROM Check Sum ERROR (load default).\n");
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* Checksum error, load default
|
|
|
|
*/
|
|
|
|
eeprom->subvendor_id[0] = PCI_VENDOR_TEKRAM2 & 0xFF;
|
|
|
|
eeprom->subvendor_id[1] = PCI_VENDOR_TEKRAM2 >> 8;
|
|
|
|
eeprom->subsys_id[0] = PCI_PRODUCT_TEKRAM2_DC315 & 0xFF;
|
|
|
|
eeprom->subsys_id[1] = PCI_PRODUCT_TEKRAM2_DC315 >> 8;
|
|
|
|
eeprom->subclass = 0x00;
|
|
|
|
eeprom->vendor_id[0] = PCI_VENDOR_TEKRAM2 & 0xFF;
|
|
|
|
eeprom->vendor_id[1] = PCI_VENDOR_TEKRAM2 >> 8;
|
|
|
|
eeprom->device_id[0] = PCI_PRODUCT_TEKRAM2_DC315 & 0xFF;
|
|
|
|
eeprom->device_id[1] = PCI_PRODUCT_TEKRAM2_DC315 >> 8;
|
|
|
|
eeprom->reserved0 = 0x00;
|
|
|
|
|
|
|
|
for (i = 0, target = eeprom->target;
|
|
|
|
i < TRM_MAX_TARGETS;
|
|
|
|
i++, target++) {
|
|
|
|
target->config0 = 0x77;
|
|
|
|
target->period = 0x00;
|
|
|
|
target->config2 = 0x00;
|
|
|
|
target->config3 = 0x00;
|
|
|
|
}
|
|
|
|
|
|
|
|
eeprom->scsi_id = 7;
|
|
|
|
eeprom->channel_cfg = 0x0F;
|
|
|
|
eeprom->delay_time = 0;
|
|
|
|
eeprom->max_tag = 4;
|
|
|
|
eeprom->reserved1 = 0x15;
|
|
|
|
eeprom->boot_target = 0;
|
|
|
|
eeprom->boot_lun = 0;
|
|
|
|
eeprom->reserved2 = 0;
|
|
|
|
memset(eeprom->reserved3, 0, sizeof(eeprom->reserved3));
|
|
|
|
|
|
|
|
chksum = 0;
|
|
|
|
ep = (u_int16_t *)eeprom;
|
|
|
|
for (i = 0; i < 63; i++)
|
|
|
|
chksum += le16toh(*ep++);
|
|
|
|
|
|
|
|
chksum = TRM_NVRAM_CKSUM - chksum;
|
|
|
|
eeprom->checksum0 = chksum & 0xFF;
|
|
|
|
eeprom->checksum1 = chksum >> 8;
|
|
|
|
|
|
|
|
trm_eeprom_write_all(sc, eeprom);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the SCSI chip ctrl registers
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_init_adapter(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
u_int8_t bval;
|
|
|
|
|
|
|
|
/* program configuration 0 */
|
|
|
|
bval = PHASELATCH | INITIATOR | BLOCKRST;
|
|
|
|
if (sc->sc_config & HCC_PARITY)
|
|
|
|
bval |= PARITYCHECK;
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_CONFIG0, bval);
|
|
|
|
|
|
|
|
/* program configuration 1 */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_CONFIG1,
|
|
|
|
ACTIVE_NEG | ACTIVE_NEGPLUS);
|
|
|
|
|
|
|
|
/* 250ms selection timeout */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_TIMEOUT, SEL_TIMEOUT);
|
|
|
|
|
|
|
|
/* Mask all the interrupt */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_INTEN, 0);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_INTEN, 0);
|
|
|
|
|
|
|
|
/* Reset SCSI module */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_SCSI_CONTROL, DO_RSTMODULE);
|
|
|
|
|
|
|
|
/* program Host ID */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_HOSTID, sc->sc_id);
|
|
|
|
|
|
|
|
/* set ansynchronous transfer */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_OFFSET, 0);
|
|
|
|
|
|
|
|
/* Trun LED control off */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_GEN_CONTROL,
|
|
|
|
bus_space_read_2(iot, ioh, TRM_GEN_CONTROL) & ~EN_LED);
|
|
|
|
|
|
|
|
/* DMA config */
|
|
|
|
bus_space_write_2(iot, ioh, TRM_DMA_CONFIG,
|
|
|
|
bus_space_read_2(iot, ioh, TRM_DMA_CONFIG) | DMA_ENHANCE);
|
|
|
|
|
|
|
|
/* Clear pending interrupt status */
|
|
|
|
bus_space_read_1(iot, ioh, TRM_SCSI_INTSTATUS);
|
|
|
|
|
|
|
|
/* Enable SCSI interrupt */
|
|
|
|
bus_space_write_1(iot, ioh, TRM_SCSI_INTEN,
|
|
|
|
EN_SELECT | EN_SELTIMEOUT | EN_DISCONNECT | EN_RESELECTED |
|
|
|
|
EN_SCSIRESET | EN_BUSSERVICE | EN_CMDDONE);
|
|
|
|
bus_space_write_1(iot, ioh, TRM_DMA_INTEN, EN_SCSIINTR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize the internal structures for a given SCSI host
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
trm_init(sc)
|
|
|
|
struct trm_softc *sc;
|
|
|
|
{
|
|
|
|
bus_dma_segment_t seg;
|
|
|
|
int error, rseg, all_sgsize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EEPROM CHECKSUM
|
|
|
|
*/
|
|
|
|
trm_check_eeprom(sc, &sc->sc_eeprom);
|
|
|
|
/*
|
|
|
|
* MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK
|
|
|
|
* allocate the space for all SCSI control blocks (SRB) for DMA memory
|
|
|
|
*/
|
|
|
|
all_sgsize = TRM_MAX_SRB * TRM_SG_SIZE;
|
|
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat, all_sgsize, PAGE_SIZE,
|
|
|
|
0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
|
|
|
|
printf("%s: unable to allocate SCSI REQUEST BLOCKS, "
|
|
|
|
"error = %d\n", sc->sc_dev.dv_xname, error);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
|
|
|
|
all_sgsize, (caddr_t *) &sc->sc_sglist,
|
|
|
|
BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
|
|
|
|
printf("%s: unable to map SCSI REQUEST BLOCKS, "
|
|
|
|
"error = %d\n", sc->sc_dev.dv_xname, error);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, all_sgsize, 1,
|
|
|
|
all_sgsize, 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
|
|
|
|
printf("%s: unable to create SRB DMA maps, "
|
|
|
|
"error = %d\n", sc->sc_dev.dv_xname, error);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
|
|
|
|
sc->sc_sglist, all_sgsize, NULL, BUS_DMA_NOWAIT)) != 0) {
|
|
|
|
printf("%s: unable to load SRB DMA maps, "
|
|
|
|
"error = %d\n", sc->sc_dev.dv_xname, error);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
#ifdef TRM_DEBUG
|
|
|
|
printf("\n\n%s: all_sgsize=%x\n", sc->sc_dev.dv_xname, all_sgsize);
|
|
|
|
#endif
|
|
|
|
memset(sc->sc_sglist, 0, all_sgsize);
|
|
|
|
trm_init_sc(sc);
|
|
|
|
trm_init_adapter(sc);
|
|
|
|
trm_reset(sc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* attach and init a host adapter
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
trm_attach(parent, self, aux)
|
|
|
|
struct device *parent;
|
|
|
|
struct device *self;
|
|
|
|
void *aux;
|
|
|
|
{
|
|
|
|
struct pci_attach_args *const pa = aux;
|
|
|
|
struct trm_softc *sc = (void *) self;
|
|
|
|
bus_space_tag_t iot; /* bus space tag */
|
|
|
|
bus_space_handle_t ioh; /* bus space handle */
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
pcireg_t command;
|
|
|
|
const char *intrstr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These cards do not allow memory mapped accesses
|
|
|
|
* pa_pc: chipset tag
|
|
|
|
* pa_tag: pci tag
|
|
|
|
*/
|
|
|
|
command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
if ((command & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE)) !=
|
|
|
|
(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE)) {
|
|
|
|
command |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
pci_conf_write(pa->pa_pc, pa->pa_tag,
|
|
|
|
PCI_COMMAND_STATUS_REG, command);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* mask for get correct base address of pci IO port
|
|
|
|
*/
|
|
|
|
if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0,
|
|
|
|
&iot, &ioh, NULL, NULL)) {
|
|
|
|
printf("%s: unable to map registers\n", sc->sc_dev.dv_xname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* test checksum of eeprom..& initial "ACB" adapter control block...
|
|
|
|
*/
|
|
|
|
sc->sc_iot = iot;
|
|
|
|
sc->sc_ioh = ioh;
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
if (trm_init(sc)) {
|
|
|
|
/*
|
|
|
|
* Error during initialization!
|
|
|
|
*/
|
|
|
|
printf(": Error during initialization\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Now try to attach all the sub-devices
|
|
|
|
*/
|
|
|
|
if (sc->sc_config & HCC_WIDE_CARD)
|
|
|
|
printf(": Tekram DC395UW/F (TRM-S1040) Fast40 "
|
|
|
|
"Ultra Wide SCSI Adapter\n");
|
|
|
|
else
|
|
|
|
printf(": Tekram DC395U, DC315/U (TRM-S1040) Fast20 "
|
|
|
|
"Ultra SCSI Adapter\n");
|
|
|
|
|
|
|
|
printf("%s: Adapter ID=%d, Max tag number=%d, %d SCBs\n",
|
|
|
|
sc->sc_dev.dv_xname, sc->sc_id, sc->maxtag, TRM_MAX_SRB);
|
|
|
|
/*
|
|
|
|
* Now tell the generic SCSI layer about our bus.
|
|
|
|
* map and establish interrupt
|
|
|
|
*/
|
|
|
|
if (pci_intr_map(pa, &ih)) {
|
|
|
|
printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
intrstr = pci_intr_string(pa->pa_pc, ih);
|
|
|
|
|
|
|
|
if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, trm_intr, sc) == NULL) {
|
|
|
|
printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname);
|
|
|
|
if (intrstr != NULL)
|
|
|
|
printf(" at %s", intrstr);
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (intrstr != NULL)
|
|
|
|
printf("%s: interrupting at %s\n",
|
|
|
|
sc->sc_dev.dv_xname, intrstr);
|
|
|
|
config_found(&sc->sc_dev, &sc->sc_channel, scsiprint);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* match pci device
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
trm_probe(parent, match, aux)
|
|
|
|
struct device *parent;
|
|
|
|
struct cfdata *match;
|
|
|
|
void *aux;
|
|
|
|
{
|
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
|
|
|
|
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_TEKRAM2)
|
|
|
|
switch (PCI_PRODUCT(pa->pa_id)) {
|
|
|
|
case PCI_PRODUCT_TEKRAM2_DC315:
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|