📄 aic79xx_core.c
字号:
/* * Core routines and tables shareable across OS platforms. * * Copyright (c) 1994-2002 Justin T. Gibbs. * Copyright (c) 2000-2003 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#202 $ * * $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/***************************** Lookup Tables **********************************/char *ahd_chip_names[] ={ "NONE", "aic7901", "aic7902", "aic7901A"};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_handle_proto_violation(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, u_int lun, cam_status status, char *message, int verbose_level);#ifdef AHD_TARGET_MODEstatic void ahd_setup_target_msgin(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, struct scb *scb);#endifstatic u_int ahd_sglist_size(struct ahd_softc *ahd);static u_int 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_add_col_list(struct ahd_softc *ahd, struct scb *scb, u_int col_idx);static void ahd_rem_col_list(struct ahd_softc *ahd, struct scb *scb);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;static ahd_callback_t ahd_stat_timer;#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);static int ahd_probe_stack_size(struct ahd_softc *ahd);static int ahd_scb_active_in_fifo(struct ahd_softc *ahd, struct scb *scb);static void ahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb);#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; 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)); ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN); ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET); ahd_unpause(ahd);}voidahd_clear_fifo(struct ahd_softc *ahd, u_int fifo){ ahd_mode_state saved_modes;#ifdef 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 ********************************//* * Flush and completed commands that are sitting in the command * complete queues down on the chip but have yet to be dma'ed back up. */voidahd_flush_qoutfifo(struct ahd_softc *ahd){ struct scb *scb; ahd_mode_state saved_modes; u_int saved_scbptr; u_int ccscbctl; u_int scbid; u_int next_scbid; saved_modes = ahd_save_modes(ahd); /* * Complete any SCBs that just finished being * DMA'ed into the qoutfifo. */ ahd_run_qoutfifo(ahd); /* * Flush the good status FIFO for compelted packetized commands. */ ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); saved_scbptr = ahd_get_scbptr(ahd); while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) { u_int fifo_mode; u_int i; scbid = (ahd_inb(ahd, GSFIFO+1) << 8) | ahd_inb(ahd, GSFIFO); scb = ahd_lookup_scb(ahd, scbid); if (scb == NULL) { printf("%s: Warning - GSFIFO SCB %d invalid\n", ahd_name(ahd), scbid); continue; } /* * Determine if this transaction is still active in * any FIFO. If it is, we must flush that FIFO to * the host before completing the command. */ fifo_mode = 0; for (i = 0; i < 2; i++) { /* Toggle to the other mode. */ fifo_mode ^= 1; ahd_set_modes(ahd, fifo_mode, fifo_mode); if (ahd_scb_active_in_fifo(ahd, scb) == 0) continue; ahd_run_data_fifo(ahd, scb); /* * Clearing this transaction in this FIFO may * cause a CFG4DATA for this same transaction * to assert in the other FIFO. Make sure we * loop one more time and check the other FIFO. */ i = 0; } ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); ahd_set_scbptr(ahd, scbid); if ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_LIST_NULL) == 0 && ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_FULL_RESID) != 0 || (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR) & SG_LIST_NULL) != 0)) { u_int comp_head; /* * The transfer completed with a residual. * Place this SCB on the complete DMA list * so that we Update our in-core copy of the * SCB before completing the command. */ ahd_outb(ahd, SCB_SCSI_STATUS, 0); ahd_outb(ahd, SCB_SGPTR, ahd_inb_scbram(ahd, SCB_SGPTR) | SG_STATUS_VALID); ahd_outw(ahd, SCB_TAG, SCB_GET_TAG(scb)); comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -