📄 aic79xx_core.c
字号:
/* * Core routines and tables shareable across OS platforms. * * Copyright (c) 1994-2001 Justin T. Gibbs. * Copyright (c) 2000-2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#79 $ * * $FreeBSD$ */#ifdef __linux__#include "aic79xx_osm.h"#include "aic79xx_inline.h"#include "aicasm/aicasm_insformat.h"#else#include <dev/aic7xxx/aic79xx_osm.h>#include <dev/aic7xxx/aic79xx_inline.h>#include <dev/aic7xxx/aicasm/aicasm_insformat.h>#endif/****************************** Softc Data ************************************/struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);/***************************** Lookup Tables **********************************/char *ahd_chip_names[] ={ "NONE", "aic7901", "aic7902"};static const u_int num_chip_names = NUM_ELEMENTS(ahd_chip_names);/* * Hardware error codes. */struct ahd_hard_error_entry { uint8_t errno; char *errmesg;};static struct ahd_hard_error_entry ahd_hard_errors[] = { { DSCTMOUT, "Discard Timer has timed out" }, { ILLOPCODE, "Illegal Opcode in sequencer program" }, { SQPARERR, "Sequencer Parity Error" }, { DPARERR, "Data-path Parity Error" }, { MPARERR, "Scratch or SCB Memory Parity Error" }, { CIOPARERR, "CIOBUS Parity Error" },};static const u_int num_errors = NUM_ELEMENTS(ahd_hard_errors);static struct ahd_phase_table_entry ahd_phase_table[] ={ { P_DATAOUT, MSG_NOOP, "in Data-out phase" }, { P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" }, { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" }, { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" }, { P_COMMAND, MSG_NOOP, "in Command phase" }, { P_MESGOUT, MSG_NOOP, "in Message-out phase" }, { P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" }, { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" }, { P_BUSFREE, MSG_NOOP, "while idle" }, { 0, MSG_NOOP, "in unknown phase" }};/* * In most cases we only wish to itterate over real phases, so * exclude the last element from the count. */static const u_int num_phases = NUM_ELEMENTS(ahd_phase_table) - 1;/* Our Sequencer Program */#include "aic79xx_seq.h"/**************************** Function Declarations ***************************/static void ahd_handle_transmission_error(struct ahd_softc *ahd);static void ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1);static int ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime);static int ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);static void ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static struct ahd_tmode_tstate* ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel);#ifdef AHD_TARGET_MODEstatic void ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force);#endifstatic void ahd_devlimited_syncrate(struct ahd_softc *ahd, struct ahd_initiator_tinfo *, u_int *period, u_int *ppr_options, role_t role);static void ahd_update_neg_table(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, struct ahd_transinfo *tinfo);static void ahd_update_pending_scbs(struct ahd_softc *ahd);static void ahd_fetch_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_scb_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, struct scb *scb);static void ahd_setup_initiator_msgout(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, struct scb *scb);static void ahd_build_transfer_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_construct_sdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int period, u_int offset);static void ahd_construct_wdtr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int bus_width);static void ahd_construct_ppr(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, u_int period, u_int offset, u_int bus_width, u_int ppr_options);static void ahd_clear_msg_state(struct ahd_softc *ahd);static void ahd_handle_message_phase(struct ahd_softc *ahd);typedef enum { AHDMSG_1B, AHDMSG_2B, AHDMSG_EXT} ahd_msgtype;static int ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type, u_int msgval, int full);static int ahd_parse_msg(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static int ahd_handle_msg_reject(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_handle_ign_wide_residue(struct ahd_softc *ahd, struct ahd_devinfo *devinfo);static void ahd_reinitialize_dataptrs(struct ahd_softc *ahd);static void ahd_handle_devreset(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, cam_status status, char *message, int verbose_level);#if AHD_TARGET_MODEstatic void ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, struct scb *scb);#endifstatic bus_size_t ahd_sglist_size(struct ahd_softc *ahd);static bus_size_t ahd_sglist_allocsize(struct ahd_softc *ahd);static bus_dmamap_callback_t ahd_dmamap_cb; static void ahd_initialize_hscbs(struct ahd_softc *ahd);static int ahd_init_scbdata(struct ahd_softc *ahd);static void ahd_fini_scbdata(struct ahd_softc *ahd);static void ahd_setup_iocell_workaround(struct ahd_softc *ahd);static void ahd_iocell_first_selection(struct ahd_softc *ahd);static void ahd_chip_init(struct ahd_softc *ahd);static void ahd_qinfifo_requeue(struct ahd_softc *ahd, struct scb *prev_scb, struct scb *scb);static int ahd_qinfifo_count(struct ahd_softc *ahd);static int ahd_search_scb_list(struct ahd_softc *ahd, int target, char channel, int lun, u_int tag, role_t role, uint32_t status, ahd_search_action action, u_int *list_head, u_int tid);static void ahd_stitch_tid_list(struct ahd_softc *ahd, u_int tid_prev, u_int tid_cur, u_int tid_next);static void ahd_add_scb_to_free_list(struct ahd_softc *ahd, u_int scbid);static u_int ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid, u_int prev, u_int next, u_int tid);static void ahd_reset_current_bus(struct ahd_softc *ahd);static ahd_callback_t ahd_reset_poll;#ifdef AHD_DUMP_SEQstatic void ahd_dumpseq(struct ahd_softc *ahd);#endifstatic void ahd_loadseq(struct ahd_softc *ahd);static int ahd_check_patch(struct ahd_softc *ahd, struct patch **start_patch, u_int start_instr, u_int *skip_addr);static u_int ahd_resolve_seqaddr(struct ahd_softc *ahd, u_int address);static void ahd_download_instr(struct ahd_softc *ahd, u_int instrptr, uint8_t *dconsts);#ifdef AHD_TARGET_MODEstatic void ahd_queue_lstate_event(struct ahd_softc *ahd, struct ahd_tmode_lstate *lstate, u_int initiator_id, u_int event_type, u_int event_arg);static void ahd_update_scsiid(struct ahd_softc *ahd, u_int targid_mask);static int ahd_handle_target_cmd(struct ahd_softc *ahd, struct target_cmd *cmd);#endif/******************************** Private Inlines *****************************/static __inline void ahd_assert_atn(struct ahd_softc *ahd);static __inline int ahd_currently_packetized(struct ahd_softc *ahd);static __inline int ahd_set_active_fifo(struct ahd_softc *ahd);static __inline voidahd_assert_atn(struct ahd_softc *ahd){ ahd_outb(ahd, SCSISIGO, ATNO);}/* * Determine if the current connection has a packetized * agreement. This does not necessarily mean that we * are currently in a packetized transfer. We could * just as easily be sending or receiving a message. */static __inline intahd_currently_packetized(struct ahd_softc *ahd){ ahd_mode_state saved_modes; int packetized; saved_modes = ahd_save_modes(ahd); if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) { /* * The packetized bit refers to the last * connection, not the current one. Check * for non-zero LQISTATE instead. */ ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG); packetized = ahd_inb(ahd, LQISTATE) != 0; } else { ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED; } ahd_restore_modes(ahd, saved_modes); return (packetized);}static __inline intahd_set_active_fifo(struct ahd_softc *ahd){ u_int active_fifo; AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK); active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;/* XXX This is a three possition switch in the B. */ switch (active_fifo) { case 0: case 1: ahd_set_modes(ahd, active_fifo, active_fifo); return (1); default: return (0); }}/************************* Sequencer Execution Control ************************//* * Restart the sequencer program from address zero */voidahd_restart(struct ahd_softc *ahd){ ahd_pause(ahd); ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); /* No more pending messages */ ahd_clear_msg_state(ahd); ahd_outb(ahd, SCSISIGO, 0); /* De-assert BSY */ ahd_outb(ahd, MSG_OUT, MSG_NOOP); /* No message to send */ ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET); ahd_outb(ahd, SEQINTCTL, 0); ahd_outb(ahd, LASTPHASE, P_BUSFREE); ahd_outb(ahd, SEQ_FLAGS, 0); ahd_outb(ahd, SAVED_SCSIID, 0xFF); ahd_outb(ahd, SAVED_LUN, 0xFF); /* * Ensure that the sequencer's idea of TQINPOS * matches our own. The sequencer increments TQINPOS * only after it sees a DMA complete and a reset could * occur before the increment leaving the kernel to believe * the command arrived but the sequencer to not. */ ahd_outb(ahd, TQINPOS, ahd->tqinfifonext); /* Always allow reselection */ ahd_outb(ahd, SCSISEQ1, ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP)); /* Ensure that no DMA operations are in progress */ ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); ahd_outb(ahd, SCBHCNT, 0); ahd_outb(ahd, CCSCBCTL, CCSCBRESET); ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET); ahd_unpause(ahd);}voidahd_clear_fifo(struct ahd_softc *ahd, u_int fifo){ ahd_mode_state saved_modes;#if AHD_DEBUG if ((ahd_debug & AHD_SHOW_FIFOS) != 0) printf("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);#endif saved_modes = ahd_save_modes(ahd); ahd_set_modes(ahd, fifo, fifo); ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT); if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) ahd_outb(ahd, CCSGCTL, CCSGRESET); ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR); ahd_outb(ahd, SG_STATE, 0); ahd_restore_modes(ahd, saved_modes);}/************************* Input/Output Queues ********************************/voidahd_run_qoutfifo(struct ahd_softc *ahd){ struct scb *scb; u_int scb_index; ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD); while (ahd->qoutfifo[ahd->qoutfifonext] != SCB_LIST_NULL_LE) { scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]); scb = ahd_lookup_scb(ahd, scb_index); if (scb == NULL) { printf("%s: WARNING no command for scb %d " "(cmdcmplt)\nQOUTPOS = %d\n", ahd_name(ahd), scb_index, ahd->qoutfifonext); ahd_dump_card_state(ahd); ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1); continue; } if ((ahd->qoutfifonext & 0x01) == 0x01) { /* * Clear 32bits of QOUTFIFO at a time * so that we don't clobber an incoming * 16bit DMA to the array on architectures * that only support 32bit load and store * operations. */ ahd->qoutfifo[ahd->qoutfifonext - 1] = SCB_LIST_NULL_LE; ahd->qoutfifo[ahd->qoutfifonext] = SCB_LIST_NULL_LE; ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_dmamap, /*offset*/(ahd->qoutfifonext - 1)*2, /*len*/4, BUS_DMASYNC_PREREAD); } ahd->qoutfifonext = AHD_QOUT_WRAP(ahd->qoutfifonext+1); ahd_complete_scb(ahd, scb); }}voidahd_run_untagged_queues(struct ahd_softc *ahd){ int i; for (i = 0; i < 16; i++) ahd_run_untagged_queue(ahd, &ahd->untagged_queues[i]);}voidahd_run_untagged_queue(struct ahd_softc *ahd, struct scb_tailq *queue){ struct scb *scb; if (ahd->untagged_queue_lock != 0) return; if ((scb = TAILQ_FIRST(queue)) != NULL && (scb->flags & SCB_ACTIVE) == 0) { scb->flags |= SCB_ACTIVE; ahd_queue_scb(ahd, scb); }}/************************* Interrupt Handling *********************************/voidahd_handle_hwerrint(struct ahd_softc *ahd){ /* * Some catastrophic hardware error has occurred. * Print it for the user and disable the controller. */ int i; int error; error = ahd_inb(ahd, ERROR); for (i = 0; i < num_errors; i++) { if ((error & ahd_hard_errors[i].errno) != 0) printf("%s: hwerrint, %s\n", ahd_name(ahd), ahd_hard_errors[i].errmesg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -