diff --git a/sys/dev/amd/amd.c b/sys/dev/amd/amd.c new file mode 100644 index 000000000000..c8a5d3bc05f2 --- /dev/null +++ b/sys/dev/amd/amd.c @@ -0,0 +1,2436 @@ +/* + ********************************************************************* + * FILE NAME : amd.c + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. + ********************************************************************* + */ + +/* + ********************************************************************* + * HISTORY: + * + * REV# DATE NAME DESCRIPTION + * 1.00 07/02/96 CLH First release for RELEASE-2.1.0 + * 1.01 08/20/96 CLH Update for RELEASE-2.1.5 + * 1.02 11/06/96 CLH Fixed more than 1 LUN scanning + * 1.03 12/20/96 CLH Modify to support 2.2-ALPHA + * 1.04 12/26/97 CLH Modify to support RELEASE-2.2.5 + * 1.05 01/01/99 ERICH CHEN Modify to support RELEASE-3.0.x (CAM) + ********************************************************************* + */ + +/* #define AMD_DEBUG0 */ +/* #define AMD_DEBUG_SCSI_PHASE */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define PCI_DEVICE_ID_AMD53C974 0x20201022ul +#define PCI_BASE_ADDR0 0x10 + +typedef u_int (phase_handler_t)(struct amd_softc *, struct amd_srb *, u_int); +typedef phase_handler_t *phase_handler_func_t; + +static void amd_intr(void *vamd); +static int amdstart(struct amd_softc *amd, struct amd_srb * pSRB); +static phase_handler_t amd_NopPhase; + +static phase_handler_t amd_DataOutPhase0; +static phase_handler_t amd_DataInPhase0; +#define amd_CommandPhase0 amd_NopPhase +static phase_handler_t amd_StatusPhase0; +static phase_handler_t amd_MsgOutPhase0; +static phase_handler_t amd_MsgInPhase0; +static phase_handler_t amd_DataOutPhase1; +static phase_handler_t amd_DataInPhase1; +static phase_handler_t amd_CommandPhase1; +static phase_handler_t amd_StatusPhase1; +static phase_handler_t amd_MsgOutPhase1; +static phase_handler_t amd_MsgInPhase1; + +static void amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb); +static int amdparsemsg(struct amd_softc *amd); +static int amdhandlemsgreject(struct amd_softc *amd); +static void amdconstructsdtr(struct amd_softc *amd, + u_int period, u_int offset); +static u_int amdfindclockrate(struct amd_softc *amd, u_int *period); +static int amdsentmsg(struct amd_softc *amd, u_int msgtype, int full); + +static void DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int dir); +static void amd_Disconnect(struct amd_softc *amd); +static void amd_Reselect(struct amd_softc *amd); +static void SRBdone(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_ScsiRstDetect(struct amd_softc *amd); +static void amd_ResetSCSIBus(struct amd_softc *amd); +static void RequestSense(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_InvalidCmd(struct amd_softc *amd); + +static void amd_timeout(void *arg1); +static void amd_reset(struct amd_softc *amd); +static u_int8_t * phystovirt(struct amd_srb *pSRB, u_int32_t xferCnt); + +void amd_linkSRB(struct amd_softc *amd); +static struct amd_softc * + amd_init(int unit, pcici_t config_id); +static void amd_load_defaults(struct amd_softc *amd); +static void amd_load_eeprom_or_defaults(struct amd_softc *amd); +static int amd_EEpromInDO(struct amd_softc *amd); +static u_int16_t EEpromGetData1(struct amd_softc *amd); +static void amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval); +static void amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry); +static void amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd); +static void amd_ReadEEprom(struct amd_softc *amd); + +static const char *amd_probe(pcici_t tag, pcidi_t type); +static void amd_attach(pcici_t tag, int unit); +static void amdcompletematch(struct amd_softc *amd, target_id_t target, + lun_id_t lun, u_int tag, struct srb_queue *queue, + cam_status status); +static void amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type); +static void amdsettags(struct amd_softc *amd, u_int target, int tagenb); + +static __inline void amd_clear_msg_state(struct amd_softc *amd); + +static __inline void +amd_clear_msg_state(struct amd_softc *amd) +{ + amd->msgout_len = 0; + amd->msgout_index = 0; + amd->msgin_index = 0; +} + +static u_long amd_count; + +/* CAM SIM entry points */ +#define ccb_srb_ptr spriv_ptr0 +#define ccb_amd_ptr spriv_ptr1 +static void amd_action(struct cam_sim *sim, union ccb *ccb); +static void amd_poll(struct cam_sim *sim); + +/* + * PCI device module setup + */ +static struct pci_device amd_device = +{ + "amd", + amd_probe, + amd_attach, + &amd_count, + NULL +}; + +#ifdef COMPAT_PCI_DRIVER +COMPAT_PCI_DRIVER(amd, amd_device); +#else +DATA_SET(pcidevice_set, amd_device); +#endif + +/* + * State engine function tables indexed by SCSI phase number + */ +phase_handler_func_t amd_SCSI_phase0[] = { + amd_DataOutPhase0, + amd_DataInPhase0, + amd_CommandPhase0, + amd_StatusPhase0, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase0, + amd_MsgInPhase0 +}; + +phase_handler_func_t amd_SCSI_phase1[] = { + amd_DataOutPhase1, + amd_DataInPhase1, + amd_CommandPhase1, + amd_StatusPhase1, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase1, + amd_MsgInPhase1 +}; + +/* + * EEProm/BIOS negotiation periods + */ +u_int8_t eeprom_period[] = { + 25, /* 10.0MHz */ + 32, /* 8.0MHz */ + 38, /* 6.6MHz */ + 44, /* 5.7MHz */ + 50, /* 5.0MHz */ + 63, /* 4.0MHz */ + 83, /* 3.0MHz */ + 125 /* 2.0MHz */ +}; + +/* + * chip clock setting to SCSI specified sync parameter table. + */ +u_int8_t tinfo_sync_period[] = { + 25, /* 10.0 */ + 32, /* 8.0 */ + 38, /* 6.6 */ + 44, /* 5.7 */ + 50, /* 5.0 */ + 57, /* 4.4 */ + 63, /* 4.0 */ + 70, /* 3.6 */ + 76, /* 3.3 */ + 83 /* 3.0 */ +}; + +static __inline struct amd_srb * +amdgetsrb(struct amd_softc * amd) +{ + int intflag; + struct amd_srb * pSRB; + + intflag = splcam(); + pSRB = TAILQ_FIRST(&amd->free_srbs); + if (pSRB) + TAILQ_REMOVE(&amd->free_srbs, pSRB, links); + splx(intflag); + return (pSRB); +} + +static void +amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb) +{ + struct scsi_request_sense sense_cmd; + struct ccb_scsiio *csio; + u_int8_t *cdb; + u_int cdb_len; + + csio = &srb->pccb->csio; + + if (srb->SRBFlag & AUTO_REQSENSE) { + sense_cmd.opcode = REQUEST_SENSE; + sense_cmd.byte2 = srb->pccb->ccb_h.target_lun << 5; + sense_cmd.unused[0] = 0; + sense_cmd.unused[1] = 0; + sense_cmd.length = csio->sense_len; + sense_cmd.control = 0; + cdb = &sense_cmd.opcode; + cdb_len = sizeof(sense_cmd); + } else { + cdb = &srb->CmdBlock[0]; + cdb_len = srb->ScsiCmdLen; + } + amd_write8_multi(amd, SCSIFIFOREG, cdb, cdb_len); +} + +/* + * Attempt to start a waiting transaction. Interrupts must be disabled + * upon entry to this function. + */ +static void +amdrunwaiting(struct amd_softc *amd) { + struct amd_srb *srb; + + if (amd->last_phase != SCSI_BUS_FREE) + return; + + srb = TAILQ_FIRST(&amd->waiting_srbs); + if (srb == NULL) + return; + + if (amdstart(amd, srb) == 0) { + TAILQ_REMOVE(&amd->waiting_srbs, srb, links); + TAILQ_INSERT_HEAD(&amd->running_srbs, srb, links); + } +} + +static void +amdexecutesrb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + struct amd_srb *srb; + union ccb *ccb; + struct amd_softc *amd; + int s; + + srb = (struct amd_srb *)arg; + ccb = srb->pccb; + amd = (struct amd_softc *)ccb->ccb_h.ccb_amd_ptr; + + if (error != 0) { + if (error != EFBIG) + printf("amd%d: Unexepected error 0x%x returned from " + "bus_dmamap_load\n", amd->unit, error); + if (ccb->ccb_h.status == CAM_REQ_INPROG) { + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; + } + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + return; + } + + if (nseg != 0) { + struct amd_sg *sg; + bus_dma_segment_t *end_seg; + bus_dmasync_op_t op; + + end_seg = dm_segs + nseg; + + /* Copy the segments into our SG list */ + srb->pSGlist = &srb->SGsegment[0]; + sg = srb->pSGlist; + while (dm_segs < end_seg) { + sg->SGXLen = dm_segs->ds_len; + sg->SGXPtr = dm_segs->ds_addr; + sg++; + dm_segs++; + } + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_PREREAD; + else + op = BUS_DMASYNC_PREWRITE; + + bus_dmamap_sync(amd->buffer_dmat, srb->dmamap, op); + + } + srb->SGcount = nseg; + srb->SGIndex = 0; + srb->AdaptStatus = 0; + srb->TargetStatus = 0; + srb->MsgCnt = 0; + srb->SRBStatus = 0; + srb->SRBFlag = 0; + srb->SRBState = 0; + srb->TotalXferredLen = 0; + srb->SGPhysAddr = 0; + srb->SGToBeXferLen = 0; + srb->EndMessage = 0; + + s = splcam(); + + /* + * Last time we need to check if this CCB needs to + * be aborted. + */ + if (ccb->ccb_h.status != CAM_REQ_INPROG) { + if (nseg != 0) + bus_dmamap_unload(amd->buffer_dmat, srb->dmamap); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + splx(s); + return; + } + ccb->ccb_h.status |= CAM_SIM_QUEUED; +#if 0 + /* XXX Need a timeout handler */ + ccb->ccb_h.timeout_ch = + timeout(amdtimeout, (caddr_t)srb, + (ccb->ccb_h.timeout * hz) / 1000); +#endif + TAILQ_INSERT_TAIL(&amd->waiting_srbs, srb, links); + amdrunwaiting(amd); + splx(s); +} + +static void +amd_action(struct cam_sim * psim, union ccb * pccb) +{ + struct amd_softc * amd; + u_int target_id, target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("amd_action\n")); + + amd = (struct amd_softc *) cam_sim_softc(psim); + target_id = pccb->ccb_h.target_id; + target_lun = pccb->ccb_h.target_lun; + + switch (pccb->ccb_h.func_code) { + case XPT_SCSI_IO: + { + struct amd_srb * pSRB; + struct ccb_scsiio *pcsio; + + pcsio = &pccb->csio; + + /* + * Assign an SRB and connect it with this ccb. + */ + pSRB = amdgetsrb(amd); + + if (!pSRB) { + /* Freeze SIMQ */ + pccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(pccb); + return; + } + pSRB->pccb = pccb; + pccb->ccb_h.ccb_srb_ptr = pSRB; + pccb->ccb_h.ccb_amd_ptr = amd; + pSRB->ScsiCmdLen = pcsio->cdb_len; + bcopy(pcsio->cdb_io.cdb_bytes, pSRB->CmdBlock, pcsio->cdb_len); + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + if ((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { + /* + * We've been given a pointer + * to a single buffer. + */ + if ((pccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { + int s; + int error; + + s = splsoftvm(); + error = + bus_dmamap_load(amd->buffer_dmat, + pSRB->dmamap, + pcsio->data_ptr, + pcsio->dxfer_len, + amdexecutesrb, + pSRB, /*flags*/0); + if (error == EINPROGRESS) { + /* + * So as to maintain + * ordering, freeze the + * controller queue + * until our mapping is + * returned. + */ + xpt_freeze_simq(amd->psim, 1); + pccb->ccb_h.status |= + CAM_RELEASE_SIMQ; + } + splx(s); + } else { + struct bus_dma_segment seg; + + /* Pointer to physical buffer */ + seg.ds_addr = + (bus_addr_t)pcsio->data_ptr; + seg.ds_len = pcsio->dxfer_len; + amdexecutesrb(pSRB, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 + || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { + TAILQ_INSERT_HEAD(&amd->free_srbs, + pSRB, links); + pccb->ccb_h.status = CAM_PROVIDE_FAIL; + xpt_done(pccb); + return; + } + + /* Just use the segments provided */ + segs = + (struct bus_dma_segment *)pcsio->data_ptr; + amdexecutesrb(pSRB, segs, pcsio->sglist_cnt, 0); + } + } else + amdexecutesrb(pSRB, NULL, 0, 0); + break; + } + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &pccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 7; + cpi->max_lun = amd->max_lun; /* 7 or 0 */ + cpi->initiator_id = amd->AdaptSCSIID; + cpi->bus_id = cam_sim_bus(psim); + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "TRM-AMD", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(psim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_ABORT: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_RESET_BUS: + { + + int i; + + amd_ResetSCSIBus(amd); + amd->ACBFlag = 0; + + for (i = 0; i < 500; i++) { + DELAY(1000); /* Wait until our interrupt + * handler sees it */ + } + + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_RESET_DEV: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_TERM_IO: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + struct amd_transinfo *tinfo; + int intflag; + + cts = &pccb->cts; + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + /* current transfer settings */ + if (targ_info->disc_tag & AMD_CUR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; /* no tag & disconnect */ + } + if (targ_info->disc_tag & AMD_CUR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->current; + } else { + /* default(user) transfer settings */ + if (targ_info->disc_tag & AMD_USR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; + } + if (targ_info->disc_tag & AMD_USR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->user; + } + + cts->sync_period = tinfo->period; + cts->sync_offset = tinfo->offset; + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + splx(intflag); + cts->valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID + | CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_DISC_VALID + | CCB_TRANS_TQ_VALID; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + u_int update_type; + int intflag; + int last_entry; + + cts = &pccb->cts; + update_type = 0; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + update_type |= AMD_TRANS_GOAL; + } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + update_type |= AMD_TRANS_USER; + } + if (update_type == 0 + || update_type == (AMD_TRANS_USER|AMD_TRANS_GOAL)) { + cts->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + } + + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + + if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_DISCENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_USR_DISCENB; + } + } + } + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_TAGENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_USR_TAGENB; + } + } + } + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_offset = targ_info->goal.offset; + else + cts->sync_offset = targ_info->user.offset; + } + + if (cts->sync_offset > AMD_MAX_SYNC_OFFSET) + cts->sync_offset = AMD_MAX_SYNC_OFFSET; + + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_period = targ_info->goal.period; + else + cts->sync_period = targ_info->user.period; + } + + last_entry = sizeof(tinfo_sync_period) - 1; + if ((cts->sync_period != 0) + && (cts->sync_period < tinfo_sync_period[0])) + cts->sync_period = tinfo_sync_period[0]; + if (cts->sync_period > tinfo_sync_period[last_entry]) + cts->sync_period = 0; + if (cts->sync_offset == 0) + cts->sync_period = 0; + + if ((update_type & AMD_TRANS_USER) != 0) { + targ_info->user.period = cts->sync_period; + targ_info->user.offset = cts->sync_offset; + } + if ((update_type & AMD_TRANS_GOAL) != 0) { + targ_info->goal.period = cts->sync_period; + targ_info->goal.offset = cts->sync_offset; + } + splx(intflag); + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended; + + ccg = &pccb->ccg; + size_mb = ccg->volume_size/((1024L * 1024L)/ccg->block_size); + extended = (amd->eepromBuf[EE_MODE2] & GREATER_1G) != 0; + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + default: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + } +} + +static void +amd_poll(struct cam_sim * psim) +{ + amd_intr(cam_sim_softc(psim)); +} + +static u_int8_t * +phystovirt(struct amd_srb * pSRB, u_int32_t xferCnt) +{ + int dataPtr; + struct ccb_scsiio *pcsio; + u_int8_t i; + struct amd_sg * pseg; + + dataPtr = 0; + pcsio = &pSRB->pccb->csio; + + dataPtr = (int) pcsio->data_ptr; + pseg = pSRB->SGsegment; + for (i = 0; i < pSRB->SGIndex; i++) { + dataPtr += (int) pseg->SGXLen; + pseg++; + } + dataPtr += (int) xferCnt; + return ((u_int8_t *) dataPtr); +} + +static void +ResetDevParam(struct amd_softc * amd) +{ + u_int target; + + for (target = 0; target <= amd->max_id; target++) { + if (amd->AdaptSCSIID != target) { + amdsetsync(amd, target, /*clockrate*/0, + /*period*/0, /*offset*/0, AMD_TRANS_CUR); + } + } +} + +static void +amdcompletematch(struct amd_softc *amd, target_id_t target, lun_id_t lun, + u_int tag, struct srb_queue *queue, cam_status status) +{ + struct amd_srb *srb; + struct amd_srb *next_srb; + + for (srb = TAILQ_FIRST(queue); srb != NULL; srb = next_srb) { + union ccb *ccb; + + next_srb = TAILQ_NEXT(srb, links); + if (srb->pccb->ccb_h.target_id != target + && target != CAM_TARGET_WILDCARD) + continue; + + if (srb->pccb->ccb_h.target_lun != lun + && lun != CAM_LUN_WILDCARD) + continue; + + if (srb->TagNumber != tag + && tag != AMD_TAG_WILDCARD) + continue; + + ccb = srb->pccb; + TAILQ_REMOVE(queue, srb, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0 + && (status & CAM_DEV_QFRZN) != 0) + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = status; + xpt_done(ccb); + } + +} + +static void +amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type) +{ + struct amd_target_info *tinfo; + u_int old_period; + u_int old_offset; + + tinfo = &amd->tinfo[target]; + old_period = tinfo->current.period; + old_offset = tinfo->current.offset; + if ((type & AMD_TRANS_CUR) != 0 + && (old_period != period || old_offset != offset)) { + struct cam_path *path; + + tinfo->current.period = period; + tinfo->current.offset = offset; + tinfo->sync_period_reg = clockrate; + tinfo->sync_offset_reg = offset; + tinfo->CtrlR3 &= ~FAST_SCSI; + tinfo->CtrlR4 &= ~EATER_25NS; + if (clockrate > 7) + tinfo->CtrlR4 |= EATER_25NS; + else + tinfo->CtrlR3 |= FAST_SCSI; + + if ((type & AMD_TRANS_ACTIVE) == AMD_TRANS_ACTIVE) { + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + } + /* If possible, update the XPT's notion of our transfer rate */ + if (xpt_create_path(&path, /*periph*/NULL, + cam_sim_path(amd->psim), target, + CAM_LUN_WILDCARD) == CAM_REQ_CMP) { + struct ccb_trans_settings neg; + + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + neg.sync_period = period; + neg.sync_offset = offset; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_async(AC_TRANSFER_NEG, path, &neg); + xpt_free_path(path); + } + } + if ((type & AMD_TRANS_GOAL) != 0) { + tinfo->goal.period = period; + tinfo->goal.offset = offset; + } + + if ((type & AMD_TRANS_USER) != 0) { + tinfo->user.period = period; + tinfo->user.offset = offset; + } +} + +static void +amdsettags(struct amd_softc *amd, u_int target, int tagenb) +{ + panic("Implement me!\n"); +} + + +/* + ********************************************************************** + * Function : amd_reset (struct amd_softc * amd) + * Purpose : perform a hard reset on the SCSI bus( and AMD chip). + * Inputs : cmd - command which caused the SCSI RESET + ********************************************************************** + */ +static void +amd_reset(struct amd_softc * amd) +{ + int intflag; + u_int8_t bval; + u_int16_t i; + + +#ifdef AMD_DEBUG0 + printf("DC390: RESET"); +#endif + + intflag = splcam(); + bval = amd_read8(amd, CNTLREG1); + bval |= DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* disable interrupt */ + amd_ResetSCSIBus(amd); + + for (i = 0; i < 500; i++) { + DELAY(1000); + } + + bval = amd_read8(amd, CNTLREG1); + bval &= ~DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* re-enable interrupt */ + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + splx(intflag); + return; +} + +void +amd_timeout(void *arg1) +{ + struct amd_srb * pSRB; + + pSRB = (struct amd_srb *) arg1; +} + +static int +amdstart(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + struct amd_target_info *targ_info; + u_int identify_msg; + u_int command; + u_int target; + u_int lun; + int tagged; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target = pccb->ccb_h.target_id; + lun = pccb->ccb_h.target_lun; + targ_info = &amd->tinfo[target]; + + amd_clear_msg_state(amd); + amd_write8(amd, SCSIDESTIDREG, target); + amd_write8(amd, SYNCPERIOREG, targ_info->sync_period_reg); + amd_write8(amd, SYNCOFFREG, targ_info->sync_offset_reg); + amd_write8(amd, CNTLREG1, targ_info->CtrlR1); + amd_write8(amd, CNTLREG3, targ_info->CtrlR3); + amd_write8(amd, CNTLREG4, targ_info->CtrlR4); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + identify_msg = MSG_IDENTIFYFLAG | lun; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0 + && (pSRB->CmdBlock[0] != REQUEST_SENSE) + && (pSRB->SRBFlag & AUTO_REQSENSE) == 0) + identify_msg |= MSG_IDENTIFY_DISCFLAG; + + amd_write8(amd, SCSIFIFOREG, identify_msg); + tagged = 0; + if ((targ_info->disc_tag & AMD_CUR_TAGENB) == 0 + || (identify_msg & MSG_IDENTIFY_DISCFLAG) == 0) + pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + if (targ_info->current.period != targ_info->goal.period + || targ_info->current.offset != targ_info->goal.offset) { + command = SEL_W_ATN_STOP; + amdconstructsdtr(amd, targ_info->goal.period, + targ_info->goal.offset); + } else if ((pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + command = SEL_W_ATN2; + pSRB->SRBState = SRB_START; + amd_write8(amd, SCSIFIFOREG, pcsio->tag_action); + amd_write8(amd, SCSIFIFOREG, pSRB->TagNumber); + tagged++; + } else { + command = SEL_W_ATN; + pSRB->SRBState = SRB_START; + } + if (command != SEL_W_ATN_STOP) + amdsetupcommand(amd, pSRB); + + if (amd_read8(amd, SCSISTATREG) & INTERRUPT) { + pSRB->SRBState = SRB_READY; + return (1); + } else { + amd->last_phase = SCSI_ARBITRATING; + amd_write8(amd, SCSICMDREG, command); + amd->active_srb = pSRB; + amd->cur_target = target; + amd->cur_lun = lun; + return (0); + } +} + +/* + * Catch an interrupt from the adapter. + * Process pending device interrupts. + */ +static void +amd_intr(void *arg) +{ + struct amd_softc *amd; + struct amd_srb *pSRB; + u_int internstat = 0; + u_int scsistat; + u_int intstat; + + amd = (struct amd_softc *)arg; + + if (amd == NULL) { +#ifdef AMD_DEBUG0 + printf("amd_intr: amd NULL return......"); +#endif + return; + } + + scsistat = amd_read8(amd, SCSISTATREG); + if (!(scsistat & INTERRUPT)) { +#ifdef AMD_DEBUG0 + printf("amd_intr: scsistat = NULL ,return......"); +#endif + return; + } +#ifdef AMD_DEBUG_SCSI_PHASE + printf("scsistat=%2x,", scsistat); +#endif + + internstat = amd_read8(amd, INTERNSTATREG); + intstat = amd_read8(amd, INTSTATREG); + +#ifdef AMD_DEBUG_SCSI_PHASE + printf("intstat=%2x,", intstat); +#endif + + if (intstat & DISCONNECTED) { + amd_Disconnect(amd); + return; + } + if (intstat & RESELECTED) { + amd_Reselect(amd); + return; + } + if (intstat & INVALID_CMD) { + amd_InvalidCmd(amd); + return; + } + if (intstat & SCSI_RESET_) { + amd_ScsiRstDetect(amd); + return; + } + if (intstat & (SUCCESSFUL_OP + SERVICE_REQUEST)) { + pSRB = amd->active_srb; + /* + * Run our state engine. First perform + * post processing for the last phase we + * were in, followed by any processing + * required to handle the current phase. + */ + scsistat = + amd_SCSI_phase0[amd->last_phase](amd, pSRB, scsistat); + amd->last_phase = scsistat & SCSI_PHASE_MASK; + (void)amd_SCSI_phase1[amd->last_phase](amd, pSRB, scsistat); + } +} + +static u_int +amd_DataOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while ((amd_read8(amd, DMA_Status)&DMA_XFER_DONE) == 0) + ; + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { + ResidCnt = amd_read8(amd, CURRENTFIFOREG) & 0x1f; + ResidCnt += amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + } + } + amd_write8(amd, DMA_Cmd, WRITE_DIRECTION | DMA_IDLE_CMD); + return (scsistat); +} + +static u_int +amd_DataInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + u_int8_t bval; + u_int16_t i, residual; + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + u_int8_t * ptr; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while (1) { + bval = amd_read8(amd, DMA_Status); + if ((bval & DMA_XFER_DONE) != 0) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { /* phase changed */ + residual = 0; + bval = amd_read8(amd, CURRENTFIFOREG); + while (bval & 0x1f) { + if ((bval & 0x1f) == 1) { + for (i = 0; i < 0x100; i++) { + bval = amd_read8(amd, CURRENTFIFOREG); + if (!(bval & 0x1f)) { + goto din_1; + } else if (i == 0x0ff) { + residual = 1; + goto din_1; + } + } + } else { + bval = amd_read8(amd, CURRENTFIFOREG); + } + } + din_1: + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_BLAST_CMD); + for (i = 0; i < 0x8000; i++) { + if ((amd_read8(amd, DMA_Status)&BLAST_COMPLETE)) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + ResidCnt = amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + if (residual) { + /* get residual byte */ + bval = amd_read8(amd, SCSIFIFOREG); + ptr = phystovirt(pSRB, xferCnt); + *ptr = bval; + pSRB->SGPhysAddr++; + pSRB->TotalXferredLen++; + pSRB->SGToBeXferLen--; + } + } + } + return (scsistat); +} + +static u_int +amd_StatusPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + pSRB->TargetStatus = amd_read8(amd, SCSIFIFOREG); + /* get message */ + pSRB->EndMessage = amd_read8(amd, SCSIFIFOREG); + pSRB->SRBState = SRB_COMPLETED; + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} + +static u_int +amd_MsgOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { + scsistat = SCSI_NOP0; + } + return (scsistat); +} + +static u_int +amd_MsgInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + int done; + + amd->msgin_buf[amd->msgin_index] = amd_read8(amd, SCSIFIFOREG); + + done = amdparsemsg(amd); + if (done) + amd->msgin_index = 0; + else + amd->msgin_index++; + return (SCSI_NOP0); +} + +static int +amdparsemsg(struct amd_softc *amd) +{ + struct amd_target_info *targ_info; + int reject; + int done; + int response; + + done = FALSE; + response = FALSE; + reject = FALSE; + + targ_info = &amd->tinfo[amd->cur_target]; + + /* + * Parse as much of the message as is availible, + * rejecting it if we don't support it. When + * the entire message is availible and has been + * handled, return TRUE indicating that we have + * parsed an entire message. + */ + switch (amd->msgin_buf[0]) { + case MSG_DISCONNECT: + amd->active_srb->SRBState = SRB_DISCONNECT; + amd->disc_count[amd->cur_target][amd->cur_lun]++; + done = TRUE; + break; + case MSG_SIMPLE_Q_TAG: + { + struct amd_srb *disc_srb; + + if (amd->msgin_index < 1) + break; + disc_srb = &amd->SRB_array[amd->msgin_buf[1]]; + if (amd->active_srb != NULL + || disc_srb->SRBState != SRB_DISCONNECT + || disc_srb->pccb->ccb_h.target_id != amd->cur_target + || disc_srb->pccb->ccb_h.target_lun != amd->cur_lun) { + printf("amd%d: Unexpected tagged reselection " + "for target %d, Issuing Abort\n", amd->unit, + amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + response = TRUE; + break; + } + amd->active_srb = disc_srb; + amd->disc_count[amd->cur_target][amd->cur_lun]--; + done = TRUE; + break; + } + case MSG_MESSAGE_REJECT: + response = amdhandlemsgreject(amd); + if (response == FALSE) + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + /* FALLTHROUGH */ + case MSG_NOOP: + done = TRUE; + break; + case MSG_EXTENDED: + { + u_int clockrate; + u_int period; + u_int offset; + u_int saved_offset; + + /* Wait for enough of the message to begin validation */ + if (amd->msgin_index < 1) + break; + if (amd->msgin_buf[1] != MSG_EXT_SDTR_LEN) { + reject = TRUE; + break; + } + + /* Wait for opcode */ + if (amd->msgin_index < 2) + break; + + if (amd->msgin_buf[2] != MSG_EXT_SDTR) { + reject = TRUE; + break; + } + + /* + * Wait until we have both args before validating + * and acting on this message. + * + * Add one to MSG_EXT_SDTR_LEN to account for + * the extended message preamble. + */ + if (amd->msgin_index < (MSG_EXT_SDTR_LEN + 1)) + break; + + period = amd->msgin_buf[3]; + saved_offset = offset = amd->msgin_buf[4]; + clockrate = amdfindclockrate(amd, &period); + if (offset > AMD_MAX_SYNC_OFFSET) + offset = AMD_MAX_SYNC_OFFSET; + if (period == 0 || offset == 0) { + offset = 0; + period = 0; + clockrate = 0; + } + amdsetsync(amd, amd->cur_target, clockrate, period, offset, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + + /* + * See if we initiated Sync Negotiation + * and didn't have to fall down to async + * transfers. + */ + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/TRUE)) { + /* We started it */ + if (saved_offset != offset) { + /* Went too low - force async */ + reject = TRUE; + } + } else { + /* + * Send our own SDTR in reply + */ + if (bootverbose) + printf("Sending SDTR!\n"); + amd->msgout_index = 0; + amd->msgout_len = 0; + amdconstructsdtr(amd, period, offset); + amd->msgout_index = 0; + response = TRUE; + } + done = TRUE; + break; + } + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* XXX Implement!!! */ + done = TRUE; + break; + default: + reject = TRUE; + break; + } + + if (reject) { + amd->msgout_index = 0; + amd->msgout_len = 1; + amd->msgout_buf[0] = MSG_MESSAGE_REJECT; + done = TRUE; + response = TRUE; + } + + if (response) + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + + if (done && !response) + /* Clear the outgoing message buffer */ + amd->msgout_len = 0; + + /* Drop Ack */ + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + + return (done); +} + +static u_int +amdfindclockrate(struct amd_softc *amd, u_int *period) +{ + u_int i; + u_int clockrate; + + for (i = 0; i < sizeof(tinfo_sync_period); i++) { + u_int8_t *table_entry; + + table_entry = &tinfo_sync_period[i]; + if (*period <= *table_entry) { + /* + * When responding to a target that requests + * sync, the requested rate may fall between + * two rates that we can output, but still be + * a rate that we can receive. Because of this, + * we want to respond to the target with + * the same rate that it sent to us even + * if the period we use to send data to it + * is lower. Only lower the response period + * if we must. + */ + if (i == 0) { + *period = *table_entry; + } + break; + } + } + + if (i == sizeof(tinfo_sync_period)) { + /* Too slow for us. Use asnyc transfers. */ + *period = 0; + clockrate = 0; + } else + clockrate = i + 4; + + return (clockrate); +} + +/* + * See if we sent a particular extended message to the target. + * If "full" is true, the target saw the full message. + * If "full" is false, the target saw at least the first + * byte of the message. + */ +static int +amdsentmsg(struct amd_softc *amd, u_int msgtype, int full) +{ + int found; + int index; + + found = FALSE; + index = 0; + + while (index < amd->msgout_len) { + if ((amd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 + || amd->msgout_buf[index] == MSG_MESSAGE_REJECT) + index++; + else if (amd->msgout_buf[index] >= MSG_SIMPLE_Q_TAG + && amd->msgout_buf[index] < MSG_IGN_WIDE_RESIDUE) { + /* Skip tag type and tag id */ + index += 2; + } else if (amd->msgout_buf[index] == MSG_EXTENDED) { + /* Found a candidate */ + if (amd->msgout_buf[index+2] == msgtype) { + u_int end_index; + + end_index = index + 1 + + amd->msgout_buf[index + 1]; + if (full) { + if (amd->msgout_index > end_index) + found = TRUE; + } else if (amd->msgout_index > index) + found = TRUE; + } + break; + } else { + panic("amdsentmsg: Inconsistent msg buffer"); + } + } + return (found); +} + +static void +amdconstructsdtr(struct amd_softc *amd, u_int period, u_int offset) +{ + amd->msgout_buf[amd->msgout_index++] = MSG_EXTENDED; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR_LEN; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR; + amd->msgout_buf[amd->msgout_index++] = period; + amd->msgout_buf[amd->msgout_index++] = offset; + amd->msgout_len += 5; +} + +static int +amdhandlemsgreject(struct amd_softc *amd) +{ + /* + * If we had an outstanding SDTR for this + * target, this is a signal that the target + * is refusing negotiation. Also watch out + * for rejected tag messages. + */ + struct amd_srb *srb; + struct amd_target_info *targ_info; + int response = FALSE; + + srb = amd->active_srb; + targ_info = &amd->tinfo[amd->cur_target]; + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/FALSE)) { + /* note asynch xfers and clear flag */ + amdsetsync(amd, amd->cur_target, /*clockrate*/0, + /*period*/0, /*offset*/0, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + printf("amd%d:%d: refuses synchronous negotiation. " + "Using asynchronous transfers\n", + amd->unit, amd->cur_target); + } else if ((srb != NULL) + && (srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + struct ccb_trans_settings neg; + + printf("amd%d:%d: refuses tagged commands. Performing " + "non-tagged I/O\n", amd->unit, amd->cur_target); + + amdsettags(amd, amd->cur_target, FALSE); + neg.flags = 0; + neg.valid = CCB_TRANS_TQ_VALID; + xpt_setup_ccb(&neg.ccb_h, srb->pccb->ccb_h.path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, srb->pccb->ccb_h.path, &neg); + + /* + * Resend the identify for this CCB as the target + * may believe that the selection is invalid otherwise. + */ + if (amd->msgout_len != 0) + bcopy(&amd->msgout_buf[0], &amd->msgout_buf[1], + amd->msgout_len); + amd->msgout_buf[0] = MSG_IDENTIFYFLAG + | srb->pccb->ccb_h.target_lun; + amd->msgout_len++; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (srb->pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) + amd->msgout_buf[0] |= MSG_IDENTIFY_DISCFLAG; + + srb->pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + + /* + * Requeue all tagged commands for this target + * currently in our posession so they can be + * converted to untagged commands. + */ + amdcompletematch(amd, amd->cur_target, amd->cur_lun, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_REQUEUE_REQ); + } else { + /* + * Otherwise, we ignore it. + */ + printf("amd%d:%d: Message reject received -- ignored\n", + amd->unit, amd->cur_target); + } + return (response); +} + +#if 0 + if (!(pSRB->SRBState & SRB_MSGIN_MULTI)) { + if (bval == MSG_DISCONNECT) { + pSRB->SRBState = SRB_DISCONNECT; + } else if (bval == MSG_SAVEDATAPOINTER) { + goto min6; + } else if ((bval == MSG_EXTENDED) + || ((bval >= MSG_SIMPLE_Q_TAG) + && (bval <= MSG_ORDERED_Q_TAG))) { + pSRB->SRBState |= SRB_MSGIN_MULTI; + pSRB->MsgInBuf[0] = bval; + pSRB->MsgCnt = 1; + pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; + } else if (bval == MSG_MESSAGE_REJECT) { + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + + if (pSRB->SRBState & DO_SYNC_NEGO) { + goto set_async; + } + } else if (bval == MSG_RESTOREPOINTERS) { + goto min6; + } else { + goto min6; + } + } else { /* minx: */ + *pSRB->pMsgPtr = bval; + pSRB->MsgCnt++; + pSRB->pMsgPtr++; + if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_Q_TAG) + && (pSRB->MsgInBuf[0] <= MSG_ORDERED_Q_TAG)) { + if (pSRB->MsgCnt == 2) { + pSRB->SRBState = 0; + pSRB = &amd->SRB_array[pSRB->MsgInBuf[1]]; + if (pSRB->SRBState & SRB_DISCONNECT) == 0) { + pSRB = amd->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + EnableMsgOut2(amd, pSRB); + } else { + if (pDCB->DCBFlag & ABORT_DEV_) { + pSRB->SRBState = SRB_ABORT_SENT; + EnableMsgOut1(amd, pSRB); + } + pDCB->pActiveSRB = pSRB; + pSRB->SRBState = SRB_DATA_XFER; + } + } + } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) + && (pSRB->MsgCnt == 5)) { + pSRB->SRBState &= ~(SRB_MSGIN_MULTI + DO_SYNC_NEGO); + if ((pSRB->MsgInBuf[1] != 3) + || (pSRB->MsgInBuf[2] != 1)) { /* reject_msg: */ + pSRB->MsgCnt = 1; + pSRB->MsgInBuf[0] = MSG_MESSAGE_REJECT; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } else if (!(pSRB->MsgInBuf[3]) + || !(pSRB->MsgInBuf[4])) { + set_async: /* set async */ + + pDCB = pSRB->pSRBDCB; + /* disable sync & sync nego */ + pDCB->SyncMode &= ~(SYNC_ENABLE|SYNC_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + + pDCB->tinfo.goal.period = 0; + pDCB->tinfo.goal.offset = 0; + + pDCB->tinfo.current.period = 0; + pDCB->tinfo.current.offset = 0; + pDCB->tinfo.current.width = + MSG_EXT_WDTR_BUS_8_BIT; + + pDCB->CtrlR3 = FAST_CLK; /* non_fast */ + pDCB->CtrlR4 &= 0x3f; + pDCB->CtrlR4 |= EATER_25NS; + goto re_prog; + } else {/* set sync */ + + pDCB = pSRB->pSRBDCB; + /* enable sync & sync nego */ + pDCB->SyncMode |= SYNC_ENABLE|SYNC_NEGO_DONE; + + /* set sync offset */ + pDCB->SyncOffset &= 0x0f0; + pDCB->SyncOffset |= pSRB->MsgInBuf[4]; + + /* set sync period */ + pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3]; + + wval = (u_int16_t) pSRB->MsgInBuf[3]; + wval = wval << 2; + wval--; + wval1 = wval / 25; + if ((wval1 * 25) != wval) { + wval1++; + } + bval = FAST_CLK|FAST_SCSI; + pDCB->CtrlR4 &= 0x3f; + if (wval1 >= 8) { + /* Fast SCSI */ + wval1--; + bval = FAST_CLK; + pDCB->CtrlR4 |= EATER_25NS; + } + pDCB->CtrlR3 = bval; + pDCB->SyncPeriod = (u_int8_t) wval1; + + pDCB->tinfo.goal.period = + tinfo_sync_period[pDCB->SyncPeriod - 4]; + pDCB->tinfo.goal.offset = pDCB->SyncOffset; + pDCB->tinfo.current.period = + tinfo_sync_period[pDCB->SyncPeriod - 4];; + pDCB->tinfo.current.offset = pDCB->SyncOffset; + + /* + * program SCSI control register + */ + re_prog: + amd_write8(amd, SYNCPERIOREG, pDCB->SyncPeriod); + amd_write8(amd, SYNCOFFREG, pDCB->SyncOffset); + amd_write8(amd, CNTLREG3, pDCB->CtrlR3); + amd_write8(amd, CNTLREG4, pDCB->CtrlR4); + } + } + } +min6: + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} +#endif + +static u_int +amd_DataOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, WRITE_DIRECTION); + return (scsistat); +} + +static u_int +amd_DataInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, READ_DIRECTION); + return (scsistat); +} + +static void +DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int ioDir) +{ + struct amd_sg * psgl; + u_int32_t lval; + + if (pSRB->SGIndex < pSRB->SGcount) { + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir);/* |EN_DMA_INT */ + + if (!pSRB->SGToBeXferLen) { + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } + lval = pSRB->SGToBeXferLen; + amd_write8(amd, CTCREG_LOW, lval); + amd_write8(amd, CTCREG_MID, lval >> 8); + amd_write8(amd, CURTXTCNTREG, lval >> 16); + + amd_write32(amd, DMA_XferCnt, pSRB->SGToBeXferLen); + + amd_write32(amd, DMA_XferAddr, pSRB->SGPhysAddr); + + pSRB->SRBState = SRB_DATA_XFER; + + amd_write8(amd, SCSICMDREG, DMA_COMMAND|INFO_XFER_CMD); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir); /* |EN_DMA_INT */ + + amd_write8(amd, DMA_Cmd, DMA_START_CMD|ioDir);/* |EN_DMA_INT */ + } else { /* xfer pad */ + if (pSRB->SGcount) { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + amd_write8(amd, CTCREG_LOW, 0); + amd_write8(amd, CTCREG_MID, 0); + amd_write8(amd, CURTXTCNTREG, 0); + + pSRB->SRBState |= SRB_XFERPAD; + amd_write8(amd, SCSICMDREG, DMA_COMMAND|XFER_PAD_BYTE); + } +} + +static u_int +amd_CommandPhase1(struct amd_softc *amd, struct amd_srb *srb, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + amdsetupcommand(amd, srb); + + srb->SRBState = SRB_COMMAND; + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_StatusPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + pSRB->SRBState = SRB_STATUS; + amd_write8(amd, SCSICMDREG, INITIATOR_CMD_CMPLTE); + return (scsistat); +} + +static u_int +amd_MsgOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->msgout_len == 0) { + amd->msgout_buf[0] = MSG_NOOP; + amd->msgout_len = 1; + } + amd_write8_multi(amd, SCSIFIFOREG, amd->msgout_buf, amd->msgout_len); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_MsgInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_NopPhase(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + return (scsistat); +} + +static void +amd_Disconnect(struct amd_softc * amd) +{ + struct amd_srb *srb; + int target; + int lun; + + srb = amd->active_srb; + amd->active_srb = NULL; + amd->last_phase = SCSI_BUS_FREE; + amd_write8(amd, SCSICMDREG, EN_SEL_RESEL); + target = amd->cur_target; + lun = amd->cur_lun; + + if (srb == NULL) { + /* Invalid reselection */ + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_ABORT_SENT) { + /* Clean up and done this srb */ +#if 0 + while (( = TAILQ_FIRST(&amd->running_srbs)) != NULL) { + /* XXX What about "done'ing" these srbs??? */ + if (pSRB->pSRBDCB == pDCB) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + } + } + amdrunwaiting(amd); +#endif + } else { + if ((srb->SRBState & (SRB_START | SRB_MSGOUT)) + || !(srb->SRBState & (SRB_DISCONNECT | SRB_COMPLETED))) { + srb->TargetStatus = AMD_SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } else if (srb->SRBState & SRB_DISCONNECT) { + if (!(srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID)) + amd->untagged_srbs[target][lun] = srb; + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_COMPLETED) { + disc1: + srb->SRBState = SRB_FREE; + SRBdone(amd, srb); + } + } + return; +} + +static void +amd_Reselect(struct amd_softc *amd) +{ + struct amd_target_info *tinfo; + u_int16_t disc_count; + + amd_clear_msg_state(amd); + if (amd->active_srb != NULL) { + /* Requeue the SRB for our attempted Selection */ + TAILQ_REMOVE(&amd->running_srbs, amd->active_srb, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, amd->active_srb, links); + amd->active_srb = NULL; + } + /* get ID */ + amd->cur_target = amd_read8(amd, SCSIFIFOREG); + amd->cur_target ^= amd->HostID_Bit; + amd->cur_target = ffs(amd->cur_target) - 1; + amd->cur_lun = amd_read8(amd, SCSIFIFOREG) & 7; + tinfo = &amd->tinfo[amd->cur_target]; + amd->active_srb = amd->untagged_srbs[amd->cur_target][amd->cur_lun]; + disc_count = amd->disc_count[amd->cur_target][amd->cur_lun]; + if (disc_count == 0) { + printf("amd%d: Unexpected reselection for target %d, " + "Issuing Abort\n", amd->unit, amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } + if (amd->active_srb != NULL) { + amd->disc_count[amd->cur_target][amd->cur_lun]--; + amd->untagged_srbs[amd->cur_target][amd->cur_lun] = NULL; + } + + amd_write8(amd, SCSIDESTIDREG, amd->cur_target); + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG1, tinfo->CtrlR1); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD);/* drop /ACK */ + amd->last_phase = SCSI_NOP0; +} + +static void +SRBdone(struct amd_softc *amd, struct amd_srb *pSRB) +{ + u_int8_t bval, i, status; + union ccb *pccb; + struct ccb_scsiio *pcsio; + int intflag; + struct amd_sg *ptr2; + u_int32_t swlval; + u_int target_id, target_lun; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target_id = pSRB->pccb->ccb_h.target_id; + target_lun = pSRB->pccb->ccb_h.target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, + ("SRBdone - TagNumber %d\n", pSRB->TagNumber)); + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + bus_dmasync_op_t op; + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_POSTREAD; + else + op = BUS_DMASYNC_POSTWRITE; + bus_dmamap_sync(amd->buffer_dmat, pSRB->dmamap, op); + bus_dmamap_unload(amd->buffer_dmat, pSRB->dmamap); + } + + status = pSRB->TargetStatus; + pccb->ccb_h.status = CAM_REQ_CMP; + if (pSRB->SRBFlag & AUTO_REQSENSE) { + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = SCSI_STATUS_CHECK_COND; + + if (status == SCSI_STATUS_CHECK_COND) { + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + goto ckc_e; + } + *((u_int32_t *)&(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + + pcsio->sense_resid = pcsio->sense_len + - pSRB->TotalXferredLen; + pSRB->TotalXferredLen = pSRB->Segment1[1]; + if (pSRB->TotalXferredLen) { + /* ???? */ + pcsio->resid = pcsio->dxfer_len + - pSRB->TotalXferredLen; + /* The resid field contains valid data */ + /* Flush resid bytes on complete */ + } else { + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + } + pccb->ccb_h.status = CAM_AUTOSNS_VALID; + goto ckc_e; + } + if (status) { + if (status == SCSI_STATUS_CHECK_COND) { + + if ((pSRB->SGIndex < pSRB->SGcount) + && (pSRB->SGcount) && (pSRB->SGToBeXferLen)) { + bval = pSRB->SGcount; + swlval = pSRB->SGToBeXferLen; + ptr2 = pSRB->pSGlist; + ptr2++; + for (i = pSRB->SGIndex + 1; i < bval; i++) { + swlval += ptr2->SGXLen; + ptr2++; + } + /* ??????? */ + pcsio->resid = (u_int32_t) swlval; + +#ifdef AMD_DEBUG0 + printf("XferredLen=%8x,NotYetXferLen=%8x,", + pSRB->TotalXferredLen, swlval); +#endif + } + if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { +#ifdef AMD_DEBUG0 + printf("RequestSense..................\n"); +#endif + RequestSense(amd, pSRB); + return; + } + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == SCSI_STATUS_QUEUE_FULL) { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pcsio->scsi_status = SCSI_STATUS_QUEUE_FULL; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == AMD_SCSI_STAT_SEL_TIMEOUT) { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + + pcsio->scsi_status = AMD_SCSI_STAT_SEL_TIMEOUT; + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + } else if (status == SCSI_STATUS_BUSY) { +#ifdef AMD_DEBUG0 + printf("DC390: target busy at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_BUSY; + pccb->ccb_h.status = CAM_SCSI_BUSY; + } else if (status == SCSI_STATUS_RESERV_CONFLICT) { +#ifdef AMD_DEBUG0 + printf("DC390: target reserved at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_RESERV_CONFLICT; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; /* XXX */ + } else { + pSRB->AdaptStatus = 0; +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup at %s %d\n", + __FILE__, __LINE__); +#endif + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + } + } else { + status = pSRB->AdaptStatus; + if (status & H_OVER_UNDER_RUN) { + pSRB->TargetStatus = 0; + + pccb->ccb_h.status = CAM_DATA_RUN_ERR; + } else if (pSRB->SRBStatus & PARITY_ERROR) { +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup %s %d\n", + __FILE__, __LINE__); +#endif + /* Driver failed to perform operation */ + pccb->ccb_h.status = CAM_UNCOR_PARITY; + } else { /* No error */ + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + /* there is no error, (sense is invalid) */ + } + } +ckc_e: + intflag = splcam(); + if ((pccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + /* CAM request not yet complete =>device_Q frozen */ + xpt_freeze_devq(pccb->ccb_h.path, 1); + pccb->ccb_h.status |= CAM_DEV_QFRZN; + } + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + amdrunwaiting(amd); + splx(intflag); + xpt_done(pccb); + +} + +static void +amd_ResetSCSIBus(struct amd_softc * amd) +{ + int intflag; + + intflag = splcam(); + amd->ACBFlag |= RESET_DEV; + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, RST_SCSI_BUS_CMD); + splx(intflag); + return; +} + +static void +amd_ScsiRstDetect(struct amd_softc * amd) +{ + int intflag; + u_int32_t wlval; + +#ifdef AMD_DEBUG0 + printf("amd_ScsiRstDetect \n"); +#endif + + wlval = 1000; + while (--wlval) { /* delay 1 sec */ + DELAY(1000); + } + intflag = splcam(); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->ACBFlag & RESET_DEV) { + amd->ACBFlag |= RESET_DONE; + } else { + amd->ACBFlag |= RESET_DETECT; + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + amdrunwaiting(amd); + } + splx(intflag); + return; +} + +static void +RequestSense(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->Segment0[0] = *((u_int32_t *) & (pSRB->CmdBlock[0])); + pSRB->Segment0[1] = *((u_int32_t *) & (pSRB->CmdBlock[4])); + pSRB->Segment1[0] = (pSRB->ScsiCmdLen << 8) + pSRB->SGcount; + pSRB->Segment1[1] = pSRB->TotalXferredLen; + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + pSRB->Segmentx.SGXPtr = (u_int32_t) vtophys(&pcsio->sense_data); + pSRB->Segmentx.SGXLen = (u_int32_t) pcsio->sense_len; + + pSRB->pSGlist = &pSRB->Segmentx; + pSRB->SGcount = 1; + pSRB->SGIndex = 0; + + *((u_int32_t *) & (pSRB->CmdBlock[0])) = 0x00000003; + pSRB->CmdBlock[1] = pSRB->pccb->ccb_h.target_lun << 5; + *((u_int16_t *) & (pSRB->CmdBlock[4])) = pcsio->sense_len; + pSRB->ScsiCmdLen = 6; + + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if (amdstart(amd, pSRB) != 0) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, pSRB, links); + } +} + +static void +amd_InvalidCmd(struct amd_softc * amd) +{ + struct amd_srb *srb; + + srb = amd->active_srb; + if (srb->SRBState & (SRB_START|SRB_MSGOUT)) + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); +} + +void +amd_linkSRB(struct amd_softc *amd) +{ + u_int16_t count, i; + struct amd_srb *psrb; + + count = amd->SRBCount; + + for (i = 0; i < count; i++) { + psrb = (struct amd_srb *)&amd->SRB_array[i]; + psrb->TagNumber = i; + TAILQ_INSERT_TAIL(&amd->free_srbs, psrb, links); + } +} + +void +amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval) +{ + if (mode == ENABLE_CE) { + *regval = 0xc0; + } else { + *regval = 0x80; + } + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + if (mode == DISABLE_CE) { + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + } + DELAY(160); +} + +void +amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry) +{ + u_int bval; + + bval = 0; + if (Carry) { + bval = 0x40; + *regval = 0x80; + pci_cfgwrite(amd->config_id, *regval, bval, /*bytes*/1); + } + DELAY(160); + bval |= 0x80; + pci_cfgwrite(amd->config_id, *regval, bval, /*bytes*/1); + DELAY(160); + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + DELAY(160); +} + +static int +amd_EEpromInDO(struct amd_softc *amd) +{ + pci_cfgwrite(amd->config_id, 0x80, 0x80, /*bytes*/1); + DELAY(160); + pci_cfgwrite(amd->config_id, 0x80, 0x40, /*bytes*/1); + DELAY(160); + if (pci_cfgread(amd->config_id, 0, /*bytes*/1) == 0x22) + return (1); + return (0); +} + +static u_int16_t +EEpromGetData1(struct amd_softc *amd) +{ + u_int i; + u_int carryFlag; + u_int16_t wval; + + wval = 0; + for (i = 0; i < 16; i++) { + wval <<= 1; + carryFlag = amd_EEpromInDO(amd); + wval |= carryFlag; + } + return (wval); +} + +static void +amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd) +{ + u_int i, j; + int carryFlag; + + carryFlag = 1; + j = 0x80; + for (i = 0; i < 9; i++) { + amd_EEpromOutDI(amd, regval, carryFlag); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + +static void +amd_ReadEEprom(struct amd_softc *amd) +{ + int regval; + u_int i; + u_int16_t *ptr; + u_int8_t cmd; + + ptr = (u_int16_t *)&amd->eepromBuf[0]; + cmd = EEPROM_READ; + for (i = 0; i < 0x40; i++) { + amd_EnDisableCE(amd, ENABLE_CE, ®val); + amd_Prepare(amd, ®val, cmd); + *ptr = EEpromGetData1(amd); + ptr++; + cmd++; + amd_EnDisableCE(amd, DISABLE_CE, ®val); + } +} + +static void +amd_load_defaults(struct amd_softc *amd) +{ + int target; + + bzero(&amd->eepromBuf, sizeof amd->eepromBuf); + for (target = 0; target < MAX_SCSI_ID; target++) + amd->eepromBuf[target << 2] = + (TAG_QUEUING|EN_DISCONNECT|SYNC_NEGO|PARITY_CHK); + amd->eepromBuf[EE_ADAPT_SCSI_ID] = 7; + amd->eepromBuf[EE_MODE2] = ACTIVE_NEGATION|LUN_CHECK|GREATER_1G; + amd->eepromBuf[EE_TAG_CMD_NUM] = 4; +} + +static void +amd_load_eeprom_or_defaults(struct amd_softc *amd) +{ + u_int16_t wval, *ptr; + u_int8_t i; + + amd_ReadEEprom(amd); + wval = 0; + ptr = (u_int16_t *) & amd->eepromBuf[0]; + for (i = 0; i < EE_DATA_SIZE; i += 2, ptr++) + wval += *ptr; + + if (wval != EE_CHECKSUM) { + if (bootverbose) + printf("amd%d: SEEPROM data unavailable. " + "Using default device parameters.\n", + amd->unit); + amd_load_defaults(amd); + } +} + +/* + ********************************************************************** + * Function : static int amd_init (struct Scsi_Host *host) + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure/ + ********************************************************************** + */ +static struct amd_softc * +amd_init(int unit, pcici_t config_id) +{ + struct amd_softc *amd; + u_int bval; + u_int i; + + amd = (struct amd_softc *)malloc(sizeof(struct amd_softc), + M_DEVBUF, M_WAITOK); + if (amd == NULL) { + printf("DC390%d: cannot allocate ACB !\n", unit); + return (amd); + } + bzero(amd, sizeof(struct amd_softc)); + amd->tag = I386_BUS_SPACE_IO; + amd->bsh = pci_conf_read(config_id, PCI_MAP_REG_START) & 0xFFFE; + /* DMA tag for mapping buffers into device visible space. */ + if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/0, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/MAXBSIZE, /*nsegments*/AMD_NSEG, + /*maxsegsz*/AMD_MAXTRANSFER_SIZE, + /*flags*/BUS_DMA_ALLOCNOW, + &amd->buffer_dmat) != 0) { + free(amd, M_DEVBUF); + return (NULL); + } + TAILQ_INIT(&amd->free_srbs); + TAILQ_INIT(&amd->running_srbs); + TAILQ_INIT(&amd->waiting_srbs); + amd->last_phase = SCSI_BUS_FREE; + amd->config_id = config_id; + amd->unit = unit; + amd->SRBCount = MAX_SRB_CNT; + amd->status = 0; + amd_load_eeprom_or_defaults(amd); + amd->max_id = 7; + if (amd->eepromBuf[EE_MODE2] & LUN_CHECK) { + amd->max_lun = 7; + } else { + amd->max_lun = 0; + } + amd->AdaptSCSIID = amd->eepromBuf[EE_ADAPT_SCSI_ID]; + amd->HostID_Bit = (1 << amd->AdaptSCSIID); + amd->AdaptSCSILUN = 0; + /* (eepromBuf[EE_TAG_CMD_NUM]) << 2; */ + amd->ACBFlag = 0; + amd->Gmode2 = amd->eepromBuf[EE_MODE2]; + amd_linkSRB(amd); + for (i = 0; i <= amd->max_id; i++) { + + if (amd->AdaptSCSIID != i) { + struct amd_target_info *tinfo; + PEEprom prom; + + tinfo = &amd->tinfo[i]; + prom = (PEEprom)&amd->eepromBuf[i << 2]; + if ((prom->EE_MODE1 & EN_DISCONNECT) != 0) { + tinfo->disc_tag |= AMD_USR_DISCENB; + if ((prom->EE_MODE1 & TAG_QUEUING) != 0) + tinfo->disc_tag |= AMD_USR_TAGENB; + } + if ((prom->EE_MODE1 & SYNC_NEGO) != 0) { + tinfo->user.period = + eeprom_period[prom->EE_SPEED]; + tinfo->user.offset = AMD_MAX_SYNC_OFFSET; + } + tinfo->CtrlR1 = amd->AdaptSCSIID; + if ((prom->EE_MODE1 & PARITY_CHK) != 0) + tinfo->CtrlR1 |= PARITY_ERR_REPO; + tinfo->CtrlR3 = FAST_CLK; + tinfo->CtrlR4 = EATER_25NS; + if ((amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) != 0) + tinfo->CtrlR4 |= NEGATE_REQACKDATA; + } + } + amd_write8(amd, SCSITIMEOUTREG, 153); /* 250ms selection timeout */ + /* Conversion factor = 0 , 40MHz clock */ + amd_write8(amd, CLKFACTREG, CLK_FREQ_40MHZ); + /* NOP cmd - clear command register */ + amd_write8(amd, SCSICMDREG, NOP_CMD); + amd_write8(amd, CNTLREG2, EN_FEATURE|EN_SCSI2_CMD); + amd_write8(amd, CNTLREG3, FAST_CLK); + bval = EATER_25NS; + if (amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) { + bval |= NEGATE_REQACKDATA; + } + amd_write8(amd, CNTLREG4, bval); + + /* Disable SCSI bus reset interrupt */ + amd_write8(amd, CNTLREG1, DIS_INT_ON_SCSI_RST); + return (amd); +} + +/* + * attach and init a host adapter + */ +static void +amd_attach(pcici_t config_id, int unit) +{ + struct cam_devq *devq; /* Device Queue to use for this SIM */ + u_int8_t intstat; + u_int32_t wlval; + struct amd_softc *amd = NULL; + + wlval = pci_conf_read(config_id, PCI_ID_REG); + + if (wlval == PCI_DEVICE_ID_AMD53C974) { + if ((amd = amd_init(unit, config_id)) == NULL) + return; + + /* Reset Pending INT */ + intstat = amd_read8(amd, INTSTATREG); + } + + /* After setting up the adapter, map our interrupt */ + if (!pci_map_int(config_id, amd_intr, amd, &cam_imask)) { + if (bootverbose) + printf("amd%d: unable to register interrupt handler!\n", + unit); + free(amd, M_DEVBUF); + return; + } + + /* + * Now let the CAM generic SCSI layer find the SCSI devices on + * the bus * start queue to reset to the idle loop. * + * Create device queue of SIM(s) * (MAX_START_JOB - 1) : + * max_sim_transactions + */ + devq = cam_simq_alloc(MAX_START_JOB); + if (devq == NULL) { + free(amd, M_DEVBUF); + return; + } + + amd->psim = cam_sim_alloc(amd_action, amd_poll, "amd", + amd, amd->unit, 1, MAX_TAGS_CMD_QUEUE, + devq); + if (amd->psim == NULL) { + cam_simq_free(devq); + free(amd, M_DEVBUF); + return; + } + + if (xpt_bus_register(amd->psim, 0) != CAM_SUCCESS) { + cam_sim_free(amd->psim, /*free_devq*/TRUE); + free(amd, M_DEVBUF); + return; + } + + if (xpt_create_path(&amd->ppath, /* periph */ NULL, + cam_sim_path(amd->psim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(amd->psim)); + cam_sim_free(amd->psim, /* free_simq */ TRUE); + free(amd, M_DEVBUF); + return; + } +} + +static const char * +amd_probe(pcici_t tag, pcidi_t type) +{ + if (type == PCI_DEVICE_ID_AMD53C974) { + return ("Tekram DC390(T)/AMD53c974 SCSI Host Adapter"); + } else { + return (NULL); + } +} diff --git a/sys/dev/amd/amd.h b/sys/dev/amd/amd.h new file mode 100644 index 000000000000..c140e313cf6d --- /dev/null +++ b/sys/dev/amd/amd.h @@ -0,0 +1,579 @@ +/* + ********************************************************************* + * FILE NAME : amd.h + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. + ********************************************************************* + */ + +#ifndef AMD_H +#define AMD_H + +#define AMD_TRANS_CUR 0x01 /* Modify current neogtiation status */ +#define AMD_TRANS_ACTIVE 0x03 /* Assume this is the active target */ +#define AMD_TRANS_GOAL 0x04 /* Modify negotiation goal */ +#define AMD_TRANS_USER 0x08 /* Modify user negotiation settings */ + +/* + * Per target transfer parameters. + */ +struct amd_transinfo { + u_int8_t period; + u_int8_t offset; +}; + +struct amd_target_info { + /* + * Records the currently active and user/default settings for + * tagged queueing and disconnection for each target. + */ + u_int8_t disc_tag; +#define AMD_CUR_DISCENB 0x01 +#define AMD_CUR_TAGENB 0x02 +#define AMD_USR_DISCENB 0x04 +#define AMD_USR_TAGENB 0x08 + u_int8_t CtrlR1; + u_int8_t CtrlR3; + u_int8_t CtrlR4; + u_int8_t sync_period_reg; + u_int8_t sync_offset_reg; + + /* + * Currently active transfer settings. + */ + struct amd_transinfo current; + /* + * Transfer settings we wish to achieve + * through negotiation. + */ + struct amd_transinfo goal; + /* + * User defined or default transfer settings. + */ + struct amd_transinfo user; +}; + +/* + * Scatter/Gather Segment entry. + */ +struct amd_sg { + u_int32_t SGXLen; + u_int32_t SGXPtr; +}; + +/* + * Chipset feature limits + */ +#define MAX_SCSI_ID 8 +#define AMD_MAX_SYNC_OFFSET 15 +#define AMD_TARGET_MAX 7 +#define AMD_LUN_MAX 7 +#define AMD_NSEG (btoc(MAXPHYS) + 1) +#define AMD_MAXTRANSFER_SIZE 0xFFFFFF /* restricted by 24 bit counter */ +#define MAX_DEVICES 10 +#define MAX_TAGS_CMD_QUEUE 256 +#define MAX_CMD_PER_LUN 6 +#define MAX_SRB_CNT 256 +#define MAX_START_JOB 256 + +/* + * BIT position to integer mapping. + */ +#define BIT(N) (0x01 << N) + +/* + * EEPROM storage offsets and data structures. + */ +typedef struct _EEprom { + u_int8_t EE_MODE1; + u_int8_t EE_SPEED; + u_int8_t xx1; + u_int8_t xx2; +} EEprom, *PEEprom; + +#define EE_ADAPT_SCSI_ID 64 +#define EE_MODE2 65 +#define EE_DELAY 66 +#define EE_TAG_CMD_NUM 67 +#define EE_DATA_SIZE 128 +#define EE_CHECKSUM 0x1234 + +/* + * EE_MODE1 bits definition + */ +#define PARITY_CHK BIT(0) +#define SYNC_NEGO BIT(1) +#define EN_DISCONNECT BIT(2) +#define SEND_START BIT(3) +#define TAG_QUEUING BIT(4) + +/* + * EE_MODE2 bits definition + */ +#define MORE2_DRV BIT(0) +#define GREATER_1G BIT(1) +#define RST_SCSI_BUS BIT(2) +#define ACTIVE_NEGATION BIT(3) +#define NO_SEEK BIT(4) +#define LUN_CHECK BIT(5) + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +#define AMD_TAG_WILDCARD ((u_int)(~0)) + +/* + * SCSI Request Block + */ +struct amd_srb { + TAILQ_ENTRY(amd_srb) links; + u_int8_t CmdBlock[12]; + union ccb *pccb; + bus_dmamap_t dmamap; + struct amd_sg *pSGlist; + + u_int32_t TotalXferredLen; + u_int32_t SGPhysAddr; /* a segment starting address */ + u_int32_t SGToBeXferLen; /* to be xfer length */ + u_int32_t Segment0[2]; + u_int32_t Segment1[2]; + + struct amd_sg SGsegment[AMD_NSEG]; + struct amd_sg Segmentx;/* a one entry of S/G list table */ + u_int8_t *pMsgPtr; + u_int16_t SRBState; + + u_int8_t AdaptStatus; + u_int8_t TargetStatus; + u_int8_t MsgCnt; + u_int8_t EndMessage; + u_int8_t TagNumber; + u_int8_t SGcount; + u_int8_t SGIndex; + u_int8_t IORBFlag; /* ;81h-Reset, 2-retry */ + + u_int8_t SRBStatus; + u_int8_t SRBFlag; + /* ; b0-AutoReqSense,b6-Read,b7-write */ + /* ; b4-settimeout,b5-Residual valid */ + u_int8_t ScsiCmdLen; +}; + +TAILQ_HEAD(srb_queue, amd_srb); + +/* + * Per-adapter, software configuration. + */ +struct amd_softc { + bus_space_tag_t tag; + bus_space_handle_t bsh; + bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */ + int unit; + + int last_phase; + int cur_target; + int cur_lun; + struct amd_srb *active_srb; + struct amd_srb *untagged_srbs[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + struct amd_target_info tinfo[AMD_TARGET_MAX+1]; + u_int16_t disc_count[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + + struct srb_queue free_srbs; + struct srb_queue waiting_srbs; + struct srb_queue running_srbs; + + pcici_t config_id; + struct amd_srb *pTmpSRB; + + u_int16_t SRBCount; + + u_int16_t max_id; + u_int16_t max_lun; + + /* Hooks into the CAM XPT */ + struct cam_sim *psim; + struct cam_path *ppath; + + u_int8_t msgin_buf[6]; + u_int8_t msgout_buf[6]; + u_int msgin_index; + u_int msgout_index; + u_int msgout_len; + + u_int8_t status; + u_int8_t AdaptSCSIID; /* ; Adapter SCSI Target ID */ + u_int8_t AdaptSCSILUN; /* ; Adapter SCSI LUN */ + + u_int8_t ACBFlag; + + u_int8_t Gmode2; + + u_int8_t HostID_Bit; + + u_int8_t InitDCB_flag[8][8]; /* flag of initDCB for device */ + struct amd_srb SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */ + struct amd_srb TmpSRB; + /* Setup data stored in an 93c46 serial eeprom */ + u_int8_t eepromBuf[EE_DATA_SIZE]; +}; + +/* + * ----SRB State machine definition + */ +#define SRB_FREE 0 +#define SRB_READY BIT(1) +#define SRB_MSGOUT BIT(2) /* ;arbitration+msg_out 1st byte */ +#define SRB_MSGIN BIT(3) +#define SRB_MSGIN_MULTI BIT(4) +#define SRB_COMMAND BIT(5) +#define SRB_START BIT(6) /* ;arbitration+msg_out+command_out */ +#define SRB_DISCONNECT BIT(7) +#define SRB_DATA_XFER BIT(8) +#define SRB_XFERPAD BIT(9) +#define SRB_STATUS BIT(10) +#define SRB_COMPLETED BIT(11) +#define SRB_ABORT_SENT BIT(12) +#define DO_SYNC_NEGO BIT(13) +#define SRB_UNEXPECT_RESEL BIT(14) + +/* + * ---ACB Flag + */ +#define RESET_DEV BIT(0) +#define RESET_DETECT BIT(1) +#define RESET_DONE BIT(2) + +/* + * ---DCB Flag + */ +#define ABORT_DEV_ BIT(0) + +/* + * ---SRB status + */ +#define SRB_OK BIT(0) +#define ABORTION BIT(1) +#define OVER_RUN BIT(2) +#define UNDER_RUN BIT(3) +#define PARITY_ERROR BIT(4) +#define SRB_ERROR BIT(5) + +/* + * ---SRB Flags + */ +#define DATAOUT BIT(7) +#define DATAIN BIT(6) +#define RESIDUAL_VALID BIT(5) +#define ENABLE_TIMER BIT(4) +#define RESET_DEV0 BIT(2) +#define ABORT_DEV BIT(1) +#define AUTO_REQSENSE BIT(0) + +/* + * ---Adapter status + */ +#define H_STATUS_GOOD 0 +#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 0x0FF + +/* + * AMD specific "status" codes returned in the SCSI status byte. + */ +#define AMD_SCSI_STAT_UNEXP_BUS_F 0xFD /* ; Unexpect Bus Free */ +#define AMD_SCSI_STAT_BUS_RST_DETECT 0xFE /* ; Scsi Bus Reset detected */ +#define AMD_SCSI_STAT_SEL_TIMEOUT 0xFF /* ; Selection Time out */ + +/* + * ---Sync_Mode + */ +#define SYNC_DISABLE 0 +#define SYNC_ENABLE BIT(0) +#define SYNC_NEGO_DONE BIT(1) +#define WIDE_ENABLE BIT(2) +#define WIDE_NEGO_DONE BIT(3) +#define EN_TAG_QUEUING BIT(4) +#define EN_ATN_STOP BIT(5) + +#define SYNC_NEGO_OFFSET 15 + +/* + * ---SCSI bus phase + */ +#define SCSI_DATA_OUT 0 +#define SCSI_DATA_IN 1 +#define SCSI_COMMAND 2 +#define SCSI_STATUS 3 +#define SCSI_NOP0 4 +#define SCSI_ARBITRATING 5 +#define SCSI_MSG_OUT 6 +#define SCSI_MSG_IN 7 +#define SCSI_BUS_FREE 8 + +/* + *========================================================== + * AMD 53C974 Registers bit Definition + *========================================================== + */ + +/* + * ------SCSI Register------- + * Command Reg.(+0CH) + */ +#define DMA_COMMAND BIT(7) +#define NOP_CMD 0 +#define CLEAR_FIFO_CMD 1 +#define RST_DEVICE_CMD 2 +#define RST_SCSI_BUS_CMD 3 +#define INFO_XFER_CMD 0x10 +#define INITIATOR_CMD_CMPLTE 0x11 +#define MSG_ACCEPTED_CMD 0x12 +#define XFER_PAD_BYTE 0x18 +#define SET_ATN_CMD 0x1A +#define RESET_ATN_CMD 0x1B +#define SEL_W_ATN 0x42 +#define SEL_W_ATN_STOP 0x43 +#define EN_SEL_RESEL 0x44 +#define SEL_W_ATN2 0x46 +#define DATA_XFER_CMD INFO_XFER_CMD + + +/* + * ------SCSI Register------- + * SCSI Status Reg.(+10H) + */ +#define INTERRUPT BIT(7) +#define ILLEGAL_OP_ERR BIT(6) +#define PARITY_ERR BIT(5) +#define COUNT_2_ZERO BIT(4) +#define GROUP_CODE_VALID BIT(3) +#define SCSI_PHASE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Interrupt Status Reg.(+14H) + */ +#define SCSI_RESET_ BIT(7) +#define INVALID_CMD BIT(6) +#define DISCONNECTED BIT(5) +#define SERVICE_REQUEST BIT(4) +#define SUCCESSFUL_OP BIT(3) +#define RESELECTED BIT(2) +#define SEL_ATTENTION BIT(1) +#define SELECTED BIT(0) + +/* + * ------SCSI Register------- + * Internal State Reg.(+18H) + */ +#define SYNC_OFFSET_FLAG BIT(3) +#define INTRN_STATE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Clock Factor Reg.(+24H) + */ +#define CLK_FREQ_40MHZ 0 +#define CLK_FREQ_35MHZ (BIT(2)+BIT(1)+BIT(0)) +#define CLK_FREQ_30MHZ (BIT(2)+BIT(1)) +#define CLK_FREQ_25MHZ (BIT(2)+BIT(0)) +#define CLK_FREQ_20MHZ BIT(2) +#define CLK_FREQ_15MHZ (BIT(1)+BIT(0)) +#define CLK_FREQ_10MHZ BIT(1) + +/* + * ------SCSI Register------- + * Control Reg. 1(+20H) + */ +#define EXTENDED_TIMING BIT(7) +#define DIS_INT_ON_SCSI_RST BIT(6) +#define PARITY_ERR_REPO BIT(4) +#define SCSI_ID_ON_BUS (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Control Reg. 2(+2CH) + */ +#define EN_FEATURE BIT(6) +#define EN_SCSI2_CMD BIT(3) + +/* + * ------SCSI Register------- + * Control Reg. 3(+30H) + */ +#define ID_MSG_CHECK BIT(7) +#define EN_QTAG_MSG BIT(6) +#define EN_GRP2_CMD BIT(5) +#define FAST_SCSI BIT(4) /* ;10MB/SEC */ +#define FAST_CLK BIT(3) /* ;25 - 40 MHZ */ + +/* + * ------SCSI Register------- + * Control Reg. 4(+34H) + */ +#define EATER_12NS 0 +#define EATER_25NS BIT(7) +#define EATER_35NS BIT(6) +#define EATER_0NS (BIT(7)+BIT(6)) +#define NEGATE_REQACKDATA BIT(2) +#define NEGATE_REQACK BIT(3) + +/* + *======================================== + * DMA Register + *======================================== + */ + +/* + * -------DMA Register-------- + * DMA Command Reg.(+40H) + */ +#define READ_DIRECTION BIT(7) +#define WRITE_DIRECTION 0 +#define EN_DMA_INT BIT(6) +#define MAP_TO_MDL BIT(5) +#define DMA_DIAGNOSTIC BIT(4) +#define DMA_IDLE_CMD 0 +#define DMA_BLAST_CMD BIT(0) +#define DMA_ABORT_CMD BIT(1) +#define DMA_START_CMD (BIT(1)|BIT(0)) + +/* + * -------DMA Register-------- + * DMA Status Reg.(+54H) + */ +#define PCI_MS_ABORT BIT(6) +#define BLAST_COMPLETE BIT(5) +#define SCSI_INTERRUPT BIT(4) +#define DMA_XFER_DONE BIT(3) +#define DMA_XFER_ABORT BIT(2) +#define DMA_XFER_ERROR BIT(1) +#define POWER_DOWN BIT(0) + +/* + * -------DMA Register-------- + * DMA SCSI Bus and Ctrl.(+70H) + * EN_INT_ON_PCI_ABORT + */ + +/* + *========================================================== + * SCSI Chip register address offset + *========================================================== + */ +#define CTCREG_LOW 0x00 /* (R) current transfer count register low */ +#define STCREG_LOW 0x00 /* (W) start transfer count register low */ + +#define CTCREG_MID 0x04 /* (R) current transfer count register + * middle */ +#define STCREG_MID 0x04 /* (W) start transfer count register middle */ + +#define SCSIFIFOREG 0x08 /* (R/W) SCSI FIFO register */ + +#define SCSICMDREG 0x0C /* (R/W) SCSI command register */ + +#define SCSISTATREG 0x10 /* (R) SCSI status register */ +#define SCSIDESTIDREG 0x10 /* (W) SCSI destination ID register */ + +#define INTSTATREG 0x14 /* (R) interrupt status register */ +#define SCSITIMEOUTREG 0x14 /* (W) SCSI timeout register */ + + +#define INTERNSTATREG 0x18 /* (R) internal state register */ +#define SYNCPERIOREG 0x18 /* (W) synchronous transfer period register */ + +#define CURRENTFIFOREG 0x1C /* (R) current FIFO/internal state register */ +#define SYNCOFFREG 0x1C/* (W) synchronous transfer period register */ + +#define CNTLREG1 0x20 /* (R/W) control register 1 */ +#define CLKFACTREG 0x24 /* (W) clock factor register */ +#define CNTLREG2 0x2C /* (R/W) control register 2 */ +#define CNTLREG3 0x30 /* (R/W) control register 3 */ +#define CNTLREG4 0x34 /* (R/W) control register 4 */ + +#define CURTXTCNTREG 0x38 /* (R) current transfer count register + * high/part-unique ID code */ +#define STCREG_HIGH 0x38 /* (W) Start current transfer count register + * high */ + +/* + ********************************************************* + * + * SCSI DMA register + * + ********************************************************* + */ +#define DMA_Cmd 0x40 /* (R/W) command register */ +#define DMA_XferCnt 0x44 /* (R/W) starting transfer count */ +#define DMA_XferAddr 0x48 /* (R/W) starting Physical address */ +#define DMA_Wk_ByteCntr 0x4C /* ( R ) working byte counter */ +#define DMA_Wk_AddrCntr 0x50 /* ( R ) working address counter */ +#define DMA_Status 0x54 /* ( R ) status register */ +#define DMA_MDL_Addr 0x58 /* (R/W) starting memory descriptor list (MDL) + * address */ +#define DMA_Wk_MDL_Cntr 0x5C /* ( R ) working MDL counter */ +#define DMA_ScsiBusCtrl 0x70 /* (bits R/W) SCSI BUS and control */ + +/* ******************************************************* */ +#define am_target SCSISTATREG +#define am_timeout INTSTATREG +#define am_seq_step SYNCPERIOREG +#define am_fifo_count SYNCOFFREG + + +#define amd_read8(amd, port) \ + bus_space_read_1((amd)->tag, (amd)->bsh, port) + +#define amd_read16(amd, port) \ + bus_space_read_2((amd)->tag, (amd)->bsh, port) + +#define amd_read32(amd, port) \ + bus_space_read_4((amd)->tag, (amd)->bsh, port) + +#define amd_write8(amd, port, value) \ + bus_space_write_1((amd)->tag, (amd)->bsh, port, value) + +#define amd_write8_multi(amd, port, ptr, len) \ + bus_space_write_multi_1((amd)->tag, (amd)->bsh, port, ptr, len) + +#define amd_write16(amd, port, value) \ + bus_space_write_2((amd)->tag, (amd)->bsh, port, value) + +#define amd_write32(amd, port, value) \ + bus_space_write_4((amd)->tag, (amd)->bsh, port, value) + +#endif /* AMD_H */ diff --git a/sys/pci/amd.c b/sys/pci/amd.c new file mode 100644 index 000000000000..c8a5d3bc05f2 --- /dev/null +++ b/sys/pci/amd.c @@ -0,0 +1,2436 @@ +/* + ********************************************************************* + * FILE NAME : amd.c + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. + ********************************************************************* + */ + +/* + ********************************************************************* + * HISTORY: + * + * REV# DATE NAME DESCRIPTION + * 1.00 07/02/96 CLH First release for RELEASE-2.1.0 + * 1.01 08/20/96 CLH Update for RELEASE-2.1.5 + * 1.02 11/06/96 CLH Fixed more than 1 LUN scanning + * 1.03 12/20/96 CLH Modify to support 2.2-ALPHA + * 1.04 12/26/97 CLH Modify to support RELEASE-2.2.5 + * 1.05 01/01/99 ERICH CHEN Modify to support RELEASE-3.0.x (CAM) + ********************************************************************* + */ + +/* #define AMD_DEBUG0 */ +/* #define AMD_DEBUG_SCSI_PHASE */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define PCI_DEVICE_ID_AMD53C974 0x20201022ul +#define PCI_BASE_ADDR0 0x10 + +typedef u_int (phase_handler_t)(struct amd_softc *, struct amd_srb *, u_int); +typedef phase_handler_t *phase_handler_func_t; + +static void amd_intr(void *vamd); +static int amdstart(struct amd_softc *amd, struct amd_srb * pSRB); +static phase_handler_t amd_NopPhase; + +static phase_handler_t amd_DataOutPhase0; +static phase_handler_t amd_DataInPhase0; +#define amd_CommandPhase0 amd_NopPhase +static phase_handler_t amd_StatusPhase0; +static phase_handler_t amd_MsgOutPhase0; +static phase_handler_t amd_MsgInPhase0; +static phase_handler_t amd_DataOutPhase1; +static phase_handler_t amd_DataInPhase1; +static phase_handler_t amd_CommandPhase1; +static phase_handler_t amd_StatusPhase1; +static phase_handler_t amd_MsgOutPhase1; +static phase_handler_t amd_MsgInPhase1; + +static void amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb); +static int amdparsemsg(struct amd_softc *amd); +static int amdhandlemsgreject(struct amd_softc *amd); +static void amdconstructsdtr(struct amd_softc *amd, + u_int period, u_int offset); +static u_int amdfindclockrate(struct amd_softc *amd, u_int *period); +static int amdsentmsg(struct amd_softc *amd, u_int msgtype, int full); + +static void DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int dir); +static void amd_Disconnect(struct amd_softc *amd); +static void amd_Reselect(struct amd_softc *amd); +static void SRBdone(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_ScsiRstDetect(struct amd_softc *amd); +static void amd_ResetSCSIBus(struct amd_softc *amd); +static void RequestSense(struct amd_softc *amd, struct amd_srb *pSRB); +static void amd_InvalidCmd(struct amd_softc *amd); + +static void amd_timeout(void *arg1); +static void amd_reset(struct amd_softc *amd); +static u_int8_t * phystovirt(struct amd_srb *pSRB, u_int32_t xferCnt); + +void amd_linkSRB(struct amd_softc *amd); +static struct amd_softc * + amd_init(int unit, pcici_t config_id); +static void amd_load_defaults(struct amd_softc *amd); +static void amd_load_eeprom_or_defaults(struct amd_softc *amd); +static int amd_EEpromInDO(struct amd_softc *amd); +static u_int16_t EEpromGetData1(struct amd_softc *amd); +static void amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval); +static void amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry); +static void amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd); +static void amd_ReadEEprom(struct amd_softc *amd); + +static const char *amd_probe(pcici_t tag, pcidi_t type); +static void amd_attach(pcici_t tag, int unit); +static void amdcompletematch(struct amd_softc *amd, target_id_t target, + lun_id_t lun, u_int tag, struct srb_queue *queue, + cam_status status); +static void amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type); +static void amdsettags(struct amd_softc *amd, u_int target, int tagenb); + +static __inline void amd_clear_msg_state(struct amd_softc *amd); + +static __inline void +amd_clear_msg_state(struct amd_softc *amd) +{ + amd->msgout_len = 0; + amd->msgout_index = 0; + amd->msgin_index = 0; +} + +static u_long amd_count; + +/* CAM SIM entry points */ +#define ccb_srb_ptr spriv_ptr0 +#define ccb_amd_ptr spriv_ptr1 +static void amd_action(struct cam_sim *sim, union ccb *ccb); +static void amd_poll(struct cam_sim *sim); + +/* + * PCI device module setup + */ +static struct pci_device amd_device = +{ + "amd", + amd_probe, + amd_attach, + &amd_count, + NULL +}; + +#ifdef COMPAT_PCI_DRIVER +COMPAT_PCI_DRIVER(amd, amd_device); +#else +DATA_SET(pcidevice_set, amd_device); +#endif + +/* + * State engine function tables indexed by SCSI phase number + */ +phase_handler_func_t amd_SCSI_phase0[] = { + amd_DataOutPhase0, + amd_DataInPhase0, + amd_CommandPhase0, + amd_StatusPhase0, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase0, + amd_MsgInPhase0 +}; + +phase_handler_func_t amd_SCSI_phase1[] = { + amd_DataOutPhase1, + amd_DataInPhase1, + amd_CommandPhase1, + amd_StatusPhase1, + amd_NopPhase, + amd_NopPhase, + amd_MsgOutPhase1, + amd_MsgInPhase1 +}; + +/* + * EEProm/BIOS negotiation periods + */ +u_int8_t eeprom_period[] = { + 25, /* 10.0MHz */ + 32, /* 8.0MHz */ + 38, /* 6.6MHz */ + 44, /* 5.7MHz */ + 50, /* 5.0MHz */ + 63, /* 4.0MHz */ + 83, /* 3.0MHz */ + 125 /* 2.0MHz */ +}; + +/* + * chip clock setting to SCSI specified sync parameter table. + */ +u_int8_t tinfo_sync_period[] = { + 25, /* 10.0 */ + 32, /* 8.0 */ + 38, /* 6.6 */ + 44, /* 5.7 */ + 50, /* 5.0 */ + 57, /* 4.4 */ + 63, /* 4.0 */ + 70, /* 3.6 */ + 76, /* 3.3 */ + 83 /* 3.0 */ +}; + +static __inline struct amd_srb * +amdgetsrb(struct amd_softc * amd) +{ + int intflag; + struct amd_srb * pSRB; + + intflag = splcam(); + pSRB = TAILQ_FIRST(&amd->free_srbs); + if (pSRB) + TAILQ_REMOVE(&amd->free_srbs, pSRB, links); + splx(intflag); + return (pSRB); +} + +static void +amdsetupcommand(struct amd_softc *amd, struct amd_srb *srb) +{ + struct scsi_request_sense sense_cmd; + struct ccb_scsiio *csio; + u_int8_t *cdb; + u_int cdb_len; + + csio = &srb->pccb->csio; + + if (srb->SRBFlag & AUTO_REQSENSE) { + sense_cmd.opcode = REQUEST_SENSE; + sense_cmd.byte2 = srb->pccb->ccb_h.target_lun << 5; + sense_cmd.unused[0] = 0; + sense_cmd.unused[1] = 0; + sense_cmd.length = csio->sense_len; + sense_cmd.control = 0; + cdb = &sense_cmd.opcode; + cdb_len = sizeof(sense_cmd); + } else { + cdb = &srb->CmdBlock[0]; + cdb_len = srb->ScsiCmdLen; + } + amd_write8_multi(amd, SCSIFIFOREG, cdb, cdb_len); +} + +/* + * Attempt to start a waiting transaction. Interrupts must be disabled + * upon entry to this function. + */ +static void +amdrunwaiting(struct amd_softc *amd) { + struct amd_srb *srb; + + if (amd->last_phase != SCSI_BUS_FREE) + return; + + srb = TAILQ_FIRST(&amd->waiting_srbs); + if (srb == NULL) + return; + + if (amdstart(amd, srb) == 0) { + TAILQ_REMOVE(&amd->waiting_srbs, srb, links); + TAILQ_INSERT_HEAD(&amd->running_srbs, srb, links); + } +} + +static void +amdexecutesrb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) +{ + struct amd_srb *srb; + union ccb *ccb; + struct amd_softc *amd; + int s; + + srb = (struct amd_srb *)arg; + ccb = srb->pccb; + amd = (struct amd_softc *)ccb->ccb_h.ccb_amd_ptr; + + if (error != 0) { + if (error != EFBIG) + printf("amd%d: Unexepected error 0x%x returned from " + "bus_dmamap_load\n", amd->unit, error); + if (ccb->ccb_h.status == CAM_REQ_INPROG) { + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; + } + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + return; + } + + if (nseg != 0) { + struct amd_sg *sg; + bus_dma_segment_t *end_seg; + bus_dmasync_op_t op; + + end_seg = dm_segs + nseg; + + /* Copy the segments into our SG list */ + srb->pSGlist = &srb->SGsegment[0]; + sg = srb->pSGlist; + while (dm_segs < end_seg) { + sg->SGXLen = dm_segs->ds_len; + sg->SGXPtr = dm_segs->ds_addr; + sg++; + dm_segs++; + } + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_PREREAD; + else + op = BUS_DMASYNC_PREWRITE; + + bus_dmamap_sync(amd->buffer_dmat, srb->dmamap, op); + + } + srb->SGcount = nseg; + srb->SGIndex = 0; + srb->AdaptStatus = 0; + srb->TargetStatus = 0; + srb->MsgCnt = 0; + srb->SRBStatus = 0; + srb->SRBFlag = 0; + srb->SRBState = 0; + srb->TotalXferredLen = 0; + srb->SGPhysAddr = 0; + srb->SGToBeXferLen = 0; + srb->EndMessage = 0; + + s = splcam(); + + /* + * Last time we need to check if this CCB needs to + * be aborted. + */ + if (ccb->ccb_h.status != CAM_REQ_INPROG) { + if (nseg != 0) + bus_dmamap_unload(amd->buffer_dmat, srb->dmamap); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + xpt_done(ccb); + splx(s); + return; + } + ccb->ccb_h.status |= CAM_SIM_QUEUED; +#if 0 + /* XXX Need a timeout handler */ + ccb->ccb_h.timeout_ch = + timeout(amdtimeout, (caddr_t)srb, + (ccb->ccb_h.timeout * hz) / 1000); +#endif + TAILQ_INSERT_TAIL(&amd->waiting_srbs, srb, links); + amdrunwaiting(amd); + splx(s); +} + +static void +amd_action(struct cam_sim * psim, union ccb * pccb) +{ + struct amd_softc * amd; + u_int target_id, target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("amd_action\n")); + + amd = (struct amd_softc *) cam_sim_softc(psim); + target_id = pccb->ccb_h.target_id; + target_lun = pccb->ccb_h.target_lun; + + switch (pccb->ccb_h.func_code) { + case XPT_SCSI_IO: + { + struct amd_srb * pSRB; + struct ccb_scsiio *pcsio; + + pcsio = &pccb->csio; + + /* + * Assign an SRB and connect it with this ccb. + */ + pSRB = amdgetsrb(amd); + + if (!pSRB) { + /* Freeze SIMQ */ + pccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(pccb); + return; + } + pSRB->pccb = pccb; + pccb->ccb_h.ccb_srb_ptr = pSRB; + pccb->ccb_h.ccb_amd_ptr = amd; + pSRB->ScsiCmdLen = pcsio->cdb_len; + bcopy(pcsio->cdb_io.cdb_bytes, pSRB->CmdBlock, pcsio->cdb_len); + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + if ((pccb->ccb_h.flags & CAM_SCATTER_VALID) == 0) { + /* + * We've been given a pointer + * to a single buffer. + */ + if ((pccb->ccb_h.flags & CAM_DATA_PHYS) == 0) { + int s; + int error; + + s = splsoftvm(); + error = + bus_dmamap_load(amd->buffer_dmat, + pSRB->dmamap, + pcsio->data_ptr, + pcsio->dxfer_len, + amdexecutesrb, + pSRB, /*flags*/0); + if (error == EINPROGRESS) { + /* + * So as to maintain + * ordering, freeze the + * controller queue + * until our mapping is + * returned. + */ + xpt_freeze_simq(amd->psim, 1); + pccb->ccb_h.status |= + CAM_RELEASE_SIMQ; + } + splx(s); + } else { + struct bus_dma_segment seg; + + /* Pointer to physical buffer */ + seg.ds_addr = + (bus_addr_t)pcsio->data_ptr; + seg.ds_len = pcsio->dxfer_len; + amdexecutesrb(pSRB, &seg, 1, 0); + } + } else { + struct bus_dma_segment *segs; + + if ((pccb->ccb_h.flags & CAM_SG_LIST_PHYS) == 0 + || (pccb->ccb_h.flags & CAM_DATA_PHYS) != 0) { + TAILQ_INSERT_HEAD(&amd->free_srbs, + pSRB, links); + pccb->ccb_h.status = CAM_PROVIDE_FAIL; + xpt_done(pccb); + return; + } + + /* Just use the segments provided */ + segs = + (struct bus_dma_segment *)pcsio->data_ptr; + amdexecutesrb(pSRB, segs, pcsio->sglist_cnt, 0); + } + } else + amdexecutesrb(pSRB, NULL, 0, 0); + break; + } + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &pccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 7; + cpi->max_lun = amd->max_lun; /* 7 or 0 */ + cpi->initiator_id = amd->AdaptSCSIID; + cpi->bus_id = cam_sim_bus(psim); + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "TRM-AMD", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(psim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_ABORT: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_RESET_BUS: + { + + int i; + + amd_ResetSCSIBus(amd); + amd->ACBFlag = 0; + + for (i = 0; i < 500; i++) { + DELAY(1000); /* Wait until our interrupt + * handler sees it */ + } + + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_RESET_DEV: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + case XPT_TERM_IO: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + struct amd_transinfo *tinfo; + int intflag; + + cts = &pccb->cts; + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + /* current transfer settings */ + if (targ_info->disc_tag & AMD_CUR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; /* no tag & disconnect */ + } + if (targ_info->disc_tag & AMD_CUR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->current; + } else { + /* default(user) transfer settings */ + if (targ_info->disc_tag & AMD_USR_DISCENB) { + cts->flags = CCB_TRANS_DISC_ENB; + } else { + cts->flags = 0; + } + if (targ_info->disc_tag & AMD_USR_TAGENB) { + cts->flags |= CCB_TRANS_TAG_ENB; + } + tinfo = &targ_info->user; + } + + cts->sync_period = tinfo->period; + cts->sync_offset = tinfo->offset; + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + splx(intflag); + cts->valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID + | CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_DISC_VALID + | CCB_TRANS_TQ_VALID; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts; + struct amd_target_info *targ_info; + u_int update_type; + int intflag; + int last_entry; + + cts = &pccb->cts; + update_type = 0; + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + update_type |= AMD_TRANS_GOAL; + } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + update_type |= AMD_TRANS_USER; + } + if (update_type == 0 + || update_type == (AMD_TRANS_USER|AMD_TRANS_GOAL)) { + cts->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + } + + intflag = splcam(); + targ_info = &amd->tinfo[target_id]; + + if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_DISCENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_DISCENB; + } else { + targ_info->disc_tag &= ~AMD_USR_DISCENB; + } + } + } + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if (update_type & AMD_TRANS_GOAL) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_CUR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_CUR_TAGENB; + } + } + if (update_type & AMD_TRANS_USER) { + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + targ_info->disc_tag |= AMD_USR_TAGENB; + } else { + targ_info->disc_tag &= ~AMD_USR_TAGENB; + } + } + } + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_offset = targ_info->goal.offset; + else + cts->sync_offset = targ_info->user.offset; + } + + if (cts->sync_offset > AMD_MAX_SYNC_OFFSET) + cts->sync_offset = AMD_MAX_SYNC_OFFSET; + + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) { + if (update_type & AMD_TRANS_GOAL) + cts->sync_period = targ_info->goal.period; + else + cts->sync_period = targ_info->user.period; + } + + last_entry = sizeof(tinfo_sync_period) - 1; + if ((cts->sync_period != 0) + && (cts->sync_period < tinfo_sync_period[0])) + cts->sync_period = tinfo_sync_period[0]; + if (cts->sync_period > tinfo_sync_period[last_entry]) + cts->sync_period = 0; + if (cts->sync_offset == 0) + cts->sync_period = 0; + + if ((update_type & AMD_TRANS_USER) != 0) { + targ_info->user.period = cts->sync_period; + targ_info->user.offset = cts->sync_offset; + } + if ((update_type & AMD_TRANS_GOAL) != 0) { + targ_info->goal.period = cts->sync_period; + targ_info->goal.offset = cts->sync_offset; + } + splx(intflag); + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended; + + ccg = &pccb->ccg; + size_mb = ccg->volume_size/((1024L * 1024L)/ccg->block_size); + extended = (amd->eepromBuf[EE_MODE2] & GREATER_1G) != 0; + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + pccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(pccb); + break; + } + default: + pccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(pccb); + break; + } +} + +static void +amd_poll(struct cam_sim * psim) +{ + amd_intr(cam_sim_softc(psim)); +} + +static u_int8_t * +phystovirt(struct amd_srb * pSRB, u_int32_t xferCnt) +{ + int dataPtr; + struct ccb_scsiio *pcsio; + u_int8_t i; + struct amd_sg * pseg; + + dataPtr = 0; + pcsio = &pSRB->pccb->csio; + + dataPtr = (int) pcsio->data_ptr; + pseg = pSRB->SGsegment; + for (i = 0; i < pSRB->SGIndex; i++) { + dataPtr += (int) pseg->SGXLen; + pseg++; + } + dataPtr += (int) xferCnt; + return ((u_int8_t *) dataPtr); +} + +static void +ResetDevParam(struct amd_softc * amd) +{ + u_int target; + + for (target = 0; target <= amd->max_id; target++) { + if (amd->AdaptSCSIID != target) { + amdsetsync(amd, target, /*clockrate*/0, + /*period*/0, /*offset*/0, AMD_TRANS_CUR); + } + } +} + +static void +amdcompletematch(struct amd_softc *amd, target_id_t target, lun_id_t lun, + u_int tag, struct srb_queue *queue, cam_status status) +{ + struct amd_srb *srb; + struct amd_srb *next_srb; + + for (srb = TAILQ_FIRST(queue); srb != NULL; srb = next_srb) { + union ccb *ccb; + + next_srb = TAILQ_NEXT(srb, links); + if (srb->pccb->ccb_h.target_id != target + && target != CAM_TARGET_WILDCARD) + continue; + + if (srb->pccb->ccb_h.target_lun != lun + && lun != CAM_LUN_WILDCARD) + continue; + + if (srb->TagNumber != tag + && tag != AMD_TAG_WILDCARD) + continue; + + ccb = srb->pccb; + TAILQ_REMOVE(queue, srb, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, srb, links); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0 + && (status & CAM_DEV_QFRZN) != 0) + xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); + ccb->ccb_h.status = status; + xpt_done(ccb); + } + +} + +static void +amdsetsync(struct amd_softc *amd, u_int target, u_int clockrate, + u_int period, u_int offset, u_int type) +{ + struct amd_target_info *tinfo; + u_int old_period; + u_int old_offset; + + tinfo = &amd->tinfo[target]; + old_period = tinfo->current.period; + old_offset = tinfo->current.offset; + if ((type & AMD_TRANS_CUR) != 0 + && (old_period != period || old_offset != offset)) { + struct cam_path *path; + + tinfo->current.period = period; + tinfo->current.offset = offset; + tinfo->sync_period_reg = clockrate; + tinfo->sync_offset_reg = offset; + tinfo->CtrlR3 &= ~FAST_SCSI; + tinfo->CtrlR4 &= ~EATER_25NS; + if (clockrate > 7) + tinfo->CtrlR4 |= EATER_25NS; + else + tinfo->CtrlR3 |= FAST_SCSI; + + if ((type & AMD_TRANS_ACTIVE) == AMD_TRANS_ACTIVE) { + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + } + /* If possible, update the XPT's notion of our transfer rate */ + if (xpt_create_path(&path, /*periph*/NULL, + cam_sim_path(amd->psim), target, + CAM_LUN_WILDCARD) == CAM_REQ_CMP) { + struct ccb_trans_settings neg; + + xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1); + neg.sync_period = period; + neg.sync_offset = offset; + neg.valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID; + xpt_async(AC_TRANSFER_NEG, path, &neg); + xpt_free_path(path); + } + } + if ((type & AMD_TRANS_GOAL) != 0) { + tinfo->goal.period = period; + tinfo->goal.offset = offset; + } + + if ((type & AMD_TRANS_USER) != 0) { + tinfo->user.period = period; + tinfo->user.offset = offset; + } +} + +static void +amdsettags(struct amd_softc *amd, u_int target, int tagenb) +{ + panic("Implement me!\n"); +} + + +/* + ********************************************************************** + * Function : amd_reset (struct amd_softc * amd) + * Purpose : perform a hard reset on the SCSI bus( and AMD chip). + * Inputs : cmd - command which caused the SCSI RESET + ********************************************************************** + */ +static void +amd_reset(struct amd_softc * amd) +{ + int intflag; + u_int8_t bval; + u_int16_t i; + + +#ifdef AMD_DEBUG0 + printf("DC390: RESET"); +#endif + + intflag = splcam(); + bval = amd_read8(amd, CNTLREG1); + bval |= DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* disable interrupt */ + amd_ResetSCSIBus(amd); + + for (i = 0; i < 500; i++) { + DELAY(1000); + } + + bval = amd_read8(amd, CNTLREG1); + bval &= ~DIS_INT_ON_SCSI_RST; + amd_write8(amd, CNTLREG1, bval); /* re-enable interrupt */ + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + splx(intflag); + return; +} + +void +amd_timeout(void *arg1) +{ + struct amd_srb * pSRB; + + pSRB = (struct amd_srb *) arg1; +} + +static int +amdstart(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + struct amd_target_info *targ_info; + u_int identify_msg; + u_int command; + u_int target; + u_int lun; + int tagged; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target = pccb->ccb_h.target_id; + lun = pccb->ccb_h.target_lun; + targ_info = &amd->tinfo[target]; + + amd_clear_msg_state(amd); + amd_write8(amd, SCSIDESTIDREG, target); + amd_write8(amd, SYNCPERIOREG, targ_info->sync_period_reg); + amd_write8(amd, SYNCOFFREG, targ_info->sync_offset_reg); + amd_write8(amd, CNTLREG1, targ_info->CtrlR1); + amd_write8(amd, CNTLREG3, targ_info->CtrlR3); + amd_write8(amd, CNTLREG4, targ_info->CtrlR4); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + identify_msg = MSG_IDENTIFYFLAG | lun; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0 + && (pSRB->CmdBlock[0] != REQUEST_SENSE) + && (pSRB->SRBFlag & AUTO_REQSENSE) == 0) + identify_msg |= MSG_IDENTIFY_DISCFLAG; + + amd_write8(amd, SCSIFIFOREG, identify_msg); + tagged = 0; + if ((targ_info->disc_tag & AMD_CUR_TAGENB) == 0 + || (identify_msg & MSG_IDENTIFY_DISCFLAG) == 0) + pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + if (targ_info->current.period != targ_info->goal.period + || targ_info->current.offset != targ_info->goal.offset) { + command = SEL_W_ATN_STOP; + amdconstructsdtr(amd, targ_info->goal.period, + targ_info->goal.offset); + } else if ((pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + command = SEL_W_ATN2; + pSRB->SRBState = SRB_START; + amd_write8(amd, SCSIFIFOREG, pcsio->tag_action); + amd_write8(amd, SCSIFIFOREG, pSRB->TagNumber); + tagged++; + } else { + command = SEL_W_ATN; + pSRB->SRBState = SRB_START; + } + if (command != SEL_W_ATN_STOP) + amdsetupcommand(amd, pSRB); + + if (amd_read8(amd, SCSISTATREG) & INTERRUPT) { + pSRB->SRBState = SRB_READY; + return (1); + } else { + amd->last_phase = SCSI_ARBITRATING; + amd_write8(amd, SCSICMDREG, command); + amd->active_srb = pSRB; + amd->cur_target = target; + amd->cur_lun = lun; + return (0); + } +} + +/* + * Catch an interrupt from the adapter. + * Process pending device interrupts. + */ +static void +amd_intr(void *arg) +{ + struct amd_softc *amd; + struct amd_srb *pSRB; + u_int internstat = 0; + u_int scsistat; + u_int intstat; + + amd = (struct amd_softc *)arg; + + if (amd == NULL) { +#ifdef AMD_DEBUG0 + printf("amd_intr: amd NULL return......"); +#endif + return; + } + + scsistat = amd_read8(amd, SCSISTATREG); + if (!(scsistat & INTERRUPT)) { +#ifdef AMD_DEBUG0 + printf("amd_intr: scsistat = NULL ,return......"); +#endif + return; + } +#ifdef AMD_DEBUG_SCSI_PHASE + printf("scsistat=%2x,", scsistat); +#endif + + internstat = amd_read8(amd, INTERNSTATREG); + intstat = amd_read8(amd, INTSTATREG); + +#ifdef AMD_DEBUG_SCSI_PHASE + printf("intstat=%2x,", intstat); +#endif + + if (intstat & DISCONNECTED) { + amd_Disconnect(amd); + return; + } + if (intstat & RESELECTED) { + amd_Reselect(amd); + return; + } + if (intstat & INVALID_CMD) { + amd_InvalidCmd(amd); + return; + } + if (intstat & SCSI_RESET_) { + amd_ScsiRstDetect(amd); + return; + } + if (intstat & (SUCCESSFUL_OP + SERVICE_REQUEST)) { + pSRB = amd->active_srb; + /* + * Run our state engine. First perform + * post processing for the last phase we + * were in, followed by any processing + * required to handle the current phase. + */ + scsistat = + amd_SCSI_phase0[amd->last_phase](amd, pSRB, scsistat); + amd->last_phase = scsistat & SCSI_PHASE_MASK; + (void)amd_SCSI_phase1[amd->last_phase](amd, pSRB, scsistat); + } +} + +static u_int +amd_DataOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while ((amd_read8(amd, DMA_Status)&DMA_XFER_DONE) == 0) + ; + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { + ResidCnt = amd_read8(amd, CURRENTFIFOREG) & 0x1f; + ResidCnt += amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + } + } + amd_write8(amd, DMA_Cmd, WRITE_DIRECTION | DMA_IDLE_CMD); + return (scsistat); +} + +static u_int +amd_DataInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + u_int8_t bval; + u_int16_t i, residual; + struct amd_sg *psgl; + u_int32_t ResidCnt, xferCnt; + u_int8_t * ptr; + + if (!(pSRB->SRBState & SRB_XFERPAD)) { + if (scsistat & PARITY_ERR) { + pSRB->SRBStatus |= PARITY_ERROR; + } + if (scsistat & COUNT_2_ZERO) { + while (1) { + bval = amd_read8(amd, DMA_Status); + if ((bval & DMA_XFER_DONE) != 0) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + pSRB->TotalXferredLen += pSRB->SGToBeXferLen; + pSRB->SGIndex++; + if (pSRB->SGIndex < pSRB->SGcount) { + pSRB->pSGlist++; + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } else { + pSRB->SGToBeXferLen = 0; + } + } else { /* phase changed */ + residual = 0; + bval = amd_read8(amd, CURRENTFIFOREG); + while (bval & 0x1f) { + if ((bval & 0x1f) == 1) { + for (i = 0; i < 0x100; i++) { + bval = amd_read8(amd, CURRENTFIFOREG); + if (!(bval & 0x1f)) { + goto din_1; + } else if (i == 0x0ff) { + residual = 1; + goto din_1; + } + } + } else { + bval = amd_read8(amd, CURRENTFIFOREG); + } + } + din_1: + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_BLAST_CMD); + for (i = 0; i < 0x8000; i++) { + if ((amd_read8(amd, DMA_Status)&BLAST_COMPLETE)) + break; + } + amd_write8(amd, DMA_Cmd, READ_DIRECTION|DMA_IDLE_CMD); + + ResidCnt = amd_read8(amd, CTCREG_LOW) + | (amd_read8(amd, CTCREG_MID) << 8) + | (amd_read8(amd, CURTXTCNTREG) << 16); + xferCnt = pSRB->SGToBeXferLen - ResidCnt; + pSRB->SGPhysAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = ResidCnt; + if (residual) { + /* get residual byte */ + bval = amd_read8(amd, SCSIFIFOREG); + ptr = phystovirt(pSRB, xferCnt); + *ptr = bval; + pSRB->SGPhysAddr++; + pSRB->TotalXferredLen++; + pSRB->SGToBeXferLen--; + } + } + } + return (scsistat); +} + +static u_int +amd_StatusPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + pSRB->TargetStatus = amd_read8(amd, SCSIFIFOREG); + /* get message */ + pSRB->EndMessage = amd_read8(amd, SCSIFIFOREG); + pSRB->SRBState = SRB_COMPLETED; + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} + +static u_int +amd_MsgOutPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + if (pSRB->SRBState & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT)) { + scsistat = SCSI_NOP0; + } + return (scsistat); +} + +static u_int +amd_MsgInPhase0(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + int done; + + amd->msgin_buf[amd->msgin_index] = amd_read8(amd, SCSIFIFOREG); + + done = amdparsemsg(amd); + if (done) + amd->msgin_index = 0; + else + amd->msgin_index++; + return (SCSI_NOP0); +} + +static int +amdparsemsg(struct amd_softc *amd) +{ + struct amd_target_info *targ_info; + int reject; + int done; + int response; + + done = FALSE; + response = FALSE; + reject = FALSE; + + targ_info = &amd->tinfo[amd->cur_target]; + + /* + * Parse as much of the message as is availible, + * rejecting it if we don't support it. When + * the entire message is availible and has been + * handled, return TRUE indicating that we have + * parsed an entire message. + */ + switch (amd->msgin_buf[0]) { + case MSG_DISCONNECT: + amd->active_srb->SRBState = SRB_DISCONNECT; + amd->disc_count[amd->cur_target][amd->cur_lun]++; + done = TRUE; + break; + case MSG_SIMPLE_Q_TAG: + { + struct amd_srb *disc_srb; + + if (amd->msgin_index < 1) + break; + disc_srb = &amd->SRB_array[amd->msgin_buf[1]]; + if (amd->active_srb != NULL + || disc_srb->SRBState != SRB_DISCONNECT + || disc_srb->pccb->ccb_h.target_id != amd->cur_target + || disc_srb->pccb->ccb_h.target_lun != amd->cur_lun) { + printf("amd%d: Unexpected tagged reselection " + "for target %d, Issuing Abort\n", amd->unit, + amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + response = TRUE; + break; + } + amd->active_srb = disc_srb; + amd->disc_count[amd->cur_target][amd->cur_lun]--; + done = TRUE; + break; + } + case MSG_MESSAGE_REJECT: + response = amdhandlemsgreject(amd); + if (response == FALSE) + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + /* FALLTHROUGH */ + case MSG_NOOP: + done = TRUE; + break; + case MSG_EXTENDED: + { + u_int clockrate; + u_int period; + u_int offset; + u_int saved_offset; + + /* Wait for enough of the message to begin validation */ + if (amd->msgin_index < 1) + break; + if (amd->msgin_buf[1] != MSG_EXT_SDTR_LEN) { + reject = TRUE; + break; + } + + /* Wait for opcode */ + if (amd->msgin_index < 2) + break; + + if (amd->msgin_buf[2] != MSG_EXT_SDTR) { + reject = TRUE; + break; + } + + /* + * Wait until we have both args before validating + * and acting on this message. + * + * Add one to MSG_EXT_SDTR_LEN to account for + * the extended message preamble. + */ + if (amd->msgin_index < (MSG_EXT_SDTR_LEN + 1)) + break; + + period = amd->msgin_buf[3]; + saved_offset = offset = amd->msgin_buf[4]; + clockrate = amdfindclockrate(amd, &period); + if (offset > AMD_MAX_SYNC_OFFSET) + offset = AMD_MAX_SYNC_OFFSET; + if (period == 0 || offset == 0) { + offset = 0; + period = 0; + clockrate = 0; + } + amdsetsync(amd, amd->cur_target, clockrate, period, offset, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + + /* + * See if we initiated Sync Negotiation + * and didn't have to fall down to async + * transfers. + */ + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/TRUE)) { + /* We started it */ + if (saved_offset != offset) { + /* Went too low - force async */ + reject = TRUE; + } + } else { + /* + * Send our own SDTR in reply + */ + if (bootverbose) + printf("Sending SDTR!\n"); + amd->msgout_index = 0; + amd->msgout_len = 0; + amdconstructsdtr(amd, period, offset); + amd->msgout_index = 0; + response = TRUE; + } + done = TRUE; + break; + } + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* XXX Implement!!! */ + done = TRUE; + break; + default: + reject = TRUE; + break; + } + + if (reject) { + amd->msgout_index = 0; + amd->msgout_len = 1; + amd->msgout_buf[0] = MSG_MESSAGE_REJECT; + done = TRUE; + response = TRUE; + } + + if (response) + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + + if (done && !response) + /* Clear the outgoing message buffer */ + amd->msgout_len = 0; + + /* Drop Ack */ + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + + return (done); +} + +static u_int +amdfindclockrate(struct amd_softc *amd, u_int *period) +{ + u_int i; + u_int clockrate; + + for (i = 0; i < sizeof(tinfo_sync_period); i++) { + u_int8_t *table_entry; + + table_entry = &tinfo_sync_period[i]; + if (*period <= *table_entry) { + /* + * When responding to a target that requests + * sync, the requested rate may fall between + * two rates that we can output, but still be + * a rate that we can receive. Because of this, + * we want to respond to the target with + * the same rate that it sent to us even + * if the period we use to send data to it + * is lower. Only lower the response period + * if we must. + */ + if (i == 0) { + *period = *table_entry; + } + break; + } + } + + if (i == sizeof(tinfo_sync_period)) { + /* Too slow for us. Use asnyc transfers. */ + *period = 0; + clockrate = 0; + } else + clockrate = i + 4; + + return (clockrate); +} + +/* + * See if we sent a particular extended message to the target. + * If "full" is true, the target saw the full message. + * If "full" is false, the target saw at least the first + * byte of the message. + */ +static int +amdsentmsg(struct amd_softc *amd, u_int msgtype, int full) +{ + int found; + int index; + + found = FALSE; + index = 0; + + while (index < amd->msgout_len) { + if ((amd->msgout_buf[index] & MSG_IDENTIFYFLAG) != 0 + || amd->msgout_buf[index] == MSG_MESSAGE_REJECT) + index++; + else if (amd->msgout_buf[index] >= MSG_SIMPLE_Q_TAG + && amd->msgout_buf[index] < MSG_IGN_WIDE_RESIDUE) { + /* Skip tag type and tag id */ + index += 2; + } else if (amd->msgout_buf[index] == MSG_EXTENDED) { + /* Found a candidate */ + if (amd->msgout_buf[index+2] == msgtype) { + u_int end_index; + + end_index = index + 1 + + amd->msgout_buf[index + 1]; + if (full) { + if (amd->msgout_index > end_index) + found = TRUE; + } else if (amd->msgout_index > index) + found = TRUE; + } + break; + } else { + panic("amdsentmsg: Inconsistent msg buffer"); + } + } + return (found); +} + +static void +amdconstructsdtr(struct amd_softc *amd, u_int period, u_int offset) +{ + amd->msgout_buf[amd->msgout_index++] = MSG_EXTENDED; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR_LEN; + amd->msgout_buf[amd->msgout_index++] = MSG_EXT_SDTR; + amd->msgout_buf[amd->msgout_index++] = period; + amd->msgout_buf[amd->msgout_index++] = offset; + amd->msgout_len += 5; +} + +static int +amdhandlemsgreject(struct amd_softc *amd) +{ + /* + * If we had an outstanding SDTR for this + * target, this is a signal that the target + * is refusing negotiation. Also watch out + * for rejected tag messages. + */ + struct amd_srb *srb; + struct amd_target_info *targ_info; + int response = FALSE; + + srb = amd->active_srb; + targ_info = &amd->tinfo[amd->cur_target]; + if (amdsentmsg(amd, MSG_EXT_SDTR, /*full*/FALSE)) { + /* note asynch xfers and clear flag */ + amdsetsync(amd, amd->cur_target, /*clockrate*/0, + /*period*/0, /*offset*/0, + AMD_TRANS_ACTIVE|AMD_TRANS_GOAL); + printf("amd%d:%d: refuses synchronous negotiation. " + "Using asynchronous transfers\n", + amd->unit, amd->cur_target); + } else if ((srb != NULL) + && (srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) { + struct ccb_trans_settings neg; + + printf("amd%d:%d: refuses tagged commands. Performing " + "non-tagged I/O\n", amd->unit, amd->cur_target); + + amdsettags(amd, amd->cur_target, FALSE); + neg.flags = 0; + neg.valid = CCB_TRANS_TQ_VALID; + xpt_setup_ccb(&neg.ccb_h, srb->pccb->ccb_h.path, /*priority*/1); + xpt_async(AC_TRANSFER_NEG, srb->pccb->ccb_h.path, &neg); + + /* + * Resend the identify for this CCB as the target + * may believe that the selection is invalid otherwise. + */ + if (amd->msgout_len != 0) + bcopy(&amd->msgout_buf[0], &amd->msgout_buf[1], + amd->msgout_len); + amd->msgout_buf[0] = MSG_IDENTIFYFLAG + | srb->pccb->ccb_h.target_lun; + amd->msgout_len++; + if ((targ_info->disc_tag & AMD_CUR_DISCENB) != 0 + && (srb->pccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0) + amd->msgout_buf[0] |= MSG_IDENTIFY_DISCFLAG; + + srb->pccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + + /* + * Requeue all tagged commands for this target + * currently in our posession so they can be + * converted to untagged commands. + */ + amdcompletematch(amd, amd->cur_target, amd->cur_lun, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_REQUEUE_REQ); + } else { + /* + * Otherwise, we ignore it. + */ + printf("amd%d:%d: Message reject received -- ignored\n", + amd->unit, amd->cur_target); + } + return (response); +} + +#if 0 + if (!(pSRB->SRBState & SRB_MSGIN_MULTI)) { + if (bval == MSG_DISCONNECT) { + pSRB->SRBState = SRB_DISCONNECT; + } else if (bval == MSG_SAVEDATAPOINTER) { + goto min6; + } else if ((bval == MSG_EXTENDED) + || ((bval >= MSG_SIMPLE_Q_TAG) + && (bval <= MSG_ORDERED_Q_TAG))) { + pSRB->SRBState |= SRB_MSGIN_MULTI; + pSRB->MsgInBuf[0] = bval; + pSRB->MsgCnt = 1; + pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; + } else if (bval == MSG_MESSAGE_REJECT) { + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + + if (pSRB->SRBState & DO_SYNC_NEGO) { + goto set_async; + } + } else if (bval == MSG_RESTOREPOINTERS) { + goto min6; + } else { + goto min6; + } + } else { /* minx: */ + *pSRB->pMsgPtr = bval; + pSRB->MsgCnt++; + pSRB->pMsgPtr++; + if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_Q_TAG) + && (pSRB->MsgInBuf[0] <= MSG_ORDERED_Q_TAG)) { + if (pSRB->MsgCnt == 2) { + pSRB->SRBState = 0; + pSRB = &amd->SRB_array[pSRB->MsgInBuf[1]]; + if (pSRB->SRBState & SRB_DISCONNECT) == 0) { + pSRB = amd->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + EnableMsgOut2(amd, pSRB); + } else { + if (pDCB->DCBFlag & ABORT_DEV_) { + pSRB->SRBState = SRB_ABORT_SENT; + EnableMsgOut1(amd, pSRB); + } + pDCB->pActiveSRB = pSRB; + pSRB->SRBState = SRB_DATA_XFER; + } + } + } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) + && (pSRB->MsgCnt == 5)) { + pSRB->SRBState &= ~(SRB_MSGIN_MULTI + DO_SYNC_NEGO); + if ((pSRB->MsgInBuf[1] != 3) + || (pSRB->MsgInBuf[2] != 1)) { /* reject_msg: */ + pSRB->MsgCnt = 1; + pSRB->MsgInBuf[0] = MSG_MESSAGE_REJECT; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } else if (!(pSRB->MsgInBuf[3]) + || !(pSRB->MsgInBuf[4])) { + set_async: /* set async */ + + pDCB = pSRB->pSRBDCB; + /* disable sync & sync nego */ + pDCB->SyncMode &= ~(SYNC_ENABLE|SYNC_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + + pDCB->tinfo.goal.period = 0; + pDCB->tinfo.goal.offset = 0; + + pDCB->tinfo.current.period = 0; + pDCB->tinfo.current.offset = 0; + pDCB->tinfo.current.width = + MSG_EXT_WDTR_BUS_8_BIT; + + pDCB->CtrlR3 = FAST_CLK; /* non_fast */ + pDCB->CtrlR4 &= 0x3f; + pDCB->CtrlR4 |= EATER_25NS; + goto re_prog; + } else {/* set sync */ + + pDCB = pSRB->pSRBDCB; + /* enable sync & sync nego */ + pDCB->SyncMode |= SYNC_ENABLE|SYNC_NEGO_DONE; + + /* set sync offset */ + pDCB->SyncOffset &= 0x0f0; + pDCB->SyncOffset |= pSRB->MsgInBuf[4]; + + /* set sync period */ + pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3]; + + wval = (u_int16_t) pSRB->MsgInBuf[3]; + wval = wval << 2; + wval--; + wval1 = wval / 25; + if ((wval1 * 25) != wval) { + wval1++; + } + bval = FAST_CLK|FAST_SCSI; + pDCB->CtrlR4 &= 0x3f; + if (wval1 >= 8) { + /* Fast SCSI */ + wval1--; + bval = FAST_CLK; + pDCB->CtrlR4 |= EATER_25NS; + } + pDCB->CtrlR3 = bval; + pDCB->SyncPeriod = (u_int8_t) wval1; + + pDCB->tinfo.goal.period = + tinfo_sync_period[pDCB->SyncPeriod - 4]; + pDCB->tinfo.goal.offset = pDCB->SyncOffset; + pDCB->tinfo.current.period = + tinfo_sync_period[pDCB->SyncPeriod - 4];; + pDCB->tinfo.current.offset = pDCB->SyncOffset; + + /* + * program SCSI control register + */ + re_prog: + amd_write8(amd, SYNCPERIOREG, pDCB->SyncPeriod); + amd_write8(amd, SYNCOFFREG, pDCB->SyncOffset); + amd_write8(amd, CNTLREG3, pDCB->CtrlR3); + amd_write8(amd, CNTLREG4, pDCB->CtrlR4); + } + } + } +min6: + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD); + return (SCSI_NOP0); +} +#endif + +static u_int +amd_DataOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, WRITE_DIRECTION); + return (scsistat); +} + +static u_int +amd_DataInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + DataIO_Comm(amd, pSRB, READ_DIRECTION); + return (scsistat); +} + +static void +DataIO_Comm(struct amd_softc *amd, struct amd_srb *pSRB, u_int ioDir) +{ + struct amd_sg * psgl; + u_int32_t lval; + + if (pSRB->SGIndex < pSRB->SGcount) { + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir);/* |EN_DMA_INT */ + + if (!pSRB->SGToBeXferLen) { + psgl = pSRB->pSGlist; + pSRB->SGPhysAddr = psgl->SGXPtr; + pSRB->SGToBeXferLen = psgl->SGXLen; + } + lval = pSRB->SGToBeXferLen; + amd_write8(amd, CTCREG_LOW, lval); + amd_write8(amd, CTCREG_MID, lval >> 8); + amd_write8(amd, CURTXTCNTREG, lval >> 16); + + amd_write32(amd, DMA_XferCnt, pSRB->SGToBeXferLen); + + amd_write32(amd, DMA_XferAddr, pSRB->SGPhysAddr); + + pSRB->SRBState = SRB_DATA_XFER; + + amd_write8(amd, SCSICMDREG, DMA_COMMAND|INFO_XFER_CMD); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD|ioDir); /* |EN_DMA_INT */ + + amd_write8(amd, DMA_Cmd, DMA_START_CMD|ioDir);/* |EN_DMA_INT */ + } else { /* xfer pad */ + if (pSRB->SGcount) { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + amd_write8(amd, CTCREG_LOW, 0); + amd_write8(amd, CTCREG_MID, 0); + amd_write8(amd, CURTXTCNTREG, 0); + + pSRB->SRBState |= SRB_XFERPAD; + amd_write8(amd, SCSICMDREG, DMA_COMMAND|XFER_PAD_BYTE); + } +} + +static u_int +amd_CommandPhase1(struct amd_softc *amd, struct amd_srb *srb, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, RESET_ATN_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + amdsetupcommand(amd, srb); + + srb->SRBState = SRB_COMMAND; + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_StatusPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + pSRB->SRBState = SRB_STATUS; + amd_write8(amd, SCSICMDREG, INITIATOR_CMD_CMPLTE); + return (scsistat); +} + +static u_int +amd_MsgOutPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->msgout_len == 0) { + amd->msgout_buf[0] = MSG_NOOP; + amd->msgout_len = 1; + } + amd_write8_multi(amd, SCSIFIFOREG, amd->msgout_buf, amd->msgout_len); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_MsgInPhase1(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + amd_write8(amd, SCSICMDREG, INFO_XFER_CMD); + return (scsistat); +} + +static u_int +amd_NopPhase(struct amd_softc *amd, struct amd_srb *pSRB, u_int scsistat) +{ + return (scsistat); +} + +static void +amd_Disconnect(struct amd_softc * amd) +{ + struct amd_srb *srb; + int target; + int lun; + + srb = amd->active_srb; + amd->active_srb = NULL; + amd->last_phase = SCSI_BUS_FREE; + amd_write8(amd, SCSICMDREG, EN_SEL_RESEL); + target = amd->cur_target; + lun = amd->cur_lun; + + if (srb == NULL) { + /* Invalid reselection */ + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_ABORT_SENT) { + /* Clean up and done this srb */ +#if 0 + while (( = TAILQ_FIRST(&amd->running_srbs)) != NULL) { + /* XXX What about "done'ing" these srbs??? */ + if (pSRB->pSRBDCB == pDCB) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + } + } + amdrunwaiting(amd); +#endif + } else { + if ((srb->SRBState & (SRB_START | SRB_MSGOUT)) + || !(srb->SRBState & (SRB_DISCONNECT | SRB_COMPLETED))) { + srb->TargetStatus = AMD_SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } else if (srb->SRBState & SRB_DISCONNECT) { + if (!(srb->pccb->ccb_h.flags & CAM_TAG_ACTION_VALID)) + amd->untagged_srbs[target][lun] = srb; + amdrunwaiting(amd); + } else if (srb->SRBState & SRB_COMPLETED) { + disc1: + srb->SRBState = SRB_FREE; + SRBdone(amd, srb); + } + } + return; +} + +static void +amd_Reselect(struct amd_softc *amd) +{ + struct amd_target_info *tinfo; + u_int16_t disc_count; + + amd_clear_msg_state(amd); + if (amd->active_srb != NULL) { + /* Requeue the SRB for our attempted Selection */ + TAILQ_REMOVE(&amd->running_srbs, amd->active_srb, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, amd->active_srb, links); + amd->active_srb = NULL; + } + /* get ID */ + amd->cur_target = amd_read8(amd, SCSIFIFOREG); + amd->cur_target ^= amd->HostID_Bit; + amd->cur_target = ffs(amd->cur_target) - 1; + amd->cur_lun = amd_read8(amd, SCSIFIFOREG) & 7; + tinfo = &amd->tinfo[amd->cur_target]; + amd->active_srb = amd->untagged_srbs[amd->cur_target][amd->cur_lun]; + disc_count = amd->disc_count[amd->cur_target][amd->cur_lun]; + if (disc_count == 0) { + printf("amd%d: Unexpected reselection for target %d, " + "Issuing Abort\n", amd->unit, amd->cur_target); + amd->msgout_buf[0] = MSG_ABORT; + amd->msgout_len = 1; + amd_write8(amd, SCSICMDREG, SET_ATN_CMD); + } + if (amd->active_srb != NULL) { + amd->disc_count[amd->cur_target][amd->cur_lun]--; + amd->untagged_srbs[amd->cur_target][amd->cur_lun] = NULL; + } + + amd_write8(amd, SCSIDESTIDREG, amd->cur_target); + amd_write8(amd, SYNCPERIOREG, tinfo->sync_period_reg); + amd_write8(amd, SYNCOFFREG, tinfo->sync_offset_reg); + amd_write8(amd, CNTLREG1, tinfo->CtrlR1); + amd_write8(amd, CNTLREG3, tinfo->CtrlR3); + amd_write8(amd, CNTLREG4, tinfo->CtrlR4); + amd_write8(amd, SCSICMDREG, MSG_ACCEPTED_CMD);/* drop /ACK */ + amd->last_phase = SCSI_NOP0; +} + +static void +SRBdone(struct amd_softc *amd, struct amd_srb *pSRB) +{ + u_int8_t bval, i, status; + union ccb *pccb; + struct ccb_scsiio *pcsio; + int intflag; + struct amd_sg *ptr2; + u_int32_t swlval; + u_int target_id, target_lun; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + target_id = pSRB->pccb->ccb_h.target_id; + target_lun = pSRB->pccb->ccb_h.target_lun; + + CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, + ("SRBdone - TagNumber %d\n", pSRB->TagNumber)); + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { + bus_dmasync_op_t op; + + if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) + op = BUS_DMASYNC_POSTREAD; + else + op = BUS_DMASYNC_POSTWRITE; + bus_dmamap_sync(amd->buffer_dmat, pSRB->dmamap, op); + bus_dmamap_unload(amd->buffer_dmat, pSRB->dmamap); + } + + status = pSRB->TargetStatus; + pccb->ccb_h.status = CAM_REQ_CMP; + if (pSRB->SRBFlag & AUTO_REQSENSE) { + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = SCSI_STATUS_CHECK_COND; + + if (status == SCSI_STATUS_CHECK_COND) { + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + goto ckc_e; + } + *((u_int32_t *)&(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + + pcsio->sense_resid = pcsio->sense_len + - pSRB->TotalXferredLen; + pSRB->TotalXferredLen = pSRB->Segment1[1]; + if (pSRB->TotalXferredLen) { + /* ???? */ + pcsio->resid = pcsio->dxfer_len + - pSRB->TotalXferredLen; + /* The resid field contains valid data */ + /* Flush resid bytes on complete */ + } else { + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + } + pccb->ccb_h.status = CAM_AUTOSNS_VALID; + goto ckc_e; + } + if (status) { + if (status == SCSI_STATUS_CHECK_COND) { + + if ((pSRB->SGIndex < pSRB->SGcount) + && (pSRB->SGcount) && (pSRB->SGToBeXferLen)) { + bval = pSRB->SGcount; + swlval = pSRB->SGToBeXferLen; + ptr2 = pSRB->pSGlist; + ptr2++; + for (i = pSRB->SGIndex + 1; i < bval; i++) { + swlval += ptr2->SGXLen; + ptr2++; + } + /* ??????? */ + pcsio->resid = (u_int32_t) swlval; + +#ifdef AMD_DEBUG0 + printf("XferredLen=%8x,NotYetXferLen=%8x,", + pSRB->TotalXferredLen, swlval); +#endif + } + if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { +#ifdef AMD_DEBUG0 + printf("RequestSense..................\n"); +#endif + RequestSense(amd, pSRB); + return; + } + pcsio->scsi_status = SCSI_STATUS_CHECK_COND; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == SCSI_STATUS_QUEUE_FULL) { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pcsio->scsi_status = SCSI_STATUS_QUEUE_FULL; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + goto ckc_e; + } else if (status == AMD_SCSI_STAT_SEL_TIMEOUT) { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + + pcsio->scsi_status = AMD_SCSI_STAT_SEL_TIMEOUT; + pccb->ccb_h.status = CAM_SEL_TIMEOUT; + } else if (status == SCSI_STATUS_BUSY) { +#ifdef AMD_DEBUG0 + printf("DC390: target busy at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_BUSY; + pccb->ccb_h.status = CAM_SCSI_BUSY; + } else if (status == SCSI_STATUS_RESERV_CONFLICT) { +#ifdef AMD_DEBUG0 + printf("DC390: target reserved at %s %d\n", + __FILE__, __LINE__); +#endif + pcsio->scsi_status = SCSI_STATUS_RESERV_CONFLICT; + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; /* XXX */ + } else { + pSRB->AdaptStatus = 0; +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup at %s %d\n", + __FILE__, __LINE__); +#endif + pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + } + } else { + status = pSRB->AdaptStatus; + if (status & H_OVER_UNDER_RUN) { + pSRB->TargetStatus = 0; + + pccb->ccb_h.status = CAM_DATA_RUN_ERR; + } else if (pSRB->SRBStatus & PARITY_ERROR) { +#ifdef AMD_DEBUG0 + printf("DC390: driver stuffup %s %d\n", + __FILE__, __LINE__); +#endif + /* Driver failed to perform operation */ + pccb->ccb_h.status = CAM_UNCOR_PARITY; + } else { /* No error */ + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + /* there is no error, (sense is invalid) */ + } + } +ckc_e: + intflag = splcam(); + if ((pccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + /* CAM request not yet complete =>device_Q frozen */ + xpt_freeze_devq(pccb->ccb_h.path, 1); + pccb->ccb_h.status |= CAM_DEV_QFRZN; + } + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->free_srbs, pSRB, links); + amdrunwaiting(amd); + splx(intflag); + xpt_done(pccb); + +} + +static void +amd_ResetSCSIBus(struct amd_softc * amd) +{ + int intflag; + + intflag = splcam(); + amd->ACBFlag |= RESET_DEV; + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, RST_SCSI_BUS_CMD); + splx(intflag); + return; +} + +static void +amd_ScsiRstDetect(struct amd_softc * amd) +{ + int intflag; + u_int32_t wlval; + +#ifdef AMD_DEBUG0 + printf("amd_ScsiRstDetect \n"); +#endif + + wlval = 1000; + while (--wlval) { /* delay 1 sec */ + DELAY(1000); + } + intflag = splcam(); + + amd_write8(amd, DMA_Cmd, DMA_IDLE_CMD); + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); + + if (amd->ACBFlag & RESET_DEV) { + amd->ACBFlag |= RESET_DONE; + } else { + amd->ACBFlag |= RESET_DETECT; + ResetDevParam(amd); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->running_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amdcompletematch(amd, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD, + AMD_TAG_WILDCARD, &amd->waiting_srbs, + CAM_DEV_QFRZN|CAM_SCSI_BUS_RESET); + amd->active_srb = NULL; + amd->ACBFlag = 0; + amdrunwaiting(amd); + } + splx(intflag); + return; +} + +static void +RequestSense(struct amd_softc *amd, struct amd_srb *pSRB) +{ + union ccb *pccb; + struct ccb_scsiio *pcsio; + + pccb = pSRB->pccb; + pcsio = &pccb->csio; + + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->Segment0[0] = *((u_int32_t *) & (pSRB->CmdBlock[0])); + pSRB->Segment0[1] = *((u_int32_t *) & (pSRB->CmdBlock[4])); + pSRB->Segment1[0] = (pSRB->ScsiCmdLen << 8) + pSRB->SGcount; + pSRB->Segment1[1] = pSRB->TotalXferredLen; + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + pSRB->Segmentx.SGXPtr = (u_int32_t) vtophys(&pcsio->sense_data); + pSRB->Segmentx.SGXLen = (u_int32_t) pcsio->sense_len; + + pSRB->pSGlist = &pSRB->Segmentx; + pSRB->SGcount = 1; + pSRB->SGIndex = 0; + + *((u_int32_t *) & (pSRB->CmdBlock[0])) = 0x00000003; + pSRB->CmdBlock[1] = pSRB->pccb->ccb_h.target_lun << 5; + *((u_int16_t *) & (pSRB->CmdBlock[4])) = pcsio->sense_len; + pSRB->ScsiCmdLen = 6; + + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if (amdstart(amd, pSRB) != 0) { + TAILQ_REMOVE(&amd->running_srbs, pSRB, links); + TAILQ_INSERT_HEAD(&amd->waiting_srbs, pSRB, links); + } +} + +static void +amd_InvalidCmd(struct amd_softc * amd) +{ + struct amd_srb *srb; + + srb = amd->active_srb; + if (srb->SRBState & (SRB_START|SRB_MSGOUT)) + amd_write8(amd, SCSICMDREG, CLEAR_FIFO_CMD); +} + +void +amd_linkSRB(struct amd_softc *amd) +{ + u_int16_t count, i; + struct amd_srb *psrb; + + count = amd->SRBCount; + + for (i = 0; i < count; i++) { + psrb = (struct amd_srb *)&amd->SRB_array[i]; + psrb->TagNumber = i; + TAILQ_INSERT_TAIL(&amd->free_srbs, psrb, links); + } +} + +void +amd_EnDisableCE(struct amd_softc *amd, int mode, int *regval) +{ + if (mode == ENABLE_CE) { + *regval = 0xc0; + } else { + *regval = 0x80; + } + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + if (mode == DISABLE_CE) { + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + } + DELAY(160); +} + +void +amd_EEpromOutDI(struct amd_softc *amd, int *regval, int Carry) +{ + u_int bval; + + bval = 0; + if (Carry) { + bval = 0x40; + *regval = 0x80; + pci_cfgwrite(amd->config_id, *regval, bval, /*bytes*/1); + } + DELAY(160); + bval |= 0x80; + pci_cfgwrite(amd->config_id, *regval, bval, /*bytes*/1); + DELAY(160); + pci_cfgwrite(amd->config_id, *regval, 0, /*bytes*/1); + DELAY(160); +} + +static int +amd_EEpromInDO(struct amd_softc *amd) +{ + pci_cfgwrite(amd->config_id, 0x80, 0x80, /*bytes*/1); + DELAY(160); + pci_cfgwrite(amd->config_id, 0x80, 0x40, /*bytes*/1); + DELAY(160); + if (pci_cfgread(amd->config_id, 0, /*bytes*/1) == 0x22) + return (1); + return (0); +} + +static u_int16_t +EEpromGetData1(struct amd_softc *amd) +{ + u_int i; + u_int carryFlag; + u_int16_t wval; + + wval = 0; + for (i = 0; i < 16; i++) { + wval <<= 1; + carryFlag = amd_EEpromInDO(amd); + wval |= carryFlag; + } + return (wval); +} + +static void +amd_Prepare(struct amd_softc *amd, int *regval, u_int8_t EEpromCmd) +{ + u_int i, j; + int carryFlag; + + carryFlag = 1; + j = 0x80; + for (i = 0; i < 9; i++) { + amd_EEpromOutDI(amd, regval, carryFlag); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + +static void +amd_ReadEEprom(struct amd_softc *amd) +{ + int regval; + u_int i; + u_int16_t *ptr; + u_int8_t cmd; + + ptr = (u_int16_t *)&amd->eepromBuf[0]; + cmd = EEPROM_READ; + for (i = 0; i < 0x40; i++) { + amd_EnDisableCE(amd, ENABLE_CE, ®val); + amd_Prepare(amd, ®val, cmd); + *ptr = EEpromGetData1(amd); + ptr++; + cmd++; + amd_EnDisableCE(amd, DISABLE_CE, ®val); + } +} + +static void +amd_load_defaults(struct amd_softc *amd) +{ + int target; + + bzero(&amd->eepromBuf, sizeof amd->eepromBuf); + for (target = 0; target < MAX_SCSI_ID; target++) + amd->eepromBuf[target << 2] = + (TAG_QUEUING|EN_DISCONNECT|SYNC_NEGO|PARITY_CHK); + amd->eepromBuf[EE_ADAPT_SCSI_ID] = 7; + amd->eepromBuf[EE_MODE2] = ACTIVE_NEGATION|LUN_CHECK|GREATER_1G; + amd->eepromBuf[EE_TAG_CMD_NUM] = 4; +} + +static void +amd_load_eeprom_or_defaults(struct amd_softc *amd) +{ + u_int16_t wval, *ptr; + u_int8_t i; + + amd_ReadEEprom(amd); + wval = 0; + ptr = (u_int16_t *) & amd->eepromBuf[0]; + for (i = 0; i < EE_DATA_SIZE; i += 2, ptr++) + wval += *ptr; + + if (wval != EE_CHECKSUM) { + if (bootverbose) + printf("amd%d: SEEPROM data unavailable. " + "Using default device parameters.\n", + amd->unit); + amd_load_defaults(amd); + } +} + +/* + ********************************************************************** + * Function : static int amd_init (struct Scsi_Host *host) + * Purpose : initialize the internal structures for a given SCSI host + * Inputs : host - pointer to this host adapter's structure/ + ********************************************************************** + */ +static struct amd_softc * +amd_init(int unit, pcici_t config_id) +{ + struct amd_softc *amd; + u_int bval; + u_int i; + + amd = (struct amd_softc *)malloc(sizeof(struct amd_softc), + M_DEVBUF, M_WAITOK); + if (amd == NULL) { + printf("DC390%d: cannot allocate ACB !\n", unit); + return (amd); + } + bzero(amd, sizeof(struct amd_softc)); + amd->tag = I386_BUS_SPACE_IO; + amd->bsh = pci_conf_read(config_id, PCI_MAP_REG_START) & 0xFFFE; + /* DMA tag for mapping buffers into device visible space. */ + if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/0, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/MAXBSIZE, /*nsegments*/AMD_NSEG, + /*maxsegsz*/AMD_MAXTRANSFER_SIZE, + /*flags*/BUS_DMA_ALLOCNOW, + &amd->buffer_dmat) != 0) { + free(amd, M_DEVBUF); + return (NULL); + } + TAILQ_INIT(&amd->free_srbs); + TAILQ_INIT(&amd->running_srbs); + TAILQ_INIT(&amd->waiting_srbs); + amd->last_phase = SCSI_BUS_FREE; + amd->config_id = config_id; + amd->unit = unit; + amd->SRBCount = MAX_SRB_CNT; + amd->status = 0; + amd_load_eeprom_or_defaults(amd); + amd->max_id = 7; + if (amd->eepromBuf[EE_MODE2] & LUN_CHECK) { + amd->max_lun = 7; + } else { + amd->max_lun = 0; + } + amd->AdaptSCSIID = amd->eepromBuf[EE_ADAPT_SCSI_ID]; + amd->HostID_Bit = (1 << amd->AdaptSCSIID); + amd->AdaptSCSILUN = 0; + /* (eepromBuf[EE_TAG_CMD_NUM]) << 2; */ + amd->ACBFlag = 0; + amd->Gmode2 = amd->eepromBuf[EE_MODE2]; + amd_linkSRB(amd); + for (i = 0; i <= amd->max_id; i++) { + + if (amd->AdaptSCSIID != i) { + struct amd_target_info *tinfo; + PEEprom prom; + + tinfo = &amd->tinfo[i]; + prom = (PEEprom)&amd->eepromBuf[i << 2]; + if ((prom->EE_MODE1 & EN_DISCONNECT) != 0) { + tinfo->disc_tag |= AMD_USR_DISCENB; + if ((prom->EE_MODE1 & TAG_QUEUING) != 0) + tinfo->disc_tag |= AMD_USR_TAGENB; + } + if ((prom->EE_MODE1 & SYNC_NEGO) != 0) { + tinfo->user.period = + eeprom_period[prom->EE_SPEED]; + tinfo->user.offset = AMD_MAX_SYNC_OFFSET; + } + tinfo->CtrlR1 = amd->AdaptSCSIID; + if ((prom->EE_MODE1 & PARITY_CHK) != 0) + tinfo->CtrlR1 |= PARITY_ERR_REPO; + tinfo->CtrlR3 = FAST_CLK; + tinfo->CtrlR4 = EATER_25NS; + if ((amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) != 0) + tinfo->CtrlR4 |= NEGATE_REQACKDATA; + } + } + amd_write8(amd, SCSITIMEOUTREG, 153); /* 250ms selection timeout */ + /* Conversion factor = 0 , 40MHz clock */ + amd_write8(amd, CLKFACTREG, CLK_FREQ_40MHZ); + /* NOP cmd - clear command register */ + amd_write8(amd, SCSICMDREG, NOP_CMD); + amd_write8(amd, CNTLREG2, EN_FEATURE|EN_SCSI2_CMD); + amd_write8(amd, CNTLREG3, FAST_CLK); + bval = EATER_25NS; + if (amd->eepromBuf[EE_MODE2] & ACTIVE_NEGATION) { + bval |= NEGATE_REQACKDATA; + } + amd_write8(amd, CNTLREG4, bval); + + /* Disable SCSI bus reset interrupt */ + amd_write8(amd, CNTLREG1, DIS_INT_ON_SCSI_RST); + return (amd); +} + +/* + * attach and init a host adapter + */ +static void +amd_attach(pcici_t config_id, int unit) +{ + struct cam_devq *devq; /* Device Queue to use for this SIM */ + u_int8_t intstat; + u_int32_t wlval; + struct amd_softc *amd = NULL; + + wlval = pci_conf_read(config_id, PCI_ID_REG); + + if (wlval == PCI_DEVICE_ID_AMD53C974) { + if ((amd = amd_init(unit, config_id)) == NULL) + return; + + /* Reset Pending INT */ + intstat = amd_read8(amd, INTSTATREG); + } + + /* After setting up the adapter, map our interrupt */ + if (!pci_map_int(config_id, amd_intr, amd, &cam_imask)) { + if (bootverbose) + printf("amd%d: unable to register interrupt handler!\n", + unit); + free(amd, M_DEVBUF); + return; + } + + /* + * Now let the CAM generic SCSI layer find the SCSI devices on + * the bus * start queue to reset to the idle loop. * + * Create device queue of SIM(s) * (MAX_START_JOB - 1) : + * max_sim_transactions + */ + devq = cam_simq_alloc(MAX_START_JOB); + if (devq == NULL) { + free(amd, M_DEVBUF); + return; + } + + amd->psim = cam_sim_alloc(amd_action, amd_poll, "amd", + amd, amd->unit, 1, MAX_TAGS_CMD_QUEUE, + devq); + if (amd->psim == NULL) { + cam_simq_free(devq); + free(amd, M_DEVBUF); + return; + } + + if (xpt_bus_register(amd->psim, 0) != CAM_SUCCESS) { + cam_sim_free(amd->psim, /*free_devq*/TRUE); + free(amd, M_DEVBUF); + return; + } + + if (xpt_create_path(&amd->ppath, /* periph */ NULL, + cam_sim_path(amd->psim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(amd->psim)); + cam_sim_free(amd->psim, /* free_simq */ TRUE); + free(amd, M_DEVBUF); + return; + } +} + +static const char * +amd_probe(pcici_t tag, pcidi_t type) +{ + if (type == PCI_DEVICE_ID_AMD53C974) { + return ("Tekram DC390(T)/AMD53c974 SCSI Host Adapter"); + } else { + return (NULL); + } +} diff --git a/sys/pci/amd.h b/sys/pci/amd.h new file mode 100644 index 000000000000..c140e313cf6d --- /dev/null +++ b/sys/pci/amd.h @@ -0,0 +1,579 @@ +/* + ********************************************************************* + * FILE NAME : amd.h + * BY : C.L. Huang (ching@tekram.com.tw) + * Erich Chen (erich@tekram.com.tw) + * Description: Device Driver for the amd53c974 PCI Bus Master + * SCSI Host adapter found on cards such as + * the Tekram DC-390(T). + * (C)Copyright 1995-1999 Tekram Technology Co., Ltd. + * + * 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. + ********************************************************************* + */ + +#ifndef AMD_H +#define AMD_H + +#define AMD_TRANS_CUR 0x01 /* Modify current neogtiation status */ +#define AMD_TRANS_ACTIVE 0x03 /* Assume this is the active target */ +#define AMD_TRANS_GOAL 0x04 /* Modify negotiation goal */ +#define AMD_TRANS_USER 0x08 /* Modify user negotiation settings */ + +/* + * Per target transfer parameters. + */ +struct amd_transinfo { + u_int8_t period; + u_int8_t offset; +}; + +struct amd_target_info { + /* + * Records the currently active and user/default settings for + * tagged queueing and disconnection for each target. + */ + u_int8_t disc_tag; +#define AMD_CUR_DISCENB 0x01 +#define AMD_CUR_TAGENB 0x02 +#define AMD_USR_DISCENB 0x04 +#define AMD_USR_TAGENB 0x08 + u_int8_t CtrlR1; + u_int8_t CtrlR3; + u_int8_t CtrlR4; + u_int8_t sync_period_reg; + u_int8_t sync_offset_reg; + + /* + * Currently active transfer settings. + */ + struct amd_transinfo current; + /* + * Transfer settings we wish to achieve + * through negotiation. + */ + struct amd_transinfo goal; + /* + * User defined or default transfer settings. + */ + struct amd_transinfo user; +}; + +/* + * Scatter/Gather Segment entry. + */ +struct amd_sg { + u_int32_t SGXLen; + u_int32_t SGXPtr; +}; + +/* + * Chipset feature limits + */ +#define MAX_SCSI_ID 8 +#define AMD_MAX_SYNC_OFFSET 15 +#define AMD_TARGET_MAX 7 +#define AMD_LUN_MAX 7 +#define AMD_NSEG (btoc(MAXPHYS) + 1) +#define AMD_MAXTRANSFER_SIZE 0xFFFFFF /* restricted by 24 bit counter */ +#define MAX_DEVICES 10 +#define MAX_TAGS_CMD_QUEUE 256 +#define MAX_CMD_PER_LUN 6 +#define MAX_SRB_CNT 256 +#define MAX_START_JOB 256 + +/* + * BIT position to integer mapping. + */ +#define BIT(N) (0x01 << N) + +/* + * EEPROM storage offsets and data structures. + */ +typedef struct _EEprom { + u_int8_t EE_MODE1; + u_int8_t EE_SPEED; + u_int8_t xx1; + u_int8_t xx2; +} EEprom, *PEEprom; + +#define EE_ADAPT_SCSI_ID 64 +#define EE_MODE2 65 +#define EE_DELAY 66 +#define EE_TAG_CMD_NUM 67 +#define EE_DATA_SIZE 128 +#define EE_CHECKSUM 0x1234 + +/* + * EE_MODE1 bits definition + */ +#define PARITY_CHK BIT(0) +#define SYNC_NEGO BIT(1) +#define EN_DISCONNECT BIT(2) +#define SEND_START BIT(3) +#define TAG_QUEUING BIT(4) + +/* + * EE_MODE2 bits definition + */ +#define MORE2_DRV BIT(0) +#define GREATER_1G BIT(1) +#define RST_SCSI_BUS BIT(2) +#define ACTIVE_NEGATION BIT(3) +#define NO_SEEK BIT(4) +#define LUN_CHECK BIT(5) + +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 + +#define AMD_TAG_WILDCARD ((u_int)(~0)) + +/* + * SCSI Request Block + */ +struct amd_srb { + TAILQ_ENTRY(amd_srb) links; + u_int8_t CmdBlock[12]; + union ccb *pccb; + bus_dmamap_t dmamap; + struct amd_sg *pSGlist; + + u_int32_t TotalXferredLen; + u_int32_t SGPhysAddr; /* a segment starting address */ + u_int32_t SGToBeXferLen; /* to be xfer length */ + u_int32_t Segment0[2]; + u_int32_t Segment1[2]; + + struct amd_sg SGsegment[AMD_NSEG]; + struct amd_sg Segmentx;/* a one entry of S/G list table */ + u_int8_t *pMsgPtr; + u_int16_t SRBState; + + u_int8_t AdaptStatus; + u_int8_t TargetStatus; + u_int8_t MsgCnt; + u_int8_t EndMessage; + u_int8_t TagNumber; + u_int8_t SGcount; + u_int8_t SGIndex; + u_int8_t IORBFlag; /* ;81h-Reset, 2-retry */ + + u_int8_t SRBStatus; + u_int8_t SRBFlag; + /* ; b0-AutoReqSense,b6-Read,b7-write */ + /* ; b4-settimeout,b5-Residual valid */ + u_int8_t ScsiCmdLen; +}; + +TAILQ_HEAD(srb_queue, amd_srb); + +/* + * Per-adapter, software configuration. + */ +struct amd_softc { + bus_space_tag_t tag; + bus_space_handle_t bsh; + bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */ + int unit; + + int last_phase; + int cur_target; + int cur_lun; + struct amd_srb *active_srb; + struct amd_srb *untagged_srbs[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + struct amd_target_info tinfo[AMD_TARGET_MAX+1]; + u_int16_t disc_count[AMD_TARGET_MAX+1][AMD_LUN_MAX+1]; + + struct srb_queue free_srbs; + struct srb_queue waiting_srbs; + struct srb_queue running_srbs; + + pcici_t config_id; + struct amd_srb *pTmpSRB; + + u_int16_t SRBCount; + + u_int16_t max_id; + u_int16_t max_lun; + + /* Hooks into the CAM XPT */ + struct cam_sim *psim; + struct cam_path *ppath; + + u_int8_t msgin_buf[6]; + u_int8_t msgout_buf[6]; + u_int msgin_index; + u_int msgout_index; + u_int msgout_len; + + u_int8_t status; + u_int8_t AdaptSCSIID; /* ; Adapter SCSI Target ID */ + u_int8_t AdaptSCSILUN; /* ; Adapter SCSI LUN */ + + u_int8_t ACBFlag; + + u_int8_t Gmode2; + + u_int8_t HostID_Bit; + + u_int8_t InitDCB_flag[8][8]; /* flag of initDCB for device */ + struct amd_srb SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */ + struct amd_srb TmpSRB; + /* Setup data stored in an 93c46 serial eeprom */ + u_int8_t eepromBuf[EE_DATA_SIZE]; +}; + +/* + * ----SRB State machine definition + */ +#define SRB_FREE 0 +#define SRB_READY BIT(1) +#define SRB_MSGOUT BIT(2) /* ;arbitration+msg_out 1st byte */ +#define SRB_MSGIN BIT(3) +#define SRB_MSGIN_MULTI BIT(4) +#define SRB_COMMAND BIT(5) +#define SRB_START BIT(6) /* ;arbitration+msg_out+command_out */ +#define SRB_DISCONNECT BIT(7) +#define SRB_DATA_XFER BIT(8) +#define SRB_XFERPAD BIT(9) +#define SRB_STATUS BIT(10) +#define SRB_COMPLETED BIT(11) +#define SRB_ABORT_SENT BIT(12) +#define DO_SYNC_NEGO BIT(13) +#define SRB_UNEXPECT_RESEL BIT(14) + +/* + * ---ACB Flag + */ +#define RESET_DEV BIT(0) +#define RESET_DETECT BIT(1) +#define RESET_DONE BIT(2) + +/* + * ---DCB Flag + */ +#define ABORT_DEV_ BIT(0) + +/* + * ---SRB status + */ +#define SRB_OK BIT(0) +#define ABORTION BIT(1) +#define OVER_RUN BIT(2) +#define UNDER_RUN BIT(3) +#define PARITY_ERROR BIT(4) +#define SRB_ERROR BIT(5) + +/* + * ---SRB Flags + */ +#define DATAOUT BIT(7) +#define DATAIN BIT(6) +#define RESIDUAL_VALID BIT(5) +#define ENABLE_TIMER BIT(4) +#define RESET_DEV0 BIT(2) +#define ABORT_DEV BIT(1) +#define AUTO_REQSENSE BIT(0) + +/* + * ---Adapter status + */ +#define H_STATUS_GOOD 0 +#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 0x0FF + +/* + * AMD specific "status" codes returned in the SCSI status byte. + */ +#define AMD_SCSI_STAT_UNEXP_BUS_F 0xFD /* ; Unexpect Bus Free */ +#define AMD_SCSI_STAT_BUS_RST_DETECT 0xFE /* ; Scsi Bus Reset detected */ +#define AMD_SCSI_STAT_SEL_TIMEOUT 0xFF /* ; Selection Time out */ + +/* + * ---Sync_Mode + */ +#define SYNC_DISABLE 0 +#define SYNC_ENABLE BIT(0) +#define SYNC_NEGO_DONE BIT(1) +#define WIDE_ENABLE BIT(2) +#define WIDE_NEGO_DONE BIT(3) +#define EN_TAG_QUEUING BIT(4) +#define EN_ATN_STOP BIT(5) + +#define SYNC_NEGO_OFFSET 15 + +/* + * ---SCSI bus phase + */ +#define SCSI_DATA_OUT 0 +#define SCSI_DATA_IN 1 +#define SCSI_COMMAND 2 +#define SCSI_STATUS 3 +#define SCSI_NOP0 4 +#define SCSI_ARBITRATING 5 +#define SCSI_MSG_OUT 6 +#define SCSI_MSG_IN 7 +#define SCSI_BUS_FREE 8 + +/* + *========================================================== + * AMD 53C974 Registers bit Definition + *========================================================== + */ + +/* + * ------SCSI Register------- + * Command Reg.(+0CH) + */ +#define DMA_COMMAND BIT(7) +#define NOP_CMD 0 +#define CLEAR_FIFO_CMD 1 +#define RST_DEVICE_CMD 2 +#define RST_SCSI_BUS_CMD 3 +#define INFO_XFER_CMD 0x10 +#define INITIATOR_CMD_CMPLTE 0x11 +#define MSG_ACCEPTED_CMD 0x12 +#define XFER_PAD_BYTE 0x18 +#define SET_ATN_CMD 0x1A +#define RESET_ATN_CMD 0x1B +#define SEL_W_ATN 0x42 +#define SEL_W_ATN_STOP 0x43 +#define EN_SEL_RESEL 0x44 +#define SEL_W_ATN2 0x46 +#define DATA_XFER_CMD INFO_XFER_CMD + + +/* + * ------SCSI Register------- + * SCSI Status Reg.(+10H) + */ +#define INTERRUPT BIT(7) +#define ILLEGAL_OP_ERR BIT(6) +#define PARITY_ERR BIT(5) +#define COUNT_2_ZERO BIT(4) +#define GROUP_CODE_VALID BIT(3) +#define SCSI_PHASE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Interrupt Status Reg.(+14H) + */ +#define SCSI_RESET_ BIT(7) +#define INVALID_CMD BIT(6) +#define DISCONNECTED BIT(5) +#define SERVICE_REQUEST BIT(4) +#define SUCCESSFUL_OP BIT(3) +#define RESELECTED BIT(2) +#define SEL_ATTENTION BIT(1) +#define SELECTED BIT(0) + +/* + * ------SCSI Register------- + * Internal State Reg.(+18H) + */ +#define SYNC_OFFSET_FLAG BIT(3) +#define INTRN_STATE_MASK (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Clock Factor Reg.(+24H) + */ +#define CLK_FREQ_40MHZ 0 +#define CLK_FREQ_35MHZ (BIT(2)+BIT(1)+BIT(0)) +#define CLK_FREQ_30MHZ (BIT(2)+BIT(1)) +#define CLK_FREQ_25MHZ (BIT(2)+BIT(0)) +#define CLK_FREQ_20MHZ BIT(2) +#define CLK_FREQ_15MHZ (BIT(1)+BIT(0)) +#define CLK_FREQ_10MHZ BIT(1) + +/* + * ------SCSI Register------- + * Control Reg. 1(+20H) + */ +#define EXTENDED_TIMING BIT(7) +#define DIS_INT_ON_SCSI_RST BIT(6) +#define PARITY_ERR_REPO BIT(4) +#define SCSI_ID_ON_BUS (BIT(2)+BIT(1)+BIT(0)) + +/* + * ------SCSI Register------- + * Control Reg. 2(+2CH) + */ +#define EN_FEATURE BIT(6) +#define EN_SCSI2_CMD BIT(3) + +/* + * ------SCSI Register------- + * Control Reg. 3(+30H) + */ +#define ID_MSG_CHECK BIT(7) +#define EN_QTAG_MSG BIT(6) +#define EN_GRP2_CMD BIT(5) +#define FAST_SCSI BIT(4) /* ;10MB/SEC */ +#define FAST_CLK BIT(3) /* ;25 - 40 MHZ */ + +/* + * ------SCSI Register------- + * Control Reg. 4(+34H) + */ +#define EATER_12NS 0 +#define EATER_25NS BIT(7) +#define EATER_35NS BIT(6) +#define EATER_0NS (BIT(7)+BIT(6)) +#define NEGATE_REQACKDATA BIT(2) +#define NEGATE_REQACK BIT(3) + +/* + *======================================== + * DMA Register + *======================================== + */ + +/* + * -------DMA Register-------- + * DMA Command Reg.(+40H) + */ +#define READ_DIRECTION BIT(7) +#define WRITE_DIRECTION 0 +#define EN_DMA_INT BIT(6) +#define MAP_TO_MDL BIT(5) +#define DMA_DIAGNOSTIC BIT(4) +#define DMA_IDLE_CMD 0 +#define DMA_BLAST_CMD BIT(0) +#define DMA_ABORT_CMD BIT(1) +#define DMA_START_CMD (BIT(1)|BIT(0)) + +/* + * -------DMA Register-------- + * DMA Status Reg.(+54H) + */ +#define PCI_MS_ABORT BIT(6) +#define BLAST_COMPLETE BIT(5) +#define SCSI_INTERRUPT BIT(4) +#define DMA_XFER_DONE BIT(3) +#define DMA_XFER_ABORT BIT(2) +#define DMA_XFER_ERROR BIT(1) +#define POWER_DOWN BIT(0) + +/* + * -------DMA Register-------- + * DMA SCSI Bus and Ctrl.(+70H) + * EN_INT_ON_PCI_ABORT + */ + +/* + *========================================================== + * SCSI Chip register address offset + *========================================================== + */ +#define CTCREG_LOW 0x00 /* (R) current transfer count register low */ +#define STCREG_LOW 0x00 /* (W) start transfer count register low */ + +#define CTCREG_MID 0x04 /* (R) current transfer count register + * middle */ +#define STCREG_MID 0x04 /* (W) start transfer count register middle */ + +#define SCSIFIFOREG 0x08 /* (R/W) SCSI FIFO register */ + +#define SCSICMDREG 0x0C /* (R/W) SCSI command register */ + +#define SCSISTATREG 0x10 /* (R) SCSI status register */ +#define SCSIDESTIDREG 0x10 /* (W) SCSI destination ID register */ + +#define INTSTATREG 0x14 /* (R) interrupt status register */ +#define SCSITIMEOUTREG 0x14 /* (W) SCSI timeout register */ + + +#define INTERNSTATREG 0x18 /* (R) internal state register */ +#define SYNCPERIOREG 0x18 /* (W) synchronous transfer period register */ + +#define CURRENTFIFOREG 0x1C /* (R) current FIFO/internal state register */ +#define SYNCOFFREG 0x1C/* (W) synchronous transfer period register */ + +#define CNTLREG1 0x20 /* (R/W) control register 1 */ +#define CLKFACTREG 0x24 /* (W) clock factor register */ +#define CNTLREG2 0x2C /* (R/W) control register 2 */ +#define CNTLREG3 0x30 /* (R/W) control register 3 */ +#define CNTLREG4 0x34 /* (R/W) control register 4 */ + +#define CURTXTCNTREG 0x38 /* (R) current transfer count register + * high/part-unique ID code */ +#define STCREG_HIGH 0x38 /* (W) Start current transfer count register + * high */ + +/* + ********************************************************* + * + * SCSI DMA register + * + ********************************************************* + */ +#define DMA_Cmd 0x40 /* (R/W) command register */ +#define DMA_XferCnt 0x44 /* (R/W) starting transfer count */ +#define DMA_XferAddr 0x48 /* (R/W) starting Physical address */ +#define DMA_Wk_ByteCntr 0x4C /* ( R ) working byte counter */ +#define DMA_Wk_AddrCntr 0x50 /* ( R ) working address counter */ +#define DMA_Status 0x54 /* ( R ) status register */ +#define DMA_MDL_Addr 0x58 /* (R/W) starting memory descriptor list (MDL) + * address */ +#define DMA_Wk_MDL_Cntr 0x5C /* ( R ) working MDL counter */ +#define DMA_ScsiBusCtrl 0x70 /* (bits R/W) SCSI BUS and control */ + +/* ******************************************************* */ +#define am_target SCSISTATREG +#define am_timeout INTSTATREG +#define am_seq_step SYNCPERIOREG +#define am_fifo_count SYNCOFFREG + + +#define amd_read8(amd, port) \ + bus_space_read_1((amd)->tag, (amd)->bsh, port) + +#define amd_read16(amd, port) \ + bus_space_read_2((amd)->tag, (amd)->bsh, port) + +#define amd_read32(amd, port) \ + bus_space_read_4((amd)->tag, (amd)->bsh, port) + +#define amd_write8(amd, port, value) \ + bus_space_write_1((amd)->tag, (amd)->bsh, port, value) + +#define amd_write8_multi(amd, port, ptr, len) \ + bus_space_write_multi_1((amd)->tag, (amd)->bsh, port, ptr, len) + +#define amd_write16(amd, port, value) \ + bus_space_write_2((amd)->tag, (amd)->bsh, port, value) + +#define amd_write32(amd, port, value) \ + bus_space_write_4((amd)->tag, (amd)->bsh, port, value) + +#endif /* AMD_H */