17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5602ca9eaScth * Common Development and Distribution License (the "License").
6602ca9eaScth * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22602ca9eaScth * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
24*89b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate * ISSUES
307c478bd9Sstevel@tonic-gate *
317c478bd9Sstevel@tonic-gate * - more consistent error messages
327c478bd9Sstevel@tonic-gate * - report name of device on errors?
337c478bd9Sstevel@tonic-gate * - if wide target renegotiates sync, back to narrow?
347c478bd9Sstevel@tonic-gate * - last_msgout is not accurate ????
357c478bd9Sstevel@tonic-gate * - resolve XXXX
367c478bd9Sstevel@tonic-gate * - improve msg reject code (use special msg reject handler)
377c478bd9Sstevel@tonic-gate * - better use of IDE message
387c478bd9Sstevel@tonic-gate * - keep track if ATN remains asserted and target not going into
397c478bd9Sstevel@tonic-gate * a msg-out phase
407c478bd9Sstevel@tonic-gate * - improve comments
417c478bd9Sstevel@tonic-gate * - no slave accesses when start address is odd and dma hasn't started
427c478bd9Sstevel@tonic-gate * this affect asserting ATN
437c478bd9Sstevel@tonic-gate */
447c478bd9Sstevel@tonic-gate
457c478bd9Sstevel@tonic-gate /*
467c478bd9Sstevel@tonic-gate * fas - QLogic fas366 wide/fast SCSI Processor HBA driver with
477c478bd9Sstevel@tonic-gate * tagged and non-tagged queueing support
487c478bd9Sstevel@tonic-gate */
497c478bd9Sstevel@tonic-gate #if defined(lint) && !defined(DEBUG)
507c478bd9Sstevel@tonic-gate #define DEBUG 1
517c478bd9Sstevel@tonic-gate #define FASDEBUG
527c478bd9Sstevel@tonic-gate #endif
537c478bd9Sstevel@tonic-gate
547c478bd9Sstevel@tonic-gate #define DMA_REG_TRACING /* enable dma register access tracing */
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate * standard header files
597c478bd9Sstevel@tonic-gate */
607c478bd9Sstevel@tonic-gate #include <sys/note.h>
617c478bd9Sstevel@tonic-gate #include <sys/scsi/scsi.h>
627c478bd9Sstevel@tonic-gate #include <sys/file.h>
637c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate * private header files
677c478bd9Sstevel@tonic-gate */
687c478bd9Sstevel@tonic-gate #include <sys/scsi/adapters/fasdma.h>
697c478bd9Sstevel@tonic-gate #include <sys/scsi/adapters/fasreg.h>
707c478bd9Sstevel@tonic-gate #include <sys/scsi/adapters/fasvar.h>
717c478bd9Sstevel@tonic-gate #include <sys/scsi/adapters/fascmd.h>
727c478bd9Sstevel@tonic-gate #include <sys/scsi/impl/scsi_reset_notify.h>
737c478bd9Sstevel@tonic-gate
747c478bd9Sstevel@tonic-gate /*
757c478bd9Sstevel@tonic-gate * tunables
767c478bd9Sstevel@tonic-gate */
777c478bd9Sstevel@tonic-gate static int fas_selection_timeout = 250; /* 250 milliseconds */
787c478bd9Sstevel@tonic-gate static uchar_t fas_default_offset = DEFAULT_OFFSET;
797c478bd9Sstevel@tonic-gate
807c478bd9Sstevel@tonic-gate /*
817c478bd9Sstevel@tonic-gate * needed for presto support, do not remove
827c478bd9Sstevel@tonic-gate */
837c478bd9Sstevel@tonic-gate static int fas_enable_sbus64 = 1;
847c478bd9Sstevel@tonic-gate
857c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
867c478bd9Sstevel@tonic-gate int fasdebug = 0;
877c478bd9Sstevel@tonic-gate int fasdebug_instance = -1; /* debug all instances */
887c478bd9Sstevel@tonic-gate static int fas_burstsizes_limit = -1;
897c478bd9Sstevel@tonic-gate static int fas_no_sync_wide_backoff = 0;
907c478bd9Sstevel@tonic-gate #endif /* FASDEBUG */
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate /*
937c478bd9Sstevel@tonic-gate * Local static data protected by global mutex
947c478bd9Sstevel@tonic-gate */
957c478bd9Sstevel@tonic-gate static kmutex_t fas_global_mutex; /* to allow concurrent attach */
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate static int fas_scsi_watchdog_tick; /* in seconds, for all */
987c478bd9Sstevel@tonic-gate /* instances */
997c478bd9Sstevel@tonic-gate static clock_t fas_tick; /* fas_watch() interval in Hz */
1007c478bd9Sstevel@tonic-gate static timeout_id_t fas_reset_watch; /* timeout id for reset watch */
1017c478bd9Sstevel@tonic-gate static timeout_id_t fas_timeout_id = 0;
1027c478bd9Sstevel@tonic-gate static int fas_timeout_initted = 0;
1037c478bd9Sstevel@tonic-gate
1047c478bd9Sstevel@tonic-gate static krwlock_t fas_global_rwlock;
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate static void *fas_state; /* soft state ptr */
1077c478bd9Sstevel@tonic-gate static struct fas *fas_head; /* link all softstate structures */
1087c478bd9Sstevel@tonic-gate static struct fas *fas_tail; /* for fas_watch() */
1097c478bd9Sstevel@tonic-gate
1107c478bd9Sstevel@tonic-gate static kmutex_t fas_log_mutex;
1117c478bd9Sstevel@tonic-gate static char fas_log_buf[256];
1127c478bd9Sstevel@tonic-gate _NOTE(MUTEX_PROTECTS_DATA(fas_global_mutex, fas_reset_watch))
1137c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(fas_state fas_head fas_tail \
1147c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick fas_tick))
1157c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("safe sharing", fas::f_quiesce_timeid))
1167c478bd9Sstevel@tonic-gate
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate * dma attribute structure for scsi engine
1197c478bd9Sstevel@tonic-gate */
1207c478bd9Sstevel@tonic-gate static ddi_dma_attr_t dma_fasattr = {
1217c478bd9Sstevel@tonic-gate DMA_ATTR_V0, (unsigned long long)0,
1227c478bd9Sstevel@tonic-gate (unsigned long long)0xffffffff, (unsigned long long)((1<<24)-1),
1237c478bd9Sstevel@tonic-gate 1, DEFAULT_BURSTSIZE, 1,
1247c478bd9Sstevel@tonic-gate (unsigned long long)0xffffffff, (unsigned long long)0xffffffff,
1257c478bd9Sstevel@tonic-gate 1, 512, 0
1267c478bd9Sstevel@tonic-gate };
1277c478bd9Sstevel@tonic-gate
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate * optional torture test stuff
1307c478bd9Sstevel@tonic-gate */
1317c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
1327c478bd9Sstevel@tonic-gate #define FAS_TEST
1337c478bd9Sstevel@tonic-gate static int fas_ptest_emsgin;
1347c478bd9Sstevel@tonic-gate static int fas_ptest_msgin;
1357c478bd9Sstevel@tonic-gate static int fas_ptest_msg = -1;
1367c478bd9Sstevel@tonic-gate static int fas_ptest_status;
1377c478bd9Sstevel@tonic-gate static int fas_ptest_data_in;
1387c478bd9Sstevel@tonic-gate static int fas_atest;
1397c478bd9Sstevel@tonic-gate static int fas_atest_disc;
1407c478bd9Sstevel@tonic-gate static int fas_atest_reconn;
1417c478bd9Sstevel@tonic-gate static void fas_test_abort(struct fas *fas, int slot);
1427c478bd9Sstevel@tonic-gate static int fas_rtest;
1437c478bd9Sstevel@tonic-gate static int fas_rtest_type;
1447c478bd9Sstevel@tonic-gate static void fas_test_reset(struct fas *fas, int slot);
1457c478bd9Sstevel@tonic-gate static int fas_force_timeout;
1467c478bd9Sstevel@tonic-gate static int fas_btest;
1477c478bd9Sstevel@tonic-gate static int fas_test_stop;
1487c478bd9Sstevel@tonic-gate static int fas_transport_busy;
1497c478bd9Sstevel@tonic-gate static int fas_transport_busy_rqs;
1507c478bd9Sstevel@tonic-gate static int fas_transport_reject;
1517c478bd9Sstevel@tonic-gate static int fas_arqs_failure;
1527c478bd9Sstevel@tonic-gate static int fas_tran_err;
1537c478bd9Sstevel@tonic-gate static int fas_test_untagged;
1547c478bd9Sstevel@tonic-gate static int fas_enable_untagged;
1557c478bd9Sstevel@tonic-gate #endif
1567c478bd9Sstevel@tonic-gate
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate * warlock directives
1597c478bd9Sstevel@tonic-gate */
1607c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(dma fasdebug))
1617c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("just test variables", fas_transport_busy))
1627c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("just test variables", fas_transport_busy_rqs))
1637c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("just test variables", fas_transport_reject))
1647c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("just test variables", fas_arqs_failure))
1657c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("just test variables", fas_tran_err))
1667c478bd9Sstevel@tonic-gate _NOTE(MUTEX_PROTECTS_DATA(fas_log_mutex, fas_log_buf))
1677c478bd9Sstevel@tonic-gate _NOTE(MUTEX_PROTECTS_DATA(fas_global_mutex, fas_reset_watch))
1687c478bd9Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(fas_state fas_head fas_tail \
1697c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick fas_tick))
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate * function prototypes
1737c478bd9Sstevel@tonic-gate *
1747c478bd9Sstevel@tonic-gate * scsa functions are exported by means of the transport table:
1757c478bd9Sstevel@tonic-gate */
1767c478bd9Sstevel@tonic-gate static int fas_scsi_tgt_probe(struct scsi_device *sd,
1777c478bd9Sstevel@tonic-gate int (*waitfunc)(void));
1787c478bd9Sstevel@tonic-gate static int fas_scsi_tgt_init(dev_info_t *, dev_info_t *,
1797c478bd9Sstevel@tonic-gate scsi_hba_tran_t *, struct scsi_device *);
1807c478bd9Sstevel@tonic-gate static int fas_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt);
1817c478bd9Sstevel@tonic-gate static int fas_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
1827c478bd9Sstevel@tonic-gate static int fas_scsi_reset(struct scsi_address *ap, int level);
1837c478bd9Sstevel@tonic-gate static int fas_scsi_getcap(struct scsi_address *ap, char *cap, int whom);
1847c478bd9Sstevel@tonic-gate static int fas_scsi_setcap(struct scsi_address *ap, char *cap, int value,
1857c478bd9Sstevel@tonic-gate int whom);
1867c478bd9Sstevel@tonic-gate static struct scsi_pkt *fas_scsi_init_pkt(struct scsi_address *ap,
1877c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen,
1887c478bd9Sstevel@tonic-gate int tgtlen, int flags, int (*callback)(), caddr_t arg);
1897c478bd9Sstevel@tonic-gate static void fas_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
1907c478bd9Sstevel@tonic-gate static void fas_scsi_dmafree(struct scsi_address *ap,
1917c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt);
1927c478bd9Sstevel@tonic-gate static void fas_scsi_sync_pkt(struct scsi_address *ap,
1937c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt);
1947c478bd9Sstevel@tonic-gate
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate * internal functions:
1977c478bd9Sstevel@tonic-gate */
1987c478bd9Sstevel@tonic-gate static int fas_prepare_pkt(struct fas *fas, struct fas_cmd *sp);
1997c478bd9Sstevel@tonic-gate static int fas_alloc_tag(struct fas *fas, struct fas_cmd *sp);
2007c478bd9Sstevel@tonic-gate static int fas_accept_pkt(struct fas *fas, struct fas_cmd *sp, int flag);
2017c478bd9Sstevel@tonic-gate static void fas_empty_waitQ(struct fas *fas);
2027c478bd9Sstevel@tonic-gate static void fas_move_waitQ_to_readyQ(struct fas *fas);
2037c478bd9Sstevel@tonic-gate static void fas_check_waitQ_and_mutex_exit(struct fas *fas);
2047c478bd9Sstevel@tonic-gate static int fas_istart(struct fas *fas);
2057c478bd9Sstevel@tonic-gate static int fas_ustart(struct fas *fas);
2067c478bd9Sstevel@tonic-gate static int fas_startcmd(struct fas *fas, struct fas_cmd *sp);
2077c478bd9Sstevel@tonic-gate
2087c478bd9Sstevel@tonic-gate static int fas_pkt_alloc_extern(struct fas *fas, struct fas_cmd *sp,
2097c478bd9Sstevel@tonic-gate int cmdlen, int tgtlen, int statuslen, int kf);
2107c478bd9Sstevel@tonic-gate static void fas_pkt_destroy_extern(struct fas *fas, struct fas_cmd *sp);
2117c478bd9Sstevel@tonic-gate static int fas_kmem_cache_constructor(void *buf, void *cdrarg, int kmflags);
2127c478bd9Sstevel@tonic-gate static void fas_kmem_cache_destructor(void *buf, void *cdrarg);
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate static int fas_finish(struct fas *fas);
2157c478bd9Sstevel@tonic-gate static void fas_handle_qfull(struct fas *fas, struct fas_cmd *sp);
2167c478bd9Sstevel@tonic-gate static void fas_restart_cmd(void *);
2177c478bd9Sstevel@tonic-gate static int fas_dopoll(struct fas *fas, int timeout);
2187c478bd9Sstevel@tonic-gate static void fas_runpoll(struct fas *fas, short slot, struct fas_cmd *sp);
2197c478bd9Sstevel@tonic-gate static uint_t fas_intr(caddr_t arg);
2207c478bd9Sstevel@tonic-gate static int fas_intr_svc(struct fas *fas);
2217c478bd9Sstevel@tonic-gate static int fas_phasemanage(struct fas *fas);
2227c478bd9Sstevel@tonic-gate static int fas_handle_unknown(struct fas *fas);
2237c478bd9Sstevel@tonic-gate static int fas_handle_cmd_start(struct fas *fas);
2247c478bd9Sstevel@tonic-gate static int fas_handle_cmd_done(struct fas *fas);
2257c478bd9Sstevel@tonic-gate static int fas_handle_msg_out_start(struct fas *fas);
2267c478bd9Sstevel@tonic-gate static int fas_handle_msg_out_done(struct fas *fas);
2277c478bd9Sstevel@tonic-gate static int fas_handle_clearing(struct fas *fas);
2287c478bd9Sstevel@tonic-gate static int fas_handle_data_start(struct fas *fas);
2297c478bd9Sstevel@tonic-gate static int fas_handle_data_done(struct fas *fas);
2307c478bd9Sstevel@tonic-gate static int fas_handle_c_cmplt(struct fas *fas);
2317c478bd9Sstevel@tonic-gate static int fas_handle_msg_in_start(struct fas *fas);
2327c478bd9Sstevel@tonic-gate static int fas_handle_more_msgin(struct fas *fas);
2337c478bd9Sstevel@tonic-gate static int fas_handle_msg_in_done(struct fas *fas);
2347c478bd9Sstevel@tonic-gate static int fas_onebyte_msg(struct fas *fas);
2357c478bd9Sstevel@tonic-gate static int fas_twobyte_msg(struct fas *fas);
2367c478bd9Sstevel@tonic-gate static int fas_multibyte_msg(struct fas *fas);
2377c478bd9Sstevel@tonic-gate static void fas_revert_to_async(struct fas *fas, int tgt);
2387c478bd9Sstevel@tonic-gate static int fas_finish_select(struct fas *fas);
2397c478bd9Sstevel@tonic-gate static int fas_reselect_preempt(struct fas *fas);
2407c478bd9Sstevel@tonic-gate static int fas_reconnect(struct fas *fas);
2417c478bd9Sstevel@tonic-gate static int fas_handle_selection(struct fas *fas);
2427c478bd9Sstevel@tonic-gate static void fas_head_of_readyQ(struct fas *fas, struct fas_cmd *sp);
2437c478bd9Sstevel@tonic-gate static int fas_handle_gross_err(struct fas *fas);
2447c478bd9Sstevel@tonic-gate static int fas_illegal_cmd_or_bus_reset(struct fas *fas);
2457c478bd9Sstevel@tonic-gate static int fas_check_dma_error(struct fas *fas);
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate static void fas_make_sdtr(struct fas *fas, int msgout_offset, int target);
2487c478bd9Sstevel@tonic-gate static void fas_make_wdtr(struct fas *fas, int msgout_offset, int target,
2497c478bd9Sstevel@tonic-gate int width);
2507c478bd9Sstevel@tonic-gate static void fas_update_props(struct fas *fas, int tgt);
2517c478bd9Sstevel@tonic-gate static void fas_update_this_prop(struct fas *fas, char *property, int value);
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate static int fas_commoncap(struct scsi_address *ap, char *cap, int val,
2547c478bd9Sstevel@tonic-gate int tgtonly, int doset);
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate static void fas_watch(void *arg);
2577c478bd9Sstevel@tonic-gate static void fas_watchsubr(struct fas *fas);
2587c478bd9Sstevel@tonic-gate static void fas_cmd_timeout(struct fas *fas, int slot);
2597c478bd9Sstevel@tonic-gate static void fas_sync_wide_backoff(struct fas *fas, struct fas_cmd *sp,
2607c478bd9Sstevel@tonic-gate int slot);
2617c478bd9Sstevel@tonic-gate static void fas_reset_sync_wide(struct fas *fas);
2627c478bd9Sstevel@tonic-gate static void fas_set_wide_conf3(struct fas *fas, int target, int width);
2637c478bd9Sstevel@tonic-gate static void fas_force_renegotiation(struct fas *fas, int target);
2647c478bd9Sstevel@tonic-gate
2657c478bd9Sstevel@tonic-gate static int fas_set_new_window(struct fas *fas, struct fas_cmd *sp);
2667c478bd9Sstevel@tonic-gate static int fas_restore_pointers(struct fas *fas, struct fas_cmd *sp);
2677c478bd9Sstevel@tonic-gate static int fas_next_window(struct fas *fas, struct fas_cmd *sp, uint64_t end);
2687c478bd9Sstevel@tonic-gate
2697c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/
2707c478bd9Sstevel@tonic-gate static void fas_log(struct fas *fas, int level, const char *fmt, ...);
2717c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/
2727c478bd9Sstevel@tonic-gate static void fas_printf(struct fas *fas, const char *fmt, ...);
2737c478bd9Sstevel@tonic-gate static void fas_printstate(struct fas *fas, char *msg);
2747c478bd9Sstevel@tonic-gate static void fas_dump_cmd(struct fas *fas, struct fas_cmd *sp);
2757c478bd9Sstevel@tonic-gate static void fas_short_dump_cmd(struct fas *fas, struct fas_cmd *sp);
2767c478bd9Sstevel@tonic-gate static char *fas_state_name(ushort_t state);
2777c478bd9Sstevel@tonic-gate
2787c478bd9Sstevel@tonic-gate static void fas_makeproxy_cmd(struct fas_cmd *sp,
2797c478bd9Sstevel@tonic-gate struct scsi_address *ap, struct scsi_pkt *pkt, int nmsg, ...);
2807c478bd9Sstevel@tonic-gate static int fas_do_proxy_cmd(struct fas *fas, struct fas_cmd *sp,
2817c478bd9Sstevel@tonic-gate struct scsi_address *ap, char *what);
2827c478bd9Sstevel@tonic-gate
2837c478bd9Sstevel@tonic-gate static void fas_internal_reset(struct fas *fas, int reset_action);
2847c478bd9Sstevel@tonic-gate static int fas_alloc_active_slots(struct fas *fas, int slot, int flag);
2857c478bd9Sstevel@tonic-gate
2867c478bd9Sstevel@tonic-gate static int fas_abort_curcmd(struct fas *fas);
2877c478bd9Sstevel@tonic-gate static int fas_abort_cmd(struct fas *fas, struct fas_cmd *sp, int slot);
2887c478bd9Sstevel@tonic-gate static int fas_do_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
2897c478bd9Sstevel@tonic-gate static int fas_do_scsi_reset(struct scsi_address *ap, int level);
2907c478bd9Sstevel@tonic-gate static int fas_remove_from_readyQ(struct fas *fas, struct fas_cmd *sp,
2917c478bd9Sstevel@tonic-gate int slot);
2927c478bd9Sstevel@tonic-gate static void fas_flush_readyQ(struct fas *fas, int slot);
2937c478bd9Sstevel@tonic-gate static void fas_flush_tagQ(struct fas *fas, int slot);
2947c478bd9Sstevel@tonic-gate static void fas_flush_cmd(struct fas *fas, struct fas_cmd *sp,
2957c478bd9Sstevel@tonic-gate uchar_t reason, uint_t stat);
2967c478bd9Sstevel@tonic-gate static int fas_abort_connected_cmd(struct fas *fas, struct fas_cmd *sp,
2977c478bd9Sstevel@tonic-gate uchar_t msg);
2987c478bd9Sstevel@tonic-gate static int fas_abort_disconnected_cmd(struct fas *fas, struct scsi_address *ap,
2997c478bd9Sstevel@tonic-gate struct fas_cmd *sp, uchar_t msg, int slot);
3007c478bd9Sstevel@tonic-gate static void fas_mark_packets(struct fas *fas, int slot, uchar_t reason,
3017c478bd9Sstevel@tonic-gate uint_t stat);
3027c478bd9Sstevel@tonic-gate static void fas_set_pkt_reason(struct fas *fas, struct fas_cmd *sp,
3037c478bd9Sstevel@tonic-gate uchar_t reason, uint_t stat);
3047c478bd9Sstevel@tonic-gate
3057c478bd9Sstevel@tonic-gate static int fas_reset_bus(struct fas *fas);
3067c478bd9Sstevel@tonic-gate static int fas_reset_recovery(struct fas *fas);
3077c478bd9Sstevel@tonic-gate static int fas_reset_connected_cmd(struct fas *fas, struct scsi_address *ap);
3087c478bd9Sstevel@tonic-gate static int fas_reset_disconnected_cmd(struct fas *fas, struct scsi_address *ap);
3097c478bd9Sstevel@tonic-gate static void fas_start_watch_reset_delay(struct fas *);
3107c478bd9Sstevel@tonic-gate static void fas_setup_reset_delay(struct fas *fas);
3117c478bd9Sstevel@tonic-gate static void fas_watch_reset_delay(void *arg);
3127c478bd9Sstevel@tonic-gate static int fas_watch_reset_delay_subr(struct fas *fas);
3137c478bd9Sstevel@tonic-gate static void fas_reset_cleanup(struct fas *fas, int slot);
3147c478bd9Sstevel@tonic-gate static int fas_scsi_reset_notify(struct scsi_address *ap, int flag,
3157c478bd9Sstevel@tonic-gate void (*callback)(caddr_t), caddr_t arg);
3167c478bd9Sstevel@tonic-gate static int fas_scsi_quiesce(dev_info_t *hba_dip);
3177c478bd9Sstevel@tonic-gate static int fas_scsi_unquiesce(dev_info_t *hba_dip);
3187c478bd9Sstevel@tonic-gate
3197c478bd9Sstevel@tonic-gate static void fas_set_throttles(struct fas *fas, int slot,
3207c478bd9Sstevel@tonic-gate int n, int what);
3217c478bd9Sstevel@tonic-gate static void fas_set_all_lun_throttles(struct fas *fas, int slot, int what);
3227c478bd9Sstevel@tonic-gate static void fas_full_throttle(struct fas *fas, int slot);
3237c478bd9Sstevel@tonic-gate static void fas_remove_cmd(struct fas *fas, struct fas_cmd *sp, int timeout);
3247c478bd9Sstevel@tonic-gate static void fas_decrement_ncmds(struct fas *fas, struct fas_cmd *sp);
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate static int fas_quiesce_bus(struct fas *fas);
3277c478bd9Sstevel@tonic-gate static int fas_unquiesce_bus(struct fas *fas);
3287c478bd9Sstevel@tonic-gate static void fas_ncmds_checkdrain(void *arg);
3297c478bd9Sstevel@tonic-gate static int fas_check_outstanding(struct fas *fas);
3307c478bd9Sstevel@tonic-gate
3317c478bd9Sstevel@tonic-gate static int fas_create_arq_pkt(struct fas *fas, struct scsi_address *ap);
3327c478bd9Sstevel@tonic-gate static int fas_delete_arq_pkt(struct fas *fas, struct scsi_address *ap);
3337c478bd9Sstevel@tonic-gate static int fas_handle_sts_chk(struct fas *fas, struct fas_cmd *sp);
3347c478bd9Sstevel@tonic-gate void fas_complete_arq_pkt(struct scsi_pkt *pkt);
3357c478bd9Sstevel@tonic-gate
3367c478bd9Sstevel@tonic-gate void fas_call_pkt_comp(struct fas *fas, struct fas_cmd *sp);
3377c478bd9Sstevel@tonic-gate void fas_empty_callbackQ(struct fas *fas);
3387c478bd9Sstevel@tonic-gate int fas_init_callbacks(struct fas *fas);
3397c478bd9Sstevel@tonic-gate void fas_destroy_callbacks(struct fas *fas);
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate static int fas_check_dma_error(struct fas *fas);
3427c478bd9Sstevel@tonic-gate static int fas_init_chip(struct fas *fas, uchar_t id);
3437c478bd9Sstevel@tonic-gate
3447c478bd9Sstevel@tonic-gate static void fas_read_fifo(struct fas *fas);
3457c478bd9Sstevel@tonic-gate static void fas_write_fifo(struct fas *fas, uchar_t *buf, int length, int pad);
3467c478bd9Sstevel@tonic-gate
3477c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
3487c478bd9Sstevel@tonic-gate static void fas_reg_cmd_write(struct fas *fas, uint8_t cmd);
3497c478bd9Sstevel@tonic-gate static void fas_reg_write(struct fas *fas, volatile uint8_t *p, uint8_t what);
3507c478bd9Sstevel@tonic-gate static uint8_t fas_reg_read(struct fas *fas, volatile uint8_t *p);
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate static void fas_dma_reg_write(struct fas *fas, volatile uint32_t *p,
3537c478bd9Sstevel@tonic-gate uint32_t what);
3547c478bd9Sstevel@tonic-gate static uint32_t fas_dma_reg_read(struct fas *fas, volatile uint32_t *p);
3557c478bd9Sstevel@tonic-gate #else
3567c478bd9Sstevel@tonic-gate #define fas_reg_cmd_write(fas, cmd) \
3577c478bd9Sstevel@tonic-gate fas->f_reg->fas_cmd = (cmd), fas->f_last_cmd = (cmd)
3587c478bd9Sstevel@tonic-gate #define fas_reg_write(fas, p, what) *(p) = (what)
3597c478bd9Sstevel@tonic-gate #define fas_reg_read(fas, p) *(p)
3607c478bd9Sstevel@tonic-gate #define fas_dma_reg_write(fas, p, what) *(p) = (what)
3617c478bd9Sstevel@tonic-gate #define fas_dma_reg_read(fas, p) *(p)
3627c478bd9Sstevel@tonic-gate #endif
3637c478bd9Sstevel@tonic-gate
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate * autoconfiguration data and routines.
3667c478bd9Sstevel@tonic-gate */
3677c478bd9Sstevel@tonic-gate static int fas_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
3687c478bd9Sstevel@tonic-gate static int fas_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
3697c478bd9Sstevel@tonic-gate static int fas_dr_detach(dev_info_t *dev);
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate static struct dev_ops fas_ops = {
3727c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev, */
3737c478bd9Sstevel@tonic-gate 0, /* refcnt */
3747c478bd9Sstevel@tonic-gate ddi_no_info, /* info */
3757c478bd9Sstevel@tonic-gate nulldev, /* identify */
3767c478bd9Sstevel@tonic-gate nulldev, /* probe */
3777c478bd9Sstevel@tonic-gate fas_attach, /* attach */
3787c478bd9Sstevel@tonic-gate fas_detach, /* detach */
3797c478bd9Sstevel@tonic-gate nodev, /* reset */
3807c478bd9Sstevel@tonic-gate NULL, /* driver operations */
3817c478bd9Sstevel@tonic-gate NULL, /* bus operations */
38219397407SSherry Moore NULL, /* power */
38319397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
3847c478bd9Sstevel@tonic-gate };
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
3877c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
38819397407SSherry Moore "FAS SCSI HBA Driver", /* Name of the module. */
3897c478bd9Sstevel@tonic-gate &fas_ops, /* driver ops */
3907c478bd9Sstevel@tonic-gate };
3917c478bd9Sstevel@tonic-gate
3927c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
3937c478bd9Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL
3947c478bd9Sstevel@tonic-gate };
3957c478bd9Sstevel@tonic-gate
3967c478bd9Sstevel@tonic-gate int
_init(void)3977c478bd9Sstevel@tonic-gate _init(void)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate int rval;
4007c478bd9Sstevel@tonic-gate /* CONSTCOND */
4017c478bd9Sstevel@tonic-gate ASSERT(NO_COMPETING_THREADS);
4027c478bd9Sstevel@tonic-gate
4037c478bd9Sstevel@tonic-gate rval = ddi_soft_state_init(&fas_state, sizeof (struct fas),
4047c478bd9Sstevel@tonic-gate FAS_INITIAL_SOFT_SPACE);
4057c478bd9Sstevel@tonic-gate if (rval != 0) {
4067c478bd9Sstevel@tonic-gate return (rval);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate
4097c478bd9Sstevel@tonic-gate if ((rval = scsi_hba_init(&modlinkage)) != 0) {
4107c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&fas_state);
4117c478bd9Sstevel@tonic-gate return (rval);
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate
4147c478bd9Sstevel@tonic-gate mutex_init(&fas_global_mutex, NULL, MUTEX_DRIVER, NULL);
4157c478bd9Sstevel@tonic-gate rw_init(&fas_global_rwlock, NULL, RW_DRIVER, NULL);
4167c478bd9Sstevel@tonic-gate
4177c478bd9Sstevel@tonic-gate mutex_init(&fas_log_mutex, NULL, MUTEX_DRIVER, NULL);
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate if ((rval = mod_install(&modlinkage)) != 0) {
4207c478bd9Sstevel@tonic-gate mutex_destroy(&fas_log_mutex);
4217c478bd9Sstevel@tonic-gate rw_destroy(&fas_global_rwlock);
4227c478bd9Sstevel@tonic-gate mutex_destroy(&fas_global_mutex);
4237c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&fas_state);
4247c478bd9Sstevel@tonic-gate scsi_hba_fini(&modlinkage);
4257c478bd9Sstevel@tonic-gate return (rval);
4267c478bd9Sstevel@tonic-gate }
4277c478bd9Sstevel@tonic-gate
4287c478bd9Sstevel@tonic-gate return (rval);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate
4317c478bd9Sstevel@tonic-gate int
_fini(void)4327c478bd9Sstevel@tonic-gate _fini(void)
4337c478bd9Sstevel@tonic-gate {
4347c478bd9Sstevel@tonic-gate int rval;
4357c478bd9Sstevel@tonic-gate /* CONSTCOND */
4367c478bd9Sstevel@tonic-gate ASSERT(NO_COMPETING_THREADS);
4377c478bd9Sstevel@tonic-gate
4387c478bd9Sstevel@tonic-gate if ((rval = mod_remove(&modlinkage)) == 0) {
4397c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&fas_state);
4407c478bd9Sstevel@tonic-gate scsi_hba_fini(&modlinkage);
4417c478bd9Sstevel@tonic-gate mutex_destroy(&fas_log_mutex);
4427c478bd9Sstevel@tonic-gate rw_destroy(&fas_global_rwlock);
4437c478bd9Sstevel@tonic-gate mutex_destroy(&fas_global_mutex);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate return (rval);
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate
4487c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)4497c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
4507c478bd9Sstevel@tonic-gate {
4517c478bd9Sstevel@tonic-gate /* CONSTCOND */
4527c478bd9Sstevel@tonic-gate ASSERT(NO_COMPETING_THREADS);
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate
4577c478bd9Sstevel@tonic-gate static int
fas_scsi_tgt_probe(struct scsi_device * sd,int (* waitfunc)(void))4587c478bd9Sstevel@tonic-gate fas_scsi_tgt_probe(struct scsi_device *sd,
4597c478bd9Sstevel@tonic-gate int (*waitfunc)(void))
4607c478bd9Sstevel@tonic-gate {
4617c478bd9Sstevel@tonic-gate dev_info_t *dip = ddi_get_parent(sd->sd_dev);
4627c478bd9Sstevel@tonic-gate int rval = SCSIPROBE_FAILURE;
4637c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran;
4647c478bd9Sstevel@tonic-gate struct fas *fas;
4657c478bd9Sstevel@tonic-gate int tgt = sd->sd_address.a_target;
4667c478bd9Sstevel@tonic-gate
4677c478bd9Sstevel@tonic-gate tran = ddi_get_driver_private(dip);
4687c478bd9Sstevel@tonic-gate ASSERT(tran != NULL);
4697c478bd9Sstevel@tonic-gate fas = TRAN2FAS(tran);
4707c478bd9Sstevel@tonic-gate
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate * force renegotiation since inquiry cmds do not cause
4737c478bd9Sstevel@tonic-gate * check conditions
4747c478bd9Sstevel@tonic-gate */
4757c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
4767c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, tgt);
4777c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
4787c478bd9Sstevel@tonic-gate rval = scsi_hba_probe(sd, waitfunc);
4797c478bd9Sstevel@tonic-gate
4807c478bd9Sstevel@tonic-gate /*
4817c478bd9Sstevel@tonic-gate * the scsi-options precedence is:
4827c478bd9Sstevel@tonic-gate * target-scsi-options highest
4837c478bd9Sstevel@tonic-gate * device-type-scsi-options
4847c478bd9Sstevel@tonic-gate * per bus scsi-options
4857c478bd9Sstevel@tonic-gate * global scsi-options lowest
4867c478bd9Sstevel@tonic-gate */
4877c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
4887c478bd9Sstevel@tonic-gate if ((rval == SCSIPROBE_EXISTS) &&
4897c478bd9Sstevel@tonic-gate ((fas->f_target_scsi_options_defined & (1 << tgt)) == 0)) {
4907c478bd9Sstevel@tonic-gate int options;
4917c478bd9Sstevel@tonic-gate
4927c478bd9Sstevel@tonic-gate options = scsi_get_device_type_scsi_options(dip, sd, -1);
4937c478bd9Sstevel@tonic-gate if (options != -1) {
4947c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[tgt] = options;
4957c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE,
49619397407SSherry Moore "?target%x-scsi-options = 0x%x\n", tgt,
49719397407SSherry Moore fas->f_target_scsi_options[tgt]);
4987c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, tgt);
4997c478bd9Sstevel@tonic-gate }
5007c478bd9Sstevel@tonic-gate }
5017c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
5027c478bd9Sstevel@tonic-gate
5037c478bd9Sstevel@tonic-gate IPRINTF2("target%x-scsi-options= 0x%x\n",
50419397407SSherry Moore tgt, fas->f_target_scsi_options[tgt]);
5057c478bd9Sstevel@tonic-gate
5067c478bd9Sstevel@tonic-gate return (rval);
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5117c478bd9Sstevel@tonic-gate static int
fas_scsi_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * hba_tran,struct scsi_device * sd)5127c478bd9Sstevel@tonic-gate fas_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
5137c478bd9Sstevel@tonic-gate scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate return (((sd->sd_address.a_target < NTARGETS_WIDE) &&
5167c478bd9Sstevel@tonic-gate (sd->sd_address.a_lun < NLUNS_PER_TARGET)) ?
51719397407SSherry Moore DDI_SUCCESS : DDI_FAILURE);
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate
5207c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5217c478bd9Sstevel@tonic-gate static int
fas_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5227c478bd9Sstevel@tonic-gate fas_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5237c478bd9Sstevel@tonic-gate {
5247c478bd9Sstevel@tonic-gate struct fas *fas = NULL;
5257c478bd9Sstevel@tonic-gate volatile struct dma *dmar = NULL;
5267c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg;
5277c478bd9Sstevel@tonic-gate ddi_dma_attr_t *fas_dma_attr;
5287c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t dev_attr;
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate int instance, id, slot, i, hm_rev;
5317c478bd9Sstevel@tonic-gate size_t rlen;
5327c478bd9Sstevel@tonic-gate uint_t count;
5337c478bd9Sstevel@tonic-gate char buf[64];
5347c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran = NULL;
5357c478bd9Sstevel@tonic-gate char intr_added = 0;
5367c478bd9Sstevel@tonic-gate char mutex_init_done = 0;
5377c478bd9Sstevel@tonic-gate char hba_attached = 0;
5387c478bd9Sstevel@tonic-gate char bound_handle = 0;
5397c478bd9Sstevel@tonic-gate char *prop_template = "target%d-scsi-options";
5407c478bd9Sstevel@tonic-gate char prop_str[32];
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate /* CONSTCOND */
5437c478bd9Sstevel@tonic-gate ASSERT(NO_COMPETING_THREADS);
5447c478bd9Sstevel@tonic-gate
5457c478bd9Sstevel@tonic-gate switch (cmd) {
5467c478bd9Sstevel@tonic-gate case DDI_ATTACH:
5477c478bd9Sstevel@tonic-gate break;
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate case DDI_RESUME:
5507c478bd9Sstevel@tonic-gate if ((tran = ddi_get_driver_private(dip)) == NULL)
5517c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5527c478bd9Sstevel@tonic-gate
5537c478bd9Sstevel@tonic-gate fas = TRAN2FAS(tran);
5547c478bd9Sstevel@tonic-gate if (!fas) {
5557c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5567c478bd9Sstevel@tonic-gate }
5577c478bd9Sstevel@tonic-gate /*
5587c478bd9Sstevel@tonic-gate * Reset hardware and softc to "no outstanding commands"
5597c478bd9Sstevel@tonic-gate * Note that a check condition can result on first command
5607c478bd9Sstevel@tonic-gate * to a target.
5617c478bd9Sstevel@tonic-gate */
5627c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
5637c478bd9Sstevel@tonic-gate fas_internal_reset(fas,
5647c478bd9Sstevel@tonic-gate FAS_RESET_SOFTC|FAS_RESET_FAS|FAS_RESET_DMA);
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate fas->f_suspended = 0;
5697c478bd9Sstevel@tonic-gate
5707c478bd9Sstevel@tonic-gate /* make sure that things get started */
5717c478bd9Sstevel@tonic-gate (void) fas_istart(fas);
5727c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
5737c478bd9Sstevel@tonic-gate
5747c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
5757c478bd9Sstevel@tonic-gate if (fas_timeout_id == 0) {
5767c478bd9Sstevel@tonic-gate fas_timeout_id = timeout(fas_watch, NULL, fas_tick);
5777c478bd9Sstevel@tonic-gate fas_timeout_initted = 1;
5787c478bd9Sstevel@tonic-gate }
5797c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
5807c478bd9Sstevel@tonic-gate
5817c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate default:
5847c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate
5877c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip);
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate /*
5907c478bd9Sstevel@tonic-gate * Since we know that some instantiations of this device can
5917c478bd9Sstevel@tonic-gate * be plugged into slave-only SBus slots, check to see whether
5927c478bd9Sstevel@tonic-gate * this is one such.
5937c478bd9Sstevel@tonic-gate */
5947c478bd9Sstevel@tonic-gate if (ddi_slaveonly(dip) == DDI_SUCCESS) {
5957c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
5967c478bd9Sstevel@tonic-gate "fas%d: device in slave-only slot", instance);
5977c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5987c478bd9Sstevel@tonic-gate }
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate if (ddi_intr_hilevel(dip, 0)) {
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate * Interrupt number '0' is a high-level interrupt.
6037c478bd9Sstevel@tonic-gate * At this point you either add a special interrupt
6047c478bd9Sstevel@tonic-gate * handler that triggers a soft interrupt at a lower level,
6057c478bd9Sstevel@tonic-gate * or - more simply and appropriately here - you just
6067c478bd9Sstevel@tonic-gate * fail the attach.
6077c478bd9Sstevel@tonic-gate */
6087c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6097c478bd9Sstevel@tonic-gate "fas%d: Device is using a hilevel intr", instance);
6107c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate
6137c478bd9Sstevel@tonic-gate /*
6147c478bd9Sstevel@tonic-gate * Allocate softc information.
6157c478bd9Sstevel@tonic-gate */
6167c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(fas_state, instance) != DDI_SUCCESS) {
6177c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6187c478bd9Sstevel@tonic-gate "fas%d: cannot allocate soft state", instance);
6197c478bd9Sstevel@tonic-gate goto fail;
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate
6227c478bd9Sstevel@tonic-gate fas = (struct fas *)ddi_get_soft_state(fas_state, instance);
6237c478bd9Sstevel@tonic-gate
6247c478bd9Sstevel@tonic-gate if (fas == NULL) {
6257c478bd9Sstevel@tonic-gate goto fail;
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate /*
6297c478bd9Sstevel@tonic-gate * map in device registers
6307c478bd9Sstevel@tonic-gate */
6317c478bd9Sstevel@tonic-gate dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
6327c478bd9Sstevel@tonic-gate dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
6337c478bd9Sstevel@tonic-gate dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
6347c478bd9Sstevel@tonic-gate
6357c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(dip, (uint_t)0, (caddr_t *)&dmar,
6367c478bd9Sstevel@tonic-gate (off_t)0, (off_t)sizeof (struct dma),
6377c478bd9Sstevel@tonic-gate &dev_attr, &fas->f_dmar_acc_handle) != DDI_SUCCESS) {
6387c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas%d: cannot map dma", instance);
6397c478bd9Sstevel@tonic-gate goto fail;
6407c478bd9Sstevel@tonic-gate }
6417c478bd9Sstevel@tonic-gate
6427c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(dip, (uint_t)1, (caddr_t *)&fasreg,
6437c478bd9Sstevel@tonic-gate (off_t)0, (off_t)sizeof (struct fasreg),
6447c478bd9Sstevel@tonic-gate &dev_attr, &fas->f_regs_acc_handle) != DDI_SUCCESS) {
6457c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6467c478bd9Sstevel@tonic-gate "fas%d: unable to map fas366 registers", instance);
6477c478bd9Sstevel@tonic-gate goto fail;
6487c478bd9Sstevel@tonic-gate }
6497c478bd9Sstevel@tonic-gate
6507c478bd9Sstevel@tonic-gate fas_dma_attr = &dma_fasattr;
6517c478bd9Sstevel@tonic-gate if (ddi_dma_alloc_handle(dip, fas_dma_attr,
6527c478bd9Sstevel@tonic-gate DDI_DMA_SLEEP, NULL, &fas->f_dmahandle) != DDI_SUCCESS) {
6537c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6547c478bd9Sstevel@tonic-gate "fas%d: cannot alloc dma handle", instance);
6557c478bd9Sstevel@tonic-gate goto fail;
6567c478bd9Sstevel@tonic-gate }
6577c478bd9Sstevel@tonic-gate
6587c478bd9Sstevel@tonic-gate /*
6597c478bd9Sstevel@tonic-gate * allocate cmdarea and its dma handle
6607c478bd9Sstevel@tonic-gate */
6617c478bd9Sstevel@tonic-gate if (ddi_dma_mem_alloc(fas->f_dmahandle,
6627c478bd9Sstevel@tonic-gate (uint_t)2*FIFOSIZE,
6637c478bd9Sstevel@tonic-gate &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
6647c478bd9Sstevel@tonic-gate NULL, (caddr_t *)&fas->f_cmdarea, &rlen,
66519397407SSherry Moore &fas->f_cmdarea_acc_handle) != DDI_SUCCESS) {
6667c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6677c478bd9Sstevel@tonic-gate "fas%d: cannot alloc cmd area", instance);
6687c478bd9Sstevel@tonic-gate goto fail;
6697c478bd9Sstevel@tonic-gate }
6707c478bd9Sstevel@tonic-gate
6717c478bd9Sstevel@tonic-gate fas->f_reg = fasreg;
6727c478bd9Sstevel@tonic-gate fas->f_dma = dmar;
6737c478bd9Sstevel@tonic-gate fas->f_instance = instance;
6747c478bd9Sstevel@tonic-gate
6757c478bd9Sstevel@tonic-gate if (ddi_dma_addr_bind_handle(fas->f_dmahandle,
6767c478bd9Sstevel@tonic-gate NULL, (caddr_t)fas->f_cmdarea,
6777c478bd9Sstevel@tonic-gate rlen, DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
6787c478bd9Sstevel@tonic-gate &fas->f_dmacookie, &count) != DDI_DMA_MAPPED) {
6797c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
6807c478bd9Sstevel@tonic-gate "fas%d: cannot bind cmdarea", instance);
6817c478bd9Sstevel@tonic-gate goto fail;
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate bound_handle++;
6847c478bd9Sstevel@tonic-gate
6857c478bd9Sstevel@tonic-gate ASSERT(count == 1);
6867c478bd9Sstevel@tonic-gate
6877c478bd9Sstevel@tonic-gate /*
6887c478bd9Sstevel@tonic-gate * Allocate a transport structure
6897c478bd9Sstevel@tonic-gate */
6907c478bd9Sstevel@tonic-gate tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
6917c478bd9Sstevel@tonic-gate
692602ca9eaScth /* Indicate that we are 'sizeof (scsi_*(9S))' clean. */
693602ca9eaScth scsi_size_clean(dip); /* SCSI_SIZE_CLEAN_VERIFY ok */
694602ca9eaScth
6957c478bd9Sstevel@tonic-gate /*
6967c478bd9Sstevel@tonic-gate * initialize transport structure
6977c478bd9Sstevel@tonic-gate */
6987c478bd9Sstevel@tonic-gate fas->f_tran = tran;
6997c478bd9Sstevel@tonic-gate fas->f_dev = dip;
7007c478bd9Sstevel@tonic-gate tran->tran_hba_private = fas;
7017c478bd9Sstevel@tonic-gate tran->tran_tgt_private = NULL;
7027c478bd9Sstevel@tonic-gate tran->tran_tgt_init = fas_scsi_tgt_init;
7037c478bd9Sstevel@tonic-gate tran->tran_tgt_probe = fas_scsi_tgt_probe;
7047c478bd9Sstevel@tonic-gate tran->tran_tgt_free = NULL;
7057c478bd9Sstevel@tonic-gate tran->tran_start = fas_scsi_start;
7067c478bd9Sstevel@tonic-gate tran->tran_abort = fas_scsi_abort;
7077c478bd9Sstevel@tonic-gate tran->tran_reset = fas_scsi_reset;
7087c478bd9Sstevel@tonic-gate tran->tran_getcap = fas_scsi_getcap;
7097c478bd9Sstevel@tonic-gate tran->tran_setcap = fas_scsi_setcap;
7107c478bd9Sstevel@tonic-gate tran->tran_init_pkt = fas_scsi_init_pkt;
7117c478bd9Sstevel@tonic-gate tran->tran_destroy_pkt = fas_scsi_destroy_pkt;
7127c478bd9Sstevel@tonic-gate tran->tran_dmafree = fas_scsi_dmafree;
7137c478bd9Sstevel@tonic-gate tran->tran_sync_pkt = fas_scsi_sync_pkt;
7147c478bd9Sstevel@tonic-gate tran->tran_reset_notify = fas_scsi_reset_notify;
7157c478bd9Sstevel@tonic-gate tran->tran_get_bus_addr = NULL;
7167c478bd9Sstevel@tonic-gate tran->tran_get_name = NULL;
7177c478bd9Sstevel@tonic-gate tran->tran_quiesce = fas_scsi_quiesce;
7187c478bd9Sstevel@tonic-gate tran->tran_unquiesce = fas_scsi_unquiesce;
7197c478bd9Sstevel@tonic-gate tran->tran_bus_reset = NULL;
7207c478bd9Sstevel@tonic-gate tran->tran_add_eventcall = NULL;
7217c478bd9Sstevel@tonic-gate tran->tran_get_eventcookie = NULL;
7227c478bd9Sstevel@tonic-gate tran->tran_post_event = NULL;
7237c478bd9Sstevel@tonic-gate tran->tran_remove_eventcall = NULL;
7247c478bd9Sstevel@tonic-gate
7257c478bd9Sstevel@tonic-gate fas->f_force_async = 0;
7267c478bd9Sstevel@tonic-gate
7277c478bd9Sstevel@tonic-gate /*
7287c478bd9Sstevel@tonic-gate * disable tagged queuing and wide for all targets
7297c478bd9Sstevel@tonic-gate * (will be enabled by target driver if required)
7307c478bd9Sstevel@tonic-gate * sync is enabled by default
7317c478bd9Sstevel@tonic-gate */
7327c478bd9Sstevel@tonic-gate fas->f_nowide = fas->f_notag = ALL_TARGETS;
7337c478bd9Sstevel@tonic-gate fas->f_force_narrow = ALL_TARGETS;
7347c478bd9Sstevel@tonic-gate
7357c478bd9Sstevel@tonic-gate /*
7367c478bd9Sstevel@tonic-gate * By default we assume embedded devices and save time
7377c478bd9Sstevel@tonic-gate * checking for timeouts in fas_watch() by skipping
7387c478bd9Sstevel@tonic-gate * the rest of luns
7397c478bd9Sstevel@tonic-gate * If we're talking to any non-embedded devices,
7407c478bd9Sstevel@tonic-gate * we can't cheat and skip over non-zero luns anymore
7417c478bd9Sstevel@tonic-gate * in fas_watch() and fas_ustart().
7427c478bd9Sstevel@tonic-gate */
7437c478bd9Sstevel@tonic-gate fas->f_dslot = NLUNS_PER_TARGET;
7447c478bd9Sstevel@tonic-gate
7457c478bd9Sstevel@tonic-gate /*
7467c478bd9Sstevel@tonic-gate * f_active is used for saving disconnected cmds;
7477c478bd9Sstevel@tonic-gate * For tagged targets, we need to increase the size later
7487c478bd9Sstevel@tonic-gate * Only allocate for Lun == 0, if we probe a lun > 0 then
7497c478bd9Sstevel@tonic-gate * we allocate an active structure
7507c478bd9Sstevel@tonic-gate * If TQ gets enabled then we need to increase the size
7517c478bd9Sstevel@tonic-gate * to hold 256 cmds
7527c478bd9Sstevel@tonic-gate */
7537c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot += NLUNS_PER_TARGET) {
7547c478bd9Sstevel@tonic-gate (void) fas_alloc_active_slots(fas, slot, KM_SLEEP);
7557c478bd9Sstevel@tonic-gate }
7567c478bd9Sstevel@tonic-gate
7577c478bd9Sstevel@tonic-gate /*
7587c478bd9Sstevel@tonic-gate * initialize the qfull retry counts
7597c478bd9Sstevel@tonic-gate */
7607c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
7617c478bd9Sstevel@tonic-gate fas->f_qfull_retries[i] = QFULL_RETRIES;
7627c478bd9Sstevel@tonic-gate fas->f_qfull_retry_interval[i] =
76319397407SSherry Moore drv_usectohz(QFULL_RETRY_INTERVAL * 1000);
7647c478bd9Sstevel@tonic-gate
7657c478bd9Sstevel@tonic-gate }
7667c478bd9Sstevel@tonic-gate
7677c478bd9Sstevel@tonic-gate /*
7687c478bd9Sstevel@tonic-gate * Initialize throttles.
7697c478bd9Sstevel@tonic-gate */
7707c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, MAX_THROTTLE);
7717c478bd9Sstevel@tonic-gate
7727c478bd9Sstevel@tonic-gate /*
7737c478bd9Sstevel@tonic-gate * Initialize mask of deferred property updates
7747c478bd9Sstevel@tonic-gate */
7757c478bd9Sstevel@tonic-gate fas->f_props_update = 0;
7767c478bd9Sstevel@tonic-gate
7777c478bd9Sstevel@tonic-gate /*
7787c478bd9Sstevel@tonic-gate * set host ID
7797c478bd9Sstevel@tonic-gate */
7807c478bd9Sstevel@tonic-gate fas->f_fasconf = DEFAULT_HOSTID;
7817c478bd9Sstevel@tonic-gate id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "initiator-id", -1);
7827c478bd9Sstevel@tonic-gate if (id == -1) {
7837c478bd9Sstevel@tonic-gate id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
7847c478bd9Sstevel@tonic-gate "scsi-initiator-id", -1);
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate if (id != DEFAULT_HOSTID && id >= 0 && id < NTARGETS_WIDE) {
7877c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE, "?initiator SCSI ID now %d\n", id);
7887c478bd9Sstevel@tonic-gate fas->f_fasconf = (uchar_t)id;
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate
7917c478bd9Sstevel@tonic-gate /*
7927c478bd9Sstevel@tonic-gate * find the burstsize and reduce ours if necessary
7937c478bd9Sstevel@tonic-gate */
7947c478bd9Sstevel@tonic-gate fas->f_dma_attr = fas_dma_attr;
7957c478bd9Sstevel@tonic-gate fas->f_dma_attr->dma_attr_burstsizes &=
7967c478bd9Sstevel@tonic-gate ddi_dma_burstsizes(fas->f_dmahandle);
7977c478bd9Sstevel@tonic-gate
7987c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
7997c478bd9Sstevel@tonic-gate fas->f_dma_attr->dma_attr_burstsizes &= fas_burstsizes_limit;
8007c478bd9Sstevel@tonic-gate IPRINTF1("dma burstsize=%x\n", fas->f_dma_attr->dma_attr_burstsizes);
8017c478bd9Sstevel@tonic-gate #endif
8027c478bd9Sstevel@tonic-gate /*
8037c478bd9Sstevel@tonic-gate * Attach this instance of the hba
8047c478bd9Sstevel@tonic-gate */
8057c478bd9Sstevel@tonic-gate if (scsi_hba_attach_setup(dip, fas->f_dma_attr, tran, 0) !=
8067c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
8077c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "scsi_hba_attach_setup failed");
8087c478bd9Sstevel@tonic-gate goto fail;
8097c478bd9Sstevel@tonic-gate }
8107c478bd9Sstevel@tonic-gate hba_attached++;
8117c478bd9Sstevel@tonic-gate
8127c478bd9Sstevel@tonic-gate /*
8137c478bd9Sstevel@tonic-gate * if scsi-options property exists, use it
8147c478bd9Sstevel@tonic-gate */
8157c478bd9Sstevel@tonic-gate fas->f_scsi_options = ddi_prop_get_int(DDI_DEV_T_ANY,
8167c478bd9Sstevel@tonic-gate dip, 0, "scsi-options", DEFAULT_SCSI_OPTIONS);
8177c478bd9Sstevel@tonic-gate
8187c478bd9Sstevel@tonic-gate /*
8197c478bd9Sstevel@tonic-gate * if scsi-selection-timeout property exists, use it
8207c478bd9Sstevel@tonic-gate */
8217c478bd9Sstevel@tonic-gate fas_selection_timeout = ddi_prop_get_int(DDI_DEV_T_ANY,
8227c478bd9Sstevel@tonic-gate dip, 0, "scsi-selection-timeout", SCSI_DEFAULT_SELECTION_TIMEOUT);
8237c478bd9Sstevel@tonic-gate
8247c478bd9Sstevel@tonic-gate /*
8257c478bd9Sstevel@tonic-gate * if hm-rev property doesn't exist, use old scheme for rev
8267c478bd9Sstevel@tonic-gate */
8277c478bd9Sstevel@tonic-gate hm_rev = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
8287c478bd9Sstevel@tonic-gate "hm-rev", -1);
8297c478bd9Sstevel@tonic-gate
8307c478bd9Sstevel@tonic-gate if (hm_rev == 0xa0 || hm_rev == -1) {
8317c478bd9Sstevel@tonic-gate if (DMAREV(dmar) != 0) {
8327c478bd9Sstevel@tonic-gate fas->f_hm_rev = 0x20;
8337c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
8347c478bd9Sstevel@tonic-gate "obsolete rev 2.0 FEPS chip, "
8357c478bd9Sstevel@tonic-gate "possible data corruption");
8367c478bd9Sstevel@tonic-gate } else {
8377c478bd9Sstevel@tonic-gate fas->f_hm_rev = 0x10;
8387c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
8397c478bd9Sstevel@tonic-gate "obsolete and unsupported rev 1.0 FEPS chip");
8407c478bd9Sstevel@tonic-gate goto fail;
8417c478bd9Sstevel@tonic-gate }
8427c478bd9Sstevel@tonic-gate } else if (hm_rev == 0x20) {
8437c478bd9Sstevel@tonic-gate fas->f_hm_rev = 0x21;
8447c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "obsolete rev 2.1 FEPS chip");
8457c478bd9Sstevel@tonic-gate } else {
8467c478bd9Sstevel@tonic-gate fas->f_hm_rev = (uchar_t)hm_rev;
8477c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE, "?rev %x.%x FEPS chip\n",
8487c478bd9Sstevel@tonic-gate (hm_rev >> 4) & 0xf, hm_rev & 0xf);
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate
8517c478bd9Sstevel@tonic-gate if ((fas->f_scsi_options & SCSI_OPTIONS_SYNC) == 0) {
8527c478bd9Sstevel@tonic-gate fas->f_nosync = ALL_TARGETS;
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate
8557c478bd9Sstevel@tonic-gate if ((fas->f_scsi_options & SCSI_OPTIONS_WIDE) == 0) {
8567c478bd9Sstevel@tonic-gate fas->f_nowide = ALL_TARGETS;
8577c478bd9Sstevel@tonic-gate }
8587c478bd9Sstevel@tonic-gate
8597c478bd9Sstevel@tonic-gate /*
8607c478bd9Sstevel@tonic-gate * if target<n>-scsi-options property exists, use it;
8617c478bd9Sstevel@tonic-gate * otherwise use the f_scsi_options
8627c478bd9Sstevel@tonic-gate */
8637c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
8647c478bd9Sstevel@tonic-gate (void) sprintf(prop_str, prop_template, i);
8657c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[i] = ddi_prop_get_int(
86619397407SSherry Moore DDI_DEV_T_ANY, dip, 0, prop_str, -1);
8677c478bd9Sstevel@tonic-gate
8687c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[i] != -1) {
8697c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE, "?target%x-scsi-options=0x%x\n",
8707c478bd9Sstevel@tonic-gate i, fas->f_target_scsi_options[i]);
8717c478bd9Sstevel@tonic-gate fas->f_target_scsi_options_defined |= 1 << i;
8727c478bd9Sstevel@tonic-gate } else {
8737c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[i] = fas->f_scsi_options;
8747c478bd9Sstevel@tonic-gate }
8757c478bd9Sstevel@tonic-gate if (((fas->f_target_scsi_options[i] &
8767c478bd9Sstevel@tonic-gate SCSI_OPTIONS_DR) == 0) &&
8777c478bd9Sstevel@tonic-gate (fas->f_target_scsi_options[i] & SCSI_OPTIONS_TAG)) {
8787c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[i] &= ~SCSI_OPTIONS_TAG;
8797c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
8807c478bd9Sstevel@tonic-gate "Disabled TQ since disconnects are disabled");
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate
8847c478bd9Sstevel@tonic-gate fas->f_scsi_tag_age_limit =
8857c478bd9Sstevel@tonic-gate ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "scsi-tag-age-limit",
88619397407SSherry Moore DEFAULT_TAG_AGE_LIMIT);
8877c478bd9Sstevel@tonic-gate
8887c478bd9Sstevel@tonic-gate fas->f_scsi_reset_delay = ddi_prop_get_int(DDI_DEV_T_ANY,
8897c478bd9Sstevel@tonic-gate dip, 0, "scsi-reset-delay", SCSI_DEFAULT_RESET_DELAY);
8907c478bd9Sstevel@tonic-gate if (fas->f_scsi_reset_delay == 0) {
8917c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE,
89219397407SSherry Moore "scsi_reset_delay of 0 is not recommended,"
89319397407SSherry Moore " resetting to SCSI_DEFAULT_RESET_DELAY\n");
8947c478bd9Sstevel@tonic-gate fas->f_scsi_reset_delay = SCSI_DEFAULT_RESET_DELAY;
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate
8977c478bd9Sstevel@tonic-gate /*
8987c478bd9Sstevel@tonic-gate * get iblock cookie and initialize mutexes
8997c478bd9Sstevel@tonic-gate */
9007c478bd9Sstevel@tonic-gate if (ddi_get_iblock_cookie(dip, (uint_t)0, &fas->f_iblock)
9017c478bd9Sstevel@tonic-gate != DDI_SUCCESS) {
9027c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas_attach: cannot get iblock cookie");
9037c478bd9Sstevel@tonic-gate goto fail;
9047c478bd9Sstevel@tonic-gate }
9057c478bd9Sstevel@tonic-gate
9067c478bd9Sstevel@tonic-gate mutex_init(&fas->f_mutex, NULL, MUTEX_DRIVER, fas->f_iblock);
9077c478bd9Sstevel@tonic-gate cv_init(&fas->f_cv, NULL, CV_DRIVER, NULL);
9087c478bd9Sstevel@tonic-gate
9097c478bd9Sstevel@tonic-gate /*
9107c478bd9Sstevel@tonic-gate * initialize mutex for waitQ
9117c478bd9Sstevel@tonic-gate */
9127c478bd9Sstevel@tonic-gate mutex_init(&fas->f_waitQ_mutex, NULL, MUTEX_DRIVER, fas->f_iblock);
9137c478bd9Sstevel@tonic-gate mutex_init_done++;
9147c478bd9Sstevel@tonic-gate
9157c478bd9Sstevel@tonic-gate /*
9167c478bd9Sstevel@tonic-gate * initialize callback mechanism (immediate callback)
9177c478bd9Sstevel@tonic-gate */
9187c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
9197c478bd9Sstevel@tonic-gate if (fas_init_callbacks(fas)) {
9207c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
9217c478bd9Sstevel@tonic-gate goto fail;
9227c478bd9Sstevel@tonic-gate }
9237c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
9247c478bd9Sstevel@tonic-gate
9257c478bd9Sstevel@tonic-gate /*
9267c478bd9Sstevel@tonic-gate * kstat_intr support
9277c478bd9Sstevel@tonic-gate */
9287c478bd9Sstevel@tonic-gate (void) sprintf(buf, "fas%d", instance);
9297c478bd9Sstevel@tonic-gate fas->f_intr_kstat = kstat_create("fas", instance, buf, "controller", \
93019397407SSherry Moore KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
9317c478bd9Sstevel@tonic-gate if (fas->f_intr_kstat)
9327c478bd9Sstevel@tonic-gate kstat_install(fas->f_intr_kstat);
9337c478bd9Sstevel@tonic-gate
9347c478bd9Sstevel@tonic-gate /*
9357c478bd9Sstevel@tonic-gate * install interrupt handler
9367c478bd9Sstevel@tonic-gate */
9377c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
9387c478bd9Sstevel@tonic-gate if (ddi_add_intr(dip, (uint_t)0, &fas->f_iblock, NULL,
9397c478bd9Sstevel@tonic-gate fas_intr, (caddr_t)fas)) {
9407c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas: cannot add intr");
9417c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
9427c478bd9Sstevel@tonic-gate goto fail;
9437c478bd9Sstevel@tonic-gate }
9447c478bd9Sstevel@tonic-gate intr_added++;
9457c478bd9Sstevel@tonic-gate
9467c478bd9Sstevel@tonic-gate /*
9477c478bd9Sstevel@tonic-gate * initialize fas chip
9487c478bd9Sstevel@tonic-gate */
9497c478bd9Sstevel@tonic-gate if (fas_init_chip(fas, id)) {
9507c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas: cannot initialize");
9517c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
9527c478bd9Sstevel@tonic-gate goto fail;
9537c478bd9Sstevel@tonic-gate }
9547c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
9557c478bd9Sstevel@tonic-gate
9567c478bd9Sstevel@tonic-gate /*
9577c478bd9Sstevel@tonic-gate * create kmem cache for packets
9587c478bd9Sstevel@tonic-gate */
9597c478bd9Sstevel@tonic-gate (void) sprintf(buf, "fas%d_cache", instance);
9607c478bd9Sstevel@tonic-gate fas->f_kmem_cache = kmem_cache_create(buf,
96119397407SSherry Moore EXTCMD_SIZE, 8,
96219397407SSherry Moore fas_kmem_cache_constructor, fas_kmem_cache_destructor,
96319397407SSherry Moore NULL, (void *)fas, NULL, 0);
9647c478bd9Sstevel@tonic-gate if (fas->f_kmem_cache == NULL) {
9657c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas: cannot create kmem_cache");
9667c478bd9Sstevel@tonic-gate goto fail;
9677c478bd9Sstevel@tonic-gate }
9687c478bd9Sstevel@tonic-gate
9697c478bd9Sstevel@tonic-gate /*
9707c478bd9Sstevel@tonic-gate * at this point, we are not going to fail the attach
9717c478bd9Sstevel@tonic-gate * so there is no need to undo the rest:
9727c478bd9Sstevel@tonic-gate *
9737c478bd9Sstevel@tonic-gate * add this fas to the list, this makes debugging easier
9747c478bd9Sstevel@tonic-gate * and fas_watch() needs it to walk thru all fas's
9757c478bd9Sstevel@tonic-gate */
9767c478bd9Sstevel@tonic-gate rw_enter(&fas_global_rwlock, RW_WRITER);
9777c478bd9Sstevel@tonic-gate if (fas_head == NULL) {
9787c478bd9Sstevel@tonic-gate fas_head = fas;
9797c478bd9Sstevel@tonic-gate } else {
9807c478bd9Sstevel@tonic-gate fas_tail->f_next = fas;
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate fas_tail = fas; /* point to last fas in list */
9837c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
9847c478bd9Sstevel@tonic-gate
9857c478bd9Sstevel@tonic-gate /*
9867c478bd9Sstevel@tonic-gate * there is one watchdog handler for all driver instances.
9877c478bd9Sstevel@tonic-gate * start the watchdog if it hasn't been done yet
9887c478bd9Sstevel@tonic-gate */
9897c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
9907c478bd9Sstevel@tonic-gate if (fas_scsi_watchdog_tick == 0) {
9917c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick = ddi_prop_get_int(DDI_DEV_T_ANY,
99219397407SSherry Moore dip, 0, "scsi-watchdog-tick", DEFAULT_WD_TICK);
9937c478bd9Sstevel@tonic-gate if (fas_scsi_watchdog_tick != DEFAULT_WD_TICK) {
9947c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE, "?scsi-watchdog-tick=%d\n",
9957c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick);
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate fas_tick = drv_usectohz((clock_t)
9987c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick * 1000000);
9997c478bd9Sstevel@tonic-gate IPRINTF2("fas scsi watchdog tick=%x, fas_tick=%lx\n",
10007c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick, fas_tick);
10017c478bd9Sstevel@tonic-gate if (fas_timeout_id == 0) {
10027c478bd9Sstevel@tonic-gate fas_timeout_id = timeout(fas_watch, NULL, fas_tick);
10037c478bd9Sstevel@tonic-gate fas_timeout_initted = 1;
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate }
10067c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
10077c478bd9Sstevel@tonic-gate
10087c478bd9Sstevel@tonic-gate ddi_report_dev(dip);
10097c478bd9Sstevel@tonic-gate
10107c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
10117c478bd9Sstevel@tonic-gate
10127c478bd9Sstevel@tonic-gate fail:
10137c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas%d: cannot attach", instance);
10147c478bd9Sstevel@tonic-gate if (fas) {
10157c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot++) {
10167c478bd9Sstevel@tonic-gate struct f_slots *active = fas->f_active[slot];
10177c478bd9Sstevel@tonic-gate if (active) {
10187c478bd9Sstevel@tonic-gate kmem_free(active, active->f_size);
10197c478bd9Sstevel@tonic-gate fas->f_active[slot] = NULL;
10207c478bd9Sstevel@tonic-gate }
10217c478bd9Sstevel@tonic-gate }
10227c478bd9Sstevel@tonic-gate if (mutex_init_done) {
10237c478bd9Sstevel@tonic-gate mutex_destroy(&fas->f_mutex);
10247c478bd9Sstevel@tonic-gate mutex_destroy(&fas->f_waitQ_mutex);
10257c478bd9Sstevel@tonic-gate cv_destroy(&fas->f_cv);
10267c478bd9Sstevel@tonic-gate }
10277c478bd9Sstevel@tonic-gate if (intr_added) {
10287c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, (uint_t)0, fas->f_iblock);
10297c478bd9Sstevel@tonic-gate }
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate * kstat_intr support
10327c478bd9Sstevel@tonic-gate */
10337c478bd9Sstevel@tonic-gate if (fas->f_intr_kstat) {
10347c478bd9Sstevel@tonic-gate kstat_delete(fas->f_intr_kstat);
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate if (hba_attached) {
10377c478bd9Sstevel@tonic-gate (void) scsi_hba_detach(dip);
10387c478bd9Sstevel@tonic-gate }
10397c478bd9Sstevel@tonic-gate if (tran) {
10407c478bd9Sstevel@tonic-gate scsi_hba_tran_free(tran);
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate if (fas->f_kmem_cache) {
10437c478bd9Sstevel@tonic-gate kmem_cache_destroy(fas->f_kmem_cache);
10447c478bd9Sstevel@tonic-gate }
10457c478bd9Sstevel@tonic-gate if (fas->f_cmdarea) {
10467c478bd9Sstevel@tonic-gate if (bound_handle) {
10477c478bd9Sstevel@tonic-gate (void) ddi_dma_unbind_handle(fas->f_dmahandle);
10487c478bd9Sstevel@tonic-gate }
10497c478bd9Sstevel@tonic-gate ddi_dma_mem_free(&fas->f_cmdarea_acc_handle);
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate if (fas->f_dmahandle) {
10527c478bd9Sstevel@tonic-gate ddi_dma_free_handle(&fas->f_dmahandle);
10537c478bd9Sstevel@tonic-gate }
10547c478bd9Sstevel@tonic-gate fas_destroy_callbacks(fas);
10557c478bd9Sstevel@tonic-gate if (fas->f_regs_acc_handle) {
10567c478bd9Sstevel@tonic-gate ddi_regs_map_free(&fas->f_regs_acc_handle);
10577c478bd9Sstevel@tonic-gate }
10587c478bd9Sstevel@tonic-gate if (fas->f_dmar_acc_handle) {
10597c478bd9Sstevel@tonic-gate ddi_regs_map_free(&fas->f_dmar_acc_handle);
10607c478bd9Sstevel@tonic-gate }
10617c478bd9Sstevel@tonic-gate ddi_soft_state_free(fas_state, instance);
10627c478bd9Sstevel@tonic-gate
10637c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
10647c478bd9Sstevel@tonic-gate }
10657c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
10667c478bd9Sstevel@tonic-gate }
10677c478bd9Sstevel@tonic-gate
10687c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10697c478bd9Sstevel@tonic-gate static int
fas_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)10707c478bd9Sstevel@tonic-gate fas_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
10717c478bd9Sstevel@tonic-gate {
10727c478bd9Sstevel@tonic-gate struct fas *fas, *nfas;
10737c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran;
10747c478bd9Sstevel@tonic-gate
10757c478bd9Sstevel@tonic-gate /* CONSTCOND */
10767c478bd9Sstevel@tonic-gate ASSERT(NO_COMPETING_THREADS);
10777c478bd9Sstevel@tonic-gate
10787c478bd9Sstevel@tonic-gate switch (cmd) {
10797c478bd9Sstevel@tonic-gate case DDI_DETACH:
10807c478bd9Sstevel@tonic-gate return (fas_dr_detach(dip));
10817c478bd9Sstevel@tonic-gate
10827c478bd9Sstevel@tonic-gate case DDI_SUSPEND:
10837c478bd9Sstevel@tonic-gate if ((tran = ddi_get_driver_private(dip)) == NULL)
10847c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
10857c478bd9Sstevel@tonic-gate
10867c478bd9Sstevel@tonic-gate fas = TRAN2FAS(tran);
10877c478bd9Sstevel@tonic-gate if (!fas) {
10887c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
10897c478bd9Sstevel@tonic-gate }
10907c478bd9Sstevel@tonic-gate
10917c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
10927c478bd9Sstevel@tonic-gate
10937c478bd9Sstevel@tonic-gate fas->f_suspended = 1;
10947c478bd9Sstevel@tonic-gate
10957c478bd9Sstevel@tonic-gate if (fas->f_ncmds) {
10967c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
10977c478bd9Sstevel@tonic-gate (void) fas_dopoll(fas, SHORT_POLL_TIMEOUT);
10987c478bd9Sstevel@tonic-gate }
10997c478bd9Sstevel@tonic-gate /*
11007c478bd9Sstevel@tonic-gate * disable dma and fas interrupt
11017c478bd9Sstevel@tonic-gate */
11027c478bd9Sstevel@tonic-gate fas->f_dma_csr &= ~DMA_INTEN;
11037c478bd9Sstevel@tonic-gate fas->f_dma_csr &= ~DMA_ENDVMA;
11047c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, fas->f_dma_csr);
11057c478bd9Sstevel@tonic-gate
11067c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
11077c478bd9Sstevel@tonic-gate
11087c478bd9Sstevel@tonic-gate if (fas->f_quiesce_timeid) {
11097c478bd9Sstevel@tonic-gate (void) untimeout(fas->f_quiesce_timeid);
11107c478bd9Sstevel@tonic-gate fas->f_quiesce_timeid = 0;
11117c478bd9Sstevel@tonic-gate }
11127c478bd9Sstevel@tonic-gate
11137c478bd9Sstevel@tonic-gate if (fas->f_restart_cmd_timeid) {
11147c478bd9Sstevel@tonic-gate (void) untimeout(fas->f_restart_cmd_timeid);
11157c478bd9Sstevel@tonic-gate fas->f_restart_cmd_timeid = 0;
11167c478bd9Sstevel@tonic-gate }
11177c478bd9Sstevel@tonic-gate
11187c478bd9Sstevel@tonic-gate /* Last fas? */
11197c478bd9Sstevel@tonic-gate rw_enter(&fas_global_rwlock, RW_WRITER);
11207c478bd9Sstevel@tonic-gate for (nfas = fas_head; nfas; nfas = nfas->f_next) {
11217c478bd9Sstevel@tonic-gate if (!nfas->f_suspended) {
11227c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
11237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
11247c478bd9Sstevel@tonic-gate }
11257c478bd9Sstevel@tonic-gate }
11267c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
11277c478bd9Sstevel@tonic-gate
11287c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
11297c478bd9Sstevel@tonic-gate if (fas_timeout_id != 0) {
11307c478bd9Sstevel@tonic-gate timeout_id_t tid = fas_timeout_id;
11317c478bd9Sstevel@tonic-gate fas_timeout_id = 0;
11327c478bd9Sstevel@tonic-gate fas_timeout_initted = 0;
11337c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
11347c478bd9Sstevel@tonic-gate (void) untimeout(tid);
11357c478bd9Sstevel@tonic-gate } else {
11367c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
11377c478bd9Sstevel@tonic-gate }
11387c478bd9Sstevel@tonic-gate
11397c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
11407c478bd9Sstevel@tonic-gate if (fas_reset_watch) {
11417c478bd9Sstevel@tonic-gate timeout_id_t tid = fas_reset_watch;
11427c478bd9Sstevel@tonic-gate fas_reset_watch = 0;
11437c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
11447c478bd9Sstevel@tonic-gate (void) untimeout(tid);
11457c478bd9Sstevel@tonic-gate } else {
11467c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
11477c478bd9Sstevel@tonic-gate }
11487c478bd9Sstevel@tonic-gate
11497c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
11507c478bd9Sstevel@tonic-gate
11517c478bd9Sstevel@tonic-gate default:
11527c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
11537c478bd9Sstevel@tonic-gate }
11547c478bd9Sstevel@tonic-gate _NOTE(NOT_REACHED)
11557c478bd9Sstevel@tonic-gate /* NOTREACHED */
11567c478bd9Sstevel@tonic-gate }
11577c478bd9Sstevel@tonic-gate
11587c478bd9Sstevel@tonic-gate static int
fas_dr_detach(dev_info_t * dip)11597c478bd9Sstevel@tonic-gate fas_dr_detach(dev_info_t *dip)
11607c478bd9Sstevel@tonic-gate {
11617c478bd9Sstevel@tonic-gate struct fas *fas, *f;
11627c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran;
11637c478bd9Sstevel@tonic-gate short slot;
11647c478bd9Sstevel@tonic-gate int i, j;
11657c478bd9Sstevel@tonic-gate
11667c478bd9Sstevel@tonic-gate if ((tran = ddi_get_driver_private(dip)) == NULL)
11677c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
11687c478bd9Sstevel@tonic-gate
11697c478bd9Sstevel@tonic-gate fas = TRAN2FAS(tran);
11707c478bd9Sstevel@tonic-gate if (!fas) {
11717c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
11727c478bd9Sstevel@tonic-gate }
11737c478bd9Sstevel@tonic-gate
11747c478bd9Sstevel@tonic-gate /*
11757c478bd9Sstevel@tonic-gate * disable interrupts
11767c478bd9Sstevel@tonic-gate */
11777c478bd9Sstevel@tonic-gate fas->f_dma_csr &= ~DMA_INTEN;
11787c478bd9Sstevel@tonic-gate fas->f_dma->dma_csr = fas->f_dma_csr;
11797c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, (uint_t)0, fas->f_iblock);
11807c478bd9Sstevel@tonic-gate
11817c478bd9Sstevel@tonic-gate /*
11827c478bd9Sstevel@tonic-gate * Remove device instance from the global linked list
11837c478bd9Sstevel@tonic-gate */
11847c478bd9Sstevel@tonic-gate rw_enter(&fas_global_rwlock, RW_WRITER);
11857c478bd9Sstevel@tonic-gate
11867c478bd9Sstevel@tonic-gate if (fas_head == fas) {
11877c478bd9Sstevel@tonic-gate f = fas_head = fas->f_next;
11887c478bd9Sstevel@tonic-gate } else {
11897c478bd9Sstevel@tonic-gate for (f = fas_head; f != (struct fas *)NULL; f = f->f_next) {
11907c478bd9Sstevel@tonic-gate if (f->f_next == fas) {
11917c478bd9Sstevel@tonic-gate f->f_next = fas->f_next;
11927c478bd9Sstevel@tonic-gate break;
11937c478bd9Sstevel@tonic-gate }
11947c478bd9Sstevel@tonic-gate }
11957c478bd9Sstevel@tonic-gate
11967c478bd9Sstevel@tonic-gate /*
11977c478bd9Sstevel@tonic-gate * Instance not in softc list. Since the
11987c478bd9Sstevel@tonic-gate * instance is not there in softc list, don't
11997c478bd9Sstevel@tonic-gate * enable interrupts, the instance is effectively
12007c478bd9Sstevel@tonic-gate * unusable.
12017c478bd9Sstevel@tonic-gate */
12027c478bd9Sstevel@tonic-gate if (f == (struct fas *)NULL) {
12037c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "fas_dr_detach: fas instance not"
120419397407SSherry Moore " in softc list!");
12057c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
12067c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
12077c478bd9Sstevel@tonic-gate }
12087c478bd9Sstevel@tonic-gate
12097c478bd9Sstevel@tonic-gate
12107c478bd9Sstevel@tonic-gate }
12117c478bd9Sstevel@tonic-gate
12127c478bd9Sstevel@tonic-gate if (fas_tail == fas)
12137c478bd9Sstevel@tonic-gate fas_tail = f;
12147c478bd9Sstevel@tonic-gate
12157c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
12167c478bd9Sstevel@tonic-gate
12177c478bd9Sstevel@tonic-gate if (fas->f_intr_kstat)
12187c478bd9Sstevel@tonic-gate kstat_delete(fas->f_intr_kstat);
12197c478bd9Sstevel@tonic-gate
12207c478bd9Sstevel@tonic-gate fas_destroy_callbacks(fas);
12217c478bd9Sstevel@tonic-gate
12227c478bd9Sstevel@tonic-gate scsi_hba_reset_notify_tear_down(fas->f_reset_notify_listf);
12237c478bd9Sstevel@tonic-gate
12247c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
12257c478bd9Sstevel@tonic-gate /*
12267c478bd9Sstevel@tonic-gate * destroy any outstanding tagged command info
12277c478bd9Sstevel@tonic-gate */
12287c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot++) {
12297c478bd9Sstevel@tonic-gate struct f_slots *active = fas->f_active[slot];
12307c478bd9Sstevel@tonic-gate if (active) {
12317c478bd9Sstevel@tonic-gate ushort_t tag;
12327c478bd9Sstevel@tonic-gate for (tag = 0; tag < active->f_n_slots; tag++) {
12337c478bd9Sstevel@tonic-gate struct fas_cmd *sp = active->f_slot[tag];
12347c478bd9Sstevel@tonic-gate if (sp) {
12357c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = sp->cmd_pkt;
12367c478bd9Sstevel@tonic-gate if (pkt) {
12377c478bd9Sstevel@tonic-gate (void) fas_scsi_destroy_pkt(
12387c478bd9Sstevel@tonic-gate &pkt->pkt_address, pkt);
12397c478bd9Sstevel@tonic-gate }
12407c478bd9Sstevel@tonic-gate /* sp freed in fas_scsi_destroy_pkt */
12417c478bd9Sstevel@tonic-gate active->f_slot[tag] = NULL;
12427c478bd9Sstevel@tonic-gate }
12437c478bd9Sstevel@tonic-gate }
12447c478bd9Sstevel@tonic-gate kmem_free(active, active->f_size);
12457c478bd9Sstevel@tonic-gate fas->f_active[slot] = NULL;
12467c478bd9Sstevel@tonic-gate }
12477c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] == 0);
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate
12507c478bd9Sstevel@tonic-gate /*
12517c478bd9Sstevel@tonic-gate * disallow timeout thread rescheduling
12527c478bd9Sstevel@tonic-gate */
12537c478bd9Sstevel@tonic-gate fas->f_flags |= FAS_FLG_NOTIMEOUTS;
12547c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
12557c478bd9Sstevel@tonic-gate
12567c478bd9Sstevel@tonic-gate if (fas->f_quiesce_timeid) {
12577c478bd9Sstevel@tonic-gate (void) untimeout(fas->f_quiesce_timeid);
12587c478bd9Sstevel@tonic-gate }
12597c478bd9Sstevel@tonic-gate
12607c478bd9Sstevel@tonic-gate /*
12617c478bd9Sstevel@tonic-gate * last fas? ... if active, CANCEL watch threads.
12627c478bd9Sstevel@tonic-gate */
12637c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
12647c478bd9Sstevel@tonic-gate if (fas_head == (struct fas *)NULL) {
12657c478bd9Sstevel@tonic-gate if (fas_timeout_initted) {
12667c478bd9Sstevel@tonic-gate timeout_id_t tid = fas_timeout_id;
12677c478bd9Sstevel@tonic-gate fas_timeout_initted = 0;
12687c478bd9Sstevel@tonic-gate fas_timeout_id = 0; /* don't resched */
12697c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
12707c478bd9Sstevel@tonic-gate (void) untimeout(tid);
12717c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
12727c478bd9Sstevel@tonic-gate }
12737c478bd9Sstevel@tonic-gate
12747c478bd9Sstevel@tonic-gate if (fas_reset_watch) {
12757c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
12767c478bd9Sstevel@tonic-gate (void) untimeout(fas_reset_watch);
12777c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
12787c478bd9Sstevel@tonic-gate fas_reset_watch = 0;
12797c478bd9Sstevel@tonic-gate }
12807c478bd9Sstevel@tonic-gate }
12817c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
12827c478bd9Sstevel@tonic-gate
12837c478bd9Sstevel@tonic-gate if (fas->f_restart_cmd_timeid) {
12847c478bd9Sstevel@tonic-gate (void) untimeout(fas->f_restart_cmd_timeid);
12857c478bd9Sstevel@tonic-gate fas->f_restart_cmd_timeid = 0;
12867c478bd9Sstevel@tonic-gate }
12877c478bd9Sstevel@tonic-gate
12887c478bd9Sstevel@tonic-gate /*
12897c478bd9Sstevel@tonic-gate * destroy outstanding ARQ pkts
12907c478bd9Sstevel@tonic-gate */
12917c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
12927c478bd9Sstevel@tonic-gate for (j = 0; j < NLUNS_PER_TARGET; j++) {
12937c478bd9Sstevel@tonic-gate int slot = i * NLUNS_PER_TARGET | j;
12947c478bd9Sstevel@tonic-gate if (fas->f_arq_pkt[slot]) {
12957c478bd9Sstevel@tonic-gate struct scsi_address sa;
12967c478bd9Sstevel@tonic-gate sa.a_hba_tran = NULL; /* not used */
12977c478bd9Sstevel@tonic-gate sa.a_target = (ushort_t)i;
12987c478bd9Sstevel@tonic-gate sa.a_lun = (uchar_t)j;
12997c478bd9Sstevel@tonic-gate (void) fas_delete_arq_pkt(fas, &sa);
13007c478bd9Sstevel@tonic-gate }
13017c478bd9Sstevel@tonic-gate }
13027c478bd9Sstevel@tonic-gate }
13037c478bd9Sstevel@tonic-gate
13047c478bd9Sstevel@tonic-gate /*
13057c478bd9Sstevel@tonic-gate * Remove device MT locks and CV
13067c478bd9Sstevel@tonic-gate */
13077c478bd9Sstevel@tonic-gate mutex_destroy(&fas->f_waitQ_mutex);
13087c478bd9Sstevel@tonic-gate mutex_destroy(&fas->f_mutex);
13097c478bd9Sstevel@tonic-gate cv_destroy(&fas->f_cv);
13107c478bd9Sstevel@tonic-gate
13117c478bd9Sstevel@tonic-gate /*
13127c478bd9Sstevel@tonic-gate * Release miscellaneous device resources
13137c478bd9Sstevel@tonic-gate */
13147c478bd9Sstevel@tonic-gate
13157c478bd9Sstevel@tonic-gate if (fas->f_kmem_cache) {
13167c478bd9Sstevel@tonic-gate kmem_cache_destroy(fas->f_kmem_cache);
13177c478bd9Sstevel@tonic-gate }
13187c478bd9Sstevel@tonic-gate
13197c478bd9Sstevel@tonic-gate if (fas->f_cmdarea != (uchar_t *)NULL) {
13207c478bd9Sstevel@tonic-gate (void) ddi_dma_unbind_handle(fas->f_dmahandle);
13217c478bd9Sstevel@tonic-gate ddi_dma_mem_free(&fas->f_cmdarea_acc_handle);
13227c478bd9Sstevel@tonic-gate }
13237c478bd9Sstevel@tonic-gate
13247c478bd9Sstevel@tonic-gate if (fas->f_dmahandle != (ddi_dma_handle_t)NULL) {
13257c478bd9Sstevel@tonic-gate ddi_dma_free_handle(&fas->f_dmahandle);
13267c478bd9Sstevel@tonic-gate }
13277c478bd9Sstevel@tonic-gate
13287c478bd9Sstevel@tonic-gate if (fas->f_regs_acc_handle) {
13297c478bd9Sstevel@tonic-gate ddi_regs_map_free(&fas->f_regs_acc_handle);
13307c478bd9Sstevel@tonic-gate }
13317c478bd9Sstevel@tonic-gate if (fas->f_dmar_acc_handle) {
13327c478bd9Sstevel@tonic-gate ddi_regs_map_free(&fas->f_dmar_acc_handle);
13337c478bd9Sstevel@tonic-gate }
13347c478bd9Sstevel@tonic-gate
13357c478bd9Sstevel@tonic-gate /*
13367c478bd9Sstevel@tonic-gate * Remove properties created during attach()
13377c478bd9Sstevel@tonic-gate */
13387c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip);
13397c478bd9Sstevel@tonic-gate
13407c478bd9Sstevel@tonic-gate /*
13417c478bd9Sstevel@tonic-gate * Delete the DMA limits, transport vectors and remove the device
13427c478bd9Sstevel@tonic-gate * links to the scsi_transport layer.
13437c478bd9Sstevel@tonic-gate * -- ddi_set_driver_private(dip, NULL)
13447c478bd9Sstevel@tonic-gate */
13457c478bd9Sstevel@tonic-gate (void) scsi_hba_detach(dip);
13467c478bd9Sstevel@tonic-gate
13477c478bd9Sstevel@tonic-gate /*
13487c478bd9Sstevel@tonic-gate * Free the scsi_transport structure for this device.
13497c478bd9Sstevel@tonic-gate */
13507c478bd9Sstevel@tonic-gate scsi_hba_tran_free(tran);
13517c478bd9Sstevel@tonic-gate
13527c478bd9Sstevel@tonic-gate ddi_soft_state_free(fas_state, ddi_get_instance(dip));
13537c478bd9Sstevel@tonic-gate
13547c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
13557c478bd9Sstevel@tonic-gate }
13567c478bd9Sstevel@tonic-gate
13577c478bd9Sstevel@tonic-gate static int
fas_quiesce_bus(struct fas * fas)13587c478bd9Sstevel@tonic-gate fas_quiesce_bus(struct fas *fas)
13597c478bd9Sstevel@tonic-gate {
13607c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
13617c478bd9Sstevel@tonic-gate IPRINTF("fas_quiesce: QUIESCEing\n");
13627c478bd9Sstevel@tonic-gate IPRINTF3("fas_quiesce: ncmds (%d) ndisc (%d) state (%d)\n",
136319397407SSherry Moore fas->f_ncmds, fas->f_ndisc, fas->f_softstate);
13647c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, HOLD_THROTTLE);
13657c478bd9Sstevel@tonic-gate if (fas_check_outstanding(fas)) {
13667c478bd9Sstevel@tonic-gate fas->f_softstate |= FAS_SS_DRAINING;
13677c478bd9Sstevel@tonic-gate fas->f_quiesce_timeid = timeout(fas_ncmds_checkdrain,
13687c478bd9Sstevel@tonic-gate fas, (FAS_QUIESCE_TIMEOUT * drv_usectohz(1000000)));
13697c478bd9Sstevel@tonic-gate if (cv_wait_sig(FAS_CV(fas), FAS_MUTEX(fas)) == 0) {
13707c478bd9Sstevel@tonic-gate /*
13717c478bd9Sstevel@tonic-gate * quiesce has been interrupted.
13727c478bd9Sstevel@tonic-gate */
13737c478bd9Sstevel@tonic-gate IPRINTF("fas_quiesce: abort QUIESCE\n");
13747c478bd9Sstevel@tonic-gate fas->f_softstate &= ~FAS_SS_DRAINING;
13757c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, MAX_THROTTLE);
13767c478bd9Sstevel@tonic-gate (void) fas_istart(fas);
13777c478bd9Sstevel@tonic-gate if (fas->f_quiesce_timeid != 0) {
13787c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
13797c478bd9Sstevel@tonic-gate #ifndef __lock_lint /* warlock complains but there is a NOTE on this */
13807c478bd9Sstevel@tonic-gate (void) untimeout(fas->f_quiesce_timeid);
13817c478bd9Sstevel@tonic-gate fas->f_quiesce_timeid = 0;
13827c478bd9Sstevel@tonic-gate #endif
13837c478bd9Sstevel@tonic-gate return (-1);
13847c478bd9Sstevel@tonic-gate }
13857c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
13867c478bd9Sstevel@tonic-gate return (-1);
13877c478bd9Sstevel@tonic-gate } else {
13887c478bd9Sstevel@tonic-gate IPRINTF("fas_quiesce: bus is QUIESCED\n");
13897c478bd9Sstevel@tonic-gate ASSERT(fas->f_quiesce_timeid == 0);
13907c478bd9Sstevel@tonic-gate fas->f_softstate &= ~FAS_SS_DRAINING;
13917c478bd9Sstevel@tonic-gate fas->f_softstate |= FAS_SS_QUIESCED;
13927c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
13937c478bd9Sstevel@tonic-gate return (0);
13947c478bd9Sstevel@tonic-gate }
13957c478bd9Sstevel@tonic-gate }
13967c478bd9Sstevel@tonic-gate IPRINTF("fas_quiesce: bus was not busy QUIESCED\n");
13977c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
13987c478bd9Sstevel@tonic-gate return (0);
13997c478bd9Sstevel@tonic-gate }
14007c478bd9Sstevel@tonic-gate
14017c478bd9Sstevel@tonic-gate static int
fas_unquiesce_bus(struct fas * fas)14027c478bd9Sstevel@tonic-gate fas_unquiesce_bus(struct fas *fas)
14037c478bd9Sstevel@tonic-gate {
14047c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
14057c478bd9Sstevel@tonic-gate fas->f_softstate &= ~FAS_SS_QUIESCED;
14067c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, MAX_THROTTLE);
14077c478bd9Sstevel@tonic-gate (void) fas_istart(fas);
14087c478bd9Sstevel@tonic-gate IPRINTF("fas_quiesce: bus has been UNQUIESCED\n");
14097c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
14107c478bd9Sstevel@tonic-gate
14117c478bd9Sstevel@tonic-gate return (0);
14127c478bd9Sstevel@tonic-gate }
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate /*
14157c478bd9Sstevel@tonic-gate * invoked from timeout() to check the number of outstanding commands
14167c478bd9Sstevel@tonic-gate */
14177c478bd9Sstevel@tonic-gate static void
fas_ncmds_checkdrain(void * arg)14187c478bd9Sstevel@tonic-gate fas_ncmds_checkdrain(void *arg)
14197c478bd9Sstevel@tonic-gate {
14207c478bd9Sstevel@tonic-gate struct fas *fas = arg;
14217c478bd9Sstevel@tonic-gate
14227c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
14237c478bd9Sstevel@tonic-gate IPRINTF3("fas_checkdrain: ncmds (%d) ndisc (%d) state (%d)\n",
142419397407SSherry Moore fas->f_ncmds, fas->f_ndisc, fas->f_softstate);
14257c478bd9Sstevel@tonic-gate if (fas->f_softstate & FAS_SS_DRAINING) {
14267c478bd9Sstevel@tonic-gate fas->f_quiesce_timeid = 0;
14277c478bd9Sstevel@tonic-gate if (fas_check_outstanding(fas) == 0) {
14287c478bd9Sstevel@tonic-gate IPRINTF("fas_drain: bus has drained\n");
14297c478bd9Sstevel@tonic-gate cv_signal(FAS_CV(fas));
14307c478bd9Sstevel@tonic-gate } else {
14317c478bd9Sstevel@tonic-gate /*
14327c478bd9Sstevel@tonic-gate * throttle may have been reset by a bus reset
14337c478bd9Sstevel@tonic-gate * or fas_runpoll()
14347c478bd9Sstevel@tonic-gate * XXX shouldn't be necessary
14357c478bd9Sstevel@tonic-gate */
14367c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, HOLD_THROTTLE);
14377c478bd9Sstevel@tonic-gate IPRINTF("fas_drain: rescheduling timeout\n");
14387c478bd9Sstevel@tonic-gate fas->f_quiesce_timeid = timeout(fas_ncmds_checkdrain,
14397c478bd9Sstevel@tonic-gate fas, (FAS_QUIESCE_TIMEOUT * drv_usectohz(1000000)));
14407c478bd9Sstevel@tonic-gate }
14417c478bd9Sstevel@tonic-gate }
14427c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate
14457c478bd9Sstevel@tonic-gate static int
fas_check_outstanding(struct fas * fas)14467c478bd9Sstevel@tonic-gate fas_check_outstanding(struct fas *fas)
14477c478bd9Sstevel@tonic-gate {
14487c478bd9Sstevel@tonic-gate uint_t slot;
14497c478bd9Sstevel@tonic-gate uint_t d = ((fas->f_dslot == 0)? 1 : fas->f_dslot);
14507c478bd9Sstevel@tonic-gate int ncmds = 0;
14517c478bd9Sstevel@tonic-gate
14527c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
14537c478bd9Sstevel@tonic-gate
14547c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot += d)
14557c478bd9Sstevel@tonic-gate ncmds += fas->f_tcmds[slot];
14567c478bd9Sstevel@tonic-gate
14577c478bd9Sstevel@tonic-gate return (ncmds);
14587c478bd9Sstevel@tonic-gate }
14597c478bd9Sstevel@tonic-gate
14607c478bd9Sstevel@tonic-gate
14617c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
14627c478bd9Sstevel@tonic-gate /*
14637c478bd9Sstevel@tonic-gate * fas register read/write functions with tracing
14647c478bd9Sstevel@tonic-gate */
14657c478bd9Sstevel@tonic-gate static void
fas_reg_tracing(struct fas * fas,int type,int regno,uint32_t what)14667c478bd9Sstevel@tonic-gate fas_reg_tracing(struct fas *fas, int type, int regno, uint32_t what)
14677c478bd9Sstevel@tonic-gate {
14687c478bd9Sstevel@tonic-gate fas->f_reg_trace[fas->f_reg_trace_index++] = type;
14697c478bd9Sstevel@tonic-gate fas->f_reg_trace[fas->f_reg_trace_index++] = regno;
14707c478bd9Sstevel@tonic-gate fas->f_reg_trace[fas->f_reg_trace_index++] = what;
14717c478bd9Sstevel@tonic-gate fas->f_reg_trace[fas->f_reg_trace_index++] = gethrtime();
14727c478bd9Sstevel@tonic-gate fas->f_reg_trace[fas->f_reg_trace_index] = 0xff;
14737c478bd9Sstevel@tonic-gate if (fas->f_reg_trace_index >= REG_TRACE_BUF_SIZE) {
14747c478bd9Sstevel@tonic-gate fas->f_reg_trace_index = 0;
14757c478bd9Sstevel@tonic-gate }
14767c478bd9Sstevel@tonic-gate }
14777c478bd9Sstevel@tonic-gate
14787c478bd9Sstevel@tonic-gate static void
fas_reg_cmd_write(struct fas * fas,uint8_t cmd)14797c478bd9Sstevel@tonic-gate fas_reg_cmd_write(struct fas *fas, uint8_t cmd)
14807c478bd9Sstevel@tonic-gate {
14817c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
14827c478bd9Sstevel@tonic-gate int regno = (uintptr_t)&fasreg->fas_cmd - (uintptr_t)fasreg;
14837c478bd9Sstevel@tonic-gate
14847c478bd9Sstevel@tonic-gate fasreg->fas_cmd = cmd;
14857c478bd9Sstevel@tonic-gate fas->f_last_cmd = cmd;
14867c478bd9Sstevel@tonic-gate
14877c478bd9Sstevel@tonic-gate EPRINTF1("issuing cmd %x\n", (uchar_t)cmd);
14887c478bd9Sstevel@tonic-gate fas_reg_tracing(fas, 0, regno, cmd);
14897c478bd9Sstevel@tonic-gate
14907c478bd9Sstevel@tonic-gate fas->f_reg_cmds++;
14917c478bd9Sstevel@tonic-gate }
14927c478bd9Sstevel@tonic-gate
14937c478bd9Sstevel@tonic-gate static void
fas_reg_write(struct fas * fas,volatile uint8_t * p,uint8_t what)14947c478bd9Sstevel@tonic-gate fas_reg_write(struct fas *fas, volatile uint8_t *p, uint8_t what)
14957c478bd9Sstevel@tonic-gate {
14967c478bd9Sstevel@tonic-gate int regno = (uintptr_t)p - (uintptr_t)fas->f_reg;
14977c478bd9Sstevel@tonic-gate
14987c478bd9Sstevel@tonic-gate *p = what;
14997c478bd9Sstevel@tonic-gate
15007c478bd9Sstevel@tonic-gate EPRINTF2("writing reg%x = %x\n", regno, what);
15017c478bd9Sstevel@tonic-gate fas_reg_tracing(fas, 1, regno, what);
15027c478bd9Sstevel@tonic-gate
15037c478bd9Sstevel@tonic-gate fas->f_reg_writes++;
15047c478bd9Sstevel@tonic-gate }
15057c478bd9Sstevel@tonic-gate
15067c478bd9Sstevel@tonic-gate static uint8_t
fas_reg_read(struct fas * fas,volatile uint8_t * p)15077c478bd9Sstevel@tonic-gate fas_reg_read(struct fas *fas, volatile uint8_t *p)
15087c478bd9Sstevel@tonic-gate {
15097c478bd9Sstevel@tonic-gate uint8_t what;
15107c478bd9Sstevel@tonic-gate int regno = (uintptr_t)p - (uintptr_t)fas->f_reg;
15117c478bd9Sstevel@tonic-gate
15127c478bd9Sstevel@tonic-gate what = *p;
15137c478bd9Sstevel@tonic-gate
15147c478bd9Sstevel@tonic-gate EPRINTF2("reading reg%x => %x\n", regno, what);
15157c478bd9Sstevel@tonic-gate fas_reg_tracing(fas, 2, regno, what);
15167c478bd9Sstevel@tonic-gate
15177c478bd9Sstevel@tonic-gate fas->f_reg_reads++;
15187c478bd9Sstevel@tonic-gate
15197c478bd9Sstevel@tonic-gate return (what);
15207c478bd9Sstevel@tonic-gate }
15217c478bd9Sstevel@tonic-gate
15227c478bd9Sstevel@tonic-gate /*
15237c478bd9Sstevel@tonic-gate * dma register access routines
15247c478bd9Sstevel@tonic-gate */
15257c478bd9Sstevel@tonic-gate static void
fas_dma_reg_write(struct fas * fas,volatile uint32_t * p,uint32_t what)15267c478bd9Sstevel@tonic-gate fas_dma_reg_write(struct fas *fas, volatile uint32_t *p, uint32_t what)
15277c478bd9Sstevel@tonic-gate {
15287c478bd9Sstevel@tonic-gate *p = what;
15297c478bd9Sstevel@tonic-gate fas->f_reg_dma_writes++;
15307c478bd9Sstevel@tonic-gate
15317c478bd9Sstevel@tonic-gate #ifdef DMA_REG_TRACING
15327c478bd9Sstevel@tonic-gate {
15337c478bd9Sstevel@tonic-gate int regno = (uintptr_t)p - (uintptr_t)fas->f_dma;
15347c478bd9Sstevel@tonic-gate EPRINTF2("writing dma reg%x = %x\n", regno, what);
15357c478bd9Sstevel@tonic-gate fas_reg_tracing(fas, 3, regno, what);
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate #endif
15387c478bd9Sstevel@tonic-gate }
15397c478bd9Sstevel@tonic-gate
15407c478bd9Sstevel@tonic-gate static uint32_t
fas_dma_reg_read(struct fas * fas,volatile uint32_t * p)15417c478bd9Sstevel@tonic-gate fas_dma_reg_read(struct fas *fas, volatile uint32_t *p)
15427c478bd9Sstevel@tonic-gate {
15437c478bd9Sstevel@tonic-gate uint32_t what = *p;
15447c478bd9Sstevel@tonic-gate fas->f_reg_dma_reads++;
15457c478bd9Sstevel@tonic-gate
15467c478bd9Sstevel@tonic-gate #ifdef DMA_REG_TRACING
15477c478bd9Sstevel@tonic-gate {
15487c478bd9Sstevel@tonic-gate int regno = (uintptr_t)p - (uintptr_t)fas->f_dma;
15497c478bd9Sstevel@tonic-gate EPRINTF2("reading dma reg%x => %x\n", regno, what);
15507c478bd9Sstevel@tonic-gate fas_reg_tracing(fas, 4, regno, what);
15517c478bd9Sstevel@tonic-gate }
15527c478bd9Sstevel@tonic-gate #endif
15537c478bd9Sstevel@tonic-gate return (what);
15547c478bd9Sstevel@tonic-gate }
15557c478bd9Sstevel@tonic-gate #endif
15567c478bd9Sstevel@tonic-gate
15577c478bd9Sstevel@tonic-gate #define FIFO_EMPTY(fas) (fas_reg_read(fas, &fas->f_reg->fas_stat2) & \
15587c478bd9Sstevel@tonic-gate FAS_STAT2_EMPTY)
15597c478bd9Sstevel@tonic-gate #define FIFO_CNT(fas) \
15607c478bd9Sstevel@tonic-gate (fas_reg_read(fas, &fas->f_reg->fas_fifo_flag) & FIFO_CNT_MASK)
15617c478bd9Sstevel@tonic-gate
15627c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
15637c478bd9Sstevel@tonic-gate static void
fas_assert_atn(struct fas * fas)15647c478bd9Sstevel@tonic-gate fas_assert_atn(struct fas *fas)
15657c478bd9Sstevel@tonic-gate {
15667c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_SET_ATN);
15677c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
15687c478bd9Sstevel@tonic-gate if (fas_test_stop > 1)
15697c478bd9Sstevel@tonic-gate debug_enter("asserted atn");
15707c478bd9Sstevel@tonic-gate #endif
15717c478bd9Sstevel@tonic-gate }
15727c478bd9Sstevel@tonic-gate #else
15737c478bd9Sstevel@tonic-gate #define fas_assert_atn(fas) fas_reg_cmd_write(fas, CMD_SET_ATN)
15747c478bd9Sstevel@tonic-gate #endif
15757c478bd9Sstevel@tonic-gate
15767c478bd9Sstevel@tonic-gate /*
15777c478bd9Sstevel@tonic-gate * DMA macros; we use a shadow copy of the dma_csr to save unnecessary
15787c478bd9Sstevel@tonic-gate * reads
15797c478bd9Sstevel@tonic-gate */
15807c478bd9Sstevel@tonic-gate #define FAS_DMA_WRITE(fas, count, base, cmd) { \
15817c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg; \
15827c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma; \
15837c478bd9Sstevel@tonic-gate ASSERT((fas_dma_reg_read(fas, &dmar->dma_csr) & DMA_ENDVMA) == 0); \
15847c478bd9Sstevel@tonic-gate SET_FAS_COUNT(fasreg, count); \
15857c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, cmd); \
15867c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_count, count); \
15877c478bd9Sstevel@tonic-gate fas->f_dma_csr |= \
15887c478bd9Sstevel@tonic-gate DMA_WRITE | DMA_ENDVMA | DMA_DSBL_DRAIN; \
15897c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_addr, (fas->f_lastdma = base)); \
15907c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_csr, fas->f_dma_csr); \
15917c478bd9Sstevel@tonic-gate }
15927c478bd9Sstevel@tonic-gate
15937c478bd9Sstevel@tonic-gate #define FAS_DMA_WRITE_SETUP(fas, count, base) { \
15947c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg; \
15957c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma; \
15967c478bd9Sstevel@tonic-gate ASSERT((fas_dma_reg_read(fas, &dmar->dma_csr) & DMA_ENDVMA) == 0); \
15977c478bd9Sstevel@tonic-gate SET_FAS_COUNT(fasreg, count); \
15987c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_count, count); \
15997c478bd9Sstevel@tonic-gate fas->f_dma_csr |= \
16007c478bd9Sstevel@tonic-gate DMA_WRITE | DMA_ENDVMA | DMA_DSBL_DRAIN; \
16017c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_addr, (fas->f_lastdma = base)); \
16027c478bd9Sstevel@tonic-gate }
16037c478bd9Sstevel@tonic-gate
16047c478bd9Sstevel@tonic-gate
16057c478bd9Sstevel@tonic-gate #define FAS_DMA_READ(fas, count, base, dmacount, cmd) { \
16067c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg; \
16077c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma; \
16087c478bd9Sstevel@tonic-gate ASSERT((fas_dma_reg_read(fas, &dmar->dma_csr) & DMA_ENDVMA) == 0); \
16097c478bd9Sstevel@tonic-gate SET_FAS_COUNT(fasreg, count); \
16107c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, cmd); \
16117c478bd9Sstevel@tonic-gate fas->f_dma_csr |= \
16127c478bd9Sstevel@tonic-gate (fas->f_dma_csr & ~DMA_WRITE) | DMA_ENDVMA | DMA_DSBL_DRAIN; \
16137c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_count, dmacount); \
16147c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_addr, (fas->f_lastdma = base)); \
16157c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &dmar->dma_csr, fas->f_dma_csr); \
16167c478bd9Sstevel@tonic-gate }
16177c478bd9Sstevel@tonic-gate
16187c478bd9Sstevel@tonic-gate static void
FAS_FLUSH_DMA(struct fas * fas)16197c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(struct fas *fas)
16207c478bd9Sstevel@tonic-gate {
16217c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, DMA_RESET);
16227c478bd9Sstevel@tonic-gate fas->f_dma_csr |= (DMA_INTEN|DMA_TWO_CYCLE|DMA_DSBL_PARITY|
162319397407SSherry Moore DMA_DSBL_DRAIN);
16247c478bd9Sstevel@tonic-gate fas->f_dma_csr &= ~(DMA_ENDVMA | DMA_WRITE);
16257c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, 0);
16267c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, fas->f_dma_csr);
16277c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_addr, 0);
16287c478bd9Sstevel@tonic-gate }
16297c478bd9Sstevel@tonic-gate
16307c478bd9Sstevel@tonic-gate /*
16317c478bd9Sstevel@tonic-gate * FAS_FLUSH_DMA_HARD checks on REQPEND before taking away the reset
16327c478bd9Sstevel@tonic-gate */
16337c478bd9Sstevel@tonic-gate static void
FAS_FLUSH_DMA_HARD(struct fas * fas)16347c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA_HARD(struct fas *fas)
16357c478bd9Sstevel@tonic-gate {
16367c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, DMA_RESET);
16377c478bd9Sstevel@tonic-gate fas->f_dma_csr |= (DMA_INTEN|DMA_TWO_CYCLE|DMA_DSBL_PARITY|
163819397407SSherry Moore DMA_DSBL_DRAIN);
16397c478bd9Sstevel@tonic-gate fas->f_dma_csr &= ~(DMA_ENDVMA | DMA_WRITE);
1640602ca9eaScth while (fas_dma_reg_read(fas, &fas->f_dma->dma_csr) & DMA_REQPEND)
1641602ca9eaScth ;
16427c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, 0);
16437c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr, fas->f_dma_csr);
16447c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_addr, 0);
16457c478bd9Sstevel@tonic-gate }
16467c478bd9Sstevel@tonic-gate
16477c478bd9Sstevel@tonic-gate /*
16487c478bd9Sstevel@tonic-gate * update period, conf3, offset reg, if necessary
16497c478bd9Sstevel@tonic-gate */
16507c478bd9Sstevel@tonic-gate #define FAS_SET_PERIOD_OFFSET_CONF3_REGS(fas, target) \
16517c478bd9Sstevel@tonic-gate { \
16527c478bd9Sstevel@tonic-gate uchar_t period, offset, conf3; \
16537c478bd9Sstevel@tonic-gate period = fas->f_sync_period[target] & SYNC_PERIOD_MASK; \
16547c478bd9Sstevel@tonic-gate offset = fas->f_offset[target]; \
16557c478bd9Sstevel@tonic-gate conf3 = fas->f_fasconf3[target]; \
16567c478bd9Sstevel@tonic-gate if ((period != fas->f_period_reg_last) || \
16577c478bd9Sstevel@tonic-gate (offset != fas->f_offset_reg_last) || \
16587c478bd9Sstevel@tonic-gate (conf3 != fas->f_fasconf3_reg_last)) { \
16597c478bd9Sstevel@tonic-gate fas->f_period_reg_last = period; \
16607c478bd9Sstevel@tonic-gate fas->f_offset_reg_last = offset; \
16617c478bd9Sstevel@tonic-gate fas->f_fasconf3_reg_last = conf3; \
16627c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_sync_period, period); \
16637c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_sync_offset, offset); \
16647c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_conf3, conf3); \
16657c478bd9Sstevel@tonic-gate } \
16667c478bd9Sstevel@tonic-gate }
16677c478bd9Sstevel@tonic-gate
16687c478bd9Sstevel@tonic-gate /*
16697c478bd9Sstevel@tonic-gate * fifo read/write routines
16707c478bd9Sstevel@tonic-gate * always read the fifo bytes before reading the interrupt register
16717c478bd9Sstevel@tonic-gate */
16727c478bd9Sstevel@tonic-gate
16737c478bd9Sstevel@tonic-gate static void
fas_read_fifo(struct fas * fas)16747c478bd9Sstevel@tonic-gate fas_read_fifo(struct fas *fas)
16757c478bd9Sstevel@tonic-gate {
16767c478bd9Sstevel@tonic-gate int stat = fas->f_stat;
16777c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
16787c478bd9Sstevel@tonic-gate int i;
16797c478bd9Sstevel@tonic-gate
16807c478bd9Sstevel@tonic-gate i = fas_reg_read(fas, &fasreg->fas_fifo_flag) & FIFO_CNT_MASK;
16817c478bd9Sstevel@tonic-gate EPRINTF2("fas_read_fifo: fifo cnt=%x, stat=%x\n", i, stat);
16827c478bd9Sstevel@tonic-gate ASSERT(i <= FIFOSIZE);
16837c478bd9Sstevel@tonic-gate
16847c478bd9Sstevel@tonic-gate fas->f_fifolen = 0;
16857c478bd9Sstevel@tonic-gate while (i-- > 0) {
16867c478bd9Sstevel@tonic-gate fas->f_fifo[fas->f_fifolen++] = fas_reg_read(fas,
168719397407SSherry Moore &fasreg->fas_fifo_data);
16887c478bd9Sstevel@tonic-gate fas->f_fifo[fas->f_fifolen++] = fas_reg_read(fas,
168919397407SSherry Moore &fasreg->fas_fifo_data);
16907c478bd9Sstevel@tonic-gate }
16917c478bd9Sstevel@tonic-gate if (fas->f_stat2 & FAS_STAT2_ISHUTTLE) {
16927c478bd9Sstevel@tonic-gate
16937c478bd9Sstevel@tonic-gate /* write pad byte */
16947c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_fifo_data, 0);
16957c478bd9Sstevel@tonic-gate fas->f_fifo[fas->f_fifolen++] = fas_reg_read(fas,
169619397407SSherry Moore &fasreg->fas_fifo_data);
16977c478bd9Sstevel@tonic-gate /* flush pad byte */
16987c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
16997c478bd9Sstevel@tonic-gate }
17007c478bd9Sstevel@tonic-gate EPRINTF2("fas_read_fifo: fifo len=%x, stat2=%x\n",
170119397407SSherry Moore fas->f_fifolen, stat);
17027c478bd9Sstevel@tonic-gate } /* fas_read_fifo */
17037c478bd9Sstevel@tonic-gate
17047c478bd9Sstevel@tonic-gate static void
fas_write_fifo(struct fas * fas,uchar_t * buf,int length,int pad)17057c478bd9Sstevel@tonic-gate fas_write_fifo(struct fas *fas, uchar_t *buf, int length, int pad)
17067c478bd9Sstevel@tonic-gate {
17077c478bd9Sstevel@tonic-gate int i;
17087c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
17097c478bd9Sstevel@tonic-gate
17107c478bd9Sstevel@tonic-gate EPRINTF1("writing fifo %x bytes\n", length);
17117c478bd9Sstevel@tonic-gate ASSERT(length <= 15);
17127c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
17137c478bd9Sstevel@tonic-gate for (i = 0; i < length; i++) {
17147c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_fifo_data, buf[i]);
17157c478bd9Sstevel@tonic-gate if (pad) {
17167c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_fifo_data, 0);
17177c478bd9Sstevel@tonic-gate }
17187c478bd9Sstevel@tonic-gate }
17197c478bd9Sstevel@tonic-gate }
17207c478bd9Sstevel@tonic-gate
17217c478bd9Sstevel@tonic-gate /*
17227c478bd9Sstevel@tonic-gate * Hardware and Software internal reset routines
17237c478bd9Sstevel@tonic-gate */
17247c478bd9Sstevel@tonic-gate static int
fas_init_chip(struct fas * fas,uchar_t initiator_id)17257c478bd9Sstevel@tonic-gate fas_init_chip(struct fas *fas, uchar_t initiator_id)
17267c478bd9Sstevel@tonic-gate {
17277c478bd9Sstevel@tonic-gate int i;
17287c478bd9Sstevel@tonic-gate uchar_t clock_conv;
17297c478bd9Sstevel@tonic-gate uchar_t initial_conf3;
17307c478bd9Sstevel@tonic-gate uint_t ticks;
17317c478bd9Sstevel@tonic-gate static char *prop_cfreq = "clock-frequency";
17327c478bd9Sstevel@tonic-gate
17337c478bd9Sstevel@tonic-gate /*
17347c478bd9Sstevel@tonic-gate * Determine clock frequency of attached FAS chip.
17357c478bd9Sstevel@tonic-gate */
17367c478bd9Sstevel@tonic-gate i = ddi_prop_get_int(DDI_DEV_T_ANY,
173719397407SSherry Moore fas->f_dev, DDI_PROP_DONTPASS, prop_cfreq, -1);
17387c478bd9Sstevel@tonic-gate clock_conv = (i + FIVE_MEG - 1) / FIVE_MEG;
17397c478bd9Sstevel@tonic-gate if (clock_conv != CLOCK_40MHZ) {
17407c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "Bad clock frequency");
17417c478bd9Sstevel@tonic-gate return (-1);
17427c478bd9Sstevel@tonic-gate }
17437c478bd9Sstevel@tonic-gate
17447c478bd9Sstevel@tonic-gate fas->f_clock_conv = clock_conv;
17457c478bd9Sstevel@tonic-gate fas->f_clock_cycle = CLOCK_PERIOD(i);
17467c478bd9Sstevel@tonic-gate ticks = FAS_CLOCK_TICK(fas);
17477c478bd9Sstevel@tonic-gate fas->f_stval = FAS_CLOCK_TIMEOUT(ticks, fas_selection_timeout);
17487c478bd9Sstevel@tonic-gate
17497c478bd9Sstevel@tonic-gate DPRINTF5("%d mhz, clock_conv %d, clock_cycle %d, ticks %d, stval %d\n",
175019397407SSherry Moore i, fas->f_clock_conv, fas->f_clock_cycle,
175119397407SSherry Moore ticks, fas->f_stval);
17527c478bd9Sstevel@tonic-gate /*
17537c478bd9Sstevel@tonic-gate * set up conf registers
17547c478bd9Sstevel@tonic-gate */
17557c478bd9Sstevel@tonic-gate fas->f_fasconf |= FAS_CONF_PAREN;
17567c478bd9Sstevel@tonic-gate fas->f_fasconf2 = (uchar_t)(FAS_CONF2_FENABLE | FAS_CONF2_XL32);
17577c478bd9Sstevel@tonic-gate
17587c478bd9Sstevel@tonic-gate if (initiator_id < NTARGETS) {
17597c478bd9Sstevel@tonic-gate initial_conf3 = FAS_CONF3_FASTCLK | FAS_CONF3_ODDBYTE_AUTO;
17607c478bd9Sstevel@tonic-gate } else {
17617c478bd9Sstevel@tonic-gate initial_conf3 = FAS_CONF3_FASTCLK | FAS_CONF3_ODDBYTE_AUTO |
17627c478bd9Sstevel@tonic-gate FAS_CONF3_IDBIT3;
17637c478bd9Sstevel@tonic-gate }
17647c478bd9Sstevel@tonic-gate
17657c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
17667c478bd9Sstevel@tonic-gate fas->f_fasconf3[i] = initial_conf3;
17677c478bd9Sstevel@tonic-gate }
17687c478bd9Sstevel@tonic-gate
17697c478bd9Sstevel@tonic-gate /*
17707c478bd9Sstevel@tonic-gate * Avoid resetting the scsi bus since this causes a few seconds
17717c478bd9Sstevel@tonic-gate * delay per fas in boot and also causes busy conditions in some
17727c478bd9Sstevel@tonic-gate * tape devices.
17737c478bd9Sstevel@tonic-gate */
17747c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_SOFTC|FAS_RESET_FAS|FAS_RESET_DMA);
17757c478bd9Sstevel@tonic-gate
17767c478bd9Sstevel@tonic-gate /*
17777c478bd9Sstevel@tonic-gate * initialize period and offset for each target
17787c478bd9Sstevel@tonic-gate */
17797c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
17807c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[i] & SCSI_OPTIONS_SYNC) {
17817c478bd9Sstevel@tonic-gate fas->f_offset[i] = fas_default_offset |
178219397407SSherry Moore fas->f_req_ack_delay;
17837c478bd9Sstevel@tonic-gate } else {
17847c478bd9Sstevel@tonic-gate fas->f_offset[i] = 0;
17857c478bd9Sstevel@tonic-gate }
17867c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[i] & SCSI_OPTIONS_FAST) {
17877c478bd9Sstevel@tonic-gate fas->f_neg_period[i] =
17887c478bd9Sstevel@tonic-gate (uchar_t)MIN_SYNC_PERIOD(fas);
17897c478bd9Sstevel@tonic-gate } else {
17907c478bd9Sstevel@tonic-gate fas->f_neg_period[i] =
17917c478bd9Sstevel@tonic-gate (uchar_t)CONVERT_PERIOD(DEFAULT_SYNC_PERIOD);
17927c478bd9Sstevel@tonic-gate }
17937c478bd9Sstevel@tonic-gate }
17947c478bd9Sstevel@tonic-gate return (0);
17957c478bd9Sstevel@tonic-gate }
17967c478bd9Sstevel@tonic-gate
17977c478bd9Sstevel@tonic-gate /*
17987c478bd9Sstevel@tonic-gate * reset bus, chip, dma, or soft state
17997c478bd9Sstevel@tonic-gate */
18007c478bd9Sstevel@tonic-gate static void
fas_internal_reset(struct fas * fas,int reset_action)18017c478bd9Sstevel@tonic-gate fas_internal_reset(struct fas *fas, int reset_action)
18027c478bd9Sstevel@tonic-gate {
18037c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
18047c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma;
18057c478bd9Sstevel@tonic-gate
18067c478bd9Sstevel@tonic-gate if (reset_action & FAS_RESET_SCSIBUS) {
18077c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_RESET_SCSI);
18087c478bd9Sstevel@tonic-gate fas_setup_reset_delay(fas);
18097c478bd9Sstevel@tonic-gate }
18107c478bd9Sstevel@tonic-gate
18117c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA_HARD(fas); /* resets and reinits the dma */
18127c478bd9Sstevel@tonic-gate
18137c478bd9Sstevel@tonic-gate /*
18147c478bd9Sstevel@tonic-gate * NOTE: if dma is aborted while active, indefinite hangs
18157c478bd9Sstevel@tonic-gate * may occur; it is preferable to stop the target first before
18167c478bd9Sstevel@tonic-gate * flushing the dma
18177c478bd9Sstevel@tonic-gate */
18187c478bd9Sstevel@tonic-gate if (reset_action & FAS_RESET_DMA) {
18197c478bd9Sstevel@tonic-gate int burstsizes = fas->f_dma_attr->dma_attr_burstsizes;
18207c478bd9Sstevel@tonic-gate if (burstsizes & BURST64) {
18217c478bd9Sstevel@tonic-gate IPRINTF("64 byte burstsize\n");
18227c478bd9Sstevel@tonic-gate fas->f_dma_csr |= DMA_BURST64;
18237c478bd9Sstevel@tonic-gate } else if (burstsizes & BURST32) {
18247c478bd9Sstevel@tonic-gate IPRINTF("32 byte burstsize\n");
18257c478bd9Sstevel@tonic-gate fas->f_dma_csr |= DMA_BURST32;
18267c478bd9Sstevel@tonic-gate } else {
18277c478bd9Sstevel@tonic-gate IPRINTF("16 byte burstsize\n");
18287c478bd9Sstevel@tonic-gate }
18297c478bd9Sstevel@tonic-gate if ((fas->f_hm_rev > 0x20) && (fas_enable_sbus64) &&
18307c478bd9Sstevel@tonic-gate (ddi_dma_set_sbus64(fas->f_dmahandle, burstsizes) ==
18317c478bd9Sstevel@tonic-gate DDI_SUCCESS)) {
18327c478bd9Sstevel@tonic-gate IPRINTF("enabled 64 bit sbus\n");
18337c478bd9Sstevel@tonic-gate fas->f_dma_csr |= DMA_WIDE_EN;
18347c478bd9Sstevel@tonic-gate }
18357c478bd9Sstevel@tonic-gate }
18367c478bd9Sstevel@tonic-gate
18377c478bd9Sstevel@tonic-gate if (reset_action & FAS_RESET_FAS) {
18387c478bd9Sstevel@tonic-gate /*
18397c478bd9Sstevel@tonic-gate * 2 NOPs with DMA are required here
18407c478bd9Sstevel@tonic-gate * id_code is unreliable if we don't do this)
18417c478bd9Sstevel@tonic-gate */
18427c478bd9Sstevel@tonic-gate uchar_t idcode, fcode;
18437c478bd9Sstevel@tonic-gate int dmarev;
18447c478bd9Sstevel@tonic-gate
18457c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_RESET_FAS);
18467c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_NOP | CMD_DMA);
18477c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_NOP | CMD_DMA);
18487c478bd9Sstevel@tonic-gate
18497c478bd9Sstevel@tonic-gate /*
18507c478bd9Sstevel@tonic-gate * Re-load chip configurations
18517c478bd9Sstevel@tonic-gate * Only load registers which are not loaded in fas_startcmd()
18527c478bd9Sstevel@tonic-gate */
18537c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_clock_conv,
185419397407SSherry Moore (fas->f_clock_conv & CLOCK_MASK));
18557c478bd9Sstevel@tonic-gate
18567c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_timeout, fas->f_stval);
18577c478bd9Sstevel@tonic-gate
18587c478bd9Sstevel@tonic-gate /*
18597c478bd9Sstevel@tonic-gate * enable default configurations
18607c478bd9Sstevel@tonic-gate */
18617c478bd9Sstevel@tonic-gate fas->f_idcode = idcode =
186219397407SSherry Moore fas_reg_read(fas, &fasreg->fas_id_code);
18637c478bd9Sstevel@tonic-gate fcode = (uchar_t)(idcode & FAS_FCODE_MASK) >> (uchar_t)3;
18647c478bd9Sstevel@tonic-gate fas->f_type = FAS366;
18657c478bd9Sstevel@tonic-gate IPRINTF2("Family code %d, revision %d\n",
18667c478bd9Sstevel@tonic-gate fcode, (idcode & FAS_REV_MASK));
18677c478bd9Sstevel@tonic-gate dmarev = fas_dma_reg_read(fas, &dmar->dma_csr);
18687c478bd9Sstevel@tonic-gate dmarev = (dmarev >> 11) & 0xf;
18697c478bd9Sstevel@tonic-gate IPRINTF1("DMA channel revision %d\n", dmarev);
18707c478bd9Sstevel@tonic-gate
18717c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_conf, fas->f_fasconf);
18727c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_conf2, fas->f_fasconf2);
18737c478bd9Sstevel@tonic-gate
18747c478bd9Sstevel@tonic-gate fas->f_req_ack_delay = DEFAULT_REQ_ACK_DELAY;
18757c478bd9Sstevel@tonic-gate
18767c478bd9Sstevel@tonic-gate /*
18777c478bd9Sstevel@tonic-gate * Just in case... clear interrupt
18787c478bd9Sstevel@tonic-gate */
18797c478bd9Sstevel@tonic-gate (void) fas_reg_read(fas, &fasreg->fas_intr);
18807c478bd9Sstevel@tonic-gate }
18817c478bd9Sstevel@tonic-gate
18827c478bd9Sstevel@tonic-gate if (reset_action & FAS_RESET_SOFTC) {
18837c478bd9Sstevel@tonic-gate fas->f_wdtr_sent = fas->f_sdtr_sent = 0;
18847c478bd9Sstevel@tonic-gate fas->f_wide_known = fas->f_sync_known = 0;
18857c478bd9Sstevel@tonic-gate fas->f_wide_enabled = fas->f_sync_enabled = 0;
18867c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
18877c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = fas->f_last_msgout =
18887c478bd9Sstevel@tonic-gate fas->f_last_msgin = INVALID_MSG;
18897c478bd9Sstevel@tonic-gate fas->f_abort_msg_sent = fas->f_reset_msg_sent = 0;
18907c478bd9Sstevel@tonic-gate fas->f_next_slot = 0;
18917c478bd9Sstevel@tonic-gate fas->f_current_sp = NULL;
18927c478bd9Sstevel@tonic-gate fas->f_fifolen = 0;
18937c478bd9Sstevel@tonic-gate fas->f_fasconf3_reg_last = fas->f_offset_reg_last =
189419397407SSherry Moore fas->f_period_reg_last = 0xff;
18957c478bd9Sstevel@tonic-gate
18967c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
18977c478bd9Sstevel@tonic-gate }
18987c478bd9Sstevel@tonic-gate }
18997c478bd9Sstevel@tonic-gate
19007c478bd9Sstevel@tonic-gate
19017c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
19027c478bd9Sstevel@tonic-gate /*
19037c478bd9Sstevel@tonic-gate * check if ncmds still reflects the truth
19047c478bd9Sstevel@tonic-gate * count all cmds for this driver instance and compare with ncmds
19057c478bd9Sstevel@tonic-gate */
19067c478bd9Sstevel@tonic-gate static void
fas_check_ncmds(struct fas * fas)19077c478bd9Sstevel@tonic-gate fas_check_ncmds(struct fas *fas)
19087c478bd9Sstevel@tonic-gate {
19097c478bd9Sstevel@tonic-gate int slot = 0;
19107c478bd9Sstevel@tonic-gate ushort_t tag, t;
19117c478bd9Sstevel@tonic-gate int n, total = 0;
19127c478bd9Sstevel@tonic-gate
19137c478bd9Sstevel@tonic-gate do {
19147c478bd9Sstevel@tonic-gate if (fas->f_active[slot]) {
19157c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_readyf[slot];
19167c478bd9Sstevel@tonic-gate t = fas->f_active[slot]->f_n_slots;
19177c478bd9Sstevel@tonic-gate while (sp != 0) {
19187c478bd9Sstevel@tonic-gate sp = sp->cmd_forw;
19197c478bd9Sstevel@tonic-gate total++;
19207c478bd9Sstevel@tonic-gate }
19217c478bd9Sstevel@tonic-gate for (n = tag = 0; tag < t; tag++) {
19227c478bd9Sstevel@tonic-gate if (fas->f_active[slot]->f_slot[tag] != 0) {
19237c478bd9Sstevel@tonic-gate n++;
19247c478bd9Sstevel@tonic-gate total++;
19257c478bd9Sstevel@tonic-gate }
19267c478bd9Sstevel@tonic-gate }
19277c478bd9Sstevel@tonic-gate ASSERT(n == fas->f_tcmds[slot]);
19287c478bd9Sstevel@tonic-gate }
19297c478bd9Sstevel@tonic-gate slot = NEXTSLOT(slot, fas->f_dslot);
19307c478bd9Sstevel@tonic-gate } while (slot != 0);
19317c478bd9Sstevel@tonic-gate
19327c478bd9Sstevel@tonic-gate if (total != fas->f_ncmds) {
19337c478bd9Sstevel@tonic-gate IPRINTF2("fas_check_ncmds: total=%x, ncmds=%x\n",
193419397407SSherry Moore total, fas->f_ncmds);
19357c478bd9Sstevel@tonic-gate }
19367c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
19377c478bd9Sstevel@tonic-gate }
19387c478bd9Sstevel@tonic-gate #else
19397c478bd9Sstevel@tonic-gate #define fas_check_ncmds(fas)
19407c478bd9Sstevel@tonic-gate #endif
19417c478bd9Sstevel@tonic-gate
19427c478bd9Sstevel@tonic-gate /*
19437c478bd9Sstevel@tonic-gate * SCSA Interface functions
19447c478bd9Sstevel@tonic-gate *
19457c478bd9Sstevel@tonic-gate * Visible to the external world via the transport structure.
19467c478bd9Sstevel@tonic-gate *
19477c478bd9Sstevel@tonic-gate * fas_scsi_abort: abort a current cmd or all cmds for a target
19487c478bd9Sstevel@tonic-gate */
19497c478bd9Sstevel@tonic-gate /*ARGSUSED*/
19507c478bd9Sstevel@tonic-gate static int
fas_scsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)19517c478bd9Sstevel@tonic-gate fas_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
19527c478bd9Sstevel@tonic-gate {
19537c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
19547c478bd9Sstevel@tonic-gate int rval;
19557c478bd9Sstevel@tonic-gate
19567c478bd9Sstevel@tonic-gate IPRINTF2("fas_scsi_abort: target %d.%d\n", ap->a_target, ap->a_lun);
19577c478bd9Sstevel@tonic-gate
19587c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
19597c478bd9Sstevel@tonic-gate rval = fas_do_scsi_abort(ap, pkt);
19607c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
19617c478bd9Sstevel@tonic-gate return (rval);
19627c478bd9Sstevel@tonic-gate }
19637c478bd9Sstevel@tonic-gate
19647c478bd9Sstevel@tonic-gate /*
19657c478bd9Sstevel@tonic-gate * reset handling: reset bus or target
19667c478bd9Sstevel@tonic-gate */
19677c478bd9Sstevel@tonic-gate /*ARGSUSED*/
19687c478bd9Sstevel@tonic-gate static int
fas_scsi_reset(struct scsi_address * ap,int level)19697c478bd9Sstevel@tonic-gate fas_scsi_reset(struct scsi_address *ap, int level)
19707c478bd9Sstevel@tonic-gate {
19717c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
19727c478bd9Sstevel@tonic-gate int rval;
19737c478bd9Sstevel@tonic-gate
19747c478bd9Sstevel@tonic-gate IPRINTF3("fas_scsi_reset: target %d.%d, level %d\n",
197519397407SSherry Moore ap->a_target, ap->a_lun, level);
19767c478bd9Sstevel@tonic-gate
19777c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
19787c478bd9Sstevel@tonic-gate rval = fas_do_scsi_reset(ap, level);
19797c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
19807c478bd9Sstevel@tonic-gate return (rval);
19817c478bd9Sstevel@tonic-gate }
19827c478bd9Sstevel@tonic-gate
19837c478bd9Sstevel@tonic-gate /*
19847c478bd9Sstevel@tonic-gate * entry point for reset notification setup, to register or to cancel.
19857c478bd9Sstevel@tonic-gate */
19867c478bd9Sstevel@tonic-gate static int
fas_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)19877c478bd9Sstevel@tonic-gate fas_scsi_reset_notify(struct scsi_address *ap, int flag,
19887c478bd9Sstevel@tonic-gate void (*callback)(caddr_t), caddr_t arg)
19897c478bd9Sstevel@tonic-gate {
19907c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
19917c478bd9Sstevel@tonic-gate
19927c478bd9Sstevel@tonic-gate return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
199319397407SSherry Moore &fas->f_mutex, &fas->f_reset_notify_listf));
19947c478bd9Sstevel@tonic-gate }
19957c478bd9Sstevel@tonic-gate
19967c478bd9Sstevel@tonic-gate /*
19977c478bd9Sstevel@tonic-gate * capability interface
19987c478bd9Sstevel@tonic-gate */
19997c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20007c478bd9Sstevel@tonic-gate static int
fas_scsi_getcap(struct scsi_address * ap,char * cap,int whom)20017c478bd9Sstevel@tonic-gate fas_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
20027c478bd9Sstevel@tonic-gate {
20037c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
20047c478bd9Sstevel@tonic-gate DPRINTF3("fas_scsi_getcap: tgt=%x, cap=%s, whom=%x\n",
200519397407SSherry Moore ap->a_target, cap, whom);
20067c478bd9Sstevel@tonic-gate return (fas_commoncap(ap, cap, 0, whom, 0));
20077c478bd9Sstevel@tonic-gate }
20087c478bd9Sstevel@tonic-gate
20097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20107c478bd9Sstevel@tonic-gate static int
fas_scsi_setcap(struct scsi_address * ap,char * cap,int value,int whom)20117c478bd9Sstevel@tonic-gate fas_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
20127c478bd9Sstevel@tonic-gate {
20137c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
20147c478bd9Sstevel@tonic-gate IPRINTF4("fas_scsi_setcap: tgt=%x, cap=%s, value=%x, whom=%x\n",
201519397407SSherry Moore ap->a_target, cap, value, whom);
20167c478bd9Sstevel@tonic-gate return (fas_commoncap(ap, cap, value, whom, 1));
20177c478bd9Sstevel@tonic-gate }
20187c478bd9Sstevel@tonic-gate
20197c478bd9Sstevel@tonic-gate /*
20207c478bd9Sstevel@tonic-gate * pkt and dma allocation and deallocation
20217c478bd9Sstevel@tonic-gate */
20227c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20237c478bd9Sstevel@tonic-gate static void
fas_scsi_dmafree(struct scsi_address * ap,struct scsi_pkt * pkt)20247c478bd9Sstevel@tonic-gate fas_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
20257c478bd9Sstevel@tonic-gate {
20267c478bd9Sstevel@tonic-gate struct fas_cmd *cmd = PKT2CMD(pkt);
20277c478bd9Sstevel@tonic-gate
20287c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAFREE_START,
20297c478bd9Sstevel@tonic-gate "fas_scsi_dmafree_start");
20307c478bd9Sstevel@tonic-gate
20317c478bd9Sstevel@tonic-gate if (cmd->cmd_flags & CFLAG_DMAVALID) {
20327c478bd9Sstevel@tonic-gate /*
20337c478bd9Sstevel@tonic-gate * Free the mapping.
20347c478bd9Sstevel@tonic-gate */
20357c478bd9Sstevel@tonic-gate (void) ddi_dma_unbind_handle(cmd->cmd_dmahandle);
20367c478bd9Sstevel@tonic-gate cmd->cmd_flags ^= CFLAG_DMAVALID;
20377c478bd9Sstevel@tonic-gate }
20387c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAFREE_END,
20397c478bd9Sstevel@tonic-gate "fas_scsi_dmafree_end");
20407c478bd9Sstevel@tonic-gate }
20417c478bd9Sstevel@tonic-gate
20427c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20437c478bd9Sstevel@tonic-gate static void
fas_scsi_sync_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)20447c478bd9Sstevel@tonic-gate fas_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
20457c478bd9Sstevel@tonic-gate {
20467c478bd9Sstevel@tonic-gate struct fas_cmd *sp = PKT2CMD(pkt);
20477c478bd9Sstevel@tonic-gate
20487c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_DMAVALID) {
20497c478bd9Sstevel@tonic-gate if (ddi_dma_sync(sp->cmd_dmahandle, 0, 0,
20507c478bd9Sstevel@tonic-gate (sp->cmd_flags & CFLAG_DMASEND) ?
20517c478bd9Sstevel@tonic-gate DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU) !=
20527c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
20537c478bd9Sstevel@tonic-gate fas_log(ADDR2FAS(ap), CE_WARN,
20547c478bd9Sstevel@tonic-gate "sync of pkt (%p) failed", (void *)pkt);
20557c478bd9Sstevel@tonic-gate }
20567c478bd9Sstevel@tonic-gate }
20577c478bd9Sstevel@tonic-gate }
20587c478bd9Sstevel@tonic-gate
20597c478bd9Sstevel@tonic-gate /*
20607c478bd9Sstevel@tonic-gate * initialize pkt and allocate DVMA resources
20617c478bd9Sstevel@tonic-gate */
20627c478bd9Sstevel@tonic-gate static struct scsi_pkt *
fas_scsi_init_pkt(struct scsi_address * ap,struct scsi_pkt * pkt,struct buf * bp,int cmdlen,int statuslen,int tgtlen,int flags,int (* callback)(),caddr_t arg)20637c478bd9Sstevel@tonic-gate fas_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
20647c478bd9Sstevel@tonic-gate struct buf *bp, int cmdlen, int statuslen, int tgtlen,
20657c478bd9Sstevel@tonic-gate int flags, int (*callback)(), caddr_t arg)
20667c478bd9Sstevel@tonic-gate {
20677c478bd9Sstevel@tonic-gate int kf;
20687c478bd9Sstevel@tonic-gate int failure = 1;
20697c478bd9Sstevel@tonic-gate struct fas_cmd *cmd;
20707c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
20717c478bd9Sstevel@tonic-gate struct fas_cmd *new_cmd;
20727c478bd9Sstevel@tonic-gate int rval;
20737c478bd9Sstevel@tonic-gate
20747c478bd9Sstevel@tonic-gate /* #define FAS_TEST_EXTRN_ALLOC */
20757c478bd9Sstevel@tonic-gate #ifdef FAS_TEST_EXTRN_ALLOC
20767c478bd9Sstevel@tonic-gate cmdlen *= 4; statuslen *= 4; tgtlen *= 4;
20777c478bd9Sstevel@tonic-gate #endif
20787c478bd9Sstevel@tonic-gate /*
20797c478bd9Sstevel@tonic-gate * if no pkt was passed then allocate a pkt first
20807c478bd9Sstevel@tonic-gate */
20817c478bd9Sstevel@tonic-gate if (pkt == NULL) {
20827c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_PKTALLOC_START,
20837c478bd9Sstevel@tonic-gate "fas_scsi_impl_pktalloc_start");
20847c478bd9Sstevel@tonic-gate
20857c478bd9Sstevel@tonic-gate kf = (callback == SLEEP_FUNC)? KM_SLEEP: KM_NOSLEEP;
20867c478bd9Sstevel@tonic-gate
20877c478bd9Sstevel@tonic-gate /*
20887c478bd9Sstevel@tonic-gate * only one size of pkt (with arq).
20897c478bd9Sstevel@tonic-gate */
20907c478bd9Sstevel@tonic-gate cmd = kmem_cache_alloc(fas->f_kmem_cache, kf);
20917c478bd9Sstevel@tonic-gate
20927c478bd9Sstevel@tonic-gate if (cmd) {
20937c478bd9Sstevel@tonic-gate
20947c478bd9Sstevel@tonic-gate ddi_dma_handle_t save_dma_handle;
20957c478bd9Sstevel@tonic-gate
20967c478bd9Sstevel@tonic-gate save_dma_handle = cmd->cmd_dmahandle;
20977c478bd9Sstevel@tonic-gate bzero(cmd, EXTCMD_SIZE);
20987c478bd9Sstevel@tonic-gate cmd->cmd_dmahandle = save_dma_handle;
20997c478bd9Sstevel@tonic-gate
21007c478bd9Sstevel@tonic-gate pkt = (struct scsi_pkt *)((uchar_t *)cmd +
21017c478bd9Sstevel@tonic-gate sizeof (struct fas_cmd));
21027c478bd9Sstevel@tonic-gate cmd->cmd_pkt = pkt;
21037c478bd9Sstevel@tonic-gate pkt->pkt_ha_private = (opaque_t)cmd;
21047c478bd9Sstevel@tonic-gate pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb;
21057c478bd9Sstevel@tonic-gate pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb;
21067c478bd9Sstevel@tonic-gate pkt->pkt_address = *ap;
21077c478bd9Sstevel@tonic-gate
21087c478bd9Sstevel@tonic-gate pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb;
21097c478bd9Sstevel@tonic-gate pkt->pkt_private = cmd->cmd_pkt_private;
21107c478bd9Sstevel@tonic-gate
21117c478bd9Sstevel@tonic-gate cmd->cmd_cdblen = cmdlen;
21127c478bd9Sstevel@tonic-gate cmd->cmd_scblen = statuslen;
21137c478bd9Sstevel@tonic-gate cmd->cmd_privlen = tgtlen;
21147c478bd9Sstevel@tonic-gate cmd->cmd_slot =
211519397407SSherry Moore (Tgt(cmd) * NLUNS_PER_TARGET) | Lun(cmd);
21167c478bd9Sstevel@tonic-gate failure = 0;
21177c478bd9Sstevel@tonic-gate }
21187c478bd9Sstevel@tonic-gate if (failure || (cmdlen > sizeof (cmd->cmd_cdb)) ||
21197c478bd9Sstevel@tonic-gate (tgtlen > PKT_PRIV_LEN) ||
21207c478bd9Sstevel@tonic-gate (statuslen > EXTCMDS_STATUS_SIZE)) {
21217c478bd9Sstevel@tonic-gate if (failure == 0) {
21227c478bd9Sstevel@tonic-gate /*
21237c478bd9Sstevel@tonic-gate * if extern alloc fails, all will be
21247c478bd9Sstevel@tonic-gate * deallocated, including cmd
21257c478bd9Sstevel@tonic-gate */
21267c478bd9Sstevel@tonic-gate failure = fas_pkt_alloc_extern(fas, cmd,
21277c478bd9Sstevel@tonic-gate cmdlen, tgtlen, statuslen, kf);
21287c478bd9Sstevel@tonic-gate }
21297c478bd9Sstevel@tonic-gate if (failure) {
21307c478bd9Sstevel@tonic-gate /*
21317c478bd9Sstevel@tonic-gate * nothing to deallocate so just return
21327c478bd9Sstevel@tonic-gate */
21337c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
213419397407SSherry Moore TR_FAS_SCSI_IMPL_PKTALLOC_END,
213519397407SSherry Moore "fas_scsi_impl_pktalloc_end");
21367c478bd9Sstevel@tonic-gate return (NULL);
21377c478bd9Sstevel@tonic-gate }
21387c478bd9Sstevel@tonic-gate }
21397c478bd9Sstevel@tonic-gate
21407c478bd9Sstevel@tonic-gate new_cmd = cmd;
21417c478bd9Sstevel@tonic-gate
21427c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_PKTALLOC_END,
214319397407SSherry Moore "fas_scsi_impl_pktalloc_end");
21447c478bd9Sstevel@tonic-gate } else {
21457c478bd9Sstevel@tonic-gate cmd = PKT2CMD(pkt);
21467c478bd9Sstevel@tonic-gate new_cmd = NULL;
21477c478bd9Sstevel@tonic-gate }
21487c478bd9Sstevel@tonic-gate
21497c478bd9Sstevel@tonic-gate /*
21507c478bd9Sstevel@tonic-gate * Second step of fas_scsi_init_pkt:
21517c478bd9Sstevel@tonic-gate * bind the buf to the handle
21527c478bd9Sstevel@tonic-gate */
21537c478bd9Sstevel@tonic-gate if (bp && bp->b_bcount != 0 &&
215419397407SSherry Moore (cmd->cmd_flags & CFLAG_DMAVALID) == 0) {
21557c478bd9Sstevel@tonic-gate
21567c478bd9Sstevel@tonic-gate int cmd_flags, dma_flags;
21577c478bd9Sstevel@tonic-gate uint_t dmacookie_count;
21587c478bd9Sstevel@tonic-gate
21597c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAGET_START,
21607c478bd9Sstevel@tonic-gate "fas_scsi_impl_dmaget_start");
21617c478bd9Sstevel@tonic-gate
21627c478bd9Sstevel@tonic-gate cmd_flags = cmd->cmd_flags;
21637c478bd9Sstevel@tonic-gate
21647c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) {
21657c478bd9Sstevel@tonic-gate cmd_flags &= ~CFLAG_DMASEND;
21667c478bd9Sstevel@tonic-gate dma_flags = DDI_DMA_READ | DDI_DMA_PARTIAL;
21677c478bd9Sstevel@tonic-gate } else {
21687c478bd9Sstevel@tonic-gate cmd_flags |= CFLAG_DMASEND;
21697c478bd9Sstevel@tonic-gate dma_flags = DDI_DMA_WRITE | DDI_DMA_PARTIAL;
21707c478bd9Sstevel@tonic-gate }
21717c478bd9Sstevel@tonic-gate if (flags & PKT_CONSISTENT) {
21727c478bd9Sstevel@tonic-gate cmd_flags |= CFLAG_CMDIOPB;
21737c478bd9Sstevel@tonic-gate dma_flags |= DDI_DMA_CONSISTENT;
21747c478bd9Sstevel@tonic-gate }
21757c478bd9Sstevel@tonic-gate
21767c478bd9Sstevel@tonic-gate /*
21777c478bd9Sstevel@tonic-gate * bind the handle to the buf
21787c478bd9Sstevel@tonic-gate */
21797c478bd9Sstevel@tonic-gate ASSERT(cmd->cmd_dmahandle != NULL);
21807c478bd9Sstevel@tonic-gate rval = ddi_dma_buf_bind_handle(cmd->cmd_dmahandle, bp,
218119397407SSherry Moore dma_flags, callback, arg, &cmd->cmd_dmacookie,
218219397407SSherry Moore &dmacookie_count);
21837c478bd9Sstevel@tonic-gate
21847c478bd9Sstevel@tonic-gate if (rval && rval != DDI_DMA_PARTIAL_MAP) {
21857c478bd9Sstevel@tonic-gate switch (rval) {
21867c478bd9Sstevel@tonic-gate case DDI_DMA_NORESOURCES:
21877c478bd9Sstevel@tonic-gate bioerror(bp, 0);
21887c478bd9Sstevel@tonic-gate break;
21897c478bd9Sstevel@tonic-gate case DDI_DMA_BADATTR:
21907c478bd9Sstevel@tonic-gate case DDI_DMA_NOMAPPING:
21917c478bd9Sstevel@tonic-gate bioerror(bp, EFAULT);
21927c478bd9Sstevel@tonic-gate break;
21937c478bd9Sstevel@tonic-gate case DDI_DMA_TOOBIG:
21947c478bd9Sstevel@tonic-gate default:
21957c478bd9Sstevel@tonic-gate bioerror(bp, EINVAL);
21967c478bd9Sstevel@tonic-gate break;
21977c478bd9Sstevel@tonic-gate }
21987c478bd9Sstevel@tonic-gate cmd->cmd_flags = cmd_flags & ~CFLAG_DMAVALID;
21997c478bd9Sstevel@tonic-gate if (new_cmd) {
22007c478bd9Sstevel@tonic-gate fas_scsi_destroy_pkt(ap, pkt);
22017c478bd9Sstevel@tonic-gate }
22027c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAGET_END,
220319397407SSherry Moore "fas_scsi_impl_dmaget_end");
22047c478bd9Sstevel@tonic-gate return ((struct scsi_pkt *)NULL);
22057c478bd9Sstevel@tonic-gate }
22067c478bd9Sstevel@tonic-gate ASSERT(dmacookie_count == 1);
22077c478bd9Sstevel@tonic-gate cmd->cmd_dmacount = bp->b_bcount;
22087c478bd9Sstevel@tonic-gate cmd->cmd_flags = cmd_flags | CFLAG_DMAVALID;
22097c478bd9Sstevel@tonic-gate
22107c478bd9Sstevel@tonic-gate ASSERT(cmd->cmd_dmahandle != NULL);
22117c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAGET_END,
22127c478bd9Sstevel@tonic-gate "fas_scsi_impl_dmaget_end");
22137c478bd9Sstevel@tonic-gate }
22147c478bd9Sstevel@tonic-gate
22157c478bd9Sstevel@tonic-gate return (pkt);
22167c478bd9Sstevel@tonic-gate }
22177c478bd9Sstevel@tonic-gate
22187c478bd9Sstevel@tonic-gate /*
22197c478bd9Sstevel@tonic-gate * unbind dma resources and deallocate the pkt
22207c478bd9Sstevel@tonic-gate */
22217c478bd9Sstevel@tonic-gate static void
fas_scsi_destroy_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)22227c478bd9Sstevel@tonic-gate fas_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
22237c478bd9Sstevel@tonic-gate {
22247c478bd9Sstevel@tonic-gate struct fas_cmd *sp = PKT2CMD(pkt);
22257c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
22267c478bd9Sstevel@tonic-gate
22277c478bd9Sstevel@tonic-gate /*
22287c478bd9Sstevel@tonic-gate * fas_scsi_impl_dmafree inline to speed things up
22297c478bd9Sstevel@tonic-gate */
22307c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAFREE_START,
22317c478bd9Sstevel@tonic-gate "fas_scsi_impl_dmafree_start");
22327c478bd9Sstevel@tonic-gate
22337c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_DMAVALID) {
22347c478bd9Sstevel@tonic-gate /*
22357c478bd9Sstevel@tonic-gate * Free the mapping.
22367c478bd9Sstevel@tonic-gate */
22377c478bd9Sstevel@tonic-gate (void) ddi_dma_unbind_handle(sp->cmd_dmahandle);
22387c478bd9Sstevel@tonic-gate sp->cmd_flags ^= CFLAG_DMAVALID;
22397c478bd9Sstevel@tonic-gate }
22407c478bd9Sstevel@tonic-gate
22417c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_DMAFREE_END,
22427c478bd9Sstevel@tonic-gate "fas_scsi_impl_dmafree_end");
22437c478bd9Sstevel@tonic-gate
22447c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_PKTFREE_START,
22457c478bd9Sstevel@tonic-gate "fas_scsi_impl_pktfree_start");
22467c478bd9Sstevel@tonic-gate
22477c478bd9Sstevel@tonic-gate if ((sp->cmd_flags &
22487c478bd9Sstevel@tonic-gate (CFLAG_FREE | CFLAG_CDBEXTERN | CFLAG_PRIVEXTERN |
22497c478bd9Sstevel@tonic-gate CFLAG_SCBEXTERN)) == 0) {
22507c478bd9Sstevel@tonic-gate sp->cmd_flags = CFLAG_FREE;
22517c478bd9Sstevel@tonic-gate kmem_cache_free(fas->f_kmem_cache, (void *)sp);
22527c478bd9Sstevel@tonic-gate } else {
22537c478bd9Sstevel@tonic-gate fas_pkt_destroy_extern(fas, sp);
22547c478bd9Sstevel@tonic-gate }
22557c478bd9Sstevel@tonic-gate
22567c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_SCSI_IMPL_PKTFREE_END,
22577c478bd9Sstevel@tonic-gate "fas_scsi_impl_pktfree_end");
22587c478bd9Sstevel@tonic-gate }
22597c478bd9Sstevel@tonic-gate
22607c478bd9Sstevel@tonic-gate /*
22617c478bd9Sstevel@tonic-gate * allocate and deallocate external pkt space (ie. not part of fas_cmd) for
22627c478bd9Sstevel@tonic-gate * non-standard length cdb, pkt_private, status areas
22637c478bd9Sstevel@tonic-gate * if allocation fails, then deallocate all external space and the pkt
22647c478bd9Sstevel@tonic-gate */
22657c478bd9Sstevel@tonic-gate /* ARGSUSED */
22667c478bd9Sstevel@tonic-gate static int
fas_pkt_alloc_extern(struct fas * fas,struct fas_cmd * sp,int cmdlen,int tgtlen,int statuslen,int kf)22677c478bd9Sstevel@tonic-gate fas_pkt_alloc_extern(struct fas *fas, struct fas_cmd *sp,
22687c478bd9Sstevel@tonic-gate int cmdlen, int tgtlen, int statuslen, int kf)
22697c478bd9Sstevel@tonic-gate {
22707c478bd9Sstevel@tonic-gate caddr_t cdbp, scbp, tgt;
22717c478bd9Sstevel@tonic-gate int failure = 0;
22727c478bd9Sstevel@tonic-gate
22737c478bd9Sstevel@tonic-gate tgt = cdbp = scbp = NULL;
22747c478bd9Sstevel@tonic-gate if (cmdlen > sizeof (sp->cmd_cdb)) {
22757c478bd9Sstevel@tonic-gate if ((cdbp = kmem_zalloc((size_t)cmdlen, kf)) == NULL) {
22767c478bd9Sstevel@tonic-gate failure++;
22777c478bd9Sstevel@tonic-gate } else {
22787c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_cdbp = (opaque_t)cdbp;
22797c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_CDBEXTERN;
22807c478bd9Sstevel@tonic-gate }
22817c478bd9Sstevel@tonic-gate }
22827c478bd9Sstevel@tonic-gate if (tgtlen > PKT_PRIV_LEN) {
22837c478bd9Sstevel@tonic-gate if ((tgt = kmem_zalloc(tgtlen, kf)) == NULL) {
22847c478bd9Sstevel@tonic-gate failure++;
22857c478bd9Sstevel@tonic-gate } else {
22867c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_PRIVEXTERN;
22877c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_private = tgt;
22887c478bd9Sstevel@tonic-gate }
22897c478bd9Sstevel@tonic-gate }
22907c478bd9Sstevel@tonic-gate if (statuslen > EXTCMDS_STATUS_SIZE) {
22917c478bd9Sstevel@tonic-gate if ((scbp = kmem_zalloc((size_t)statuslen, kf)) == NULL) {
22927c478bd9Sstevel@tonic-gate failure++;
22937c478bd9Sstevel@tonic-gate } else {
22947c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_SCBEXTERN;
22957c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_scbp = (opaque_t)scbp;
22967c478bd9Sstevel@tonic-gate }
22977c478bd9Sstevel@tonic-gate }
22987c478bd9Sstevel@tonic-gate if (failure) {
22997c478bd9Sstevel@tonic-gate fas_pkt_destroy_extern(fas, sp);
23007c478bd9Sstevel@tonic-gate }
23017c478bd9Sstevel@tonic-gate return (failure);
23027c478bd9Sstevel@tonic-gate }
23037c478bd9Sstevel@tonic-gate
23047c478bd9Sstevel@tonic-gate /*
23057c478bd9Sstevel@tonic-gate * deallocate external pkt space and deallocate the pkt
23067c478bd9Sstevel@tonic-gate */
23077c478bd9Sstevel@tonic-gate static void
fas_pkt_destroy_extern(struct fas * fas,struct fas_cmd * sp)23087c478bd9Sstevel@tonic-gate fas_pkt_destroy_extern(struct fas *fas, struct fas_cmd *sp)
23097c478bd9Sstevel@tonic-gate {
23107c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_FREE) {
23117c478bd9Sstevel@tonic-gate panic("fas_pkt_destroy_extern: freeing free packet");
23127c478bd9Sstevel@tonic-gate _NOTE(NOT_REACHED)
23137c478bd9Sstevel@tonic-gate /* NOTREACHED */
23147c478bd9Sstevel@tonic-gate }
23157c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CDBEXTERN) {
23167c478bd9Sstevel@tonic-gate kmem_free((caddr_t)sp->cmd_pkt->pkt_cdbp,
23177c478bd9Sstevel@tonic-gate (size_t)sp->cmd_cdblen);
23187c478bd9Sstevel@tonic-gate }
23197c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_SCBEXTERN) {
23207c478bd9Sstevel@tonic-gate kmem_free((caddr_t)sp->cmd_pkt->pkt_scbp,
23217c478bd9Sstevel@tonic-gate (size_t)sp->cmd_scblen);
23227c478bd9Sstevel@tonic-gate }
23237c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_PRIVEXTERN) {
23247c478bd9Sstevel@tonic-gate kmem_free((caddr_t)sp->cmd_pkt->pkt_private,
23257c478bd9Sstevel@tonic-gate (size_t)sp->cmd_privlen);
23267c478bd9Sstevel@tonic-gate }
23277c478bd9Sstevel@tonic-gate sp->cmd_flags = CFLAG_FREE;
23287c478bd9Sstevel@tonic-gate kmem_cache_free(fas->f_kmem_cache, (void *)sp);
23297c478bd9Sstevel@tonic-gate }
23307c478bd9Sstevel@tonic-gate
23317c478bd9Sstevel@tonic-gate /*
23327c478bd9Sstevel@tonic-gate * kmem cache constructor and destructor:
23337c478bd9Sstevel@tonic-gate * When constructing, we bzero the cmd and allocate the dma handle
23347c478bd9Sstevel@tonic-gate * When destructing, just free the dma handle
23357c478bd9Sstevel@tonic-gate */
23367c478bd9Sstevel@tonic-gate static int
fas_kmem_cache_constructor(void * buf,void * cdrarg,int kmflags)23377c478bd9Sstevel@tonic-gate fas_kmem_cache_constructor(void *buf, void *cdrarg, int kmflags)
23387c478bd9Sstevel@tonic-gate {
23397c478bd9Sstevel@tonic-gate struct fas_cmd *cmd = buf;
23407c478bd9Sstevel@tonic-gate struct fas *fas = cdrarg;
23417c478bd9Sstevel@tonic-gate int (*callback)(caddr_t) = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP:
234219397407SSherry Moore DDI_DMA_DONTWAIT;
23437c478bd9Sstevel@tonic-gate
23447c478bd9Sstevel@tonic-gate bzero(buf, EXTCMD_SIZE);
23457c478bd9Sstevel@tonic-gate
23467c478bd9Sstevel@tonic-gate /*
23477c478bd9Sstevel@tonic-gate * allocate a dma handle
23487c478bd9Sstevel@tonic-gate */
23497c478bd9Sstevel@tonic-gate if ((ddi_dma_alloc_handle(fas->f_dev, fas->f_dma_attr, callback,
23507c478bd9Sstevel@tonic-gate NULL, &cmd->cmd_dmahandle)) != DDI_SUCCESS) {
23517c478bd9Sstevel@tonic-gate return (-1);
23527c478bd9Sstevel@tonic-gate }
23537c478bd9Sstevel@tonic-gate return (0);
23547c478bd9Sstevel@tonic-gate }
23557c478bd9Sstevel@tonic-gate
23567c478bd9Sstevel@tonic-gate /*ARGSUSED*/
23577c478bd9Sstevel@tonic-gate static void
fas_kmem_cache_destructor(void * buf,void * cdrarg)23587c478bd9Sstevel@tonic-gate fas_kmem_cache_destructor(void *buf, void *cdrarg)
23597c478bd9Sstevel@tonic-gate {
23607c478bd9Sstevel@tonic-gate struct fas_cmd *cmd = buf;
23617c478bd9Sstevel@tonic-gate if (cmd->cmd_dmahandle) {
23627c478bd9Sstevel@tonic-gate ddi_dma_free_handle(&cmd->cmd_dmahandle);
23637c478bd9Sstevel@tonic-gate }
23647c478bd9Sstevel@tonic-gate }
23657c478bd9Sstevel@tonic-gate
23667c478bd9Sstevel@tonic-gate /*
23677c478bd9Sstevel@tonic-gate * fas_scsi_start - Accept commands for transport
23687c478bd9Sstevel@tonic-gate */
23697c478bd9Sstevel@tonic-gate static int
fas_scsi_start(struct scsi_address * ap,struct scsi_pkt * pkt)23707c478bd9Sstevel@tonic-gate fas_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
23717c478bd9Sstevel@tonic-gate {
23727c478bd9Sstevel@tonic-gate struct fas_cmd *sp = PKT2CMD(pkt);
23737c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
23747c478bd9Sstevel@tonic-gate int rval;
23757c478bd9Sstevel@tonic-gate int intr = 0;
23767c478bd9Sstevel@tonic-gate
23777c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_START_START, "fas_scsi_start_start");
23787c478bd9Sstevel@tonic-gate
23797c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
23807c478bd9Sstevel@tonic-gate if (fas_transport_busy > 0) {
23817c478bd9Sstevel@tonic-gate fas_transport_busy--;
23827c478bd9Sstevel@tonic-gate return (TRAN_BUSY);
23837c478bd9Sstevel@tonic-gate }
23847c478bd9Sstevel@tonic-gate if ((fas_transport_busy_rqs > 0) &&
23857c478bd9Sstevel@tonic-gate (*(sp->cmd_pkt->pkt_cdbp) == SCMD_REQUEST_SENSE)) {
23867c478bd9Sstevel@tonic-gate fas_transport_busy_rqs--;
23877c478bd9Sstevel@tonic-gate return (TRAN_BUSY);
23887c478bd9Sstevel@tonic-gate }
23897c478bd9Sstevel@tonic-gate if (fas_transport_reject > 0) {
23907c478bd9Sstevel@tonic-gate fas_transport_reject--;
23917c478bd9Sstevel@tonic-gate return (TRAN_BADPKT);
23927c478bd9Sstevel@tonic-gate }
23937c478bd9Sstevel@tonic-gate #endif
23947c478bd9Sstevel@tonic-gate /*
23957c478bd9Sstevel@tonic-gate * prepare packet before taking the mutex
23967c478bd9Sstevel@tonic-gate */
23977c478bd9Sstevel@tonic-gate rval = fas_prepare_pkt(fas, sp);
23987c478bd9Sstevel@tonic-gate if (rval != TRAN_ACCEPT) {
23997c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_START_PREPARE_PKT_END,
24007c478bd9Sstevel@tonic-gate "fas_scsi_start_end (prepare_pkt)");
24017c478bd9Sstevel@tonic-gate return (rval);
24027c478bd9Sstevel@tonic-gate }
24037c478bd9Sstevel@tonic-gate
24047c478bd9Sstevel@tonic-gate /*
24057c478bd9Sstevel@tonic-gate * fas mutex can be held for a long time; therefore, if the mutex is
24067c478bd9Sstevel@tonic-gate * held, we queue the packet in a waitQ; we now should check
24077c478bd9Sstevel@tonic-gate * the waitQ on every mutex_exit(FAS_MUTEX(fas)) but we really only
24087c478bd9Sstevel@tonic-gate * need to do this when the bus is free
24097c478bd9Sstevel@tonic-gate * don't put NOINTR cmds including proxy cmds in waitQ! These
24107c478bd9Sstevel@tonic-gate * cmds are handled by fas_runpoll()
24117c478bd9Sstevel@tonic-gate * if the waitQ is non-empty, queue the pkt anyway to preserve
24127c478bd9Sstevel@tonic-gate * order
24137c478bd9Sstevel@tonic-gate * the goal is to queue in waitQ as much as possible so at
24147c478bd9Sstevel@tonic-gate * interrupt time, we can move the packets to readyQ or start
24157c478bd9Sstevel@tonic-gate * a packet immediately. It helps to do this at interrupt
24167c478bd9Sstevel@tonic-gate * time because we can then field more interrupts
24177c478bd9Sstevel@tonic-gate */
24187c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_NOINTR) == 0) {
24197c478bd9Sstevel@tonic-gate
24207c478bd9Sstevel@tonic-gate /*
24217c478bd9Sstevel@tonic-gate * if the bus is not free, we will get an interrupt shortly
24227c478bd9Sstevel@tonic-gate * so we don't want to take the fas mutex but queue up
24237c478bd9Sstevel@tonic-gate * the packet in the waitQ
24247c478bd9Sstevel@tonic-gate * also, if the waitQ is non-empty or there is an interrupt
24257c478bd9Sstevel@tonic-gate * pending then queue up the packet in the waitQ and let the
24267c478bd9Sstevel@tonic-gate * interrupt handler empty the waitQ
24277c478bd9Sstevel@tonic-gate */
24287c478bd9Sstevel@tonic-gate mutex_enter(&fas->f_waitQ_mutex);
24297c478bd9Sstevel@tonic-gate
24307c478bd9Sstevel@tonic-gate if ((fas->f_state != STATE_FREE) ||
24317c478bd9Sstevel@tonic-gate fas->f_waitf || (intr = INTPENDING(fas))) {
24327c478bd9Sstevel@tonic-gate goto queue_in_waitQ;
24337c478bd9Sstevel@tonic-gate }
24347c478bd9Sstevel@tonic-gate
24357c478bd9Sstevel@tonic-gate /*
24367c478bd9Sstevel@tonic-gate * we didn't queue up in the waitQ, so now try to accept
24377c478bd9Sstevel@tonic-gate * the packet. if we fail to get the fas mutex, go back to
24387c478bd9Sstevel@tonic-gate * the waitQ again
24397c478bd9Sstevel@tonic-gate * do not release the waitQ mutex yet because that
24407c478bd9Sstevel@tonic-gate * leaves a window where the interrupt handler has
24417c478bd9Sstevel@tonic-gate * emptied the waitQ but not released the fas mutex yet
24427c478bd9Sstevel@tonic-gate *
24437c478bd9Sstevel@tonic-gate * the interrupt handler gets the locks in opposite order
24447c478bd9Sstevel@tonic-gate * but because we do a tryenter, there is no deadlock
24457c478bd9Sstevel@tonic-gate *
24467c478bd9Sstevel@tonic-gate * if another thread has the fas mutex then either this
24477c478bd9Sstevel@tonic-gate * thread or the other may find the bus free and
24487c478bd9Sstevel@tonic-gate * empty the waitQ
24497c478bd9Sstevel@tonic-gate */
24507c478bd9Sstevel@tonic-gate if (mutex_tryenter(FAS_MUTEX(fas))) {
24517c478bd9Sstevel@tonic-gate mutex_exit(&fas->f_waitQ_mutex);
24527c478bd9Sstevel@tonic-gate rval = fas_accept_pkt(fas, sp, TRAN_BUSY_OK);
24537c478bd9Sstevel@tonic-gate } else {
24547c478bd9Sstevel@tonic-gate /*
24557c478bd9Sstevel@tonic-gate * we didn't get the fas mutex so
24567c478bd9Sstevel@tonic-gate * the packet has to go in the waitQ now
24577c478bd9Sstevel@tonic-gate */
24587c478bd9Sstevel@tonic-gate goto queue_in_waitQ;
24597c478bd9Sstevel@tonic-gate }
24607c478bd9Sstevel@tonic-gate } else {
24617c478bd9Sstevel@tonic-gate /*
24627c478bd9Sstevel@tonic-gate * for polled cmds, we have to take the mutex and
24637c478bd9Sstevel@tonic-gate * start the packet using fas_runpoll()
24647c478bd9Sstevel@tonic-gate */
24657c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
24667c478bd9Sstevel@tonic-gate rval = fas_accept_pkt(fas, sp, TRAN_BUSY_OK);
24677c478bd9Sstevel@tonic-gate }
24687c478bd9Sstevel@tonic-gate
24697c478bd9Sstevel@tonic-gate /*
24707c478bd9Sstevel@tonic-gate * if the bus is free then empty waitQ and release the mutex
24717c478bd9Sstevel@tonic-gate * (this should be unlikely that the bus is still free after
24727c478bd9Sstevel@tonic-gate * accepting the packet. it may be the relatively unusual case
24737c478bd9Sstevel@tonic-gate * that we are throttling)
24747c478bd9Sstevel@tonic-gate */
24757c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE) {
24767c478bd9Sstevel@tonic-gate FAS_CHECK_WAITQ_AND_FAS_MUTEX_EXIT(fas);
24777c478bd9Sstevel@tonic-gate } else {
24787c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
24797c478bd9Sstevel@tonic-gate }
24807c478bd9Sstevel@tonic-gate
24817c478bd9Sstevel@tonic-gate done:
24827c478bd9Sstevel@tonic-gate TRACE_1(TR_FAC_SCSI_FAS, TR_FAS_START_END,
248319397407SSherry Moore "fas_scsi_start_end: fas 0x%p", fas);
24847c478bd9Sstevel@tonic-gate return (rval);
24857c478bd9Sstevel@tonic-gate
24867c478bd9Sstevel@tonic-gate queue_in_waitQ:
24877c478bd9Sstevel@tonic-gate if (fas->f_waitf == NULL) {
24887c478bd9Sstevel@tonic-gate fas->f_waitb = fas->f_waitf = sp;
24897c478bd9Sstevel@tonic-gate sp->cmd_forw = NULL;
24907c478bd9Sstevel@tonic-gate } else {
24917c478bd9Sstevel@tonic-gate struct fas_cmd *dp = fas->f_waitb;
24927c478bd9Sstevel@tonic-gate dp->cmd_forw = fas->f_waitb = sp;
24937c478bd9Sstevel@tonic-gate sp->cmd_forw = NULL;
24947c478bd9Sstevel@tonic-gate }
24957c478bd9Sstevel@tonic-gate
24967c478bd9Sstevel@tonic-gate /*
24977c478bd9Sstevel@tonic-gate * check again the fas mutex
24987c478bd9Sstevel@tonic-gate * if there was an interrupt then the interrupt
24997c478bd9Sstevel@tonic-gate * handler will eventually empty the waitQ
25007c478bd9Sstevel@tonic-gate */
25017c478bd9Sstevel@tonic-gate if ((intr == 0) && (fas->f_state == STATE_FREE) &&
25027c478bd9Sstevel@tonic-gate mutex_tryenter(FAS_MUTEX(fas))) {
25037c478bd9Sstevel@tonic-gate /*
25047c478bd9Sstevel@tonic-gate * double check if the bus is still free
25057c478bd9Sstevel@tonic-gate * (this actually reduced mutex contention a bit)
25067c478bd9Sstevel@tonic-gate */
25077c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE) {
25087c478bd9Sstevel@tonic-gate fas_empty_waitQ(fas);
25097c478bd9Sstevel@tonic-gate }
25107c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
25117c478bd9Sstevel@tonic-gate }
25127c478bd9Sstevel@tonic-gate mutex_exit(&fas->f_waitQ_mutex);
25137c478bd9Sstevel@tonic-gate
25147c478bd9Sstevel@tonic-gate TRACE_1(TR_FAC_SCSI_FAS, TR_FAS_START_END,
251519397407SSherry Moore "fas_scsi_start_end: fas 0x%p", fas);
25167c478bd9Sstevel@tonic-gate return (rval);
25177c478bd9Sstevel@tonic-gate }
25187c478bd9Sstevel@tonic-gate
25197c478bd9Sstevel@tonic-gate /*
25207c478bd9Sstevel@tonic-gate * prepare the pkt:
25217c478bd9Sstevel@tonic-gate * the pkt may have been resubmitted or just reused so
25227c478bd9Sstevel@tonic-gate * initialize some fields, reset the dma window, and do some checks
25237c478bd9Sstevel@tonic-gate */
25247c478bd9Sstevel@tonic-gate static int
fas_prepare_pkt(struct fas * fas,struct fas_cmd * sp)25257c478bd9Sstevel@tonic-gate fas_prepare_pkt(struct fas *fas, struct fas_cmd *sp)
25267c478bd9Sstevel@tonic-gate {
25277c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = CMD2PKT(sp);
25287c478bd9Sstevel@tonic-gate
25297c478bd9Sstevel@tonic-gate /*
25307c478bd9Sstevel@tonic-gate * Reinitialize some fields that need it; the packet may
25317c478bd9Sstevel@tonic-gate * have been resubmitted
25327c478bd9Sstevel@tonic-gate */
25337c478bd9Sstevel@tonic-gate pkt->pkt_reason = CMD_CMPLT;
25347c478bd9Sstevel@tonic-gate pkt->pkt_state = 0;
25357c478bd9Sstevel@tonic-gate pkt->pkt_statistics = 0;
25367c478bd9Sstevel@tonic-gate pkt->pkt_resid = 0;
25377c478bd9Sstevel@tonic-gate sp->cmd_age = 0;
25387c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags = pkt->pkt_flags;
25397c478bd9Sstevel@tonic-gate
25407c478bd9Sstevel@tonic-gate /*
25417c478bd9Sstevel@tonic-gate * Copy the cdb pointer to the pkt wrapper area as we
25427c478bd9Sstevel@tonic-gate * might modify this pointer. Zero status byte
25437c478bd9Sstevel@tonic-gate */
25447c478bd9Sstevel@tonic-gate sp->cmd_cdbp = pkt->pkt_cdbp;
25457c478bd9Sstevel@tonic-gate *(pkt->pkt_scbp) = 0;
25467c478bd9Sstevel@tonic-gate
25477c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_DMAVALID) {
25487c478bd9Sstevel@tonic-gate pkt->pkt_resid = sp->cmd_dmacount;
25497c478bd9Sstevel@tonic-gate
25507c478bd9Sstevel@tonic-gate /*
25517c478bd9Sstevel@tonic-gate * if the pkt was resubmitted then the
25527c478bd9Sstevel@tonic-gate * windows may be at the wrong number
25537c478bd9Sstevel@tonic-gate */
25547c478bd9Sstevel@tonic-gate if (sp->cmd_cur_win) {
25557c478bd9Sstevel@tonic-gate sp->cmd_cur_win = 0;
25567c478bd9Sstevel@tonic-gate if (fas_set_new_window(fas, sp)) {
25577c478bd9Sstevel@tonic-gate IPRINTF("cannot reset window\n");
25587c478bd9Sstevel@tonic-gate return (TRAN_BADPKT);
25597c478bd9Sstevel@tonic-gate }
25607c478bd9Sstevel@tonic-gate }
25617c478bd9Sstevel@tonic-gate sp->cmd_saved_cur_addr =
25627c478bd9Sstevel@tonic-gate sp->cmd_cur_addr = sp->cmd_dmacookie.dmac_address;
25637c478bd9Sstevel@tonic-gate
25647c478bd9Sstevel@tonic-gate /*
25657c478bd9Sstevel@tonic-gate * the common case is just one window, we worry
25667c478bd9Sstevel@tonic-gate * about multiple windows when we run out of the
25677c478bd9Sstevel@tonic-gate * current window
25687c478bd9Sstevel@tonic-gate */
25697c478bd9Sstevel@tonic-gate sp->cmd_nwin = sp->cmd_saved_win = 0;
25707c478bd9Sstevel@tonic-gate sp->cmd_data_count = sp->cmd_saved_data_count = 0;
25717c478bd9Sstevel@tonic-gate
25727c478bd9Sstevel@tonic-gate /*
25737c478bd9Sstevel@tonic-gate * consistent packets need to be sync'ed first
25747c478bd9Sstevel@tonic-gate * (only for data going out)
25757c478bd9Sstevel@tonic-gate */
25767c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & (CFLAG_CMDIOPB | CFLAG_DMASEND)) ==
257719397407SSherry Moore (CFLAG_CMDIOPB | CFLAG_DMASEND)) {
25787c478bd9Sstevel@tonic-gate (void) ddi_dma_sync(sp->cmd_dmahandle, 0, (uint_t)0,
25797c478bd9Sstevel@tonic-gate DDI_DMA_SYNC_FORDEV);
25807c478bd9Sstevel@tonic-gate }
25817c478bd9Sstevel@tonic-gate }
25827c478bd9Sstevel@tonic-gate
25837c478bd9Sstevel@tonic-gate sp->cmd_actual_cdblen = sp->cmd_cdblen;
25847c478bd9Sstevel@tonic-gate
25857c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
25867c478bd9Sstevel@tonic-gate #ifndef __lock_lint
25877c478bd9Sstevel@tonic-gate if (fas_test_untagged > 0) {
25887c478bd9Sstevel@tonic-gate if (TAGGED(Tgt(sp))) {
25897c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
25907c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags &= ~FLAG_TAGMASK;
25917c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags &= ~FLAG_NODISCON;
25927c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags |= 0x80000000;
25937c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE,
25947c478bd9Sstevel@tonic-gate "starting untagged cmd, target=%d,"
25957c478bd9Sstevel@tonic-gate " tcmds=%d, sp=0x%p, throttle=%d\n",
25967c478bd9Sstevel@tonic-gate Tgt(sp), fas->f_tcmds[slot], (void *)sp,
25977c478bd9Sstevel@tonic-gate fas->f_throttle[slot]);
25987c478bd9Sstevel@tonic-gate fas_test_untagged = -10;
25997c478bd9Sstevel@tonic-gate }
26007c478bd9Sstevel@tonic-gate }
26017c478bd9Sstevel@tonic-gate #endif
26027c478bd9Sstevel@tonic-gate #endif
26037c478bd9Sstevel@tonic-gate
26047c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
26057c478bd9Sstevel@tonic-gate if (NOTAG(Tgt(sp)) && (pkt->pkt_flags & FLAG_TAGMASK)) {
26067c478bd9Sstevel@tonic-gate IPRINTF2("tagged packet for non-tagged target %d.%d\n",
26077c478bd9Sstevel@tonic-gate Tgt(sp), Lun(sp));
26087c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_PREPARE_PKT_TRAN_BADPKT_END,
26097c478bd9Sstevel@tonic-gate "fas_prepare_pkt_end (tran_badpkt)");
26107c478bd9Sstevel@tonic-gate return (TRAN_BADPKT);
26117c478bd9Sstevel@tonic-gate }
26127c478bd9Sstevel@tonic-gate
26137c478bd9Sstevel@tonic-gate /*
26147c478bd9Sstevel@tonic-gate * the SCSA spec states that it is an error to have no
26157c478bd9Sstevel@tonic-gate * completion function when FLAG_NOINTR is not set
26167c478bd9Sstevel@tonic-gate */
26177c478bd9Sstevel@tonic-gate if ((pkt->pkt_comp == NULL) &&
26187c478bd9Sstevel@tonic-gate ((pkt->pkt_flags & FLAG_NOINTR) == 0)) {
26197c478bd9Sstevel@tonic-gate IPRINTF("intr packet with pkt_comp == 0\n");
26207c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_PREPARE_PKT_TRAN_BADPKT_END,
26217c478bd9Sstevel@tonic-gate "fas_prepare_pkt_end (tran_badpkt)");
26227c478bd9Sstevel@tonic-gate return (TRAN_BADPKT);
26237c478bd9Sstevel@tonic-gate }
26247c478bd9Sstevel@tonic-gate #endif /* FASDEBUG */
26257c478bd9Sstevel@tonic-gate
26267c478bd9Sstevel@tonic-gate if ((fas->f_target_scsi_options[Tgt(sp)] & SCSI_OPTIONS_DR) == 0) {
26277c478bd9Sstevel@tonic-gate /*
26287c478bd9Sstevel@tonic-gate * no need to reset tag bits since tag queueing will
26297c478bd9Sstevel@tonic-gate * not be enabled if disconnects are disabled
26307c478bd9Sstevel@tonic-gate */
26317c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags |= FLAG_NODISCON;
26327c478bd9Sstevel@tonic-gate }
26337c478bd9Sstevel@tonic-gate
26347c478bd9Sstevel@tonic-gate sp->cmd_flags = (sp->cmd_flags & ~CFLAG_TRANFLAG) |
263519397407SSherry Moore CFLAG_PREPARED | CFLAG_IN_TRANSPORT;
26367c478bd9Sstevel@tonic-gate
26377c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_PREPARE_PKT_TRAN_ACCEPT_END,
26387c478bd9Sstevel@tonic-gate "fas_prepare_pkt_end (tran_accept)");
26397c478bd9Sstevel@tonic-gate return (TRAN_ACCEPT);
26407c478bd9Sstevel@tonic-gate }
26417c478bd9Sstevel@tonic-gate
26427c478bd9Sstevel@tonic-gate /*
26437c478bd9Sstevel@tonic-gate * emptying the waitQ just before releasing FAS_MUTEX is a bit
26447c478bd9Sstevel@tonic-gate * tricky; if we release the waitQ mutex and then the FAS_MUTEX,
26457c478bd9Sstevel@tonic-gate * another thread could queue a cmd in the waitQ, just before
26467c478bd9Sstevel@tonic-gate * the FAS_MUTEX is released. This cmd is then stuck in the waitQ unless
26477c478bd9Sstevel@tonic-gate * another cmd comes in or fas_intr() or fas_watch() checks the waitQ.
26487c478bd9Sstevel@tonic-gate * Therefore, by releasing the FAS_MUTEX before releasing the waitQ mutex,
26497c478bd9Sstevel@tonic-gate * we prevent fas_scsi_start() filling the waitQ
26507c478bd9Sstevel@tonic-gate *
26517c478bd9Sstevel@tonic-gate * By setting NO_TRAN_BUSY, we force fas_accept_pkt() to queue up
26527c478bd9Sstevel@tonic-gate * the waitQ pkts in the readyQ.
26537c478bd9Sstevel@tonic-gate * If a QFull condition occurs, the target driver may set its throttle
26547c478bd9Sstevel@tonic-gate * too high because of the requests queued up in the readyQ but this
26557c478bd9Sstevel@tonic-gate * is not a big problem. The throttle should be periodically reset anyway.
26567c478bd9Sstevel@tonic-gate */
26577c478bd9Sstevel@tonic-gate static void
fas_empty_waitQ(struct fas * fas)26587c478bd9Sstevel@tonic-gate fas_empty_waitQ(struct fas *fas)
26597c478bd9Sstevel@tonic-gate {
26607c478bd9Sstevel@tonic-gate struct fas_cmd *sp;
26617c478bd9Sstevel@tonic-gate int rval;
26627c478bd9Sstevel@tonic-gate struct fas_cmd *waitf, *waitb;
26637c478bd9Sstevel@tonic-gate
26647c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&fas->f_waitQ_mutex));
26657c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_EMPTY_WAITQ_START,
26667c478bd9Sstevel@tonic-gate "fas_empty_waitQ_start");
26677c478bd9Sstevel@tonic-gate
26687c478bd9Sstevel@tonic-gate while (fas->f_waitf) {
26697c478bd9Sstevel@tonic-gate
26707c478bd9Sstevel@tonic-gate /* copy waitQ, zero the waitQ and release the mutex */
26717c478bd9Sstevel@tonic-gate waitf = fas->f_waitf;
26727c478bd9Sstevel@tonic-gate waitb = fas->f_waitb;
26737c478bd9Sstevel@tonic-gate fas->f_waitf = fas->f_waitb = NULL;
26747c478bd9Sstevel@tonic-gate mutex_exit(&fas->f_waitQ_mutex);
26757c478bd9Sstevel@tonic-gate
26767c478bd9Sstevel@tonic-gate do {
26777c478bd9Sstevel@tonic-gate sp = waitf;
26787c478bd9Sstevel@tonic-gate waitf = sp->cmd_forw;
26797c478bd9Sstevel@tonic-gate if (waitb == sp) {
26807c478bd9Sstevel@tonic-gate waitb = NULL;
26817c478bd9Sstevel@tonic-gate }
26827c478bd9Sstevel@tonic-gate
26837c478bd9Sstevel@tonic-gate rval = fas_accept_pkt(fas, sp, NO_TRAN_BUSY);
26847c478bd9Sstevel@tonic-gate
26857c478bd9Sstevel@tonic-gate /*
26867c478bd9Sstevel@tonic-gate * If the packet was rejected for other reasons then
26877c478bd9Sstevel@tonic-gate * complete it here
26887c478bd9Sstevel@tonic-gate */
26897c478bd9Sstevel@tonic-gate if (rval != TRAN_ACCEPT) {
26907c478bd9Sstevel@tonic-gate ASSERT(rval != TRAN_BUSY);
26917c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
26927c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_comp) {
26937c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_FINISHED;
26947c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
26957c478bd9Sstevel@tonic-gate }
26967c478bd9Sstevel@tonic-gate }
26977c478bd9Sstevel@tonic-gate
26987c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
26997c478bd9Sstevel@tonic-gate /*
27007c478bd9Sstevel@tonic-gate * stop processing the waitQ and put back
27017c478bd9Sstevel@tonic-gate * the remaining packets on the waitQ
27027c478bd9Sstevel@tonic-gate */
27037c478bd9Sstevel@tonic-gate mutex_enter(&fas->f_waitQ_mutex);
27047c478bd9Sstevel@tonic-gate if (waitf) {
27057c478bd9Sstevel@tonic-gate ASSERT(waitb != NULL);
27067c478bd9Sstevel@tonic-gate waitb->cmd_forw = fas->f_waitf;
27077c478bd9Sstevel@tonic-gate fas->f_waitf = waitf;
27087c478bd9Sstevel@tonic-gate if (fas->f_waitb == NULL) {
27097c478bd9Sstevel@tonic-gate fas->f_waitb = waitb;
27107c478bd9Sstevel@tonic-gate }
27117c478bd9Sstevel@tonic-gate }
27127c478bd9Sstevel@tonic-gate return;
27137c478bd9Sstevel@tonic-gate }
27147c478bd9Sstevel@tonic-gate } while (waitf);
27157c478bd9Sstevel@tonic-gate
27167c478bd9Sstevel@tonic-gate mutex_enter(&fas->f_waitQ_mutex);
27177c478bd9Sstevel@tonic-gate }
27187c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_EMPTY_WAITQ_END,
27197c478bd9Sstevel@tonic-gate "fas_empty_waitQ_end");
27207c478bd9Sstevel@tonic-gate }
27217c478bd9Sstevel@tonic-gate
27227c478bd9Sstevel@tonic-gate static void
fas_move_waitQ_to_readyQ(struct fas * fas)27237c478bd9Sstevel@tonic-gate fas_move_waitQ_to_readyQ(struct fas *fas)
27247c478bd9Sstevel@tonic-gate {
27257c478bd9Sstevel@tonic-gate /*
27267c478bd9Sstevel@tonic-gate * this may actually start cmds but it is most likely
27277c478bd9Sstevel@tonic-gate * that if waitQ is not empty that the bus is not free
27287c478bd9Sstevel@tonic-gate */
27297c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
27307c478bd9Sstevel@tonic-gate mutex_enter(&fas->f_waitQ_mutex);
27317c478bd9Sstevel@tonic-gate fas_empty_waitQ(fas);
27327c478bd9Sstevel@tonic-gate mutex_exit(&fas->f_waitQ_mutex);
27337c478bd9Sstevel@tonic-gate }
27347c478bd9Sstevel@tonic-gate
27357c478bd9Sstevel@tonic-gate
27367c478bd9Sstevel@tonic-gate /*
27377c478bd9Sstevel@tonic-gate * function wrapper for two frequently used macros. for the non-critical
27387c478bd9Sstevel@tonic-gate * path we use the function
27397c478bd9Sstevel@tonic-gate */
27407c478bd9Sstevel@tonic-gate static void
fas_check_waitQ_and_mutex_exit(struct fas * fas)27417c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(struct fas *fas)
27427c478bd9Sstevel@tonic-gate {
27437c478bd9Sstevel@tonic-gate _NOTE(LOCK_RELEASED_AS_SIDE_EFFECT(fas->f_mutex))
27447c478bd9Sstevel@tonic-gate FAS_CHECK_WAITQ_AND_FAS_MUTEX_EXIT(fas);
27457c478bd9Sstevel@tonic-gate FAS_EMPTY_CALLBACKQ(fas);
27467c478bd9Sstevel@tonic-gate }
27477c478bd9Sstevel@tonic-gate
27487c478bd9Sstevel@tonic-gate /*
27497c478bd9Sstevel@tonic-gate * fas_accept_pkt():
27507c478bd9Sstevel@tonic-gate * the flag argument is to force fas_accept_pkt to accept the pkt;
27517c478bd9Sstevel@tonic-gate * the caller cannot take the pkt back and it has to be queued up in
27527c478bd9Sstevel@tonic-gate * the readyQ
27537c478bd9Sstevel@tonic-gate */
27547c478bd9Sstevel@tonic-gate static int
fas_accept_pkt(struct fas * fas,struct fas_cmd * sp,int flag)27557c478bd9Sstevel@tonic-gate fas_accept_pkt(struct fas *fas, struct fas_cmd *sp, int flag)
27567c478bd9Sstevel@tonic-gate {
27577c478bd9Sstevel@tonic-gate short slot = sp->cmd_slot;
27587c478bd9Sstevel@tonic-gate int rval = TRAN_ACCEPT;
27597c478bd9Sstevel@tonic-gate
27607c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR__FAS_START_START, "fas_accept_pkt_start");
27617c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
27627c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= 0 && fas->f_ndisc >= 0);
27637c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
27647c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] >= 0);
27657c478bd9Sstevel@tonic-gate
27667c478bd9Sstevel@tonic-gate /*
27677c478bd9Sstevel@tonic-gate * prepare packet for transport if this hasn't been done yet and
27687c478bd9Sstevel@tonic-gate * do some checks
27697c478bd9Sstevel@tonic-gate */
27707c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_PREPARED) == 0) {
27717c478bd9Sstevel@tonic-gate rval = fas_prepare_pkt(fas, sp);
27727c478bd9Sstevel@tonic-gate if (rval != TRAN_ACCEPT) {
27737c478bd9Sstevel@tonic-gate IPRINTF1("prepare pkt failed, slot=%x\n", slot);
27747c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_TRANFLAG;
27757c478bd9Sstevel@tonic-gate goto done;
27767c478bd9Sstevel@tonic-gate }
27777c478bd9Sstevel@tonic-gate }
27787c478bd9Sstevel@tonic-gate
27797c478bd9Sstevel@tonic-gate if (Lun(sp)) {
27807c478bd9Sstevel@tonic-gate EPRINTF("fas_accept_pkt: switching target and lun slot scan\n");
27817c478bd9Sstevel@tonic-gate fas->f_dslot = 1;
27827c478bd9Sstevel@tonic-gate
27837c478bd9Sstevel@tonic-gate if ((fas->f_active[slot] == NULL) ||
27847c478bd9Sstevel@tonic-gate ((fas->f_active[slot]->f_n_slots != NTAGS) &&
27857c478bd9Sstevel@tonic-gate TAGGED(Tgt(sp)))) {
27867c478bd9Sstevel@tonic-gate (void) fas_alloc_active_slots(fas, slot, KM_NOSLEEP);
27877c478bd9Sstevel@tonic-gate }
27887c478bd9Sstevel@tonic-gate if ((fas->f_active[slot] == NULL) ||
27897c478bd9Sstevel@tonic-gate (NOTAG(Tgt(sp)) && (sp->cmd_pkt_flags & FLAG_TAGMASK))) {
27907c478bd9Sstevel@tonic-gate IPRINTF("fatal error on non-zero lun pkt\n");
27917c478bd9Sstevel@tonic-gate return (TRAN_FATAL_ERROR);
27927c478bd9Sstevel@tonic-gate }
27937c478bd9Sstevel@tonic-gate }
27947c478bd9Sstevel@tonic-gate
27957c478bd9Sstevel@tonic-gate /*
27967c478bd9Sstevel@tonic-gate * we accepted the command; increment the count
27977c478bd9Sstevel@tonic-gate * (we may still reject later if TRAN_BUSY_OK)
27987c478bd9Sstevel@tonic-gate */
27997c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
28007c478bd9Sstevel@tonic-gate fas->f_ncmds++;
28017c478bd9Sstevel@tonic-gate
28027c478bd9Sstevel@tonic-gate /*
28037c478bd9Sstevel@tonic-gate * if it is a nointr packet, start it now
28047c478bd9Sstevel@tonic-gate * (NO_INTR pkts are not queued in the waitQ)
28057c478bd9Sstevel@tonic-gate */
28067c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_NOINTR) {
28077c478bd9Sstevel@tonic-gate EPRINTF("starting a nointr cmd\n");
28087c478bd9Sstevel@tonic-gate fas_runpoll(fas, slot, sp);
28097c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_TRANFLAG;
28107c478bd9Sstevel@tonic-gate goto done;
28117c478bd9Sstevel@tonic-gate }
28127c478bd9Sstevel@tonic-gate
28137c478bd9Sstevel@tonic-gate /*
28147c478bd9Sstevel@tonic-gate * reset the throttle if we were draining
28157c478bd9Sstevel@tonic-gate */
28167c478bd9Sstevel@tonic-gate if ((fas->f_tcmds[slot] == 0) &&
28177c478bd9Sstevel@tonic-gate (fas->f_throttle[slot] == DRAIN_THROTTLE)) {
28187c478bd9Sstevel@tonic-gate DPRINTF("reset throttle\n");
28197c478bd9Sstevel@tonic-gate ASSERT(fas->f_reset_delay[Tgt(sp)] == 0);
28207c478bd9Sstevel@tonic-gate fas_full_throttle(fas, slot);
28217c478bd9Sstevel@tonic-gate }
28227c478bd9Sstevel@tonic-gate
28237c478bd9Sstevel@tonic-gate /*
28247c478bd9Sstevel@tonic-gate * accept the command:
28257c478bd9Sstevel@tonic-gate * If no readyQ and no bus free, and throttle is OK,
28267c478bd9Sstevel@tonic-gate * run cmd immediately.
28277c478bd9Sstevel@tonic-gate */
28287c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
28297c478bd9Sstevel@tonic-gate fas->f_total_cmds++;
28307c478bd9Sstevel@tonic-gate #endif
28317c478bd9Sstevel@tonic-gate
28327c478bd9Sstevel@tonic-gate if ((fas->f_readyf[slot] == NULL) && (fas->f_state == STATE_FREE) &&
28337c478bd9Sstevel@tonic-gate (fas->f_throttle[slot] > fas->f_tcmds[slot])) {
28347c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp == 0);
28357c478bd9Sstevel@tonic-gate (void) fas_startcmd(fas, sp);
28367c478bd9Sstevel@tonic-gate goto exit;
28377c478bd9Sstevel@tonic-gate } else {
28387c478bd9Sstevel@tonic-gate /*
28397c478bd9Sstevel@tonic-gate * If FLAG_HEAD is set, run cmd if target and bus are
28407c478bd9Sstevel@tonic-gate * available. if first cmd in ready Q is request sense
28417c478bd9Sstevel@tonic-gate * then insert after this command, there shouldn't be more
28427c478bd9Sstevel@tonic-gate * than one request sense.
28437c478bd9Sstevel@tonic-gate */
28447c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_HEAD) {
28457c478bd9Sstevel@tonic-gate struct fas_cmd *ssp = fas->f_readyf[slot];
28467c478bd9Sstevel@tonic-gate EPRINTF("que head\n");
28477c478bd9Sstevel@tonic-gate if (ssp &&
28487c478bd9Sstevel@tonic-gate *(ssp->cmd_pkt->pkt_cdbp) != SCMD_REQUEST_SENSE) {
28497c478bd9Sstevel@tonic-gate fas_head_of_readyQ(fas, sp);
28507c478bd9Sstevel@tonic-gate } else if (ssp) {
28517c478bd9Sstevel@tonic-gate struct fas_cmd *dp = ssp->cmd_forw;
28527c478bd9Sstevel@tonic-gate ssp->cmd_forw = sp;
28537c478bd9Sstevel@tonic-gate sp->cmd_forw = dp;
28547c478bd9Sstevel@tonic-gate if (fas->f_readyb[slot] == ssp) {
28557c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = sp;
28567c478bd9Sstevel@tonic-gate }
28577c478bd9Sstevel@tonic-gate } else {
28587c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = fas->f_readyb[slot] = sp;
28597c478bd9Sstevel@tonic-gate sp->cmd_forw = NULL;
28607c478bd9Sstevel@tonic-gate }
28617c478bd9Sstevel@tonic-gate
28627c478bd9Sstevel@tonic-gate /*
28637c478bd9Sstevel@tonic-gate * for tagged targets, check for qfull condition and
28647c478bd9Sstevel@tonic-gate * return TRAN_BUSY (if permitted), if throttle has been
28657c478bd9Sstevel@tonic-gate * exceeded
28667c478bd9Sstevel@tonic-gate */
28677c478bd9Sstevel@tonic-gate } else if (TAGGED(Tgt(sp)) &&
286819397407SSherry Moore (fas->f_tcmds[slot] >= fas->f_throttle[slot]) &&
286919397407SSherry Moore (fas->f_throttle[slot] > HOLD_THROTTLE) &&
287019397407SSherry Moore (flag == TRAN_BUSY_OK)) {
287119397407SSherry Moore IPRINTF2(
287219397407SSherry Moore "transport busy, slot=%x, ncmds=%x\n",
287319397407SSherry Moore slot, fas->f_ncmds);
287419397407SSherry Moore rval = TRAN_BUSY;
287519397407SSherry Moore fas->f_ncmds--;
287619397407SSherry Moore sp->cmd_flags &=
287719397407SSherry Moore ~(CFLAG_PREPARED | CFLAG_IN_TRANSPORT);
287819397407SSherry Moore goto done;
287919397407SSherry Moore /*
288019397407SSherry Moore * append to readyQ or start a new readyQ
288119397407SSherry Moore */
28827c478bd9Sstevel@tonic-gate } else if (fas->f_readyf[slot]) {
28837c478bd9Sstevel@tonic-gate struct fas_cmd *dp = fas->f_readyb[slot];
28847c478bd9Sstevel@tonic-gate ASSERT(dp != 0);
28857c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = sp;
28867c478bd9Sstevel@tonic-gate sp->cmd_forw = NULL;
28877c478bd9Sstevel@tonic-gate dp->cmd_forw = sp;
28887c478bd9Sstevel@tonic-gate } else {
28897c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = fas->f_readyb[slot] = sp;
28907c478bd9Sstevel@tonic-gate sp->cmd_forw = NULL;
28917c478bd9Sstevel@tonic-gate }
28927c478bd9Sstevel@tonic-gate
28937c478bd9Sstevel@tonic-gate }
28947c478bd9Sstevel@tonic-gate
28957c478bd9Sstevel@tonic-gate done:
28967c478bd9Sstevel@tonic-gate /*
28977c478bd9Sstevel@tonic-gate * just in case that the bus is free and we haven't
28987c478bd9Sstevel@tonic-gate * been able to restart for some reason
28997c478bd9Sstevel@tonic-gate */
29007c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE) {
29017c478bd9Sstevel@tonic-gate (void) fas_istart(fas);
29027c478bd9Sstevel@tonic-gate }
29037c478bd9Sstevel@tonic-gate
29047c478bd9Sstevel@tonic-gate exit:
29057c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
29067c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
29077c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR__FAS_START_END, "fas_accept_pkt_end");
29087c478bd9Sstevel@tonic-gate return (rval);
29097c478bd9Sstevel@tonic-gate }
29107c478bd9Sstevel@tonic-gate
29117c478bd9Sstevel@tonic-gate /*
29127c478bd9Sstevel@tonic-gate * allocate a tag byte and check for tag aging
29137c478bd9Sstevel@tonic-gate */
29147c478bd9Sstevel@tonic-gate static char fas_tag_lookup[] =
29157c478bd9Sstevel@tonic-gate {0, MSG_HEAD_QTAG, MSG_ORDERED_QTAG, 0, MSG_SIMPLE_QTAG};
29167c478bd9Sstevel@tonic-gate
29177c478bd9Sstevel@tonic-gate static int
fas_alloc_tag(struct fas * fas,struct fas_cmd * sp)29187c478bd9Sstevel@tonic-gate fas_alloc_tag(struct fas *fas, struct fas_cmd *sp)
29197c478bd9Sstevel@tonic-gate {
29207c478bd9Sstevel@tonic-gate struct f_slots *tag_slots;
29217c478bd9Sstevel@tonic-gate int tag;
29227c478bd9Sstevel@tonic-gate short slot = sp->cmd_slot;
29237c478bd9Sstevel@tonic-gate
29247c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_ALLOC_TAG_START, "fas_alloc_tag_start");
29257c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
29267c478bd9Sstevel@tonic-gate
29277c478bd9Sstevel@tonic-gate tag_slots = fas->f_active[slot];
29287c478bd9Sstevel@tonic-gate ASSERT(tag_slots->f_n_slots == NTAGS);
29297c478bd9Sstevel@tonic-gate
29307c478bd9Sstevel@tonic-gate alloc_tag:
29317c478bd9Sstevel@tonic-gate tag = (fas->f_active[slot]->f_tags)++;
29327c478bd9Sstevel@tonic-gate if (fas->f_active[slot]->f_tags >= NTAGS) {
29337c478bd9Sstevel@tonic-gate /*
29347c478bd9Sstevel@tonic-gate * we reserve tag 0 for non-tagged cmds
29357c478bd9Sstevel@tonic-gate */
29367c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_tags = 1;
29377c478bd9Sstevel@tonic-gate }
29387c478bd9Sstevel@tonic-gate EPRINTF1("tagged cmd, tag = %d\n", tag);
29397c478bd9Sstevel@tonic-gate
29407c478bd9Sstevel@tonic-gate /* Validate tag, should never fail. */
29417c478bd9Sstevel@tonic-gate if (tag_slots->f_slot[tag] == 0) {
29427c478bd9Sstevel@tonic-gate /*
29437c478bd9Sstevel@tonic-gate * Store assigned tag and tag queue type.
29447c478bd9Sstevel@tonic-gate * Note, in case of multiple choice, default to simple queue.
29457c478bd9Sstevel@tonic-gate */
29467c478bd9Sstevel@tonic-gate ASSERT(tag < NTAGS);
29477c478bd9Sstevel@tonic-gate sp->cmd_tag[1] = (uchar_t)tag;
29487c478bd9Sstevel@tonic-gate sp->cmd_tag[0] = fas_tag_lookup[((sp->cmd_pkt_flags &
294919397407SSherry Moore FLAG_TAGMASK) >> 12)];
29507c478bd9Sstevel@tonic-gate EPRINTF1("tag= %d\n", tag);
29517c478bd9Sstevel@tonic-gate tag_slots->f_slot[tag] = sp;
29527c478bd9Sstevel@tonic-gate (fas->f_tcmds[slot])++;
29537c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
29547c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_ALLOC_TAG_END,
29557c478bd9Sstevel@tonic-gate "fas_alloc_tag_end");
29567c478bd9Sstevel@tonic-gate return (0);
29577c478bd9Sstevel@tonic-gate
29587c478bd9Sstevel@tonic-gate } else {
29597c478bd9Sstevel@tonic-gate int age, i;
29607c478bd9Sstevel@tonic-gate
29617c478bd9Sstevel@tonic-gate /*
29627c478bd9Sstevel@tonic-gate * Check tag age. If timeouts enabled and
29637c478bd9Sstevel@tonic-gate * tag age greater than 1, print warning msg.
29647c478bd9Sstevel@tonic-gate * If timeouts enabled and tag age greater than
29657c478bd9Sstevel@tonic-gate * age limit, begin draining tag que to check for
29667c478bd9Sstevel@tonic-gate * lost tag cmd.
29677c478bd9Sstevel@tonic-gate */
29687c478bd9Sstevel@tonic-gate age = tag_slots->f_slot[tag]->cmd_age++;
29697c478bd9Sstevel@tonic-gate if (age >= fas->f_scsi_tag_age_limit &&
29707c478bd9Sstevel@tonic-gate tag_slots->f_slot[tag]->cmd_pkt->pkt_time) {
29717c478bd9Sstevel@tonic-gate IPRINTF2("tag %d in use, age= %d\n", tag, age);
29727c478bd9Sstevel@tonic-gate DPRINTF("draining tag queue\n");
29737c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[Tgt(sp)] == 0) {
29747c478bd9Sstevel@tonic-gate fas->f_throttle[slot] = DRAIN_THROTTLE;
29757c478bd9Sstevel@tonic-gate }
29767c478bd9Sstevel@tonic-gate }
29777c478bd9Sstevel@tonic-gate
29787c478bd9Sstevel@tonic-gate /* If tag in use, scan until a free one is found. */
29797c478bd9Sstevel@tonic-gate for (i = 1; i < NTAGS; i++) {
29807c478bd9Sstevel@tonic-gate tag = fas->f_active[slot]->f_tags;
29817c478bd9Sstevel@tonic-gate if (!tag_slots->f_slot[tag]) {
29827c478bd9Sstevel@tonic-gate EPRINTF1("found free tag %d\n", tag);
29837c478bd9Sstevel@tonic-gate break;
29847c478bd9Sstevel@tonic-gate }
29857c478bd9Sstevel@tonic-gate if (++(fas->f_active[slot]->f_tags) >= NTAGS) {
29867c478bd9Sstevel@tonic-gate /*
29877c478bd9Sstevel@tonic-gate * we reserve tag 0 for non-tagged cmds
29887c478bd9Sstevel@tonic-gate */
29897c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_tags = 1;
29907c478bd9Sstevel@tonic-gate }
29917c478bd9Sstevel@tonic-gate EPRINTF1("found in use tag %d\n", tag);
29927c478bd9Sstevel@tonic-gate }
29937c478bd9Sstevel@tonic-gate
29947c478bd9Sstevel@tonic-gate /*
29957c478bd9Sstevel@tonic-gate * If no free tags, we're in serious trouble.
29967c478bd9Sstevel@tonic-gate * the target driver submitted more than 255
29977c478bd9Sstevel@tonic-gate * requests
29987c478bd9Sstevel@tonic-gate */
29997c478bd9Sstevel@tonic-gate if (tag_slots->f_slot[tag]) {
30007c478bd9Sstevel@tonic-gate IPRINTF1("slot %x: All tags in use!!!\n", slot);
30017c478bd9Sstevel@tonic-gate goto fail;
30027c478bd9Sstevel@tonic-gate }
30037c478bd9Sstevel@tonic-gate goto alloc_tag;
30047c478bd9Sstevel@tonic-gate }
30057c478bd9Sstevel@tonic-gate
30067c478bd9Sstevel@tonic-gate fail:
30077c478bd9Sstevel@tonic-gate fas_head_of_readyQ(fas, sp);
30087c478bd9Sstevel@tonic-gate
30097c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_ALLOC_TAG_END,
30107c478bd9Sstevel@tonic-gate "fas_alloc_tag_end");
30117c478bd9Sstevel@tonic-gate return (-1);
30127c478bd9Sstevel@tonic-gate }
30137c478bd9Sstevel@tonic-gate
30147c478bd9Sstevel@tonic-gate /*
30157c478bd9Sstevel@tonic-gate * Internal Search Routine.
30167c478bd9Sstevel@tonic-gate *
30177c478bd9Sstevel@tonic-gate * Search for a command to start.
30187c478bd9Sstevel@tonic-gate */
30197c478bd9Sstevel@tonic-gate static int
fas_istart(struct fas * fas)30207c478bd9Sstevel@tonic-gate fas_istart(struct fas *fas)
30217c478bd9Sstevel@tonic-gate {
30227c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_ISTART_START,
30237c478bd9Sstevel@tonic-gate "fas_istart_start");
30247c478bd9Sstevel@tonic-gate EPRINTF("fas_istart:\n");
30257c478bd9Sstevel@tonic-gate
30267c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE && fas->f_ncmds > fas->f_ndisc) {
30277c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
30287c478bd9Sstevel@tonic-gate }
30297c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_ISTART_END,
30307c478bd9Sstevel@tonic-gate "fas_istart_end");
30317c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
30327c478bd9Sstevel@tonic-gate }
30337c478bd9Sstevel@tonic-gate
30347c478bd9Sstevel@tonic-gate static int
fas_ustart(struct fas * fas)30357c478bd9Sstevel@tonic-gate fas_ustart(struct fas *fas)
30367c478bd9Sstevel@tonic-gate {
30377c478bd9Sstevel@tonic-gate struct fas_cmd *sp;
30387c478bd9Sstevel@tonic-gate short slot = fas->f_next_slot;
30397c478bd9Sstevel@tonic-gate short start_slot = slot;
30407c478bd9Sstevel@tonic-gate short dslot = fas->f_dslot;
30417c478bd9Sstevel@tonic-gate
30427c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_USTART_START, "fas_ustart_start");
30437c478bd9Sstevel@tonic-gate EPRINTF1("fas_ustart: start_slot=%x\n", fas->f_next_slot);
30447c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp == NULL);
30457c478bd9Sstevel@tonic-gate ASSERT(dslot != 0);
30467c478bd9Sstevel@tonic-gate if (dslot == NLUNS_PER_TARGET) {
30477c478bd9Sstevel@tonic-gate ASSERT((slot % NLUNS_PER_TARGET) == 0);
30487c478bd9Sstevel@tonic-gate }
30497c478bd9Sstevel@tonic-gate
30507c478bd9Sstevel@tonic-gate /*
30517c478bd9Sstevel@tonic-gate * if readyQ not empty and we are not draining, then we
30527c478bd9Sstevel@tonic-gate * can start another cmd
30537c478bd9Sstevel@tonic-gate */
30547c478bd9Sstevel@tonic-gate do {
30557c478bd9Sstevel@tonic-gate /*
30567c478bd9Sstevel@tonic-gate * If all cmds drained from tag Q, back to full throttle and
30577c478bd9Sstevel@tonic-gate * start queueing up new cmds again.
30587c478bd9Sstevel@tonic-gate */
30597c478bd9Sstevel@tonic-gate if (fas->f_throttle[slot] == DRAIN_THROTTLE &&
30607c478bd9Sstevel@tonic-gate fas->f_tcmds[slot] == 0) {
30617c478bd9Sstevel@tonic-gate fas_full_throttle(fas, slot);
30627c478bd9Sstevel@tonic-gate }
30637c478bd9Sstevel@tonic-gate
30647c478bd9Sstevel@tonic-gate if (fas->f_readyf[slot] &&
30657c478bd9Sstevel@tonic-gate (fas->f_throttle[slot] > fas->f_tcmds[slot])) {
30667c478bd9Sstevel@tonic-gate sp = fas->f_readyf[slot];
30677c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = sp->cmd_forw;
30687c478bd9Sstevel@tonic-gate if (sp->cmd_forw == NULL) {
30697c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = NULL;
30707c478bd9Sstevel@tonic-gate }
30717c478bd9Sstevel@tonic-gate fas->f_next_slot = NEXTSLOT(slot, dslot);
30727c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_pkt_flags & FLAG_NOINTR) == 0);
30737c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_USTART_END,
30747c478bd9Sstevel@tonic-gate "fas_ustart_end");
30757c478bd9Sstevel@tonic-gate return (fas_startcmd(fas, sp));
30767c478bd9Sstevel@tonic-gate } else {
30777c478bd9Sstevel@tonic-gate slot = NEXTSLOT(slot, dslot);
30787c478bd9Sstevel@tonic-gate }
30797c478bd9Sstevel@tonic-gate } while (slot != start_slot);
30807c478bd9Sstevel@tonic-gate
30817c478bd9Sstevel@tonic-gate EPRINTF("fas_ustart: no cmds to start\n");
30827c478bd9Sstevel@tonic-gate fas->f_next_slot = NEXTSLOT(slot, dslot);
30837c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_USTART_NOT_FOUND_END,
30847c478bd9Sstevel@tonic-gate "fas_ustart_end (not_found)");
30857c478bd9Sstevel@tonic-gate return (FALSE);
30867c478bd9Sstevel@tonic-gate }
30877c478bd9Sstevel@tonic-gate
30887c478bd9Sstevel@tonic-gate /*
30897c478bd9Sstevel@tonic-gate * Start a command off
30907c478bd9Sstevel@tonic-gate */
30917c478bd9Sstevel@tonic-gate static int
fas_startcmd(struct fas * fas,struct fas_cmd * sp)30927c478bd9Sstevel@tonic-gate fas_startcmd(struct fas *fas, struct fas_cmd *sp)
30937c478bd9Sstevel@tonic-gate {
30947c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
30957c478bd9Sstevel@tonic-gate ushort_t nstate;
30967c478bd9Sstevel@tonic-gate uchar_t cmd, target, lun;
30977c478bd9Sstevel@tonic-gate ushort_t tshift;
30987c478bd9Sstevel@tonic-gate volatile uchar_t *tp = fas->f_cmdarea;
30997c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = CMD2PKT(sp);
31007c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
31017c478bd9Sstevel@tonic-gate struct f_slots *slots = fas->f_active[slot];
31027c478bd9Sstevel@tonic-gate int i, cdb_len;
31037c478bd9Sstevel@tonic-gate
31047c478bd9Sstevel@tonic-gate #define LOAD_CMDP *(tp++)
31057c478bd9Sstevel@tonic-gate
31067c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_STARTCMD_START, "fas_startcmd_start");
31077c478bd9Sstevel@tonic-gate
31087c478bd9Sstevel@tonic-gate EPRINTF2("fas_startcmd: sp=0x%p flags=%x\n",
31097c478bd9Sstevel@tonic-gate (void *)sp, sp->cmd_pkt_flags);
31107c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_FREE) == 0);
31117c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_COMPLETED) == 0);
31127c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp == NULL && fas->f_state == STATE_FREE);
31137c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_NOINTR) == 0) {
31147c478bd9Sstevel@tonic-gate ASSERT(fas->f_throttle[slot] > 0);
31157c478bd9Sstevel@tonic-gate ASSERT(fas->f_reset_delay[Tgt(sp)] == 0);
31167c478bd9Sstevel@tonic-gate }
31177c478bd9Sstevel@tonic-gate
31187c478bd9Sstevel@tonic-gate target = Tgt(sp);
31197c478bd9Sstevel@tonic-gate lun = Lun(sp);
31207c478bd9Sstevel@tonic-gate
31217c478bd9Sstevel@tonic-gate /*
31227c478bd9Sstevel@tonic-gate * if a non-tagged cmd is submitted to an active tagged target
31237c478bd9Sstevel@tonic-gate * then drain before submitting this cmd; SCSI-2 allows RQSENSE
31247c478bd9Sstevel@tonic-gate * to be untagged
31257c478bd9Sstevel@tonic-gate */
31267c478bd9Sstevel@tonic-gate if (((sp->cmd_pkt_flags & FLAG_TAGMASK) == 0) &&
31277c478bd9Sstevel@tonic-gate TAGGED(target) && fas->f_tcmds[slot] &&
31287c478bd9Sstevel@tonic-gate ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) &&
31297c478bd9Sstevel@tonic-gate (*(sp->cmd_pkt->pkt_cdbp) != SCMD_REQUEST_SENSE)) {
31307c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_NOINTR) == 0) {
31317c478bd9Sstevel@tonic-gate struct fas_cmd *dp;
31327c478bd9Sstevel@tonic-gate
31337c478bd9Sstevel@tonic-gate IPRINTF("untagged cmd, start draining\n");
31347c478bd9Sstevel@tonic-gate
31357c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[Tgt(sp)] == 0) {
31367c478bd9Sstevel@tonic-gate fas->f_throttle[slot] = DRAIN_THROTTLE;
31377c478bd9Sstevel@tonic-gate }
31387c478bd9Sstevel@tonic-gate dp = fas->f_readyf[slot];
31397c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = sp;
31407c478bd9Sstevel@tonic-gate sp->cmd_forw = dp;
31417c478bd9Sstevel@tonic-gate if (fas->f_readyb[slot] == NULL) {
31427c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = sp;
31437c478bd9Sstevel@tonic-gate }
31447c478bd9Sstevel@tonic-gate }
31457c478bd9Sstevel@tonic-gate return (FALSE);
31467c478bd9Sstevel@tonic-gate }
31477c478bd9Sstevel@tonic-gate
31487c478bd9Sstevel@tonic-gate /*
31497c478bd9Sstevel@tonic-gate * allocate a tag; if no tag available then put request back
31507c478bd9Sstevel@tonic-gate * on the ready queue and return; eventually a cmd returns and we
31517c478bd9Sstevel@tonic-gate * get going again or we timeout
31527c478bd9Sstevel@tonic-gate */
31537c478bd9Sstevel@tonic-gate if (TAGGED(target) && (sp->cmd_pkt_flags & FLAG_TAGMASK)) {
31547c478bd9Sstevel@tonic-gate if (fas_alloc_tag(fas, sp)) {
31557c478bd9Sstevel@tonic-gate return (FALSE);
31567c478bd9Sstevel@tonic-gate }
31577c478bd9Sstevel@tonic-gate } else {
31587c478bd9Sstevel@tonic-gate /*
31597c478bd9Sstevel@tonic-gate * tag slot 0 is reserved for non-tagged cmds
31607c478bd9Sstevel@tonic-gate * and should be empty because we have drained
31617c478bd9Sstevel@tonic-gate */
31627c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
31637c478bd9Sstevel@tonic-gate ASSERT(fas->f_active[slot]->f_slot[0] == NULL);
31647c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_slot[0] = sp;
31657c478bd9Sstevel@tonic-gate sp->cmd_tag[1] = 0;
31667c478bd9Sstevel@tonic-gate if (*(sp->cmd_pkt->pkt_cdbp) != SCMD_REQUEST_SENSE) {
31677c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] == 0);
31687c478bd9Sstevel@tonic-gate /*
31697c478bd9Sstevel@tonic-gate * don't start any other cmd until this
31707c478bd9Sstevel@tonic-gate * one is finished. The throttle is reset
31717c478bd9Sstevel@tonic-gate * later in fas_watch()
31727c478bd9Sstevel@tonic-gate */
31737c478bd9Sstevel@tonic-gate fas->f_throttle[slot] = 1;
31747c478bd9Sstevel@tonic-gate }
31757c478bd9Sstevel@tonic-gate (fas->f_tcmds[slot])++;
31767c478bd9Sstevel@tonic-gate
31777c478bd9Sstevel@tonic-gate }
31787c478bd9Sstevel@tonic-gate }
31797c478bd9Sstevel@tonic-gate
31807c478bd9Sstevel@tonic-gate fas->f_current_sp = sp;
31817c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
31827c478bd9Sstevel@tonic-gate tshift = 1<<target;
31837c478bd9Sstevel@tonic-gate fas->f_sdtr_sent = fas->f_wdtr_sent = 0;
31847c478bd9Sstevel@tonic-gate cdb_len = sp->cmd_actual_cdblen;
31857c478bd9Sstevel@tonic-gate
31867c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_RENEGOTIATE_WIDE_SYNC) {
31877c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, Tgt(sp));
31887c478bd9Sstevel@tonic-gate }
31897c478bd9Sstevel@tonic-gate
31907c478bd9Sstevel@tonic-gate /*
31917c478bd9Sstevel@tonic-gate * first send identify message, with or without disconnect priv.
31927c478bd9Sstevel@tonic-gate */
31937c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_NODISCON) {
31947c478bd9Sstevel@tonic-gate LOAD_CMDP = fas->f_last_msgout = MSG_IDENTIFY | lun;
31957c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_pkt_flags & FLAG_TAGMASK) == 0);
31967c478bd9Sstevel@tonic-gate } else {
31977c478bd9Sstevel@tonic-gate LOAD_CMDP = fas->f_last_msgout = MSG_DR_IDENTIFY | lun;
31987c478bd9Sstevel@tonic-gate }
31997c478bd9Sstevel@tonic-gate
32007c478bd9Sstevel@tonic-gate /*
32017c478bd9Sstevel@tonic-gate * normal case, tagQ and we have negotiated wide and sync
32027c478bd9Sstevel@tonic-gate * or we don't need to renegotiate because wide and sync
32037c478bd9Sstevel@tonic-gate * have been disabled
32047c478bd9Sstevel@tonic-gate * (proxy msg's don't have tag flag set)
32057c478bd9Sstevel@tonic-gate */
32067c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_TAGMASK) &&
32077c478bd9Sstevel@tonic-gate ((fas->f_wide_known | fas->f_nowide) &
32087c478bd9Sstevel@tonic-gate (fas->f_sync_known | fas->f_nosync) & tshift)) {
32097c478bd9Sstevel@tonic-gate
32107c478bd9Sstevel@tonic-gate EPRINTF("tag cmd\n");
32117c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_pkt_flags & FLAG_NODISCON) == 0);
32127c478bd9Sstevel@tonic-gate
32137c478bd9Sstevel@tonic-gate fas->f_last_msgout = LOAD_CMDP = sp->cmd_tag[0];
32147c478bd9Sstevel@tonic-gate LOAD_CMDP = sp->cmd_tag[1];
32157c478bd9Sstevel@tonic-gate
32167c478bd9Sstevel@tonic-gate nstate = STATE_SELECT_NORMAL;
32177c478bd9Sstevel@tonic-gate cmd = CMD_SEL_ATN3 | CMD_DMA;
32187c478bd9Sstevel@tonic-gate
32197c478bd9Sstevel@tonic-gate /*
32207c478bd9Sstevel@tonic-gate * is this a proxy message
32217c478bd9Sstevel@tonic-gate */
32227c478bd9Sstevel@tonic-gate } else if (sp->cmd_flags & CFLAG_CMDPROXY) {
32237c478bd9Sstevel@tonic-gate
32247c478bd9Sstevel@tonic-gate IPRINTF2("proxy cmd, len=%x, msg=%x\n",
32257c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA],
32267c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA+1]);
32277c478bd9Sstevel@tonic-gate /*
32287c478bd9Sstevel@tonic-gate * This is a proxy command. It will have
32297c478bd9Sstevel@tonic-gate * a message to send as part of post-selection
32307c478bd9Sstevel@tonic-gate * (e.g, MSG_ABORT or MSG_DEVICE_RESET)
32317c478bd9Sstevel@tonic-gate */
32327c478bd9Sstevel@tonic-gate fas->f_omsglen = sp->cmd_cdb[FAS_PROXY_DATA];
32337c478bd9Sstevel@tonic-gate for (i = 0; i < (uint_t)fas->f_omsglen; i++) {
32347c478bd9Sstevel@tonic-gate fas->f_cur_msgout[i] =
32357c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA+1+i];
32367c478bd9Sstevel@tonic-gate }
32377c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] = FALSE;
32387c478bd9Sstevel@tonic-gate cdb_len = 0;
32397c478bd9Sstevel@tonic-gate cmd = CMD_SEL_STOP | CMD_DMA;
32407c478bd9Sstevel@tonic-gate nstate = STATE_SELECT_N_SENDMSG;
32417c478bd9Sstevel@tonic-gate
32427c478bd9Sstevel@tonic-gate /*
32437c478bd9Sstevel@tonic-gate * always negotiate wide first and sync after wide
32447c478bd9Sstevel@tonic-gate */
32457c478bd9Sstevel@tonic-gate } else if (((fas->f_wide_known | fas->f_nowide) & tshift) == 0) {
32467c478bd9Sstevel@tonic-gate int i = 0;
32477c478bd9Sstevel@tonic-gate
32487c478bd9Sstevel@tonic-gate /* First the tag message bytes */
32497c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_TAGMASK) {
32507c478bd9Sstevel@tonic-gate fas->f_cur_msgout[i++] = sp->cmd_tag[0];
32517c478bd9Sstevel@tonic-gate fas->f_cur_msgout[i++] = sp->cmd_tag[1];
32527c478bd9Sstevel@tonic-gate }
32537c478bd9Sstevel@tonic-gate
32547c478bd9Sstevel@tonic-gate /*
32557c478bd9Sstevel@tonic-gate * Set up to send wide negotiating message. This is getting
32567c478bd9Sstevel@tonic-gate * a bit tricky as we dma out the identify message and
32577c478bd9Sstevel@tonic-gate * send the other messages via the fifo buffer.
32587c478bd9Sstevel@tonic-gate */
32597c478bd9Sstevel@tonic-gate EPRINTF1("cmd with wdtr msg, tag=%x\n", sp->cmd_tag[1]);
32607c478bd9Sstevel@tonic-gate
32617c478bd9Sstevel@tonic-gate fas_make_wdtr(fas, i, target, FAS_XFER_WIDTH);
32627c478bd9Sstevel@tonic-gate
32637c478bd9Sstevel@tonic-gate cdb_len = 0;
32647c478bd9Sstevel@tonic-gate nstate = STATE_SELECT_N_SENDMSG;
32657c478bd9Sstevel@tonic-gate cmd = CMD_SEL_STOP | CMD_DMA;
32667c478bd9Sstevel@tonic-gate
32677c478bd9Sstevel@tonic-gate /*
32687c478bd9Sstevel@tonic-gate * negotiate sync xfer rate
32697c478bd9Sstevel@tonic-gate */
32707c478bd9Sstevel@tonic-gate } else if (((fas->f_sync_known | fas->f_nosync) & tshift) == 0) {
32717c478bd9Sstevel@tonic-gate int i = 0;
32727c478bd9Sstevel@tonic-gate /*
32737c478bd9Sstevel@tonic-gate * Set up to send sync negotiating message. This is getting
32747c478bd9Sstevel@tonic-gate * a bit tricky as we dma out the identify message and
32757c478bd9Sstevel@tonic-gate * send the other messages via the fifo buffer.
32767c478bd9Sstevel@tonic-gate */
32777c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_TAGMASK) {
32787c478bd9Sstevel@tonic-gate fas->f_cur_msgout[i++] = sp->cmd_tag[0];
32797c478bd9Sstevel@tonic-gate fas->f_cur_msgout[i++] = sp->cmd_tag[1];
32807c478bd9Sstevel@tonic-gate }
32817c478bd9Sstevel@tonic-gate
32827c478bd9Sstevel@tonic-gate fas_make_sdtr(fas, i, target);
32837c478bd9Sstevel@tonic-gate
32847c478bd9Sstevel@tonic-gate cdb_len = 0;
32857c478bd9Sstevel@tonic-gate cmd = CMD_SEL_STOP | CMD_DMA;
32867c478bd9Sstevel@tonic-gate nstate = STATE_SELECT_N_SENDMSG;
32877c478bd9Sstevel@tonic-gate
32887c478bd9Sstevel@tonic-gate /*
32897c478bd9Sstevel@tonic-gate * normal cmds, no negotiations and not a proxy and no TQ
32907c478bd9Sstevel@tonic-gate */
32917c478bd9Sstevel@tonic-gate } else {
32927c478bd9Sstevel@tonic-gate
32937c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_pkt_flags & FLAG_TAGMASK) == 0);
32947c478bd9Sstevel@tonic-gate EPRINTF("std. cmd\n");
32957c478bd9Sstevel@tonic-gate
32967c478bd9Sstevel@tonic-gate nstate = STATE_SELECT_NORMAL;
32977c478bd9Sstevel@tonic-gate cmd = CMD_SEL_ATN | CMD_DMA;
32987c478bd9Sstevel@tonic-gate }
32997c478bd9Sstevel@tonic-gate
33007c478bd9Sstevel@tonic-gate /*
33017c478bd9Sstevel@tonic-gate * Now load cdb (if any)
33027c478bd9Sstevel@tonic-gate */
33037c478bd9Sstevel@tonic-gate for (i = 0; i < cdb_len; i++) {
33047c478bd9Sstevel@tonic-gate LOAD_CMDP = sp->cmd_cdbp[i];
33057c478bd9Sstevel@tonic-gate }
33067c478bd9Sstevel@tonic-gate
33077c478bd9Sstevel@tonic-gate /*
33087c478bd9Sstevel@tonic-gate * calculate total dma amount:
33097c478bd9Sstevel@tonic-gate */
33107c478bd9Sstevel@tonic-gate fas->f_lastcount = (uintptr_t)tp - (uintptr_t)fas->f_cmdarea;
33117c478bd9Sstevel@tonic-gate
33127c478bd9Sstevel@tonic-gate /*
33137c478bd9Sstevel@tonic-gate * load target id and enable bus id encoding and 32 bit counter
33147c478bd9Sstevel@tonic-gate */
33157c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_busid,
33167c478bd9Sstevel@tonic-gate (target & 0xf) | FAS_BUSID_ENCODID | FAS_BUSID_32BIT_COUNTER);
33177c478bd9Sstevel@tonic-gate
33187c478bd9Sstevel@tonic-gate FAS_SET_PERIOD_OFFSET_CONF3_REGS(fas, target);
33197c478bd9Sstevel@tonic-gate
33207c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
33217c478bd9Sstevel@tonic-gate
33227c478bd9Sstevel@tonic-gate FAS_DMA_READ(fas, fas->f_lastcount,
33237c478bd9Sstevel@tonic-gate fas->f_dmacookie.dmac_address, 16, cmd);
33247c478bd9Sstevel@tonic-gate
33257c478bd9Sstevel@tonic-gate New_state(fas, (int)nstate);
33267c478bd9Sstevel@tonic-gate
33277c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
33287c478bd9Sstevel@tonic-gate if (DDEBUGGING) {
33297c478bd9Sstevel@tonic-gate fas_dump_cmd(fas, sp);
33307c478bd9Sstevel@tonic-gate }
33317c478bd9Sstevel@tonic-gate #endif /* FASDEBUG */
33327c478bd9Sstevel@tonic-gate
33337c478bd9Sstevel@tonic-gate /*
33347c478bd9Sstevel@tonic-gate * if timeout == 0, then it has no effect on the timeout
33357c478bd9Sstevel@tonic-gate * handling; we deal with this when an actual timeout occurs.
33367c478bd9Sstevel@tonic-gate */
33377c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
33387c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] >= 1);
33397c478bd9Sstevel@tonic-gate }
33407c478bd9Sstevel@tonic-gate i = pkt->pkt_time - slots->f_timebase;
33417c478bd9Sstevel@tonic-gate
33427c478bd9Sstevel@tonic-gate if (i == 0) {
33437c478bd9Sstevel@tonic-gate EPRINTF("dup timeout\n");
33447c478bd9Sstevel@tonic-gate (slots->f_dups)++;
33457c478bd9Sstevel@tonic-gate slots->f_timeout = slots->f_timebase;
33467c478bd9Sstevel@tonic-gate } else if (i > 0) {
33477c478bd9Sstevel@tonic-gate EPRINTF("new timeout\n");
33487c478bd9Sstevel@tonic-gate slots->f_timeout = slots->f_timebase = pkt->pkt_time;
33497c478bd9Sstevel@tonic-gate slots->f_dups = 1;
33507c478bd9Sstevel@tonic-gate }
33517c478bd9Sstevel@tonic-gate
33527c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
33537c478bd9Sstevel@tonic-gate
33547c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_STARTCMD_END, "fas_startcmd_end");
33557c478bd9Sstevel@tonic-gate
33567c478bd9Sstevel@tonic-gate return (TRUE);
33577c478bd9Sstevel@tonic-gate }
33587c478bd9Sstevel@tonic-gate
33597c478bd9Sstevel@tonic-gate /*
33607c478bd9Sstevel@tonic-gate * Interrupt Entry Point.
33617c478bd9Sstevel@tonic-gate * Poll interrupts until they go away
33627c478bd9Sstevel@tonic-gate */
33637c478bd9Sstevel@tonic-gate static uint_t
fas_intr(caddr_t arg)33647c478bd9Sstevel@tonic-gate fas_intr(caddr_t arg)
33657c478bd9Sstevel@tonic-gate {
33667c478bd9Sstevel@tonic-gate struct fas *fas = (struct fas *)arg;
33677c478bd9Sstevel@tonic-gate int rval = DDI_INTR_UNCLAIMED;
33687c478bd9Sstevel@tonic-gate int kstat_updated = 0;
33697c478bd9Sstevel@tonic-gate
33707c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_POLL_START, "fas_intr_start");
33717c478bd9Sstevel@tonic-gate
33727c478bd9Sstevel@tonic-gate do {
33737c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
33747c478bd9Sstevel@tonic-gate
33757c478bd9Sstevel@tonic-gate do {
33767c478bd9Sstevel@tonic-gate if (fas_intr_svc(fas)) {
33777c478bd9Sstevel@tonic-gate /*
33787c478bd9Sstevel@tonic-gate * do not return immediately here because
33797c478bd9Sstevel@tonic-gate * we have to guarantee to always empty
33807c478bd9Sstevel@tonic-gate * the waitQ and callbackQ in the interrupt
33817c478bd9Sstevel@tonic-gate * handler
33827c478bd9Sstevel@tonic-gate */
33837c478bd9Sstevel@tonic-gate if (fas->f_polled_intr) {
33847c478bd9Sstevel@tonic-gate rval = DDI_INTR_CLAIMED;
33857c478bd9Sstevel@tonic-gate fas->f_polled_intr = 0;
33867c478bd9Sstevel@tonic-gate }
33877c478bd9Sstevel@tonic-gate } else {
33887c478bd9Sstevel@tonic-gate rval = DDI_INTR_CLAIMED;
33897c478bd9Sstevel@tonic-gate }
33907c478bd9Sstevel@tonic-gate } while (INTPENDING(fas));
33917c478bd9Sstevel@tonic-gate
33927c478bd9Sstevel@tonic-gate if (!kstat_updated && fas->f_intr_kstat &&
339319397407SSherry Moore rval == DDI_INTR_CLAIMED) {
33947c478bd9Sstevel@tonic-gate FAS_KSTAT_INTR(fas);
33957c478bd9Sstevel@tonic-gate kstat_updated++;
33967c478bd9Sstevel@tonic-gate }
33977c478bd9Sstevel@tonic-gate
33987c478bd9Sstevel@tonic-gate /*
33997c478bd9Sstevel@tonic-gate * check and empty the waitQ and the callbackQ
34007c478bd9Sstevel@tonic-gate */
34017c478bd9Sstevel@tonic-gate FAS_CHECK_WAITQ_AND_FAS_MUTEX_EXIT(fas);
34027c478bd9Sstevel@tonic-gate FAS_EMPTY_CALLBACKQ(fas);
34037c478bd9Sstevel@tonic-gate
34047c478bd9Sstevel@tonic-gate } while (INTPENDING(fas));
34057c478bd9Sstevel@tonic-gate
34067c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_POLL_END, "fas_intr_end");
34077c478bd9Sstevel@tonic-gate
34087c478bd9Sstevel@tonic-gate return (rval);
34097c478bd9Sstevel@tonic-gate }
34107c478bd9Sstevel@tonic-gate
34117c478bd9Sstevel@tonic-gate /*
34127c478bd9Sstevel@tonic-gate * General interrupt service routine.
34137c478bd9Sstevel@tonic-gate */
34147c478bd9Sstevel@tonic-gate static char *dma_bits = DMA_BITS;
34157c478bd9Sstevel@tonic-gate
34167c478bd9Sstevel@tonic-gate static int
fas_intr_svc(struct fas * fas)34177c478bd9Sstevel@tonic-gate fas_intr_svc(struct fas *fas)
34187c478bd9Sstevel@tonic-gate {
34197c478bd9Sstevel@tonic-gate static int (*evec[])(struct fas *fas) = {
34207c478bd9Sstevel@tonic-gate fas_finish_select,
34217c478bd9Sstevel@tonic-gate fas_reconnect,
34227c478bd9Sstevel@tonic-gate fas_phasemanage,
34237c478bd9Sstevel@tonic-gate fas_finish,
34247c478bd9Sstevel@tonic-gate fas_reset_recovery,
34257c478bd9Sstevel@tonic-gate fas_istart,
34267c478bd9Sstevel@tonic-gate fas_abort_curcmd,
34277c478bd9Sstevel@tonic-gate fas_reset_bus,
34287c478bd9Sstevel@tonic-gate fas_reset_bus,
34297c478bd9Sstevel@tonic-gate fas_handle_selection
34307c478bd9Sstevel@tonic-gate };
34317c478bd9Sstevel@tonic-gate int action;
34327c478bd9Sstevel@tonic-gate uchar_t intr, stat;
34337c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
34347c478bd9Sstevel@tonic-gate int i = 0;
34357c478bd9Sstevel@tonic-gate
34367c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FASSVC_START, "fas_intr_svc_start");
34377c478bd9Sstevel@tonic-gate
34387c478bd9Sstevel@tonic-gate /*
34397c478bd9Sstevel@tonic-gate * A read of FAS interrupt register clears interrupt,
34407c478bd9Sstevel@tonic-gate * so any other volatile information needs to be latched
34417c478bd9Sstevel@tonic-gate * up prior to reading the interrupt register.
34427c478bd9Sstevel@tonic-gate */
34437c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas, &fasreg->fas_stat);
34447c478bd9Sstevel@tonic-gate
34457c478bd9Sstevel@tonic-gate EPRINTF2("fas_intr_svc: state=%x stat=%x\n", fas->f_state,
344619397407SSherry Moore fas->f_stat);
34477c478bd9Sstevel@tonic-gate
34487c478bd9Sstevel@tonic-gate /*
34497c478bd9Sstevel@tonic-gate * this wasn't our interrupt?
34507c478bd9Sstevel@tonic-gate */
34517c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_STAT_IPEND) == 0) {
34527c478bd9Sstevel@tonic-gate if (fas_check_dma_error(fas)) {
34537c478bd9Sstevel@tonic-gate action = ACTION_RESET;
34547c478bd9Sstevel@tonic-gate goto start_action;
34557c478bd9Sstevel@tonic-gate }
34567c478bd9Sstevel@tonic-gate return (-1);
34577c478bd9Sstevel@tonic-gate }
34587c478bd9Sstevel@tonic-gate
34597c478bd9Sstevel@tonic-gate /*
34607c478bd9Sstevel@tonic-gate * if we are reset state, handle this first
34617c478bd9Sstevel@tonic-gate */
34627c478bd9Sstevel@tonic-gate if (fas->f_state == ACTS_RESET) {
34637c478bd9Sstevel@tonic-gate action = ACTION_FINRST;
34647c478bd9Sstevel@tonic-gate goto start_action;
34657c478bd9Sstevel@tonic-gate }
34667c478bd9Sstevel@tonic-gate
34677c478bd9Sstevel@tonic-gate /*
34687c478bd9Sstevel@tonic-gate * check for gross error. fas366 hardware seems to register
34697c478bd9Sstevel@tonic-gate * the gross error bit when a parity error is found. Make sure
34707c478bd9Sstevel@tonic-gate * to ignore the gross error bit when a parity error is detected.
34717c478bd9Sstevel@tonic-gate */
34727c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_STAT_GERR) &&
34737c478bd9Sstevel@tonic-gate (fas->f_stat & FAS_STAT_PERR) == 0) {
34747c478bd9Sstevel@tonic-gate action = fas_handle_gross_err(fas);
34757c478bd9Sstevel@tonic-gate goto start_action;
34767c478bd9Sstevel@tonic-gate }
34777c478bd9Sstevel@tonic-gate
34787c478bd9Sstevel@tonic-gate /*
34797c478bd9Sstevel@tonic-gate * now it is finally safe to read the interrupt register
34807c478bd9Sstevel@tonic-gate * if we haven't done so yet
34817c478bd9Sstevel@tonic-gate * Note: we don't read step register here but only in
34827c478bd9Sstevel@tonic-gate * fas_finish_select(). It is not entirely safe but saves
34837c478bd9Sstevel@tonic-gate * redundant PIOs or extra code in this critical path
34847c478bd9Sstevel@tonic-gate */
34857c478bd9Sstevel@tonic-gate fas->f_intr =
348619397407SSherry Moore intr = fas_reg_read(fas, (uchar_t *)&fasreg->fas_intr);
34877c478bd9Sstevel@tonic-gate
34887c478bd9Sstevel@tonic-gate /*
34897c478bd9Sstevel@tonic-gate * read the fifo if there is something there or still in the
34907c478bd9Sstevel@tonic-gate * input shuttle
34917c478bd9Sstevel@tonic-gate */
34927c478bd9Sstevel@tonic-gate stat = fas->f_stat & FAS_PHASE_MASK;
34937c478bd9Sstevel@tonic-gate
34947c478bd9Sstevel@tonic-gate if ((intr & FAS_INT_RESEL) ||
34957c478bd9Sstevel@tonic-gate ((stat != FAS_PHASE_DATA_IN) && (stat != FAS_PHASE_DATA_OUT) &&
34967c478bd9Sstevel@tonic-gate ((fas->f_state & STATE_SELECTING) == 0) &&
34977c478bd9Sstevel@tonic-gate (fas->f_state != ACTS_DATA_DONE) &&
34987c478bd9Sstevel@tonic-gate (fas->f_state != ACTS_C_CMPLT))) {
34997c478bd9Sstevel@tonic-gate
35007c478bd9Sstevel@tonic-gate fas->f_stat2 = fas_reg_read(fas, &fasreg->fas_stat2);
35017c478bd9Sstevel@tonic-gate
35027c478bd9Sstevel@tonic-gate if (((fas->f_stat2 & FAS_STAT2_EMPTY) == 0) ||
350319397407SSherry Moore (fas->f_stat2 & FAS_STAT2_ISHUTTLE)) {
35047c478bd9Sstevel@tonic-gate fas_read_fifo(fas);
35057c478bd9Sstevel@tonic-gate }
35067c478bd9Sstevel@tonic-gate }
35077c478bd9Sstevel@tonic-gate
35087c478bd9Sstevel@tonic-gate EPRINTF2("fas_intr_svc: intr=%x, stat=%x\n", fas->f_intr, fas->f_stat);
35097c478bd9Sstevel@tonic-gate EPRINTF2("dmacsr=%b\n", fas->f_dma->dma_csr, dma_bits);
35107c478bd9Sstevel@tonic-gate
35117c478bd9Sstevel@tonic-gate /*
35127c478bd9Sstevel@tonic-gate * Based upon the current state of the host adapter driver
35137c478bd9Sstevel@tonic-gate * we should be able to figure out what to do with an interrupt.
35147c478bd9Sstevel@tonic-gate *
35157c478bd9Sstevel@tonic-gate * The FAS asserts an interrupt with one or more of 8 possible
35167c478bd9Sstevel@tonic-gate * bits set in its interrupt register. These conditions are
35177c478bd9Sstevel@tonic-gate * SCSI bus reset detected, an illegal command fed to the FAS,
35187c478bd9Sstevel@tonic-gate * one of DISCONNECT, BUS SERVICE, FUNCTION COMPLETE conditions
35197c478bd9Sstevel@tonic-gate * for the FAS, a Reselection interrupt, or one of Selection
35207c478bd9Sstevel@tonic-gate * or Selection with Attention.
35217c478bd9Sstevel@tonic-gate *
35227c478bd9Sstevel@tonic-gate * Of these possible interrupts, we can deal with some right
35237c478bd9Sstevel@tonic-gate * here and now, irrespective of the current state of the driver.
35247c478bd9Sstevel@tonic-gate *
35257c478bd9Sstevel@tonic-gate * take care of the most likely interrupts first and call the action
35267c478bd9Sstevel@tonic-gate * immediately
35277c478bd9Sstevel@tonic-gate */
35287c478bd9Sstevel@tonic-gate if ((intr & (FAS_INT_RESET|FAS_INT_ILLEGAL|FAS_INT_SEL|FAS_INT_SELATN|
35297c478bd9Sstevel@tonic-gate FAS_INT_RESEL)) == 0) {
35307c478bd9Sstevel@tonic-gate /*
35317c478bd9Sstevel@tonic-gate * The rest of the reasons for an interrupt can
35327c478bd9Sstevel@tonic-gate * be handled based purely on the state that the driver
35337c478bd9Sstevel@tonic-gate * is currently in now.
35347c478bd9Sstevel@tonic-gate */
35357c478bd9Sstevel@tonic-gate if (fas->f_state & STATE_SELECTING) {
35367c478bd9Sstevel@tonic-gate action = fas_finish_select(fas);
35377c478bd9Sstevel@tonic-gate
35387c478bd9Sstevel@tonic-gate } else if (fas->f_state & STATE_ITPHASES) {
35397c478bd9Sstevel@tonic-gate action = fas_phasemanage(fas);
35407c478bd9Sstevel@tonic-gate
35417c478bd9Sstevel@tonic-gate } else {
35427c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "spurious interrupt");
35437c478bd9Sstevel@tonic-gate action = ACTION_RETURN;
35447c478bd9Sstevel@tonic-gate }
35457c478bd9Sstevel@tonic-gate
35467c478bd9Sstevel@tonic-gate } else if ((intr & FAS_INT_RESEL) && ((intr &
35477c478bd9Sstevel@tonic-gate (FAS_INT_RESET|FAS_INT_ILLEGAL|FAS_INT_SEL|FAS_INT_SELATN)) == 0)) {
35487c478bd9Sstevel@tonic-gate
35497c478bd9Sstevel@tonic-gate if ((fas->f_state & STATE_SELECTING) == 0) {
35507c478bd9Sstevel@tonic-gate ASSERT(fas->f_state == STATE_FREE);
35517c478bd9Sstevel@tonic-gate action = fas_reconnect(fas);
35527c478bd9Sstevel@tonic-gate } else {
35537c478bd9Sstevel@tonic-gate action = fas_reselect_preempt(fas);
35547c478bd9Sstevel@tonic-gate }
35557c478bd9Sstevel@tonic-gate
35567c478bd9Sstevel@tonic-gate } else if (intr & (FAS_INT_RESET | FAS_INT_ILLEGAL)) {
35577c478bd9Sstevel@tonic-gate action = fas_illegal_cmd_or_bus_reset(fas);
35587c478bd9Sstevel@tonic-gate
35597c478bd9Sstevel@tonic-gate } else if (intr & (FAS_INT_SEL|FAS_INT_SELATN)) {
35607c478bd9Sstevel@tonic-gate action = ACTION_SELECT;
35617c478bd9Sstevel@tonic-gate }
35627c478bd9Sstevel@tonic-gate
35637c478bd9Sstevel@tonic-gate start_action:
35647c478bd9Sstevel@tonic-gate while (action != ACTION_RETURN) {
35657c478bd9Sstevel@tonic-gate ASSERT((action >= 0) && (action <= ACTION_SELECT));
35667c478bd9Sstevel@tonic-gate TRACE_3(TR_FAC_SCSI_FAS, TR_FASSVC_ACTION_CALL,
356719397407SSherry Moore "fas_intr_svc call: fas 0x%p, action %d (%d)",
356819397407SSherry Moore fas, action, i);
35697c478bd9Sstevel@tonic-gate i++;
35707c478bd9Sstevel@tonic-gate action = (*evec[action])(fas);
35717c478bd9Sstevel@tonic-gate }
35727c478bd9Sstevel@tonic-gate exit:
35737c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FASSVC_END, "fas_intr_svc_end");
35747c478bd9Sstevel@tonic-gate
35757c478bd9Sstevel@tonic-gate return (0);
35767c478bd9Sstevel@tonic-gate }
35777c478bd9Sstevel@tonic-gate
35787c478bd9Sstevel@tonic-gate /*
35797c478bd9Sstevel@tonic-gate * Manage phase transitions.
35807c478bd9Sstevel@tonic-gate */
35817c478bd9Sstevel@tonic-gate static int
fas_phasemanage(struct fas * fas)35827c478bd9Sstevel@tonic-gate fas_phasemanage(struct fas *fas)
35837c478bd9Sstevel@tonic-gate {
35847c478bd9Sstevel@tonic-gate ushort_t state;
35857c478bd9Sstevel@tonic-gate int action;
35867c478bd9Sstevel@tonic-gate static int (*pvecs[])(struct fas *fas) = {
35877c478bd9Sstevel@tonic-gate fas_handle_cmd_start,
35887c478bd9Sstevel@tonic-gate fas_handle_cmd_done,
35897c478bd9Sstevel@tonic-gate fas_handle_msg_out_start,
35907c478bd9Sstevel@tonic-gate fas_handle_msg_out_done,
35917c478bd9Sstevel@tonic-gate fas_handle_msg_in_start,
35927c478bd9Sstevel@tonic-gate fas_handle_more_msgin,
35937c478bd9Sstevel@tonic-gate fas_handle_msg_in_done,
35947c478bd9Sstevel@tonic-gate fas_handle_clearing,
35957c478bd9Sstevel@tonic-gate fas_handle_data_start,
35967c478bd9Sstevel@tonic-gate fas_handle_data_done,
35977c478bd9Sstevel@tonic-gate fas_handle_c_cmplt,
35987c478bd9Sstevel@tonic-gate fas_reconnect,
35997c478bd9Sstevel@tonic-gate fas_handle_unknown,
36007c478bd9Sstevel@tonic-gate fas_reset_recovery
36017c478bd9Sstevel@tonic-gate };
36027c478bd9Sstevel@tonic-gate int i = 0;
36037c478bd9Sstevel@tonic-gate
36047c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_PHASEMANAGE_START,
360519397407SSherry Moore "fas_phasemanage_start");
36067c478bd9Sstevel@tonic-gate
36077c478bd9Sstevel@tonic-gate do {
36087c478bd9Sstevel@tonic-gate EPRINTF1("fas_phasemanage: %s\n",
36097c478bd9Sstevel@tonic-gate fas_state_name(fas->f_state & STATE_ITPHASES));
36107c478bd9Sstevel@tonic-gate
36117c478bd9Sstevel@tonic-gate TRACE_2(TR_FAC_SCSI_FAS, TR_FAS_PHASEMANAGE_CALL,
361219397407SSherry Moore "fas_phasemanage_call: fas 0x%p (%d)", fas, i++);
36137c478bd9Sstevel@tonic-gate
36147c478bd9Sstevel@tonic-gate state = fas->f_state;
36157c478bd9Sstevel@tonic-gate
36167c478bd9Sstevel@tonic-gate if (!(state == STATE_FREE || state > ACTS_ENDVEC)) {
36177c478bd9Sstevel@tonic-gate ASSERT(pvecs[state-1] != NULL);
36187c478bd9Sstevel@tonic-gate action = (*pvecs[state-1]) (fas);
36197c478bd9Sstevel@tonic-gate } else {
36207c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "lost state in phasemanage");
36217c478bd9Sstevel@tonic-gate action = ACTION_ABORT_ALLCMDS;
36227c478bd9Sstevel@tonic-gate }
36237c478bd9Sstevel@tonic-gate
36247c478bd9Sstevel@tonic-gate } while (action == ACTION_PHASEMANAGE);
36257c478bd9Sstevel@tonic-gate
36267c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_PHASEMANAGE_END,
362719397407SSherry Moore "fas_phasemanage_end");
36287c478bd9Sstevel@tonic-gate return (action);
36297c478bd9Sstevel@tonic-gate }
36307c478bd9Sstevel@tonic-gate
36317c478bd9Sstevel@tonic-gate /*
36327c478bd9Sstevel@tonic-gate * remove a cmd from active list and if timeout flag is set, then
36337c478bd9Sstevel@tonic-gate * adjust timeouts; if a the same cmd will be resubmitted soon, don't
36347c478bd9Sstevel@tonic-gate * bother to adjust timeouts (ie. don't set this flag)
36357c478bd9Sstevel@tonic-gate */
36367c478bd9Sstevel@tonic-gate static void
fas_remove_cmd(struct fas * fas,struct fas_cmd * sp,int new_timeout_flag)36377c478bd9Sstevel@tonic-gate fas_remove_cmd(struct fas *fas, struct fas_cmd *sp, int new_timeout_flag)
36387c478bd9Sstevel@tonic-gate {
36397c478bd9Sstevel@tonic-gate int tag = sp->cmd_tag[1];
36407c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
36417c478bd9Sstevel@tonic-gate struct f_slots *tag_slots = fas->f_active[slot];
36427c478bd9Sstevel@tonic-gate
36437c478bd9Sstevel@tonic-gate ASSERT(sp != NULL);
36447c478bd9Sstevel@tonic-gate EPRINTF4("remove tag %d slot %d for target %d.%d\n",
36457c478bd9Sstevel@tonic-gate tag, slot, Tgt(sp), Lun(sp));
36467c478bd9Sstevel@tonic-gate
36477c478bd9Sstevel@tonic-gate if (sp == tag_slots->f_slot[tag]) {
36487c478bd9Sstevel@tonic-gate tag_slots->f_slot[tag] = NULL;
36497c478bd9Sstevel@tonic-gate fas->f_tcmds[slot]--;
36507c478bd9Sstevel@tonic-gate }
36517c478bd9Sstevel@tonic-gate if (fas->f_current_sp == sp) {
36527c478bd9Sstevel@tonic-gate fas->f_current_sp = NULL;
36537c478bd9Sstevel@tonic-gate }
36547c478bd9Sstevel@tonic-gate
36557c478bd9Sstevel@tonic-gate ASSERT(sp != fas->f_active[sp->cmd_slot]->f_slot[sp->cmd_tag[1]]);
36567c478bd9Sstevel@tonic-gate
36577c478bd9Sstevel@tonic-gate if (new_timeout_flag != NEW_TIMEOUT) {
36587c478bd9Sstevel@tonic-gate return;
36597c478bd9Sstevel@tonic-gate }
36607c478bd9Sstevel@tonic-gate
36617c478bd9Sstevel@tonic-gate /*
36627c478bd9Sstevel@tonic-gate * Figure out what to set tag Q timeout for...
36637c478bd9Sstevel@tonic-gate *
36647c478bd9Sstevel@tonic-gate * Optimize: If we have duplicate's of same timeout
36657c478bd9Sstevel@tonic-gate * we're using, then we'll use it again until we run
36667c478bd9Sstevel@tonic-gate * out of duplicates. This should be the normal case
36677c478bd9Sstevel@tonic-gate * for block and raw I/O.
36687c478bd9Sstevel@tonic-gate * If no duplicates, we have to scan through tag que and
36697c478bd9Sstevel@tonic-gate * find the longest timeout value and use it. This is
36707c478bd9Sstevel@tonic-gate * going to take a while...
36717c478bd9Sstevel@tonic-gate */
36727c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_time == tag_slots->f_timebase) {
36737c478bd9Sstevel@tonic-gate if (--(tag_slots->f_dups) <= 0) {
36747c478bd9Sstevel@tonic-gate if (fas->f_tcmds[slot]) {
36757c478bd9Sstevel@tonic-gate struct fas_cmd *ssp;
36767c478bd9Sstevel@tonic-gate uint_t n = 0;
36777c478bd9Sstevel@tonic-gate ushort_t t = tag_slots->f_n_slots;
36787c478bd9Sstevel@tonic-gate ushort_t i;
36797c478bd9Sstevel@tonic-gate /*
36807c478bd9Sstevel@tonic-gate * This crude check assumes we don't do
36817c478bd9Sstevel@tonic-gate * this too often which seems reasonable
36827c478bd9Sstevel@tonic-gate * for block and raw I/O.
36837c478bd9Sstevel@tonic-gate */
36847c478bd9Sstevel@tonic-gate for (i = 0; i < t; i++) {
36857c478bd9Sstevel@tonic-gate ssp = tag_slots->f_slot[i];
36867c478bd9Sstevel@tonic-gate if (ssp &&
36877c478bd9Sstevel@tonic-gate (ssp->cmd_pkt->pkt_time > n)) {
36887c478bd9Sstevel@tonic-gate n = ssp->cmd_pkt->pkt_time;
36897c478bd9Sstevel@tonic-gate tag_slots->f_dups = 1;
36907c478bd9Sstevel@tonic-gate } else if (ssp &&
36917c478bd9Sstevel@tonic-gate (ssp->cmd_pkt->pkt_time == n)) {
36927c478bd9Sstevel@tonic-gate tag_slots->f_dups++;
36937c478bd9Sstevel@tonic-gate }
36947c478bd9Sstevel@tonic-gate }
36957c478bd9Sstevel@tonic-gate tag_slots->f_timebase = n;
36967c478bd9Sstevel@tonic-gate EPRINTF1("searching, new_timeout= %d\n", n);
36977c478bd9Sstevel@tonic-gate } else {
36987c478bd9Sstevel@tonic-gate tag_slots->f_dups = 0;
36997c478bd9Sstevel@tonic-gate tag_slots->f_timebase = 0;
37007c478bd9Sstevel@tonic-gate }
37017c478bd9Sstevel@tonic-gate }
37027c478bd9Sstevel@tonic-gate }
37037c478bd9Sstevel@tonic-gate tag_slots->f_timeout = tag_slots->f_timebase;
37047c478bd9Sstevel@tonic-gate
37057c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
37067c478bd9Sstevel@tonic-gate }
37077c478bd9Sstevel@tonic-gate
37087c478bd9Sstevel@tonic-gate /*
37097c478bd9Sstevel@tonic-gate * decrement f_ncmds and f_ndisc for this cmd before completing
37107c478bd9Sstevel@tonic-gate */
37117c478bd9Sstevel@tonic-gate static void
fas_decrement_ncmds(struct fas * fas,struct fas_cmd * sp)37127c478bd9Sstevel@tonic-gate fas_decrement_ncmds(struct fas *fas, struct fas_cmd *sp)
37137c478bd9Sstevel@tonic-gate {
37147c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_FREE) == 0);
37157c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_FINISHED) == 0) {
37167c478bd9Sstevel@tonic-gate fas->f_ncmds--;
37177c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CMDDISC) {
37187c478bd9Sstevel@tonic-gate fas->f_ndisc--;
37197c478bd9Sstevel@tonic-gate }
37207c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_FINISHED;
37217c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_CMDDISC;
37227c478bd9Sstevel@tonic-gate }
37237c478bd9Sstevel@tonic-gate ASSERT((fas->f_ncmds >= 0) && (fas->f_ndisc >= 0));
37247c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
37257c478bd9Sstevel@tonic-gate }
37267c478bd9Sstevel@tonic-gate
37277c478bd9Sstevel@tonic-gate /*
37287c478bd9Sstevel@tonic-gate * Most commonly called phase handlers:
37297c478bd9Sstevel@tonic-gate *
37307c478bd9Sstevel@tonic-gate * Finish routines
37317c478bd9Sstevel@tonic-gate */
37327c478bd9Sstevel@tonic-gate static int
fas_finish(struct fas * fas)37337c478bd9Sstevel@tonic-gate fas_finish(struct fas *fas)
37347c478bd9Sstevel@tonic-gate {
37357c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
37367c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = CMD2PKT(sp);
37377c478bd9Sstevel@tonic-gate int action = ACTION_SEARCH;
37387c478bd9Sstevel@tonic-gate struct scsi_status *status =
37397c478bd9Sstevel@tonic-gate (struct scsi_status *)sp->cmd_pkt->pkt_scbp;
37407c478bd9Sstevel@tonic-gate
37417c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_START,
37427c478bd9Sstevel@tonic-gate "fas_finish_start");
37437c478bd9Sstevel@tonic-gate EPRINTF("fas_finish\n");
37447c478bd9Sstevel@tonic-gate
37457c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
37467c478bd9Sstevel@tonic-gate if (fas_test_stop && (sp->cmd_pkt_flags & 0x80000000)) {
37477c478bd9Sstevel@tonic-gate debug_enter("untagged cmd completed");
37487c478bd9Sstevel@tonic-gate }
37497c478bd9Sstevel@tonic-gate #endif
37507c478bd9Sstevel@tonic-gate
37517c478bd9Sstevel@tonic-gate /*
37527c478bd9Sstevel@tonic-gate * immediately enable reselects
37537c478bd9Sstevel@tonic-gate */
37547c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_EN_RESEL);
37557c478bd9Sstevel@tonic-gate if (status->sts_chk) {
37567c478bd9Sstevel@tonic-gate /*
37577c478bd9Sstevel@tonic-gate * In the case that we are getting a check condition
37587c478bd9Sstevel@tonic-gate * clear our knowledge of synchronous capabilities.
37597c478bd9Sstevel@tonic-gate * This will unambiguously force a renegotiation
37607c478bd9Sstevel@tonic-gate * prior to any possible data transfer (we hope),
37617c478bd9Sstevel@tonic-gate * including the data transfer for a UNIT ATTENTION
37627c478bd9Sstevel@tonic-gate * condition generated by somebody powering on and
37637c478bd9Sstevel@tonic-gate * off a target.
37647c478bd9Sstevel@tonic-gate */
37657c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, Tgt(sp));
37667c478bd9Sstevel@tonic-gate }
37677c478bd9Sstevel@tonic-gate
37687c478bd9Sstevel@tonic-gate /*
37697c478bd9Sstevel@tonic-gate * backoff sync/wide if there were parity errors
37707c478bd9Sstevel@tonic-gate */
37717c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_statistics & STAT_PERR) {
37727c478bd9Sstevel@tonic-gate fas_sync_wide_backoff(fas, sp, sp->cmd_slot);
37737c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
37747c478bd9Sstevel@tonic-gate if (fas_test_stop) {
37757c478bd9Sstevel@tonic-gate debug_enter("parity error");
37767c478bd9Sstevel@tonic-gate }
37777c478bd9Sstevel@tonic-gate #endif
37787c478bd9Sstevel@tonic-gate }
37797c478bd9Sstevel@tonic-gate
37807c478bd9Sstevel@tonic-gate /*
37817c478bd9Sstevel@tonic-gate * Free from active list and update counts
37827c478bd9Sstevel@tonic-gate * We need to clean up this cmd now, just in case fas_ustart()
37837c478bd9Sstevel@tonic-gate * hits a reset or other fatal transport error
37847c478bd9Sstevel@tonic-gate */
37857c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
37867c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
37877c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
37887c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
37897c478bd9Sstevel@tonic-gate
37907c478bd9Sstevel@tonic-gate /*
37917c478bd9Sstevel@tonic-gate * go to state free and try to start a new cmd now
37927c478bd9Sstevel@tonic-gate */
37937c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
37947c478bd9Sstevel@tonic-gate
37957c478bd9Sstevel@tonic-gate if ((fas->f_ncmds > fas->f_ndisc) && (*((char *)status) == 0) &&
37967c478bd9Sstevel@tonic-gate (INTPENDING(fas) == 0)) {
37977c478bd9Sstevel@tonic-gate if (fas_ustart(fas)) {
37987c478bd9Sstevel@tonic-gate action = ACTION_RETURN;
37997c478bd9Sstevel@tonic-gate }
38007c478bd9Sstevel@tonic-gate }
38017c478bd9Sstevel@tonic-gate
38027c478bd9Sstevel@tonic-gate /*
38037c478bd9Sstevel@tonic-gate * if there was a data xfer then calculate residue and
38047c478bd9Sstevel@tonic-gate * sync data for consistent memory xfers
38057c478bd9Sstevel@tonic-gate */
38067c478bd9Sstevel@tonic-gate if (pkt->pkt_state & STATE_XFERRED_DATA) {
38077c478bd9Sstevel@tonic-gate pkt->pkt_resid = sp->cmd_dmacount - sp->cmd_data_count;
38087c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CMDIOPB) {
38097c478bd9Sstevel@tonic-gate (void) ddi_dma_sync(sp->cmd_dmahandle, 0, (uint_t)0,
38107c478bd9Sstevel@tonic-gate DDI_DMA_SYNC_FORCPU);
38117c478bd9Sstevel@tonic-gate }
38127c478bd9Sstevel@tonic-gate if (pkt->pkt_resid) {
38137c478bd9Sstevel@tonic-gate IPRINTF3("%d.%d finishes with %ld resid\n",
38147c478bd9Sstevel@tonic-gate Tgt(sp), Lun(sp), pkt->pkt_resid);
38157c478bd9Sstevel@tonic-gate }
38167c478bd9Sstevel@tonic-gate }
38177c478bd9Sstevel@tonic-gate
38187c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_NOINTR) {
38197c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
38207c478bd9Sstevel@tonic-gate action = ACTION_RETURN;
38217c478bd9Sstevel@tonic-gate } else {
38227c478bd9Sstevel@tonic-gate /*
38237c478bd9Sstevel@tonic-gate * start an autorequest sense if there was a check condition.
38247c478bd9Sstevel@tonic-gate * if arq has not been enabled, fas_handle_sts_chk will do
38257c478bd9Sstevel@tonic-gate * do the callback
38267c478bd9Sstevel@tonic-gate */
38277c478bd9Sstevel@tonic-gate if (status->sts_chk) {
38287c478bd9Sstevel@tonic-gate if (fas_handle_sts_chk(fas, sp)) {
38297c478bd9Sstevel@tonic-gate /*
38307c478bd9Sstevel@tonic-gate * we can't start an arq because one is
38317c478bd9Sstevel@tonic-gate * already in progress. the target is
38327c478bd9Sstevel@tonic-gate * probably confused
38337c478bd9Sstevel@tonic-gate */
38347c478bd9Sstevel@tonic-gate action = ACTION_ABORT_CURCMD;
38357c478bd9Sstevel@tonic-gate }
38367c478bd9Sstevel@tonic-gate } else if ((*((char *)status) & STATUS_MASK) ==
38377c478bd9Sstevel@tonic-gate STATUS_QFULL) {
38387c478bd9Sstevel@tonic-gate fas_handle_qfull(fas, sp);
38397c478bd9Sstevel@tonic-gate } else {
38407c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
38417c478bd9Sstevel@tonic-gate if (fas_arqs_failure && (status->sts_chk == 0)) {
38427c478bd9Sstevel@tonic-gate struct scsi_arq_status *arqstat;
38437c478bd9Sstevel@tonic-gate status->sts_chk = 1;
38447c478bd9Sstevel@tonic-gate arqstat = (struct scsi_arq_status *)
384519397407SSherry Moore (sp->cmd_pkt->pkt_scbp);
38467c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_reason = CMD_TRAN_ERR;
38477c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_ARQ_DONE;
38487c478bd9Sstevel@tonic-gate fas_arqs_failure = 0;
38497c478bd9Sstevel@tonic-gate }
38507c478bd9Sstevel@tonic-gate if (fas_tran_err) {
38517c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason = CMD_TRAN_ERR;
38527c478bd9Sstevel@tonic-gate fas_tran_err = 0;
38537c478bd9Sstevel@tonic-gate }
38547c478bd9Sstevel@tonic-gate #endif
38557c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
38567c478bd9Sstevel@tonic-gate }
38577c478bd9Sstevel@tonic-gate }
38587c478bd9Sstevel@tonic-gate
38597c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_END, "fas_finish_end");
38607c478bd9Sstevel@tonic-gate return (action);
38617c478bd9Sstevel@tonic-gate }
38627c478bd9Sstevel@tonic-gate
38637c478bd9Sstevel@tonic-gate /*
38647c478bd9Sstevel@tonic-gate * Complete the process of selecting a target
38657c478bd9Sstevel@tonic-gate */
38667c478bd9Sstevel@tonic-gate static int
fas_finish_select(struct fas * fas)38677c478bd9Sstevel@tonic-gate fas_finish_select(struct fas *fas)
38687c478bd9Sstevel@tonic-gate {
38697c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma;
38707c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
38717c478bd9Sstevel@tonic-gate uchar_t intr = fas->f_intr;
38727c478bd9Sstevel@tonic-gate uchar_t step;
38737c478bd9Sstevel@tonic-gate
38747c478bd9Sstevel@tonic-gate step = fas_reg_read(fas, &fas->f_reg->fas_step) & FAS_STEP_MASK;
38757c478bd9Sstevel@tonic-gate
38767c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_SELECT_START,
38777c478bd9Sstevel@tonic-gate "fas_finish_select_start");
38787c478bd9Sstevel@tonic-gate EPRINTF("fas_finish_select:\n");
38797c478bd9Sstevel@tonic-gate ASSERT(sp != 0);
38807c478bd9Sstevel@tonic-gate
38817c478bd9Sstevel@tonic-gate /*
38827c478bd9Sstevel@tonic-gate * Check for DMA gate array errors
38837c478bd9Sstevel@tonic-gate */
38847c478bd9Sstevel@tonic-gate if ((fas->f_dma_csr = fas_dma_reg_read(fas, &dmar->dma_csr))
38857c478bd9Sstevel@tonic-gate & DMA_ERRPEND) {
38867c478bd9Sstevel@tonic-gate /*
38877c478bd9Sstevel@tonic-gate * It would be desirable to set the ATN* line and attempt to
38887c478bd9Sstevel@tonic-gate * do the whole schmear of INITIATOR DETECTED ERROR here,
38897c478bd9Sstevel@tonic-gate * but that is too hard to do at present.
38907c478bd9Sstevel@tonic-gate */
38917c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
38927c478bd9Sstevel@tonic-gate "Unrecoverable DMA error during selection");
38937c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
38947c478bd9Sstevel@tonic-gate
38957c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_SELECT_RESET1_END,
38967c478bd9Sstevel@tonic-gate "fas_finish_select_end (ACTION_RESET1)");
38977c478bd9Sstevel@tonic-gate return (ACTION_RESET);
38987c478bd9Sstevel@tonic-gate }
38997c478bd9Sstevel@tonic-gate
39007c478bd9Sstevel@tonic-gate /*
39017c478bd9Sstevel@tonic-gate * Shut off DMA gate array
39027c478bd9Sstevel@tonic-gate */
39037c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(fas);
39047c478bd9Sstevel@tonic-gate
39057c478bd9Sstevel@tonic-gate /*
39067c478bd9Sstevel@tonic-gate * Did something respond to selection?
39077c478bd9Sstevel@tonic-gate */
39087c478bd9Sstevel@tonic-gate if (intr == (FAS_INT_BUS|FAS_INT_FCMP)) {
39097c478bd9Sstevel@tonic-gate /*
39107c478bd9Sstevel@tonic-gate * We succesfully selected a target (we think).
39117c478bd9Sstevel@tonic-gate * Now we figure out how botched things are
39127c478bd9Sstevel@tonic-gate * based upon the kind of selection we were
39137c478bd9Sstevel@tonic-gate * doing and the state of the step register.
39147c478bd9Sstevel@tonic-gate */
39157c478bd9Sstevel@tonic-gate switch (step) {
39167c478bd9Sstevel@tonic-gate case FAS_STEP_ARBSEL:
39177c478bd9Sstevel@tonic-gate /*
39187c478bd9Sstevel@tonic-gate * In this case, we selected the target, but went
39197c478bd9Sstevel@tonic-gate * neither into MESSAGE OUT nor COMMAND phase.
39207c478bd9Sstevel@tonic-gate * However, this isn't a fatal error, so we just
39217c478bd9Sstevel@tonic-gate * drive on.
39227c478bd9Sstevel@tonic-gate *
39237c478bd9Sstevel@tonic-gate * This might be a good point to note that we have
39247c478bd9Sstevel@tonic-gate * a target that appears to not accomodate
39257c478bd9Sstevel@tonic-gate * disconnecting,
39267c478bd9Sstevel@tonic-gate * but it really isn't worth the effort to distinguish
39277c478bd9Sstevel@tonic-gate * such targets fasecially from others.
39287c478bd9Sstevel@tonic-gate */
39297c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
39307c478bd9Sstevel@tonic-gate
39317c478bd9Sstevel@tonic-gate case FAS_STEP_SENTID:
39327c478bd9Sstevel@tonic-gate /*
39337c478bd9Sstevel@tonic-gate * In this case, we selected the target and sent
39347c478bd9Sstevel@tonic-gate * message byte and have stopped with ATN* still on.
39357c478bd9Sstevel@tonic-gate * This case should only occur if we use the SELECT
39367c478bd9Sstevel@tonic-gate * AND STOP command.
39377c478bd9Sstevel@tonic-gate */
39387c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
39397c478bd9Sstevel@tonic-gate
39407c478bd9Sstevel@tonic-gate case FAS_STEP_NOTCMD:
39417c478bd9Sstevel@tonic-gate /*
39427c478bd9Sstevel@tonic-gate * In this case, we either didn't transition to command
39437c478bd9Sstevel@tonic-gate * phase, or,
39447c478bd9Sstevel@tonic-gate * if we were using the SELECT WITH ATN3 command,
39457c478bd9Sstevel@tonic-gate * we possibly didn't send all message bytes.
39467c478bd9Sstevel@tonic-gate */
39477c478bd9Sstevel@tonic-gate break;
39487c478bd9Sstevel@tonic-gate
39497c478bd9Sstevel@tonic-gate case FAS_STEP_PCMD:
39507c478bd9Sstevel@tonic-gate /*
39517c478bd9Sstevel@tonic-gate * In this case, not all command bytes transferred.
39527c478bd9Sstevel@tonic-gate */
39537c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
39547c478bd9Sstevel@tonic-gate
39557c478bd9Sstevel@tonic-gate case FAS_STEP_DONE:
39567c478bd9Sstevel@tonic-gate /*
39577c478bd9Sstevel@tonic-gate * This is the usual 'good' completion point.
39587c478bd9Sstevel@tonic-gate * If we we sent message byte(s), we subtract
39597c478bd9Sstevel@tonic-gate * off the number of message bytes that were
39607c478bd9Sstevel@tonic-gate * ahead of the command.
39617c478bd9Sstevel@tonic-gate */
39627c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_SENT_CMD;
39637c478bd9Sstevel@tonic-gate break;
39647c478bd9Sstevel@tonic-gate
39657c478bd9Sstevel@tonic-gate default:
39667c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
39677c478bd9Sstevel@tonic-gate "bad sequence step (0x%x) in selection", step);
39687c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
39697c478bd9Sstevel@tonic-gate TR_FAS_FINISH_SELECT_RESET3_END,
39707c478bd9Sstevel@tonic-gate "fas_finish_select_end (ACTION_RESET3)");
39717c478bd9Sstevel@tonic-gate return (ACTION_RESET);
39727c478bd9Sstevel@tonic-gate }
39737c478bd9Sstevel@tonic-gate
39747c478bd9Sstevel@tonic-gate /*
39757c478bd9Sstevel@tonic-gate * OR in common state...
39767c478bd9Sstevel@tonic-gate */
39777c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= (STATE_GOT_BUS|STATE_GOT_TARGET);
39787c478bd9Sstevel@tonic-gate
39797c478bd9Sstevel@tonic-gate /*
39807c478bd9Sstevel@tonic-gate * data pointer initialization has already been done
39817c478bd9Sstevel@tonic-gate */
39827c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
39837c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_SELECT_ACTION3_END,
39847c478bd9Sstevel@tonic-gate "fas_finish_select_end (action3)");
39857c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
39867c478bd9Sstevel@tonic-gate
39877c478bd9Sstevel@tonic-gate } else if (intr == FAS_INT_DISCON) {
39887c478bd9Sstevel@tonic-gate /*
39897c478bd9Sstevel@tonic-gate * make sure we negotiate when this target comes
39907c478bd9Sstevel@tonic-gate * on line later on
39917c478bd9Sstevel@tonic-gate */
39927c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, Tgt(sp));
39937c478bd9Sstevel@tonic-gate
39947c478bd9Sstevel@tonic-gate fas->f_sdtr_sent = fas->f_wdtr_sent = 0;
39957c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_GOT_BUS;
39967c478bd9Sstevel@tonic-gate
39977c478bd9Sstevel@tonic-gate /*
39987c478bd9Sstevel@tonic-gate * Set the throttle to DRAIN_THROTTLE to make
39997c478bd9Sstevel@tonic-gate * sure any disconnected commands will get timed out
40007c478bd9Sstevel@tonic-gate * incase the drive dies
40017c478bd9Sstevel@tonic-gate */
40027c478bd9Sstevel@tonic-gate
40037c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[Tgt(sp)] == 0) {
40047c478bd9Sstevel@tonic-gate fas->f_throttle[sp->cmd_slot] = DRAIN_THROTTLE;
40057c478bd9Sstevel@tonic-gate }
40067c478bd9Sstevel@tonic-gate
40077c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_INCOMPLETE, 0);
40087c478bd9Sstevel@tonic-gate
40097c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_SELECT_FINISH_END,
40107c478bd9Sstevel@tonic-gate "fas_finish_select_end (ACTION_FINISH)");
40117c478bd9Sstevel@tonic-gate return (ACTION_FINISH);
40127c478bd9Sstevel@tonic-gate } else {
40137c478bd9Sstevel@tonic-gate fas_printstate(fas, "undetermined selection failure");
40147c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_FINISH_SELECT_RESET2_END,
40157c478bd9Sstevel@tonic-gate "fas_finish_select_end (ACTION_RESET2)");
40167c478bd9Sstevel@tonic-gate return (ACTION_RESET);
40177c478bd9Sstevel@tonic-gate }
40187c478bd9Sstevel@tonic-gate _NOTE(NOT_REACHED)
40197c478bd9Sstevel@tonic-gate /* NOTREACHED */
40207c478bd9Sstevel@tonic-gate }
40217c478bd9Sstevel@tonic-gate
40227c478bd9Sstevel@tonic-gate /*
40237c478bd9Sstevel@tonic-gate * a selection got preempted by a reselection; shut down dma
40247c478bd9Sstevel@tonic-gate * and put back cmd in the ready queue unless NOINTR
40257c478bd9Sstevel@tonic-gate */
40267c478bd9Sstevel@tonic-gate static int
fas_reselect_preempt(struct fas * fas)40277c478bd9Sstevel@tonic-gate fas_reselect_preempt(struct fas *fas)
40287c478bd9Sstevel@tonic-gate {
40297c478bd9Sstevel@tonic-gate int rval;
40307c478bd9Sstevel@tonic-gate
40317c478bd9Sstevel@tonic-gate /*
40327c478bd9Sstevel@tonic-gate * A reselection attempt glotzed our selection attempt.
40337c478bd9Sstevel@tonic-gate * we put request back in the ready queue
40347c478bd9Sstevel@tonic-gate */
40357c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
40367c478bd9Sstevel@tonic-gate
40377c478bd9Sstevel@tonic-gate /*
40387c478bd9Sstevel@tonic-gate * Shut off DMA gate array
40397c478bd9Sstevel@tonic-gate */
40407c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(fas);
40417c478bd9Sstevel@tonic-gate
40427c478bd9Sstevel@tonic-gate /*
40437c478bd9Sstevel@tonic-gate * service the reconnect now and clean up later
40447c478bd9Sstevel@tonic-gate */
40457c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
40467c478bd9Sstevel@tonic-gate rval = fas_reconnect(fas);
40477c478bd9Sstevel@tonic-gate
40487c478bd9Sstevel@tonic-gate /*
40497c478bd9Sstevel@tonic-gate * If selection for a non-tagged command is preempted, the
40507c478bd9Sstevel@tonic-gate * command could be stuck because throttle was set to DRAIN,
40517c478bd9Sstevel@tonic-gate * and a disconnected command timeout follows.
40527c478bd9Sstevel@tonic-gate */
40537c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_TAGMASK) == 0)
40547c478bd9Sstevel@tonic-gate fas->f_throttle[sp->cmd_slot] = 1;
40557c478bd9Sstevel@tonic-gate
40567c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
40577c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
40587c478bd9Sstevel@tonic-gate }
40597c478bd9Sstevel@tonic-gate
40607c478bd9Sstevel@tonic-gate /*
40617c478bd9Sstevel@tonic-gate * if we attempted to renegotiate on this cmd, undo this now
40627c478bd9Sstevel@tonic-gate */
40637c478bd9Sstevel@tonic-gate if (fas->f_wdtr_sent) {
40647c478bd9Sstevel@tonic-gate fas->f_wide_known &= ~(1<<Tgt(sp));
40657c478bd9Sstevel@tonic-gate fas->f_wdtr_sent = 0;
40667c478bd9Sstevel@tonic-gate }
40677c478bd9Sstevel@tonic-gate if (fas->f_sdtr_sent) {
40687c478bd9Sstevel@tonic-gate fas->f_sync_known &= ~(1<<Tgt(sp));
40697c478bd9Sstevel@tonic-gate fas->f_sdtr_sent = 0;
40707c478bd9Sstevel@tonic-gate }
40717c478bd9Sstevel@tonic-gate
40727c478bd9Sstevel@tonic-gate fas_head_of_readyQ(fas, sp);
40737c478bd9Sstevel@tonic-gate
40747c478bd9Sstevel@tonic-gate return (rval);
40757c478bd9Sstevel@tonic-gate }
40767c478bd9Sstevel@tonic-gate
40777c478bd9Sstevel@tonic-gate /*
40787c478bd9Sstevel@tonic-gate * Handle the reconnection of a target
40797c478bd9Sstevel@tonic-gate */
40807c478bd9Sstevel@tonic-gate static int
fas_reconnect(struct fas * fas)40817c478bd9Sstevel@tonic-gate fas_reconnect(struct fas *fas)
40827c478bd9Sstevel@tonic-gate {
40837c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
40847c478bd9Sstevel@tonic-gate struct fas_cmd *sp = NULL;
40857c478bd9Sstevel@tonic-gate uchar_t target, lun;
40867c478bd9Sstevel@tonic-gate uchar_t tmp;
40877c478bd9Sstevel@tonic-gate uchar_t slot;
40887c478bd9Sstevel@tonic-gate char *bad_reselect = NULL;
40897c478bd9Sstevel@tonic-gate
40907c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_RECONNECT_START,
40917c478bd9Sstevel@tonic-gate "fas_reconnect_start");
40927c478bd9Sstevel@tonic-gate EPRINTF("fas_reconnect:\n");
40937c478bd9Sstevel@tonic-gate
40947c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
40957c478bd9Sstevel@tonic-gate
40967c478bd9Sstevel@tonic-gate switch (fas->f_state) {
40977c478bd9Sstevel@tonic-gate default:
40987c478bd9Sstevel@tonic-gate /*
40997c478bd9Sstevel@tonic-gate * Pick up target id from fifo
41007c478bd9Sstevel@tonic-gate *
41017c478bd9Sstevel@tonic-gate * There should only be the reselecting target's id
41027c478bd9Sstevel@tonic-gate * and an identify message in the fifo.
41037c478bd9Sstevel@tonic-gate */
41047c478bd9Sstevel@tonic-gate target = fas->f_fifo[0];
41057c478bd9Sstevel@tonic-gate
41067c478bd9Sstevel@tonic-gate /*
41077c478bd9Sstevel@tonic-gate * we know the target so update period, conf3,
41087c478bd9Sstevel@tonic-gate * offset reg, if necessary, and accept the msg
41097c478bd9Sstevel@tonic-gate */
41107c478bd9Sstevel@tonic-gate FAS_SET_PERIOD_OFFSET_CONF3_REGS(fas, target);
41117c478bd9Sstevel@tonic-gate
41127c478bd9Sstevel@tonic-gate /*
41137c478bd9Sstevel@tonic-gate * now we can accept the message. an untagged
41147c478bd9Sstevel@tonic-gate * target will go immediately into data phase so
41157c478bd9Sstevel@tonic-gate * the period/offset/conf3 registers need to be
41167c478bd9Sstevel@tonic-gate * updated before accepting the message
41177c478bd9Sstevel@tonic-gate */
41187c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_MSG_ACPT);
41197c478bd9Sstevel@tonic-gate
41207c478bd9Sstevel@tonic-gate if (fas->f_fifolen != 2) {
41217c478bd9Sstevel@tonic-gate bad_reselect = "bad reselect bytes";
41227c478bd9Sstevel@tonic-gate break;
41237c478bd9Sstevel@tonic-gate }
41247c478bd9Sstevel@tonic-gate
41257c478bd9Sstevel@tonic-gate /*
41267c478bd9Sstevel@tonic-gate * normal initial reconnect; we get another interrupt later
41277c478bd9Sstevel@tonic-gate * for the tag
41287c478bd9Sstevel@tonic-gate */
41297c478bd9Sstevel@tonic-gate New_state(fas, ACTS_RESEL);
41307c478bd9Sstevel@tonic-gate
41317c478bd9Sstevel@tonic-gate if (fas->f_stat & FAS_STAT_PERR) {
41327c478bd9Sstevel@tonic-gate break;
41337c478bd9Sstevel@tonic-gate }
41347c478bd9Sstevel@tonic-gate
41357c478bd9Sstevel@tonic-gate /*
41367c478bd9Sstevel@tonic-gate * Check sanity of message.
41377c478bd9Sstevel@tonic-gate */
41387c478bd9Sstevel@tonic-gate tmp = fas->f_fifo[1];
41397c478bd9Sstevel@tonic-gate fas->f_last_msgin = tmp;
41407c478bd9Sstevel@tonic-gate
41417c478bd9Sstevel@tonic-gate if (!(IS_IDENTIFY_MSG(tmp)) || (tmp & INI_CAN_DISCON)) {
41427c478bd9Sstevel@tonic-gate bad_reselect = "bad identify msg";
41437c478bd9Sstevel@tonic-gate break;
41447c478bd9Sstevel@tonic-gate }
41457c478bd9Sstevel@tonic-gate
41467c478bd9Sstevel@tonic-gate lun = tmp & (NLUNS_PER_TARGET-1);
41477c478bd9Sstevel@tonic-gate
41487c478bd9Sstevel@tonic-gate EPRINTF2("fas_reconnect: target=%x, idmsg=%x\n",
414919397407SSherry Moore target, tmp);
41507c478bd9Sstevel@tonic-gate
41517c478bd9Sstevel@tonic-gate fas->f_resel_slot = slot = (target * NLUNS_PER_TARGET) | lun;
41527c478bd9Sstevel@tonic-gate
41537c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_busid,
415419397407SSherry Moore (target & 0xf) | FAS_BUSID_ENCODID |
415519397407SSherry Moore FAS_BUSID_32BIT_COUNTER);
41567c478bd9Sstevel@tonic-gate
41577c478bd9Sstevel@tonic-gate /*
41587c478bd9Sstevel@tonic-gate * If tag queueing in use, DMA in tag.
41597c478bd9Sstevel@tonic-gate * Otherwise, we're ready to go.
41607c478bd9Sstevel@tonic-gate * if tag 0 slot is non-empty, a non-tagged cmd is
41617c478bd9Sstevel@tonic-gate * reconnecting
41627c478bd9Sstevel@tonic-gate */
41637c478bd9Sstevel@tonic-gate if (TAGGED(target) && fas->f_tcmds[slot] &&
41647c478bd9Sstevel@tonic-gate (fas->f_active[slot]->f_slot[0] == NULL)) {
41657c478bd9Sstevel@tonic-gate volatile uchar_t *c =
416619397407SSherry Moore (uchar_t *)fas->f_cmdarea;
41677c478bd9Sstevel@tonic-gate
41687c478bd9Sstevel@tonic-gate /*
41697c478bd9Sstevel@tonic-gate * If we've been doing tagged queueing and this
41707c478bd9Sstevel@tonic-gate * request doesn't do it,
41717c478bd9Sstevel@tonic-gate * maybe it was disabled for this one. This is rather
41727c478bd9Sstevel@tonic-gate * dangerous as it blows all pending tagged cmds away.
41737c478bd9Sstevel@tonic-gate * But if target is confused, then we'll blow up
41747c478bd9Sstevel@tonic-gate * shortly.
41757c478bd9Sstevel@tonic-gate */
41767c478bd9Sstevel@tonic-gate *c++ = INVALID_MSG;
41777c478bd9Sstevel@tonic-gate *c = INVALID_MSG;
41787c478bd9Sstevel@tonic-gate
41797c478bd9Sstevel@tonic-gate FAS_DMA_WRITE_SETUP(fas, 2,
418019397407SSherry Moore fas->f_dmacookie.dmac_address);
41817c478bd9Sstevel@tonic-gate
41827c478bd9Sstevel@tonic-gate /*
41837c478bd9Sstevel@tonic-gate * For tagged queuing, we should still be in msgin
41847c478bd9Sstevel@tonic-gate * phase.
41857c478bd9Sstevel@tonic-gate * If not, then either we aren't running tagged
41867c478bd9Sstevel@tonic-gate * queueing like we thought or the target died.
41877c478bd9Sstevel@tonic-gate */
41887c478bd9Sstevel@tonic-gate if (INTPENDING(fas) == 0) {
41897c478bd9Sstevel@tonic-gate EPRINTF1("slow reconnect, slot=%x\n", slot);
41907c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
41917c478bd9Sstevel@tonic-gate TR_FAS_RECONNECT_RETURN1_END,
41927c478bd9Sstevel@tonic-gate "fas_reconnect_end (_RETURN1)");
41937c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
41947c478bd9Sstevel@tonic-gate }
41957c478bd9Sstevel@tonic-gate
41967c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas, &fasreg->fas_stat);
41977c478bd9Sstevel@tonic-gate fas->f_intr = fas_reg_read(fas, &fasreg->fas_intr);
41987c478bd9Sstevel@tonic-gate if (fas->f_intr & (FAS_INT_ILLEGAL | FAS_INT_RESET)) {
41997c478bd9Sstevel@tonic-gate return (fas_illegal_cmd_or_bus_reset(fas));
42007c478bd9Sstevel@tonic-gate }
42017c478bd9Sstevel@tonic-gate
42027c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_PHASE_MASK) !=
42037c478bd9Sstevel@tonic-gate FAS_PHASE_MSG_IN) {
42047c478bd9Sstevel@tonic-gate bad_reselect = "not in msgin phase";
42057c478bd9Sstevel@tonic-gate break;
42067c478bd9Sstevel@tonic-gate }
42077c478bd9Sstevel@tonic-gate
42087c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_DISCON) {
42097c478bd9Sstevel@tonic-gate bad_reselect = "unexpected bus free";
42107c478bd9Sstevel@tonic-gate break;
42117c478bd9Sstevel@tonic-gate }
42127c478bd9Sstevel@tonic-gate } else {
42137c478bd9Sstevel@tonic-gate fas->f_current_sp = sp = fas->f_active[slot]->f_slot[0];
42147c478bd9Sstevel@tonic-gate break;
42157c478bd9Sstevel@tonic-gate }
42167c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/
42177c478bd9Sstevel@tonic-gate
42187c478bd9Sstevel@tonic-gate case ACTS_RESEL:
42197c478bd9Sstevel@tonic-gate {
42207c478bd9Sstevel@tonic-gate volatile uchar_t *c =
422119397407SSherry Moore (uchar_t *)fas->f_cmdarea;
42227c478bd9Sstevel@tonic-gate struct f_slots *tag_slots;
42237c478bd9Sstevel@tonic-gate int id, tag;
42247c478bd9Sstevel@tonic-gate uint_t i;
42257c478bd9Sstevel@tonic-gate
42267c478bd9Sstevel@tonic-gate slot = fas->f_resel_slot;
42277c478bd9Sstevel@tonic-gate target = slot/NLUNS_PER_TARGET;
42287c478bd9Sstevel@tonic-gate
42297c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_PHASE_MASK) !=
42307c478bd9Sstevel@tonic-gate FAS_PHASE_MSG_IN) {
42317c478bd9Sstevel@tonic-gate IPRINTF1("no tag for slot %x\n", slot);
42327c478bd9Sstevel@tonic-gate if (fas->f_intr & ~(FAS_INT_BUS |
42337c478bd9Sstevel@tonic-gate FAS_INT_FCMP)) {
42347c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
42357c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
42367c478bd9Sstevel@tonic-gate TR_FAS_RECONNECT_PHASEMANAGE_END,
42377c478bd9Sstevel@tonic-gate "fas_reconnect_end (_PHASEMANAGE)");
42387c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
42397c478bd9Sstevel@tonic-gate } else {
42407c478bd9Sstevel@tonic-gate bad_reselect = "not in msgin phase";
42417c478bd9Sstevel@tonic-gate break;
42427c478bd9Sstevel@tonic-gate }
42437c478bd9Sstevel@tonic-gate }
42447c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_TRAN_INFO|CMD_DMA);
42457c478bd9Sstevel@tonic-gate fas_dma_reg_write(fas, &fas->f_dma->dma_csr,
424619397407SSherry Moore fas->f_dma_csr);
42477c478bd9Sstevel@tonic-gate
42487c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_MSG_ACPT);
42497c478bd9Sstevel@tonic-gate
42507c478bd9Sstevel@tonic-gate for (i = 0; i < (uint_t)RECONNECT_TAG_RCV_TIMEOUT;
42517c478bd9Sstevel@tonic-gate i++) {
42527c478bd9Sstevel@tonic-gate /*
42537c478bd9Sstevel@tonic-gate * timeout is not very accurate but this
42547c478bd9Sstevel@tonic-gate * should take no time at all
42557c478bd9Sstevel@tonic-gate */
42567c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
42577c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas,
425819397407SSherry Moore (uchar_t *)&fas->f_reg->fas_stat);
42597c478bd9Sstevel@tonic-gate fas->f_intr = fas_reg_read(fas,
426019397407SSherry Moore (uchar_t *)&fas->f_reg->fas_intr);
42617c478bd9Sstevel@tonic-gate if (fas->f_intr & (FAS_INT_RESET |
42627c478bd9Sstevel@tonic-gate FAS_INT_ILLEGAL)) {
426319397407SSherry Moore return (
426419397407SSherry Moore fas_illegal_cmd_or_bus_reset
426519397407SSherry Moore (fas));
42667c478bd9Sstevel@tonic-gate }
42677c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_FCMP) {
42687c478bd9Sstevel@tonic-gate break;
42697c478bd9Sstevel@tonic-gate }
42707c478bd9Sstevel@tonic-gate }
42717c478bd9Sstevel@tonic-gate }
42727c478bd9Sstevel@tonic-gate
42737c478bd9Sstevel@tonic-gate if (i == (uint_t)RECONNECT_TAG_RCV_TIMEOUT) {
42747c478bd9Sstevel@tonic-gate bad_reselect = "timeout on receiving tag msg";
42757c478bd9Sstevel@tonic-gate break;
42767c478bd9Sstevel@tonic-gate }
42777c478bd9Sstevel@tonic-gate
42787c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(fas);
42797c478bd9Sstevel@tonic-gate
42807c478bd9Sstevel@tonic-gate /*
42817c478bd9Sstevel@tonic-gate * we should really do a sync here but that
42827c478bd9Sstevel@tonic-gate * hurts performance too much; we'll just hang
42837c478bd9Sstevel@tonic-gate * around till the tag byte flips
42847c478bd9Sstevel@tonic-gate * This is necessary on any system with an
42857c478bd9Sstevel@tonic-gate * XBox
42867c478bd9Sstevel@tonic-gate */
42877c478bd9Sstevel@tonic-gate if (*c == INVALID_MSG) {
42887c478bd9Sstevel@tonic-gate EPRINTF(
42897c478bd9Sstevel@tonic-gate "fas_reconnect: invalid msg, polling\n");
42907c478bd9Sstevel@tonic-gate for (i = 0; i < 1000000; i++) {
42917c478bd9Sstevel@tonic-gate if (*c != INVALID_MSG)
42927c478bd9Sstevel@tonic-gate break;
42937c478bd9Sstevel@tonic-gate }
42947c478bd9Sstevel@tonic-gate }
42957c478bd9Sstevel@tonic-gate
42967c478bd9Sstevel@tonic-gate if (fas->f_stat & FAS_STAT_PERR) {
42977c478bd9Sstevel@tonic-gate break;
42987c478bd9Sstevel@tonic-gate }
42997c478bd9Sstevel@tonic-gate
43007c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_STAT_XZERO) == 0 ||
43017c478bd9Sstevel@tonic-gate (id = *c++) < MSG_SIMPLE_QTAG ||
43027c478bd9Sstevel@tonic-gate id > MSG_ORDERED_QTAG) {
43037c478bd9Sstevel@tonic-gate /*
43047c478bd9Sstevel@tonic-gate * Target agreed to do tagged queueing
43057c478bd9Sstevel@tonic-gate * and lied!
43067c478bd9Sstevel@tonic-gate * This problem implies the drive firmware is
43077c478bd9Sstevel@tonic-gate * broken.
43087c478bd9Sstevel@tonic-gate */
43097c478bd9Sstevel@tonic-gate bad_reselect = "botched tag";
43107c478bd9Sstevel@tonic-gate break;
43117c478bd9Sstevel@tonic-gate }
43127c478bd9Sstevel@tonic-gate tag = *c;
43137c478bd9Sstevel@tonic-gate
43147c478bd9Sstevel@tonic-gate /* Set ptr to reconnecting scsi pkt */
43157c478bd9Sstevel@tonic-gate tag_slots = fas->f_active[slot];
43167c478bd9Sstevel@tonic-gate if (tag_slots != NULL) {
43177c478bd9Sstevel@tonic-gate sp = tag_slots->f_slot[tag];
43187c478bd9Sstevel@tonic-gate } else {
43197c478bd9Sstevel@tonic-gate bad_reselect = "Invalid tag";
43207c478bd9Sstevel@tonic-gate break;
43217c478bd9Sstevel@tonic-gate }
43227c478bd9Sstevel@tonic-gate
43237c478bd9Sstevel@tonic-gate fas->f_current_sp = sp;
43247c478bd9Sstevel@tonic-gate }
43257c478bd9Sstevel@tonic-gate }
43267c478bd9Sstevel@tonic-gate
43277c478bd9Sstevel@tonic-gate if (fas->f_stat & FAS_STAT_PERR) {
43287c478bd9Sstevel@tonic-gate sp = NULL;
43297c478bd9Sstevel@tonic-gate bad_reselect = "Parity error in reconnect msg's";
43307c478bd9Sstevel@tonic-gate }
43317c478bd9Sstevel@tonic-gate
43327c478bd9Sstevel@tonic-gate if ((sp == NULL ||
43337c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
43347c478bd9Sstevel@tonic-gate (fas_atest_reconn & (1<<Tgt(sp))) ||
43357c478bd9Sstevel@tonic-gate #endif
43367c478bd9Sstevel@tonic-gate (sp->cmd_flags & (CFLAG_CMDDISC|CFLAG_CMDPROXY)) == 0)) {
43377c478bd9Sstevel@tonic-gate /*
43387c478bd9Sstevel@tonic-gate * this shouldn't really happen, so it is better
43397c478bd9Sstevel@tonic-gate * to reset the bus; some disks accept the abort
43407c478bd9Sstevel@tonic-gate * and then still reconnect
43417c478bd9Sstevel@tonic-gate */
43427c478bd9Sstevel@tonic-gate if (bad_reselect == NULL) {
43437c478bd9Sstevel@tonic-gate bad_reselect = "no command";
43447c478bd9Sstevel@tonic-gate }
43457c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
43467c478bd9Sstevel@tonic-gate if (sp && !(fas_atest_reconn & (1<<Tgt(sp))) &&
434719397407SSherry Moore fas_test_stop) {
43487c478bd9Sstevel@tonic-gate debug_enter("bad reconnect");
43497c478bd9Sstevel@tonic-gate } else {
43507c478bd9Sstevel@tonic-gate fas_atest_reconn = 0;
43517c478bd9Sstevel@tonic-gate }
43527c478bd9Sstevel@tonic-gate #endif
43537c478bd9Sstevel@tonic-gate goto bad;
43547c478bd9Sstevel@tonic-gate
43557c478bd9Sstevel@tonic-gate /*
43567c478bd9Sstevel@tonic-gate * XXX remove this case or make it an ASSERT
43577c478bd9Sstevel@tonic-gate */
43587c478bd9Sstevel@tonic-gate } else if (sp->cmd_flags & CFLAG_CMDPROXY) {
43597c478bd9Sstevel@tonic-gate /*
43607c478bd9Sstevel@tonic-gate * If we got here, we were already attempting to
43617c478bd9Sstevel@tonic-gate * run a polled proxy command for this target.
43627c478bd9Sstevel@tonic-gate * Set ATN and, copy in the message, and drive
43637c478bd9Sstevel@tonic-gate * on (ignoring any parity error on the identify).
43647c478bd9Sstevel@tonic-gate */
43657c478bd9Sstevel@tonic-gate IPRINTF1("fas_reconnect: fielding proxy cmd for %d\n",
43667c478bd9Sstevel@tonic-gate target);
43677c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
43687c478bd9Sstevel@tonic-gate fas->f_omsglen = sp->cmd_cdb[FAS_PROXY_DATA];
43697c478bd9Sstevel@tonic-gate tmp = 0;
43707c478bd9Sstevel@tonic-gate while (tmp < fas->f_omsglen) {
43717c478bd9Sstevel@tonic-gate fas->f_cur_msgout[tmp] =
43727c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA+1+tmp];
43737c478bd9Sstevel@tonic-gate tmp++;
43747c478bd9Sstevel@tonic-gate }
43757c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] = FALSE;
43767c478bd9Sstevel@tonic-gate
43777c478bd9Sstevel@tonic-gate /*
43787c478bd9Sstevel@tonic-gate * pretend that the disconnected cmd is still disconnected
43797c478bd9Sstevel@tonic-gate * (this prevents ndisc from going negative)
43807c478bd9Sstevel@tonic-gate */
43817c478bd9Sstevel@tonic-gate fas->f_ndisc++;
43827c478bd9Sstevel@tonic-gate ASSERT((fas->f_ncmds >= 0) && (fas->f_ndisc >= 0));
43837c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
43847c478bd9Sstevel@tonic-gate }
43857c478bd9Sstevel@tonic-gate
43867c478bd9Sstevel@tonic-gate ASSERT(fas->f_resel_slot == slot);
43877c478bd9Sstevel@tonic-gate ASSERT(fas->f_ndisc > 0);
43887c478bd9Sstevel@tonic-gate fas->f_ndisc--;
43897c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_CMDDISC;
43907c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
43917c478bd9Sstevel@tonic-gate
43927c478bd9Sstevel@tonic-gate /*
43937c478bd9Sstevel@tonic-gate * A reconnect may imply a restore pointers operation
43947c478bd9Sstevel@tonic-gate * Note that some older disks (Micropolis in Pbox) do not
43957c478bd9Sstevel@tonic-gate * send a save data ptr on disconnect if all data has been
43967c478bd9Sstevel@tonic-gate * xferred. So, we cannot restore ptrs yet here.
43977c478bd9Sstevel@tonic-gate */
43987c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_DMAVALID) &&
43997c478bd9Sstevel@tonic-gate (sp->cmd_data_count != sp->cmd_saved_data_count)) {
44007c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_RESTORE_PTRS;
44017c478bd9Sstevel@tonic-gate }
44027c478bd9Sstevel@tonic-gate
44037c478bd9Sstevel@tonic-gate /*
44047c478bd9Sstevel@tonic-gate * Return to await the FUNCTION COMPLETE interrupt we
44057c478bd9Sstevel@tonic-gate * should get out of accepting the IDENTIFY message.
44067c478bd9Sstevel@tonic-gate */
44077c478bd9Sstevel@tonic-gate EPRINTF2("Reconnecting %d.%d\n", target, slot % NLUNS_PER_TARGET);
44087c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_RECONNECT_RETURN2_END,
44097c478bd9Sstevel@tonic-gate "fas_reconnect_end (_RETURN2)");
44107c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
44117c478bd9Sstevel@tonic-gate
44127c478bd9Sstevel@tonic-gate bad:
44137c478bd9Sstevel@tonic-gate if (sp && (fas->f_stat & FAS_STAT_PERR)) {
44147c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
44157c478bd9Sstevel@tonic-gate }
44167c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "target %x: failed reselection (%s)",
441719397407SSherry Moore target, bad_reselect);
44187c478bd9Sstevel@tonic-gate
44197c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
44207c478bd9Sstevel@tonic-gate fas_printstate(fas, "failed reselection");
44217c478bd9Sstevel@tonic-gate #endif
44227c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_RECONNECT_RESET5_END,
44237c478bd9Sstevel@tonic-gate "fas_reconnect_end (_RESET5)");
44247c478bd9Sstevel@tonic-gate return (ACTION_RESET);
44257c478bd9Sstevel@tonic-gate }
44267c478bd9Sstevel@tonic-gate
44277c478bd9Sstevel@tonic-gate /*
44287c478bd9Sstevel@tonic-gate * handle unknown bus phase
44297c478bd9Sstevel@tonic-gate * we don't know what to expect so check status register for current
44307c478bd9Sstevel@tonic-gate * phase
44317c478bd9Sstevel@tonic-gate */
44327c478bd9Sstevel@tonic-gate int
fas_handle_unknown(struct fas * fas)44337c478bd9Sstevel@tonic-gate fas_handle_unknown(struct fas *fas)
44347c478bd9Sstevel@tonic-gate {
44357c478bd9Sstevel@tonic-gate TRACE_1(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_UNKNOWN_START,
44367c478bd9Sstevel@tonic-gate "fas_handle_unknown_start: fas 0x%p", fas);
44377c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_unknown:\n");
44387c478bd9Sstevel@tonic-gate
44397c478bd9Sstevel@tonic-gate if ((fas->f_intr & FAS_INT_DISCON) == 0) {
44407c478bd9Sstevel@tonic-gate /*
44417c478bd9Sstevel@tonic-gate * we call actions here rather than returning to phasemanage
44427c478bd9Sstevel@tonic-gate * (this is the most frequently called action)
44437c478bd9Sstevel@tonic-gate */
44447c478bd9Sstevel@tonic-gate switch (fas->f_stat & FAS_PHASE_MASK) {
44457c478bd9Sstevel@tonic-gate case FAS_PHASE_DATA_IN:
44467c478bd9Sstevel@tonic-gate case FAS_PHASE_DATA_OUT:
44477c478bd9Sstevel@tonic-gate New_state(fas, ACTS_DATA);
44487c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
44497c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_PHASE_DATA_END,
44507c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (phase_data)");
44517c478bd9Sstevel@tonic-gate return (fas_handle_data_start(fas));
44527c478bd9Sstevel@tonic-gate
44537c478bd9Sstevel@tonic-gate case FAS_PHASE_MSG_OUT:
44547c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_OUT);
44557c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
44567c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_PHASE_MSG_OUT_END,
44577c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (phase_msg_out)");
44587c478bd9Sstevel@tonic-gate return (fas_handle_msg_out_start(fas));
44597c478bd9Sstevel@tonic-gate
44607c478bd9Sstevel@tonic-gate case FAS_PHASE_MSG_IN:
44617c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN);
44627c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
44637c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_PHASE_MSG_IN_END,
44647c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (phase_msg_in)");
44657c478bd9Sstevel@tonic-gate return (fas_handle_msg_in_start(fas));
44667c478bd9Sstevel@tonic-gate
44677c478bd9Sstevel@tonic-gate case FAS_PHASE_STATUS:
44687c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
44697c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
44707c478bd9Sstevel@tonic-gate if (fas_ptest_status & (1<<Tgt(fas->f_current_sp))) {
44717c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
44727c478bd9Sstevel@tonic-gate }
44737c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
44747c478bd9Sstevel@tonic-gate
44757c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_COMP_SEQ);
44767c478bd9Sstevel@tonic-gate New_state(fas, ACTS_C_CMPLT);
44777c478bd9Sstevel@tonic-gate
44787c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
44797c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_PHASE_STATUS_END,
44807c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (phase_status)");
44817c478bd9Sstevel@tonic-gate return (fas_handle_c_cmplt(fas));
44827c478bd9Sstevel@tonic-gate
44837c478bd9Sstevel@tonic-gate case FAS_PHASE_COMMAND:
44847c478bd9Sstevel@tonic-gate New_state(fas, ACTS_CMD_START);
44857c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
44867c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_PHASE_CMD_END,
44877c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (phase_cmd)");
44887c478bd9Sstevel@tonic-gate return (fas_handle_cmd_start(fas));
44897c478bd9Sstevel@tonic-gate }
44907c478bd9Sstevel@tonic-gate
44917c478bd9Sstevel@tonic-gate fas_printstate(fas, "Unknown bus phase");
44927c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_UNKNOWN_RESET_END,
44937c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (reset)");
44947c478bd9Sstevel@tonic-gate return (ACTION_RESET);
44957c478bd9Sstevel@tonic-gate
44967c478bd9Sstevel@tonic-gate } else {
44977c478bd9Sstevel@tonic-gate /*
44987c478bd9Sstevel@tonic-gate * Okay. What to do now? Let's try (for the time being)
44997c478bd9Sstevel@tonic-gate * assuming that the target went south and dropped busy,
45007c478bd9Sstevel@tonic-gate * as a disconnect implies that either we received
45017c478bd9Sstevel@tonic-gate * a completion or a disconnect message, or that we
45027c478bd9Sstevel@tonic-gate * had sent an ABORT OPERATION or BUS DEVICE RESET
45037c478bd9Sstevel@tonic-gate * message. In either case, we expected the disconnect
45047c478bd9Sstevel@tonic-gate * and should have fielded it elsewhere.
45057c478bd9Sstevel@tonic-gate *
45067c478bd9Sstevel@tonic-gate * If we see a chip disconnect here, this is an unexpected
45077c478bd9Sstevel@tonic-gate * loss of BSY*. Clean up the state of the chip and return.
45087c478bd9Sstevel@tonic-gate *
45097c478bd9Sstevel@tonic-gate */
45107c478bd9Sstevel@tonic-gate int msgout = fas->f_cur_msgout[0];
45117c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
45127c478bd9Sstevel@tonic-gate int target = Tgt(sp);
45137c478bd9Sstevel@tonic-gate
45147c478bd9Sstevel@tonic-gate if (msgout == MSG_HEAD_QTAG || msgout == MSG_SIMPLE_QTAG) {
45157c478bd9Sstevel@tonic-gate msgout = fas->f_cur_msgout[2];
45167c478bd9Sstevel@tonic-gate }
45177c478bd9Sstevel@tonic-gate EPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
451819397407SSherry Moore fas->f_cur_msgout[0], fas->f_cur_msgout[1],
451919397407SSherry Moore fas->f_cur_msgout[2], fas->f_last_msgout);
45207c478bd9Sstevel@tonic-gate
45217c478bd9Sstevel@tonic-gate if (msgout == MSG_ABORT || msgout == MSG_ABORT_TAG ||
45227c478bd9Sstevel@tonic-gate msgout == MSG_DEVICE_RESET) {
45237c478bd9Sstevel@tonic-gate IPRINTF2("Successful %s message to target %d\n",
45247c478bd9Sstevel@tonic-gate scsi_mname(msgout), Tgt(sp));
45257c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CMDPROXY) {
45267c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] = TRUE;
45277c478bd9Sstevel@tonic-gate }
45287c478bd9Sstevel@tonic-gate if (msgout == MSG_ABORT || msgout == MSG_ABORT_TAG) {
45297c478bd9Sstevel@tonic-gate fas->f_abort_msg_sent++;
45307c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
45317c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp,
45327c478bd9Sstevel@tonic-gate CMD_ABORTED, STAT_ABORTED);
45337c478bd9Sstevel@tonic-gate }
45347c478bd9Sstevel@tonic-gate } else if (msgout == MSG_DEVICE_RESET) {
45357c478bd9Sstevel@tonic-gate fas->f_reset_msg_sent++;
45367c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
45377c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp,
45387c478bd9Sstevel@tonic-gate CMD_RESET, STAT_DEV_RESET);
45397c478bd9Sstevel@tonic-gate }
45407c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, target);
45417c478bd9Sstevel@tonic-gate }
45427c478bd9Sstevel@tonic-gate } else {
45437c478bd9Sstevel@tonic-gate if ((fas->f_last_msgout == MSG_EXTENDED) &&
45447c478bd9Sstevel@tonic-gate (fas->f_last_msgin == MSG_REJECT)) {
45457c478bd9Sstevel@tonic-gate /*
45467c478bd9Sstevel@tonic-gate * the target rejected the negotiations,
45477c478bd9Sstevel@tonic-gate * so resubmit again (no_sync/no_wide
45487c478bd9Sstevel@tonic-gate * is now set)
45497c478bd9Sstevel@tonic-gate */
45507c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
45517c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_EN_RESEL);
45527c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
45537c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
45547c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
45557c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_TRANFLAG;
45567c478bd9Sstevel@tonic-gate (void) fas_accept_pkt(fas, sp, NO_TRAN_BUSY);
45577c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
45587c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
45597c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_UNKNOWN_INT_DISCON_END,
45607c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (int_discon)");
45617c478bd9Sstevel@tonic-gate return (ACTION_SEARCH);
45627c478bd9Sstevel@tonic-gate
45637c478bd9Sstevel@tonic-gate } else if (fas->f_last_msgout == MSG_EXTENDED) {
45647c478bd9Sstevel@tonic-gate /*
45657c478bd9Sstevel@tonic-gate * target dropped off the bus during
45667c478bd9Sstevel@tonic-gate * negotiations
45677c478bd9Sstevel@tonic-gate */
45687c478bd9Sstevel@tonic-gate fas_reset_sync_wide(fas);
45697c478bd9Sstevel@tonic-gate fas->f_sdtr_sent = fas->f_wdtr_sent = 0;
45707c478bd9Sstevel@tonic-gate }
45717c478bd9Sstevel@tonic-gate
45727c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_UNX_BUS_FREE, 0);
45737c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
45747c478bd9Sstevel@tonic-gate fas_printstate(fas, "unexpected bus free");
45757c478bd9Sstevel@tonic-gate #endif
45767c478bd9Sstevel@tonic-gate }
45777c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_UNKNOWN_INT_DISCON_END,
45787c478bd9Sstevel@tonic-gate "fas_handle_unknown_end (int_discon)");
45797c478bd9Sstevel@tonic-gate return (ACTION_FINISH);
45807c478bd9Sstevel@tonic-gate }
45817c478bd9Sstevel@tonic-gate _NOTE(NOT_REACHED)
45827c478bd9Sstevel@tonic-gate /* NOTREACHED */
45837c478bd9Sstevel@tonic-gate }
45847c478bd9Sstevel@tonic-gate
45857c478bd9Sstevel@tonic-gate /*
45867c478bd9Sstevel@tonic-gate * handle target disconnecting
45877c478bd9Sstevel@tonic-gate */
45887c478bd9Sstevel@tonic-gate static int
fas_handle_clearing(struct fas * fas)45897c478bd9Sstevel@tonic-gate fas_handle_clearing(struct fas *fas)
45907c478bd9Sstevel@tonic-gate {
45917c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
45927c478bd9Sstevel@tonic-gate
45937c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CLEARING_START,
45947c478bd9Sstevel@tonic-gate "fas_handle_clearing_start");
45957c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_clearing:\n");
45967c478bd9Sstevel@tonic-gate
45977c478bd9Sstevel@tonic-gate if (fas->f_laststate == ACTS_C_CMPLT ||
45987c478bd9Sstevel@tonic-gate fas->f_laststate == ACTS_MSG_IN_DONE) {
45997c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
46007c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
46017c478bd9Sstevel@tonic-gate
46027c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas,
460319397407SSherry Moore (uchar_t *)&fasreg->fas_stat);
46047c478bd9Sstevel@tonic-gate fas->f_intr = fas_reg_read(fas,
460519397407SSherry Moore (uchar_t *)&fasreg->fas_intr);
46067c478bd9Sstevel@tonic-gate if (fas->f_intr & (FAS_INT_RESET | FAS_INT_ILLEGAL)) {
46077c478bd9Sstevel@tonic-gate return (fas_illegal_cmd_or_bus_reset(fas));
46087c478bd9Sstevel@tonic-gate }
46097c478bd9Sstevel@tonic-gate } else {
46107c478bd9Sstevel@tonic-gate /*
46117c478bd9Sstevel@tonic-gate * change e_laststate for the next time around
46127c478bd9Sstevel@tonic-gate */
46137c478bd9Sstevel@tonic-gate fas->f_laststate = ACTS_CLEARING;
46147c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
46157c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_CLEARING_RETURN1_END,
46167c478bd9Sstevel@tonic-gate "fas_handle_clearing_end (ACTION_RETURN1)");
46177c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
46187c478bd9Sstevel@tonic-gate }
46197c478bd9Sstevel@tonic-gate }
46207c478bd9Sstevel@tonic-gate
46217c478bd9Sstevel@tonic-gate if (fas->f_intr == FAS_INT_DISCON) {
46227c478bd9Sstevel@tonic-gate /*
46237c478bd9Sstevel@tonic-gate * At this point the FAS chip has disconnected. The bus should
46247c478bd9Sstevel@tonic-gate * be either quiet or someone may be attempting a reselection
46257c478bd9Sstevel@tonic-gate * of us (or somebody else). Call the routine that sets the
46267c478bd9Sstevel@tonic-gate * chip back to a correct and known state.
46277c478bd9Sstevel@tonic-gate * If the last message in was a disconnect, search
46287c478bd9Sstevel@tonic-gate * for new work to do, else return to call fas_finish()
46297c478bd9Sstevel@tonic-gate */
46307c478bd9Sstevel@tonic-gate fas->f_last_msgout = 0xff;
46317c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
46327c478bd9Sstevel@tonic-gate if (fas->f_last_msgin == MSG_DISCONNECT) {
46337c478bd9Sstevel@tonic-gate
46347c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_EN_RESEL);
46357c478bd9Sstevel@tonic-gate
46367c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
46377c478bd9Sstevel@tonic-gate
46387c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp != NULL);
46397c478bd9Sstevel@tonic-gate EPRINTF2("disconnecting %d.%d\n", Tgt(sp), Lun(sp));
46407c478bd9Sstevel@tonic-gate
46417c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_DISCON;
46427c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_CMDDISC;
46437c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
46447c478bd9Sstevel@tonic-gate fas->f_ndisc++;
46457c478bd9Sstevel@tonic-gate }
46467c478bd9Sstevel@tonic-gate ASSERT((fas->f_ncmds >= 0) && (fas->f_ndisc >= 0));
46477c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
46487c478bd9Sstevel@tonic-gate
46497c478bd9Sstevel@tonic-gate fas->f_current_sp = NULL;
46507c478bd9Sstevel@tonic-gate
46517c478bd9Sstevel@tonic-gate /*
46527c478bd9Sstevel@tonic-gate * start a cmd here to save time
46537c478bd9Sstevel@tonic-gate */
46547c478bd9Sstevel@tonic-gate if ((fas->f_ncmds > fas->f_ndisc) && fas_ustart(fas)) {
46557c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
46567c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_CLEARING_RETURN2_END,
46577c478bd9Sstevel@tonic-gate "fas_handle_clearing_end (ACTION_RETURN2)");
46587c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
46597c478bd9Sstevel@tonic-gate }
46607c478bd9Sstevel@tonic-gate
46617c478bd9Sstevel@tonic-gate
46627c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
46637c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_CLEARING_RETURN3_END,
46647c478bd9Sstevel@tonic-gate "fas_handle_clearing_end (ACTION_RETURN3)");
46657c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
46667c478bd9Sstevel@tonic-gate } else {
46677c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CLEARING_END,
46687c478bd9Sstevel@tonic-gate "fas_handle_clearing_end");
46697c478bd9Sstevel@tonic-gate return (fas_finish(fas));
46707c478bd9Sstevel@tonic-gate }
46717c478bd9Sstevel@tonic-gate } else {
46727c478bd9Sstevel@tonic-gate /*
46737c478bd9Sstevel@tonic-gate * If the target didn't disconnect from the
46747c478bd9Sstevel@tonic-gate * bus, that is a gross fatal error.
46757c478bd9Sstevel@tonic-gate * XXX this can be caused by asserting ATN
46767c478bd9Sstevel@tonic-gate * XXX check bus phase and if msgout, send a message
46777c478bd9Sstevel@tonic-gate */
46787c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
46797c478bd9Sstevel@tonic-gate "Target %d didn't disconnect after sending %s",
46807c478bd9Sstevel@tonic-gate Tgt(sp), scsi_mname(fas->f_last_msgin));
46817c478bd9Sstevel@tonic-gate
46827c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
46837c478bd9Sstevel@tonic-gate
46847c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
46857c478bd9Sstevel@tonic-gate IPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
468619397407SSherry Moore fas->f_cur_msgout[0], fas->f_cur_msgout[1],
468719397407SSherry Moore fas->f_cur_msgout[2], fas->f_last_msgout);
46887c478bd9Sstevel@tonic-gate IPRINTF1("last msgin=%x\n", fas->f_last_msgin);
46897c478bd9Sstevel@tonic-gate #endif
46907c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CLEARING_ABORT_END,
46917c478bd9Sstevel@tonic-gate "fas_handle_clearing_end (ACTION_ABORT_CURCMD)");
46927c478bd9Sstevel@tonic-gate return (ACTION_ABORT_ALLCMDS);
46937c478bd9Sstevel@tonic-gate }
46947c478bd9Sstevel@tonic-gate }
46957c478bd9Sstevel@tonic-gate
46967c478bd9Sstevel@tonic-gate /*
46977c478bd9Sstevel@tonic-gate * handle data phase start
46987c478bd9Sstevel@tonic-gate */
46997c478bd9Sstevel@tonic-gate static int
fas_handle_data_start(struct fas * fas)47007c478bd9Sstevel@tonic-gate fas_handle_data_start(struct fas *fas)
47017c478bd9Sstevel@tonic-gate {
47027c478bd9Sstevel@tonic-gate uint64_t end;
47037c478bd9Sstevel@tonic-gate uint32_t amt;
47047c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
47057c478bd9Sstevel@tonic-gate int sending, phase;
47067c478bd9Sstevel@tonic-gate
47077c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_START,
47087c478bd9Sstevel@tonic-gate "fas_handle_data_start");
47097c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_data_start:\n");
47107c478bd9Sstevel@tonic-gate
47117c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_DMAVALID) == 0) {
47127c478bd9Sstevel@tonic-gate fas_printstate(fas, "unexpected data phase");
47137c478bd9Sstevel@tonic-gate bad:
47147c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
47157c478bd9Sstevel@tonic-gate
47167c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_ABORT1_END,
47177c478bd9Sstevel@tonic-gate "fas_handle_data_end (ACTION_ABORT_CURCMD1)");
47187c478bd9Sstevel@tonic-gate return (ACTION_ABORT_CURCMD);
47197c478bd9Sstevel@tonic-gate } else {
47207c478bd9Sstevel@tonic-gate sending = (sp->cmd_flags & CFLAG_DMASEND)? 1 : 0;
47217c478bd9Sstevel@tonic-gate }
47227c478bd9Sstevel@tonic-gate
47237c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_RESTORE_PTRS) {
47247c478bd9Sstevel@tonic-gate if (fas_restore_pointers(fas, sp)) {
47257c478bd9Sstevel@tonic-gate return (ACTION_ABORT_CURCMD);
47267c478bd9Sstevel@tonic-gate }
47277c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_RESTORE_PTRS;
47287c478bd9Sstevel@tonic-gate }
47297c478bd9Sstevel@tonic-gate
47307c478bd9Sstevel@tonic-gate /*
47317c478bd9Sstevel@tonic-gate * And make sure our DMA pointers are in good shape.
47327c478bd9Sstevel@tonic-gate *
47337c478bd9Sstevel@tonic-gate * Because SCSI is SCSI, the current DMA pointer has got to be
47347c478bd9Sstevel@tonic-gate * greater than or equal to our DMA base address. All other cases
47357c478bd9Sstevel@tonic-gate * that might have affected this always set curaddr to be >=
47367c478bd9Sstevel@tonic-gate * to the DMA base address.
47377c478bd9Sstevel@tonic-gate */
47387c478bd9Sstevel@tonic-gate ASSERT(sp->cmd_cur_addr >= sp->cmd_dmacookie.dmac_address);
47397c478bd9Sstevel@tonic-gate end = (uint64_t)sp->cmd_dmacookie.dmac_address +
474019397407SSherry Moore (uint64_t)sp->cmd_dmacookie.dmac_size;
47417c478bd9Sstevel@tonic-gate
47427c478bd9Sstevel@tonic-gate DPRINTF5(
47437c478bd9Sstevel@tonic-gate "cmd_data_count=%x, dmacount=%x, curaddr=%x, end=%"
47447c478bd9Sstevel@tonic-gate PRIx64 ", nwin=%x\n",
47457c478bd9Sstevel@tonic-gate sp->cmd_data_count, sp->cmd_dmacount, sp->cmd_cur_addr, end,
47467c478bd9Sstevel@tonic-gate sp->cmd_nwin);
47477c478bd9Sstevel@tonic-gate DPRINTF2("dmac_address = %x, dmac_size=%lx\n",
47487c478bd9Sstevel@tonic-gate sp->cmd_dmacookie.dmac_address, sp->cmd_dmacookie.dmac_size);
47497c478bd9Sstevel@tonic-gate
47507c478bd9Sstevel@tonic-gate if (sp->cmd_cur_addr >= end) {
47517c478bd9Sstevel@tonic-gate if (fas_next_window(fas, sp, end)) {
47527c478bd9Sstevel@tonic-gate goto bad;
47537c478bd9Sstevel@tonic-gate }
47547c478bd9Sstevel@tonic-gate end = (uint64_t)sp->cmd_dmacookie.dmac_address +
475519397407SSherry Moore (uint64_t)sp->cmd_dmacookie.dmac_size;
47567c478bd9Sstevel@tonic-gate DPRINTF2("dmac_address=%x, dmac_size=%lx\n",
47577c478bd9Sstevel@tonic-gate sp->cmd_dmacookie.dmac_address,
47587c478bd9Sstevel@tonic-gate sp->cmd_dmacookie.dmac_size);
47597c478bd9Sstevel@tonic-gate }
47607c478bd9Sstevel@tonic-gate
47617c478bd9Sstevel@tonic-gate amt = end - sp->cmd_cur_addr;
47627c478bd9Sstevel@tonic-gate if (fas->f_dma_attr->dma_attr_count_max < amt) {
47637c478bd9Sstevel@tonic-gate amt = fas->f_dma_attr->dma_attr_count_max;
47647c478bd9Sstevel@tonic-gate }
47657c478bd9Sstevel@tonic-gate DPRINTF3("amt=%x, end=%lx, cur_addr=%x\n", amt, end, sp->cmd_cur_addr);
47667c478bd9Sstevel@tonic-gate
47677c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
47687c478bd9Sstevel@tonic-gate /*
47697c478bd9Sstevel@tonic-gate * Make sure that we don't cross a boundary we can't handle
47707c478bd9Sstevel@tonic-gate */
47717c478bd9Sstevel@tonic-gate end = (uint64_t)sp->cmd_cur_addr + (uint64_t)amt - 1;
47727c478bd9Sstevel@tonic-gate if ((end & ~fas->f_dma_attr->dma_attr_seg) !=
47737c478bd9Sstevel@tonic-gate (sp->cmd_cur_addr & ~fas->f_dma_attr->dma_attr_seg)) {
47747c478bd9Sstevel@tonic-gate EPRINTF3("curaddr %x curaddr+amt %" PRIx64
47757c478bd9Sstevel@tonic-gate " cntr_max %" PRIx64 "\n",
47767c478bd9Sstevel@tonic-gate sp->cmd_cur_addr, end, fas->f_dma_attr->dma_attr_seg);
47777c478bd9Sstevel@tonic-gate amt = (end & ~fas->f_dma_attr->dma_attr_seg) - sp->cmd_cur_addr;
47787c478bd9Sstevel@tonic-gate if (amt == 0 || amt > fas->f_dma_attr->dma_attr_count_max) {
47797c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "illegal dma boundary? %x", amt);
47807c478bd9Sstevel@tonic-gate goto bad;
47817c478bd9Sstevel@tonic-gate }
47827c478bd9Sstevel@tonic-gate }
47837c478bd9Sstevel@tonic-gate #endif
47847c478bd9Sstevel@tonic-gate
47857c478bd9Sstevel@tonic-gate end = (uint64_t)sp->cmd_dmacookie.dmac_address +
478619397407SSherry Moore (uint64_t)sp->cmd_dmacookie.dmac_size -
478719397407SSherry Moore (uint64_t)sp->cmd_cur_addr;
47887c478bd9Sstevel@tonic-gate if (amt > end) {
47897c478bd9Sstevel@tonic-gate EPRINTF4("ovflow amt %x s.b. %" PRIx64 " curaddr %x count %x\n",
47907c478bd9Sstevel@tonic-gate amt, end, sp->cmd_cur_addr, sp->cmd_dmacount);
47917c478bd9Sstevel@tonic-gate amt = (uint32_t)end;
47927c478bd9Sstevel@tonic-gate }
47937c478bd9Sstevel@tonic-gate
47947c478bd9Sstevel@tonic-gate fas->f_lastcount = amt;
47957c478bd9Sstevel@tonic-gate
47967c478bd9Sstevel@tonic-gate EPRINTF4("%d.%d cmd 0x%x to xfer %x\n", Tgt(sp), Lun(sp),
47977c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_cdbp[0], amt);
47987c478bd9Sstevel@tonic-gate
47997c478bd9Sstevel@tonic-gate phase = fas->f_stat & FAS_PHASE_MASK;
48007c478bd9Sstevel@tonic-gate
48017c478bd9Sstevel@tonic-gate if ((phase == FAS_PHASE_DATA_IN) && !sending) {
48027c478bd9Sstevel@tonic-gate FAS_DMA_WRITE(fas, amt, sp->cmd_cur_addr,
48037c478bd9Sstevel@tonic-gate CMD_TRAN_INFO|CMD_DMA);
48047c478bd9Sstevel@tonic-gate } else if ((phase == FAS_PHASE_DATA_OUT) && sending) {
48057c478bd9Sstevel@tonic-gate FAS_DMA_READ(fas, amt, sp->cmd_cur_addr, amt,
48067c478bd9Sstevel@tonic-gate CMD_TRAN_INFO|CMD_DMA);
48077c478bd9Sstevel@tonic-gate } else {
48087c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
48097c478bd9Sstevel@tonic-gate "unwanted data xfer direction for Target %d", Tgt(sp));
48107c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_DMA_DERR, 0);
48117c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_ABORT2_END,
48127c478bd9Sstevel@tonic-gate "fas_handle_data_end (ACTION_ABORT_CURCMD2)");
48137c478bd9Sstevel@tonic-gate return (ACTION_ABORT_CURCMD);
48147c478bd9Sstevel@tonic-gate }
48157c478bd9Sstevel@tonic-gate
48167c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
48177c478bd9Sstevel@tonic-gate if (!sending && (fas_ptest_data_in & (1<<Tgt(sp)))) {
48187c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
48197c478bd9Sstevel@tonic-gate }
48207c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
48217c478bd9Sstevel@tonic-gate
48227c478bd9Sstevel@tonic-gate New_state(fas, ACTS_DATA_DONE);
48237c478bd9Sstevel@tonic-gate
48247c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_END,
48257c478bd9Sstevel@tonic-gate "fas_handle_data_end (ACTION_RETURN)");
48267c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
48277c478bd9Sstevel@tonic-gate }
48287c478bd9Sstevel@tonic-gate
48297c478bd9Sstevel@tonic-gate static int
fas_handle_data_done(struct fas * fas)48307c478bd9Sstevel@tonic-gate fas_handle_data_done(struct fas *fas)
48317c478bd9Sstevel@tonic-gate {
48327c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
48337c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma;
48347c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
48357c478bd9Sstevel@tonic-gate uint32_t xfer_amt;
48367c478bd9Sstevel@tonic-gate char was_sending;
48377c478bd9Sstevel@tonic-gate uchar_t stat, fifoamt, tgt;
48387c478bd9Sstevel@tonic-gate
48397c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_DONE_START,
48407c478bd9Sstevel@tonic-gate "fas_handle_data_done_start");
48417c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_data_done\n");
48427c478bd9Sstevel@tonic-gate
48437c478bd9Sstevel@tonic-gate tgt = Tgt(sp);
48447c478bd9Sstevel@tonic-gate stat = fas->f_stat;
48457c478bd9Sstevel@tonic-gate was_sending = (sp->cmd_flags & CFLAG_DMASEND) ? 1 : 0;
48467c478bd9Sstevel@tonic-gate
48477c478bd9Sstevel@tonic-gate /*
48487c478bd9Sstevel@tonic-gate * Check for DMA errors (parity or memory fault)
48497c478bd9Sstevel@tonic-gate */
48507c478bd9Sstevel@tonic-gate if ((fas->f_dma_csr = fas_dma_reg_read(fas, &dmar->dma_csr)) &
48517c478bd9Sstevel@tonic-gate DMA_ERRPEND) {
48527c478bd9Sstevel@tonic-gate /*
48537c478bd9Sstevel@tonic-gate * It would be desirable to set the ATN* line and attempt to
48547c478bd9Sstevel@tonic-gate * do the whole schmear of INITIATOR DETECTED ERROR here,
48557c478bd9Sstevel@tonic-gate * but that is too hard to do at present.
48567c478bd9Sstevel@tonic-gate */
48577c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "Unrecoverable DMA error on dma %s",
48587c478bd9Sstevel@tonic-gate (was_sending) ? "send" : "receive");
48597c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
48607c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_DONE_RESET_END,
48617c478bd9Sstevel@tonic-gate "fas_handle_data_done_end (ACTION_RESET)");
48627c478bd9Sstevel@tonic-gate return (ACTION_RESET);
48637c478bd9Sstevel@tonic-gate }
48647c478bd9Sstevel@tonic-gate
48657c478bd9Sstevel@tonic-gate /*
48667c478bd9Sstevel@tonic-gate * Data Receive conditions:
48677c478bd9Sstevel@tonic-gate *
48687c478bd9Sstevel@tonic-gate * Check for parity errors. If we have a parity error upon
48697c478bd9Sstevel@tonic-gate * receive, the FAS chip has asserted ATN* for us already.
48707c478bd9Sstevel@tonic-gate */
48717c478bd9Sstevel@tonic-gate if (!was_sending) {
48727c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
48737c478bd9Sstevel@tonic-gate if (fas_ptest_data_in & (1<<tgt)) {
48747c478bd9Sstevel@tonic-gate fas_ptest_data_in = 0;
48757c478bd9Sstevel@tonic-gate stat |= FAS_STAT_PERR;
48767c478bd9Sstevel@tonic-gate if (fas_test_stop > 1) {
48777c478bd9Sstevel@tonic-gate debug_enter("ptest_data_in");
48787c478bd9Sstevel@tonic-gate }
48797c478bd9Sstevel@tonic-gate }
48807c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
48817c478bd9Sstevel@tonic-gate if (stat & FAS_STAT_PERR) {
48827c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
48837c478bd9Sstevel@tonic-gate "SCSI bus DATA IN phase parity error");
48847c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = MSG_INITIATOR_ERROR;
48857c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
48867c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
48877c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason = CMD_TRAN_ERR;
48887c478bd9Sstevel@tonic-gate }
48897c478bd9Sstevel@tonic-gate }
48907c478bd9Sstevel@tonic-gate
48917c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(fas);
48927c478bd9Sstevel@tonic-gate
48937c478bd9Sstevel@tonic-gate /*
48947c478bd9Sstevel@tonic-gate * Check to make sure we're still connected to the target.
48957c478bd9Sstevel@tonic-gate * If the target dropped the bus, that is a fatal error.
48967c478bd9Sstevel@tonic-gate * We don't even attempt to count what we were transferring
48977c478bd9Sstevel@tonic-gate * here. Let fas_handle_unknown clean up for us.
48987c478bd9Sstevel@tonic-gate */
48997c478bd9Sstevel@tonic-gate if (fas->f_intr != FAS_INT_BUS) {
49007c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
49017c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
49027c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_DATA_DONE_PHASEMANAGE_END,
49037c478bd9Sstevel@tonic-gate "fas_handle_data_done_end (ACTION_PHASEMANAGE)");
49047c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
49057c478bd9Sstevel@tonic-gate }
49067c478bd9Sstevel@tonic-gate
49077c478bd9Sstevel@tonic-gate /*
49087c478bd9Sstevel@tonic-gate * Figure out how far we got.
49097c478bd9Sstevel@tonic-gate * Latch up fifo amount first and double if wide has been enabled
49107c478bd9Sstevel@tonic-gate */
49117c478bd9Sstevel@tonic-gate fifoamt = FIFO_CNT(fas);
49127c478bd9Sstevel@tonic-gate if (fas->f_wide_enabled & (1<<tgt)) {
49137c478bd9Sstevel@tonic-gate fifoamt = fifoamt << 1;
49147c478bd9Sstevel@tonic-gate }
49157c478bd9Sstevel@tonic-gate
49167c478bd9Sstevel@tonic-gate if (stat & FAS_STAT_XZERO) {
49177c478bd9Sstevel@tonic-gate xfer_amt = fas->f_lastcount;
49187c478bd9Sstevel@tonic-gate } else {
49197c478bd9Sstevel@tonic-gate GET_FAS_COUNT(fasreg, xfer_amt);
49207c478bd9Sstevel@tonic-gate xfer_amt = fas->f_lastcount - xfer_amt;
49217c478bd9Sstevel@tonic-gate }
49227c478bd9Sstevel@tonic-gate DPRINTF4("fifoamt=%x, xfer_amt=%x, lastcount=%x, stat=%x\n",
49237c478bd9Sstevel@tonic-gate fifoamt, xfer_amt, fas->f_lastcount, stat);
49247c478bd9Sstevel@tonic-gate
49257c478bd9Sstevel@tonic-gate
49267c478bd9Sstevel@tonic-gate /*
49277c478bd9Sstevel@tonic-gate * Unconditionally knock off by the amount left
49287c478bd9Sstevel@tonic-gate * in the fifo if we were sending out the SCSI bus.
49297c478bd9Sstevel@tonic-gate *
49307c478bd9Sstevel@tonic-gate * If we were receiving from the SCSI bus, believe
49317c478bd9Sstevel@tonic-gate * what the chip told us (either XZERO or by the
49327c478bd9Sstevel@tonic-gate * value calculated from the counter register).
49337c478bd9Sstevel@tonic-gate * The reason we don't look at the fifo for
49347c478bd9Sstevel@tonic-gate * incoming data is that in synchronous mode
49357c478bd9Sstevel@tonic-gate * the fifo may have further data bytes, and
49367c478bd9Sstevel@tonic-gate * for async mode we assume that all data in
49377c478bd9Sstevel@tonic-gate * the fifo will have been transferred before
49387c478bd9Sstevel@tonic-gate * the fas asserts an interrupt.
49397c478bd9Sstevel@tonic-gate */
49407c478bd9Sstevel@tonic-gate if (was_sending) {
49417c478bd9Sstevel@tonic-gate xfer_amt -= fifoamt;
49427c478bd9Sstevel@tonic-gate }
49437c478bd9Sstevel@tonic-gate
49447c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
494519397407SSherry Moore {
49467c478bd9Sstevel@tonic-gate int phase = stat & FAS_PHASE_MASK;
49477c478bd9Sstevel@tonic-gate fas->f_stat2 = fas_reg_read(fas,
494819397407SSherry Moore (uchar_t *)&fasreg->fas_stat2);
49497c478bd9Sstevel@tonic-gate
49507c478bd9Sstevel@tonic-gate if (((fas->f_stat & FAS_STAT_XZERO) == 0) &&
49517c478bd9Sstevel@tonic-gate (phase != FAS_PHASE_DATA_IN) &&
49527c478bd9Sstevel@tonic-gate (phase != FAS_PHASE_DATA_OUT) &&
49537c478bd9Sstevel@tonic-gate (fas->f_stat2 & FAS_STAT2_ISHUTTLE)) {
49547c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
49557c478bd9Sstevel@tonic-gate "input shuttle not empty at end of data phase");
49567c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
49577c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_DONE_RESET_END,
495819397407SSherry Moore "fas_handle_data_done_end (ACTION_RESET)");
49597c478bd9Sstevel@tonic-gate return (ACTION_RESET);
49607c478bd9Sstevel@tonic-gate }
49617c478bd9Sstevel@tonic-gate }
49627c478bd9Sstevel@tonic-gate #endif /* FASDEBUG */
49637c478bd9Sstevel@tonic-gate
49647c478bd9Sstevel@tonic-gate /*
49657c478bd9Sstevel@tonic-gate * If this was a synchronous transfer, flag it.
49667c478bd9Sstevel@tonic-gate * Also check for the errata condition of long
49677c478bd9Sstevel@tonic-gate * last REQ/ pulse for some synchronous targets
49687c478bd9Sstevel@tonic-gate */
49697c478bd9Sstevel@tonic-gate if (fas->f_offset[tgt]) {
49707c478bd9Sstevel@tonic-gate /*
49717c478bd9Sstevel@tonic-gate * flag that a synchronous data xfer took place
49727c478bd9Sstevel@tonic-gate */
49737c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_SYNC;
49747c478bd9Sstevel@tonic-gate
49757c478bd9Sstevel@tonic-gate if (was_sending)
49767c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
49777c478bd9Sstevel@tonic-gate } else {
49787c478bd9Sstevel@tonic-gate /*
49797c478bd9Sstevel@tonic-gate * If we aren't doing Synchronous Data Transfers,
49807c478bd9Sstevel@tonic-gate * definitely offload the fifo.
49817c478bd9Sstevel@tonic-gate */
49827c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
49837c478bd9Sstevel@tonic-gate }
49847c478bd9Sstevel@tonic-gate
49857c478bd9Sstevel@tonic-gate /*
49867c478bd9Sstevel@tonic-gate * adjust pointers...
49877c478bd9Sstevel@tonic-gate */
49887c478bd9Sstevel@tonic-gate DPRINTF3("before:cmd_data_count=%x, cmd_cur_addr=%x, xfer_amt=%x\n",
49897c478bd9Sstevel@tonic-gate sp->cmd_data_count, sp->cmd_cur_addr, xfer_amt);
49907c478bd9Sstevel@tonic-gate sp->cmd_data_count += xfer_amt;
49917c478bd9Sstevel@tonic-gate sp->cmd_cur_addr += xfer_amt;
49927c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_XFERRED_DATA;
49937c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
49947c478bd9Sstevel@tonic-gate DPRINTF3("after:cmd_data_count=%x, cmd_cur_addr=%x, xfer_amt=%x\n",
49957c478bd9Sstevel@tonic-gate sp->cmd_data_count, sp->cmd_cur_addr, xfer_amt);
49967c478bd9Sstevel@tonic-gate
49977c478bd9Sstevel@tonic-gate stat &= FAS_PHASE_MASK;
49987c478bd9Sstevel@tonic-gate if (stat == FAS_PHASE_DATA_IN || stat == FAS_PHASE_DATA_OUT) {
49997c478bd9Sstevel@tonic-gate fas->f_state = ACTS_DATA;
50007c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
50017c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_DATA_DONE_ACTION1_END,
50027c478bd9Sstevel@tonic-gate "fas_handle_data_done_end (action1)");
50037c478bd9Sstevel@tonic-gate return (fas_handle_data_start(fas));
50047c478bd9Sstevel@tonic-gate }
50057c478bd9Sstevel@tonic-gate
50067c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_DATA_DONE_ACTION2_END,
50077c478bd9Sstevel@tonic-gate "fas_handle_data_done_end (action2)");
50087c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
50097c478bd9Sstevel@tonic-gate }
50107c478bd9Sstevel@tonic-gate
50117c478bd9Sstevel@tonic-gate static char msginperr[] = "SCSI bus MESSAGE IN phase parity error";
50127c478bd9Sstevel@tonic-gate
50137c478bd9Sstevel@tonic-gate static int
fas_handle_c_cmplt(struct fas * fas)50147c478bd9Sstevel@tonic-gate fas_handle_c_cmplt(struct fas *fas)
50157c478bd9Sstevel@tonic-gate {
50167c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
50177c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
50187c478bd9Sstevel@tonic-gate uchar_t sts, msg, intr, perr;
50197c478bd9Sstevel@tonic-gate
50207c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_C_CMPLT_START,
50217c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_start");
50227c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_c_cmplt:\n");
50237c478bd9Sstevel@tonic-gate
50247c478bd9Sstevel@tonic-gate
50257c478bd9Sstevel@tonic-gate /*
50267c478bd9Sstevel@tonic-gate * if target is fast, we can get cmd. completion by the time we get
50277c478bd9Sstevel@tonic-gate * here. Otherwise, we'll have to taken an interrupt.
50287c478bd9Sstevel@tonic-gate */
50297c478bd9Sstevel@tonic-gate if (fas->f_laststate == ACTS_UNKNOWN) {
50307c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
50317c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas,
503219397407SSherry Moore (uchar_t *)&fasreg->fas_stat);
50337c478bd9Sstevel@tonic-gate intr = fas_reg_read(fas, (uchar_t *)&fasreg->fas_intr);
50347c478bd9Sstevel@tonic-gate fas->f_intr = intr;
50357c478bd9Sstevel@tonic-gate if (fas->f_intr & (FAS_INT_RESET | FAS_INT_ILLEGAL)) {
50367c478bd9Sstevel@tonic-gate return (fas_illegal_cmd_or_bus_reset(fas));
50377c478bd9Sstevel@tonic-gate }
50387c478bd9Sstevel@tonic-gate } else {
50397c478bd9Sstevel@tonic-gate /*
50407c478bd9Sstevel@tonic-gate * change f_laststate for the next time around
50417c478bd9Sstevel@tonic-gate */
50427c478bd9Sstevel@tonic-gate fas->f_laststate = ACTS_C_CMPLT;
50437c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
50447c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_C_CMPLT_RETURN1_END,
50457c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_end (ACTION_RETURN1)");
50467c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
50477c478bd9Sstevel@tonic-gate }
50487c478bd9Sstevel@tonic-gate } else {
50497c478bd9Sstevel@tonic-gate intr = fas->f_intr;
50507c478bd9Sstevel@tonic-gate }
50517c478bd9Sstevel@tonic-gate
50527c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
50537c478bd9Sstevel@tonic-gate if (fas_ptest_status & (1<<Tgt(sp))) {
50547c478bd9Sstevel@tonic-gate fas_ptest_status = 0;
50557c478bd9Sstevel@tonic-gate fas->f_stat |= FAS_STAT_PERR;
50567c478bd9Sstevel@tonic-gate if (fas_test_stop > 1) {
50577c478bd9Sstevel@tonic-gate debug_enter("ptest_status");
50587c478bd9Sstevel@tonic-gate }
50597c478bd9Sstevel@tonic-gate } else if ((fas_ptest_msgin & (1<<Tgt(sp))) && fas_ptest_msg == 0) {
50607c478bd9Sstevel@tonic-gate fas_ptest_msgin = 0;
50617c478bd9Sstevel@tonic-gate fas_ptest_msg = -1;
50627c478bd9Sstevel@tonic-gate fas->f_stat |= FAS_STAT_PERR;
50637c478bd9Sstevel@tonic-gate if (fas_test_stop > 1) {
50647c478bd9Sstevel@tonic-gate debug_enter("ptest_completion");
50657c478bd9Sstevel@tonic-gate }
50667c478bd9Sstevel@tonic-gate }
50677c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
50687c478bd9Sstevel@tonic-gate
50697c478bd9Sstevel@tonic-gate if (intr == FAS_INT_DISCON) {
50707c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
50717c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_C_CMPLT_ACTION1_END,
50727c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_end (action1)");
50737c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
50747c478bd9Sstevel@tonic-gate }
50757c478bd9Sstevel@tonic-gate
50767c478bd9Sstevel@tonic-gate if ((perr = (fas->f_stat & FAS_STAT_PERR)) != 0) {
50777c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
50787c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
50797c478bd9Sstevel@tonic-gate }
50807c478bd9Sstevel@tonic-gate
50817c478bd9Sstevel@tonic-gate /*
50827c478bd9Sstevel@tonic-gate * do a msg accept now and read the fifo data
50837c478bd9Sstevel@tonic-gate */
50847c478bd9Sstevel@tonic-gate if (intr & FAS_INT_FCMP) {
50857c478bd9Sstevel@tonic-gate /*
50867c478bd9Sstevel@tonic-gate * The FAS manuals state that this sequence completes
50877c478bd9Sstevel@tonic-gate * with a BUS SERVICE interrupt if just the status
50887c478bd9Sstevel@tonic-gate * byte was received, else a FUNCTION COMPLETE interrupt
50897c478bd9Sstevel@tonic-gate * if both status and a message was received.
50907c478bd9Sstevel@tonic-gate *
50917c478bd9Sstevel@tonic-gate * if we give the MSG_ACT before reading the msg byte
50927c478bd9Sstevel@tonic-gate * we get the status byte again and if the status is zero
50937c478bd9Sstevel@tonic-gate * then we won't detect a failure
50947c478bd9Sstevel@tonic-gate */
50957c478bd9Sstevel@tonic-gate *(sp->cmd_pkt->pkt_scbp) =
50967c478bd9Sstevel@tonic-gate sts = fas_reg_read(fas, (uchar_t *)&fasreg->fas_fifo_data);
50977c478bd9Sstevel@tonic-gate fas->f_last_msgin = fas->f_imsgarea[0] =
50987c478bd9Sstevel@tonic-gate msg = fas_reg_read(fas, (uchar_t *)&fasreg->fas_fifo_data);
50997c478bd9Sstevel@tonic-gate
51007c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_MSG_ACPT);
51017c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_GOT_STATUS;
51027c478bd9Sstevel@tonic-gate
51037c478bd9Sstevel@tonic-gate /*
51047c478bd9Sstevel@tonic-gate * The manuals also state that ATN* is asserted if
51057c478bd9Sstevel@tonic-gate * bad parity is detected.
51067c478bd9Sstevel@tonic-gate *
51077c478bd9Sstevel@tonic-gate * The one case that we cannot handle is where we detect
51087c478bd9Sstevel@tonic-gate * bad parity for the status byte, but the target refuses
51097c478bd9Sstevel@tonic-gate * to go to MESSAGE OUT phase right away. This means that
51107c478bd9Sstevel@tonic-gate * if that happens, we will misconstrue the parity error
51117c478bd9Sstevel@tonic-gate * to be for the completion message, not the status byte.
51127c478bd9Sstevel@tonic-gate */
51137c478bd9Sstevel@tonic-gate if (perr) {
51147c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, msginperr);
51157c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
51167c478bd9Sstevel@tonic-gate
51177c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = MSG_MSG_PARITY;
51187c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
51197c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
51207c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
512119397407SSherry Moore TR_FAS_HANDLE_C_CMPLT_ACTION5_END,
512219397407SSherry Moore "fas_handle_c_cmplt_end (action5)");
51237c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
51247c478bd9Sstevel@tonic-gate }
51257c478bd9Sstevel@tonic-gate
51267c478bd9Sstevel@tonic-gate } else if (intr == FAS_INT_BUS) {
51277c478bd9Sstevel@tonic-gate /*
51287c478bd9Sstevel@tonic-gate * We only got the status byte.
51297c478bd9Sstevel@tonic-gate */
51307c478bd9Sstevel@tonic-gate sts = fas_reg_read(fas, (uchar_t *)&fasreg->fas_fifo_data);
51317c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_GOT_STATUS;
51327c478bd9Sstevel@tonic-gate *(sp->cmd_pkt->pkt_scbp) = sts;
51337c478bd9Sstevel@tonic-gate msg = INVALID_MSG;
51347c478bd9Sstevel@tonic-gate
51357c478bd9Sstevel@tonic-gate IPRINTF1("fas_handle_cmd_cmplt: sts=%x, no msg byte\n", sts);
51367c478bd9Sstevel@tonic-gate
51377c478bd9Sstevel@tonic-gate if (perr) {
51387c478bd9Sstevel@tonic-gate /*
51397c478bd9Sstevel@tonic-gate * If we get a parity error on a status byte
51407c478bd9Sstevel@tonic-gate * assume that it was a CHECK CONDITION
51417c478bd9Sstevel@tonic-gate */
51427c478bd9Sstevel@tonic-gate sts = STATUS_CHECK;
51437c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
51447c478bd9Sstevel@tonic-gate "SCSI bus STATUS phase parity error");
51457c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = MSG_INITIATOR_ERROR;
51467c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
51477c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
51487c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
514919397407SSherry Moore TR_FAS_HANDLE_C_CMPLT_ACTION5_END,
515019397407SSherry Moore "fas_handle_c_cmplt_end (action5)");
51517c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
51527c478bd9Sstevel@tonic-gate }
51537c478bd9Sstevel@tonic-gate
51547c478bd9Sstevel@tonic-gate } else {
51557c478bd9Sstevel@tonic-gate msg = sts = INVALID_MSG;
51567c478bd9Sstevel@tonic-gate IPRINTF("fas_handle_cmd_cmplt: unexpected intr\n");
51577c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
51587c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_C_CMPLT_ACTION2_END,
51597c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_end (action2)");
51607c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
51617c478bd9Sstevel@tonic-gate }
51627c478bd9Sstevel@tonic-gate
51637c478bd9Sstevel@tonic-gate EPRINTF2("fas_handle_c_cmplt: status=%x, msg=%x\n", sts, msg);
51647c478bd9Sstevel@tonic-gate
51657c478bd9Sstevel@tonic-gate EPRINTF1("Completion Message=%s\n", scsi_mname(msg));
51667c478bd9Sstevel@tonic-gate if (msg == MSG_COMMAND_COMPLETE) {
51677c478bd9Sstevel@tonic-gate /*
51687c478bd9Sstevel@tonic-gate * Actually, if the message was a 'linked command
51697c478bd9Sstevel@tonic-gate * complete' message, the target isn't going to be
51707c478bd9Sstevel@tonic-gate * clearing the bus.
51717c478bd9Sstevel@tonic-gate */
51727c478bd9Sstevel@tonic-gate New_state(fas, ACTS_CLEARING);
51737c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
51747c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_C_CMPLT_ACTION4_END,
51757c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_end (action4)");
51767c478bd9Sstevel@tonic-gate return (fas_handle_clearing(fas));
51777c478bd9Sstevel@tonic-gate } else {
51787c478bd9Sstevel@tonic-gate fas->f_imsglen = 1;
51797c478bd9Sstevel@tonic-gate fas->f_imsgindex = 1;
51807c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_DONE);
51817c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
51827c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_C_CMPLT_ACTION3_END,
51837c478bd9Sstevel@tonic-gate "fas_handle_c_cmplt_end (action3)");
51847c478bd9Sstevel@tonic-gate return (fas_handle_msg_in_done(fas));
51857c478bd9Sstevel@tonic-gate }
51867c478bd9Sstevel@tonic-gate }
51877c478bd9Sstevel@tonic-gate
51887c478bd9Sstevel@tonic-gate /*
51897c478bd9Sstevel@tonic-gate * prepare for accepting a message byte from the fifo
51907c478bd9Sstevel@tonic-gate */
51917c478bd9Sstevel@tonic-gate static int
fas_handle_msg_in_start(struct fas * fas)51927c478bd9Sstevel@tonic-gate fas_handle_msg_in_start(struct fas *fas)
51937c478bd9Sstevel@tonic-gate {
51947c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_START,
51957c478bd9Sstevel@tonic-gate "fas_handle_msg_in_start");
51967c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_msg_in_start\n");
51977c478bd9Sstevel@tonic-gate
51987c478bd9Sstevel@tonic-gate /*
51997c478bd9Sstevel@tonic-gate * Pick up a message byte.
52007c478bd9Sstevel@tonic-gate * Clear the FIFO so we
52017c478bd9Sstevel@tonic-gate * don't get confused.
52027c478bd9Sstevel@tonic-gate */
52037c478bd9Sstevel@tonic-gate if (!FIFO_EMPTY(fas)) {
52047c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
52057c478bd9Sstevel@tonic-gate }
52067c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_TRAN_INFO);
52077c478bd9Sstevel@tonic-gate fas->f_imsglen = 1;
52087c478bd9Sstevel@tonic-gate fas->f_imsgindex = 0;
52097c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_DONE);
52107c478bd9Sstevel@tonic-gate
52117c478bd9Sstevel@tonic-gate /*
52127c478bd9Sstevel@tonic-gate * give a little extra time by returning to phasemanage
52137c478bd9Sstevel@tonic-gate */
52147c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_END,
52157c478bd9Sstevel@tonic-gate "fas_handle_msg_in_end (ACTION_PHASEMANAGE)");
52167c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
52177c478bd9Sstevel@tonic-gate }
52187c478bd9Sstevel@tonic-gate
52197c478bd9Sstevel@tonic-gate /*
52207c478bd9Sstevel@tonic-gate * We come here after issuing a MSG_ACCEPT
52217c478bd9Sstevel@tonic-gate * command and are expecting more message bytes.
52227c478bd9Sstevel@tonic-gate * The FAS should be asserting a BUS SERVICE
52237c478bd9Sstevel@tonic-gate * interrupt status, but may have asserted
52247c478bd9Sstevel@tonic-gate * a different interrupt in the case that
52257c478bd9Sstevel@tonic-gate * the target disconnected and dropped BSY*.
52267c478bd9Sstevel@tonic-gate *
52277c478bd9Sstevel@tonic-gate * In the case that we are eating up message
52287c478bd9Sstevel@tonic-gate * bytes (and throwing them away unread) because
52297c478bd9Sstevel@tonic-gate * we have ATN* asserted (we are trying to send
52307c478bd9Sstevel@tonic-gate * a message), we do not consider it an error
52317c478bd9Sstevel@tonic-gate * if the phase has changed out of MESSAGE IN.
52327c478bd9Sstevel@tonic-gate */
52337c478bd9Sstevel@tonic-gate static int
fas_handle_more_msgin(struct fas * fas)52347c478bd9Sstevel@tonic-gate fas_handle_more_msgin(struct fas *fas)
52357c478bd9Sstevel@tonic-gate {
52367c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MORE_MSGIN_START,
52377c478bd9Sstevel@tonic-gate "fas_handle_more_msgin_start");
52387c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_more_msgin\n");
52397c478bd9Sstevel@tonic-gate
52407c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_BUS) {
52417c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_PHASE_MASK) == FAS_PHASE_MSG_IN) {
52427c478bd9Sstevel@tonic-gate /*
52437c478bd9Sstevel@tonic-gate * Fetch another byte of a message in.
52447c478bd9Sstevel@tonic-gate */
52457c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_TRAN_INFO);
52467c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_DONE);
52477c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
52487c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MORE_MSGIN_RETURN1_END,
52497c478bd9Sstevel@tonic-gate "fas_handle_more_msgin_end (ACTION_RETURN)");
52507c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
52517c478bd9Sstevel@tonic-gate }
52527c478bd9Sstevel@tonic-gate
52537c478bd9Sstevel@tonic-gate /*
52547c478bd9Sstevel@tonic-gate * If we were gobbling up a message and we have
52557c478bd9Sstevel@tonic-gate * changed phases, handle this silently, else
52567c478bd9Sstevel@tonic-gate * complain. In either case, we return to let
52577c478bd9Sstevel@tonic-gate * fas_phasemanage() handle things.
52587c478bd9Sstevel@tonic-gate *
52597c478bd9Sstevel@tonic-gate * If it wasn't a BUS SERVICE interrupt,
52607c478bd9Sstevel@tonic-gate * let fas_phasemanage() find out if the
52617c478bd9Sstevel@tonic-gate * chip disconnected.
52627c478bd9Sstevel@tonic-gate */
52637c478bd9Sstevel@tonic-gate if (fas->f_imsglen != 0) {
52647c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
52657c478bd9Sstevel@tonic-gate "Premature end of extended message");
52667c478bd9Sstevel@tonic-gate }
52677c478bd9Sstevel@tonic-gate }
52687c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
52697c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MORE_MSGIN_RETURN2_END,
52707c478bd9Sstevel@tonic-gate "fas_handle_more_msgin_end (action)");
52717c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
52727c478bd9Sstevel@tonic-gate }
52737c478bd9Sstevel@tonic-gate
52747c478bd9Sstevel@tonic-gate static int
fas_handle_msg_in_done(struct fas * fas)52757c478bd9Sstevel@tonic-gate fas_handle_msg_in_done(struct fas *fas)
52767c478bd9Sstevel@tonic-gate {
52777c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
52787c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
52797c478bd9Sstevel@tonic-gate int sndmsg = 0;
52807c478bd9Sstevel@tonic-gate uchar_t msgin;
52817c478bd9Sstevel@tonic-gate
52827c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_DONE_START,
52837c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_start");
52847c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_msg_in_done:\n");
52857c478bd9Sstevel@tonic-gate if (fas->f_laststate == ACTS_MSG_IN) {
52867c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
52877c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas,
528819397407SSherry Moore (uchar_t *)&fasreg->fas_stat);
52897c478bd9Sstevel@tonic-gate fas->f_stat2 = fas_reg_read(fas,
529019397407SSherry Moore (uchar_t *)&fasreg->fas_stat2);
52917c478bd9Sstevel@tonic-gate
52927c478bd9Sstevel@tonic-gate fas_read_fifo(fas);
52937c478bd9Sstevel@tonic-gate
52947c478bd9Sstevel@tonic-gate fas->f_intr = fas_reg_read(fas,
529519397407SSherry Moore (uchar_t *)&fasreg->fas_intr);
52967c478bd9Sstevel@tonic-gate if (fas->f_intr & (FAS_INT_RESET | FAS_INT_ILLEGAL)) {
52977c478bd9Sstevel@tonic-gate return (fas_illegal_cmd_or_bus_reset(fas));
52987c478bd9Sstevel@tonic-gate }
52997c478bd9Sstevel@tonic-gate } else {
53007c478bd9Sstevel@tonic-gate /*
53017c478bd9Sstevel@tonic-gate * change f_laststate for the next time around
53027c478bd9Sstevel@tonic-gate */
53037c478bd9Sstevel@tonic-gate fas->f_laststate = ACTS_MSG_IN_DONE;
53047c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
53057c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MSG_IN_DONE_RETURN1_END,
53067c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (ACTION_RETURN1)");
53077c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
53087c478bd9Sstevel@tonic-gate }
53097c478bd9Sstevel@tonic-gate }
53107c478bd9Sstevel@tonic-gate
53117c478bd9Sstevel@tonic-gate /*
53127c478bd9Sstevel@tonic-gate * the most common case is a disconnect message. we do
53137c478bd9Sstevel@tonic-gate * a fast path for this condition and if it fails then
53147c478bd9Sstevel@tonic-gate * we go for the detailed error handling
53157c478bd9Sstevel@tonic-gate */
53167c478bd9Sstevel@tonic-gate #ifndef FAS_TEST
53177c478bd9Sstevel@tonic-gate if (((fas->f_laststate == ACTS_MSG_IN) ||
53187c478bd9Sstevel@tonic-gate (fas->f_laststate == ACTS_MSG_IN_DONE)) &&
53197c478bd9Sstevel@tonic-gate ((fas->f_intr & FAS_INT_DISCON) == 0) &&
53207c478bd9Sstevel@tonic-gate ((fas->f_stat & FAS_STAT_PERR) == 0) &&
53217c478bd9Sstevel@tonic-gate ((sp->cmd_pkt_flags & FLAG_NODISCON) == 0)) {
53227c478bd9Sstevel@tonic-gate
53237c478bd9Sstevel@tonic-gate if ((fas->f_fifolen == 1) &&
53247c478bd9Sstevel@tonic-gate (fas->f_imsglen == 1) &&
53257c478bd9Sstevel@tonic-gate (fas->f_fifo[0] == MSG_DISCONNECT)) {
53267c478bd9Sstevel@tonic-gate
53277c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_MSG_ACPT);
53287c478bd9Sstevel@tonic-gate fas->f_imsgarea[fas->f_imsgindex++] = fas->f_fifo[0];
53297c478bd9Sstevel@tonic-gate fas->f_last_msgin = MSG_DISCONNECT;
53307c478bd9Sstevel@tonic-gate New_state(fas, ACTS_CLEARING);
53317c478bd9Sstevel@tonic-gate
53327c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
53337c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MSG_IN_DONE_ACTION_END,
53347c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (action)");
53357c478bd9Sstevel@tonic-gate
53367c478bd9Sstevel@tonic-gate return (fas_handle_clearing(fas));
53377c478bd9Sstevel@tonic-gate }
53387c478bd9Sstevel@tonic-gate }
53397c478bd9Sstevel@tonic-gate #endif /* not FAS_TEST */
53407c478bd9Sstevel@tonic-gate
53417c478bd9Sstevel@tonic-gate /*
53427c478bd9Sstevel@tonic-gate * We can be called here for both the case where
53437c478bd9Sstevel@tonic-gate * we had requested the FAS chip to fetch a message
53447c478bd9Sstevel@tonic-gate * byte from the target (at the target's request).
53457c478bd9Sstevel@tonic-gate * We can also be called in the case where we had
53467c478bd9Sstevel@tonic-gate * been using the CMD_COMP_SEQ command to pick up
53477c478bd9Sstevel@tonic-gate * both a status byte and a completion message from
53487c478bd9Sstevel@tonic-gate * a target, but where the message wasn't one of
53497c478bd9Sstevel@tonic-gate * COMMAND COMPLETE, LINKED COMMAND COMPLETE, or
53507c478bd9Sstevel@tonic-gate * LINKED COMMAND COMPLETE (with flag). This is a
53517c478bd9Sstevel@tonic-gate * legal (albeit extremely unusual) SCSI bus trans-
53527c478bd9Sstevel@tonic-gate * -ition, so we have to handle it.
53537c478bd9Sstevel@tonic-gate */
53547c478bd9Sstevel@tonic-gate if (fas->f_laststate != ACTS_C_CMPLT) {
53557c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
53567c478bd9Sstevel@tonic-gate reloop:
53577c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
53587c478bd9Sstevel@tonic-gate
53597c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_DISCON) {
53607c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
53617c478bd9Sstevel@tonic-gate "premature end of input message");
53627c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
53637c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
53647c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MSG_IN_DONE_PHASEMANAGE_END,
53657c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (ACTION_PHASEMANAGE)");
53667c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
53677c478bd9Sstevel@tonic-gate }
53687c478bd9Sstevel@tonic-gate
53697c478bd9Sstevel@tonic-gate /*
53707c478bd9Sstevel@tonic-gate * Note that if f_imsglen is zero, then we are skipping
53717c478bd9Sstevel@tonic-gate * input message bytes, so there is no reason to look for
53727c478bd9Sstevel@tonic-gate * parity errors.
53737c478bd9Sstevel@tonic-gate */
53747c478bd9Sstevel@tonic-gate if (fas->f_imsglen != 0 && (fas->f_stat & FAS_STAT_PERR)) {
53757c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, msginperr);
53767c478bd9Sstevel@tonic-gate sndmsg = MSG_MSG_PARITY;
53777c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
53787c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
53797c478bd9Sstevel@tonic-gate
53807c478bd9Sstevel@tonic-gate } else if ((msgin = fas->f_fifolen) != 1) {
53817c478bd9Sstevel@tonic-gate
53827c478bd9Sstevel@tonic-gate /*
53837c478bd9Sstevel@tonic-gate * If we have got more than one or 0 bytes in the fifo,
53847c478bd9Sstevel@tonic-gate * that is a gross screwup, and we should let the
53857c478bd9Sstevel@tonic-gate * target know that we have completely fouled up.
53867c478bd9Sstevel@tonic-gate */
53877c478bd9Sstevel@tonic-gate fas_printf(fas, "fifocount=%x", msgin);
53887c478bd9Sstevel@tonic-gate fas_printstate(fas, "input message botch");
53897c478bd9Sstevel@tonic-gate sndmsg = MSG_INITIATOR_ERROR;
53907c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
53917c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "input message botch");
53927c478bd9Sstevel@tonic-gate
53937c478bd9Sstevel@tonic-gate } else if (fas->f_imsglen == 0) {
53947c478bd9Sstevel@tonic-gate /*
53957c478bd9Sstevel@tonic-gate * If we are in the middle of gobbling up and throwing
53967c478bd9Sstevel@tonic-gate * away a message (due to a previous message input
53977c478bd9Sstevel@tonic-gate * error), drive on.
53987c478bd9Sstevel@tonic-gate */
53997c478bd9Sstevel@tonic-gate msgin = fas_reg_read(fas,
540019397407SSherry Moore (uchar_t *)&fasreg->fas_fifo_data);
54017c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_MORE);
54027c478bd9Sstevel@tonic-gate
54037c478bd9Sstevel@tonic-gate } else {
54047c478bd9Sstevel@tonic-gate msgin = fas->f_fifo[0];
54057c478bd9Sstevel@tonic-gate fas->f_imsgarea[fas->f_imsgindex++] = msgin;
54067c478bd9Sstevel@tonic-gate }
54077c478bd9Sstevel@tonic-gate
54087c478bd9Sstevel@tonic-gate } else {
54097c478bd9Sstevel@tonic-gate /*
54107c478bd9Sstevel@tonic-gate * In this case, we have been called (from
54117c478bd9Sstevel@tonic-gate * fas_handle_c_cmplt()) with the message
54127c478bd9Sstevel@tonic-gate * already stored in the message array.
54137c478bd9Sstevel@tonic-gate */
54147c478bd9Sstevel@tonic-gate msgin = fas->f_imsgarea[0];
54157c478bd9Sstevel@tonic-gate }
54167c478bd9Sstevel@tonic-gate
54177c478bd9Sstevel@tonic-gate /*
54187c478bd9Sstevel@tonic-gate * Process this message byte (but not if we are
54197c478bd9Sstevel@tonic-gate * going to be trying to send back some error
54207c478bd9Sstevel@tonic-gate * anyway)
54217c478bd9Sstevel@tonic-gate */
54227c478bd9Sstevel@tonic-gate if (sndmsg == 0 && fas->f_imsglen != 0) {
54237c478bd9Sstevel@tonic-gate
54247c478bd9Sstevel@tonic-gate if (fas->f_imsgindex < fas->f_imsglen) {
54257c478bd9Sstevel@tonic-gate
54267c478bd9Sstevel@tonic-gate EPRINTF2("message byte %d: 0x%x\n",
54277c478bd9Sstevel@tonic-gate fas->f_imsgindex-1,
54287c478bd9Sstevel@tonic-gate fas->f_imsgarea[fas->f_imsgindex-1]);
54297c478bd9Sstevel@tonic-gate
54307c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_MORE);
54317c478bd9Sstevel@tonic-gate
54327c478bd9Sstevel@tonic-gate } else if (fas->f_imsglen == 1) {
54337c478bd9Sstevel@tonic-gate
54347c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
54357c478bd9Sstevel@tonic-gate if ((fas_ptest_msgin & (1<<Tgt(sp))) &&
54367c478bd9Sstevel@tonic-gate fas_ptest_msg == msgin) {
54377c478bd9Sstevel@tonic-gate fas_ptest_msgin = 0;
54387c478bd9Sstevel@tonic-gate fas_ptest_msg = -1;
54397c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
54407c478bd9Sstevel@tonic-gate fas->f_stat |= FAS_STAT_PERR;
54417c478bd9Sstevel@tonic-gate fas->f_imsgindex -= 1;
54427c478bd9Sstevel@tonic-gate if (fas_test_stop > 1) {
54437c478bd9Sstevel@tonic-gate debug_enter("ptest msgin");
54447c478bd9Sstevel@tonic-gate }
54457c478bd9Sstevel@tonic-gate goto reloop;
54467c478bd9Sstevel@tonic-gate }
54477c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
54487c478bd9Sstevel@tonic-gate
54497c478bd9Sstevel@tonic-gate sndmsg = fas_onebyte_msg(fas);
54507c478bd9Sstevel@tonic-gate
54517c478bd9Sstevel@tonic-gate } else if (fas->f_imsglen == 2) {
54527c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
54537c478bd9Sstevel@tonic-gate if (fas_ptest_emsgin & (1<<Tgt(sp))) {
54547c478bd9Sstevel@tonic-gate fas_ptest_emsgin = 0;
54557c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
54567c478bd9Sstevel@tonic-gate fas->f_stat |= FAS_STAT_PERR;
54577c478bd9Sstevel@tonic-gate fas->f_imsgindex -= 1;
54587c478bd9Sstevel@tonic-gate if (fas_test_stop > 1) {
54597c478bd9Sstevel@tonic-gate debug_enter("ptest emsgin");
54607c478bd9Sstevel@tonic-gate }
54617c478bd9Sstevel@tonic-gate goto reloop;
54627c478bd9Sstevel@tonic-gate }
54637c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
54647c478bd9Sstevel@tonic-gate
54657c478bd9Sstevel@tonic-gate if (fas->f_imsgarea[0] == MSG_EXTENDED) {
54667c478bd9Sstevel@tonic-gate static char *tool =
54677c478bd9Sstevel@tonic-gate "Extended message 0x%x is too long";
54687c478bd9Sstevel@tonic-gate
54697c478bd9Sstevel@tonic-gate /*
54707c478bd9Sstevel@tonic-gate * Is the incoming message too long
54717c478bd9Sstevel@tonic-gate * to be stored in our local array?
54727c478bd9Sstevel@tonic-gate */
54737c478bd9Sstevel@tonic-gate if ((int)(msgin+2) > IMSGSIZE) {
54747c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
54757c478bd9Sstevel@tonic-gate tool, fas->f_imsgarea[0]);
54767c478bd9Sstevel@tonic-gate sndmsg = MSG_REJECT;
54777c478bd9Sstevel@tonic-gate } else {
54787c478bd9Sstevel@tonic-gate fas->f_imsglen = msgin + 2;
54797c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_MORE);
54807c478bd9Sstevel@tonic-gate }
54817c478bd9Sstevel@tonic-gate } else {
54827c478bd9Sstevel@tonic-gate sndmsg = fas_twobyte_msg(fas);
54837c478bd9Sstevel@tonic-gate }
54847c478bd9Sstevel@tonic-gate
54857c478bd9Sstevel@tonic-gate } else {
54867c478bd9Sstevel@tonic-gate sndmsg = fas_multibyte_msg(fas);
54877c478bd9Sstevel@tonic-gate }
54887c478bd9Sstevel@tonic-gate }
54897c478bd9Sstevel@tonic-gate
54907c478bd9Sstevel@tonic-gate if (sndmsg < 0) {
54917c478bd9Sstevel@tonic-gate /*
54927c478bd9Sstevel@tonic-gate * If sndmsg is less than zero, one of the subsidiary
54937c478bd9Sstevel@tonic-gate * routines needs to return some other state than
54947c478bd9Sstevel@tonic-gate * ACTION_RETURN.
54957c478bd9Sstevel@tonic-gate */
54967c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_DONE_SNDMSG_END,
54977c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (-sndmsg)");
54987c478bd9Sstevel@tonic-gate return (-sndmsg);
54997c478bd9Sstevel@tonic-gate
55007c478bd9Sstevel@tonic-gate } else if (sndmsg > 0) {
55017c478bd9Sstevel@tonic-gate if (IS_1BYTE_MSG(sndmsg)) {
55027c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
55037c478bd9Sstevel@tonic-gate }
55047c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = (uchar_t)sndmsg;
55057c478bd9Sstevel@tonic-gate
55067c478bd9Sstevel@tonic-gate /*
55077c478bd9Sstevel@tonic-gate * The target is not guaranteed to go to message out
55087c478bd9Sstevel@tonic-gate * phase, period. Moreover, until the entire incoming
55097c478bd9Sstevel@tonic-gate * message is transferred, the target may (and likely
55107c478bd9Sstevel@tonic-gate * will) continue to transfer message bytes (which
55117c478bd9Sstevel@tonic-gate * we will have to ignore).
55127c478bd9Sstevel@tonic-gate *
55137c478bd9Sstevel@tonic-gate * In order to do this, we'll go to 'infinite'
55147c478bd9Sstevel@tonic-gate * message in handling by setting the current input
55157c478bd9Sstevel@tonic-gate * message length to a sentinel of zero.
55167c478bd9Sstevel@tonic-gate *
55177c478bd9Sstevel@tonic-gate * This works regardless of the message we are trying
55187c478bd9Sstevel@tonic-gate * to send out. At the point in time which we want
55197c478bd9Sstevel@tonic-gate * to send a message in response to an incoming message
55207c478bd9Sstevel@tonic-gate * we do not care any more about the incoming message.
55217c478bd9Sstevel@tonic-gate *
55227c478bd9Sstevel@tonic-gate * If we are sending a message in response to detecting
55237c478bd9Sstevel@tonic-gate * a parity error on input, the FAS chip has already
55247c478bd9Sstevel@tonic-gate * set ATN* for us, but it doesn't hurt to set it here
55257c478bd9Sstevel@tonic-gate * again anyhow.
55267c478bd9Sstevel@tonic-gate */
55277c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
55287c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_MORE);
55297c478bd9Sstevel@tonic-gate fas->f_imsglen = 0;
55307c478bd9Sstevel@tonic-gate }
55317c478bd9Sstevel@tonic-gate
55327c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
55337c478bd9Sstevel@tonic-gate
55347c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_MSG_ACPT);
55357c478bd9Sstevel@tonic-gate
55367c478bd9Sstevel@tonic-gate if ((fas->f_laststate == ACTS_MSG_IN_DONE) &&
55377c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_CLEARING)) {
55387c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_DONE_ACTION_END,
55397c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (action)");
55407c478bd9Sstevel@tonic-gate return (fas_handle_clearing(fas));
55417c478bd9Sstevel@tonic-gate }
55427c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_IN_DONE_RETURN2_END,
55437c478bd9Sstevel@tonic-gate "fas_handle_msg_in_done_end (ACTION_RETURN2)");
55447c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
55457c478bd9Sstevel@tonic-gate }
55467c478bd9Sstevel@tonic-gate
55477c478bd9Sstevel@tonic-gate static int
fas_onebyte_msg(struct fas * fas)55487c478bd9Sstevel@tonic-gate fas_onebyte_msg(struct fas *fas)
55497c478bd9Sstevel@tonic-gate {
55507c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
55517c478bd9Sstevel@tonic-gate int msgout = 0;
55527c478bd9Sstevel@tonic-gate uchar_t msgin = fas->f_last_msgin = fas->f_imsgarea[0];
55537c478bd9Sstevel@tonic-gate int tgt = Tgt(sp);
55547c478bd9Sstevel@tonic-gate
55557c478bd9Sstevel@tonic-gate EPRINTF("fas_onebyte_msg\n");
55567c478bd9Sstevel@tonic-gate
55577c478bd9Sstevel@tonic-gate if (msgin & MSG_IDENTIFY) {
55587c478bd9Sstevel@tonic-gate /*
55597c478bd9Sstevel@tonic-gate * How did we get here? We should only see identify
55607c478bd9Sstevel@tonic-gate * messages on a reconnection, but we'll handle this
55617c478bd9Sstevel@tonic-gate * fine here (just in case we get this) as long as
55627c478bd9Sstevel@tonic-gate * we believe that this is a valid identify message.
55637c478bd9Sstevel@tonic-gate *
55647c478bd9Sstevel@tonic-gate * For this to be a valid incoming message,
55657c478bd9Sstevel@tonic-gate * bits 6-4 must must be zero. Also, the
55667c478bd9Sstevel@tonic-gate * bit that says that I'm an initiator and
55677c478bd9Sstevel@tonic-gate * can support disconnection cannot possibly
55687c478bd9Sstevel@tonic-gate * be set here.
55697c478bd9Sstevel@tonic-gate */
55707c478bd9Sstevel@tonic-gate
55717c478bd9Sstevel@tonic-gate char garbled = ((msgin & (BAD_IDENTIFY|INI_CAN_DISCON)) != 0);
55727c478bd9Sstevel@tonic-gate
55737c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "%s message 0x%x from Target %d",
55747c478bd9Sstevel@tonic-gate garbled ? "Garbled" : "Identify", msgin, tgt);
55757c478bd9Sstevel@tonic-gate
55767c478bd9Sstevel@tonic-gate if (garbled) {
55777c478bd9Sstevel@tonic-gate /*
55787c478bd9Sstevel@tonic-gate * If it's a garbled message,
55797c478bd9Sstevel@tonic-gate * try and tell the target...
55807c478bd9Sstevel@tonic-gate */
55817c478bd9Sstevel@tonic-gate msgout = MSG_INITIATOR_ERROR;
55827c478bd9Sstevel@tonic-gate } else {
55837c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
55847c478bd9Sstevel@tonic-gate }
55857c478bd9Sstevel@tonic-gate return (msgout);
55867c478bd9Sstevel@tonic-gate
55877c478bd9Sstevel@tonic-gate } else if (IS_2BYTE_MSG(msgin) || IS_EXTENDED_MSG(msgin)) {
55887c478bd9Sstevel@tonic-gate fas->f_imsglen = 2;
55897c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_IN_MORE);
55907c478bd9Sstevel@tonic-gate return (0);
55917c478bd9Sstevel@tonic-gate }
55927c478bd9Sstevel@tonic-gate
55937c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
55947c478bd9Sstevel@tonic-gate
55957c478bd9Sstevel@tonic-gate switch (msgin) {
55967c478bd9Sstevel@tonic-gate case MSG_DISCONNECT:
55977c478bd9Sstevel@tonic-gate /*
55987c478bd9Sstevel@tonic-gate * If we 'cannot' disconnect- reject this message.
55997c478bd9Sstevel@tonic-gate * Note that we only key off of the pkt_flags here-
56007c478bd9Sstevel@tonic-gate * the FLAG_NODISCON was set in fas_accept_pkt() if
56017c478bd9Sstevel@tonic-gate * no disconnect was enabled in scsi_options
56027c478bd9Sstevel@tonic-gate */
56037c478bd9Sstevel@tonic-gate if (sp->cmd_pkt_flags & FLAG_NODISCON) {
56047c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
56057c478bd9Sstevel@tonic-gate break;
56067c478bd9Sstevel@tonic-gate }
56077c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
56087c478bd9Sstevel@tonic-gate case MSG_COMMAND_COMPLETE:
56097c478bd9Sstevel@tonic-gate fas->f_state = ACTS_CLEARING;
56107c478bd9Sstevel@tonic-gate break;
56117c478bd9Sstevel@tonic-gate
56127c478bd9Sstevel@tonic-gate case MSG_NOP:
56137c478bd9Sstevel@tonic-gate break;
56147c478bd9Sstevel@tonic-gate
56157c478bd9Sstevel@tonic-gate /* XXX Make it a MSG_REJECT handler */
56167c478bd9Sstevel@tonic-gate case MSG_REJECT:
56177c478bd9Sstevel@tonic-gate {
56187c478bd9Sstevel@tonic-gate uchar_t reason = 0;
56197c478bd9Sstevel@tonic-gate uchar_t lastmsg = fas->f_last_msgout;
56207c478bd9Sstevel@tonic-gate /*
56217c478bd9Sstevel@tonic-gate * The target is rejecting the last message we sent.
56227c478bd9Sstevel@tonic-gate *
56237c478bd9Sstevel@tonic-gate * If the last message we attempted to send out was an
56247c478bd9Sstevel@tonic-gate * extended message, we were trying to negotiate sync
56257c478bd9Sstevel@tonic-gate * xfers- and we're okay.
56267c478bd9Sstevel@tonic-gate *
56277c478bd9Sstevel@tonic-gate * Otherwise, a target has rejected a message that
56287c478bd9Sstevel@tonic-gate * it should have handled. We will abort the operation
56297c478bd9Sstevel@tonic-gate * in progress and set the pkt_reason value here to
56307c478bd9Sstevel@tonic-gate * show why we have completed. The process of aborting
56317c478bd9Sstevel@tonic-gate * may be via a message or may be via a bus reset (as
56327c478bd9Sstevel@tonic-gate * a last resort).
56337c478bd9Sstevel@tonic-gate */
56347c478bd9Sstevel@tonic-gate msgout = (TAGGED(tgt)? MSG_ABORT_TAG : MSG_ABORT);
56357c478bd9Sstevel@tonic-gate
56367c478bd9Sstevel@tonic-gate switch (lastmsg) {
56377c478bd9Sstevel@tonic-gate case MSG_EXTENDED:
56387c478bd9Sstevel@tonic-gate if (fas->f_wdtr_sent) {
56397c478bd9Sstevel@tonic-gate /*
56407c478bd9Sstevel@tonic-gate * Disable wide, Target rejected
56417c478bd9Sstevel@tonic-gate * out WDTR message
56427c478bd9Sstevel@tonic-gate */
56437c478bd9Sstevel@tonic-gate fas_set_wide_conf3(fas, tgt, 0);
56447c478bd9Sstevel@tonic-gate fas->f_nowide |= (1<<tgt);
56457c478bd9Sstevel@tonic-gate fas->f_wdtr_sent = 0;
56467c478bd9Sstevel@tonic-gate /*
56477c478bd9Sstevel@tonic-gate * we still want to negotiate sync
56487c478bd9Sstevel@tonic-gate */
56497c478bd9Sstevel@tonic-gate if ((fas->f_nosync & (1<<tgt)) == 0) {
56507c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
56517c478bd9Sstevel@tonic-gate fas_make_sdtr(fas, 0, tgt);
56527c478bd9Sstevel@tonic-gate }
56537c478bd9Sstevel@tonic-gate } else if (fas->f_sdtr_sent) {
56547c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_CLR_ATN);
56557c478bd9Sstevel@tonic-gate fas_revert_to_async(fas, tgt);
56567c478bd9Sstevel@tonic-gate fas->f_nosync |= (1<<tgt);
56577c478bd9Sstevel@tonic-gate fas->f_sdtr_sent = 0;
56587c478bd9Sstevel@tonic-gate }
56597c478bd9Sstevel@tonic-gate msgout = 0;
56607c478bd9Sstevel@tonic-gate break;
56617c478bd9Sstevel@tonic-gate case MSG_NOP:
56627c478bd9Sstevel@tonic-gate reason = CMD_NOP_FAIL;
56637c478bd9Sstevel@tonic-gate break;
56647c478bd9Sstevel@tonic-gate case MSG_INITIATOR_ERROR:
56657c478bd9Sstevel@tonic-gate reason = CMD_IDE_FAIL;
56667c478bd9Sstevel@tonic-gate break;
56677c478bd9Sstevel@tonic-gate case MSG_MSG_PARITY:
56687c478bd9Sstevel@tonic-gate reason = CMD_PER_FAIL;
56697c478bd9Sstevel@tonic-gate break;
56707c478bd9Sstevel@tonic-gate case MSG_REJECT:
56717c478bd9Sstevel@tonic-gate reason = CMD_REJECT_FAIL;
56727c478bd9Sstevel@tonic-gate break;
56737c478bd9Sstevel@tonic-gate /* XXX - abort not good, queue full handling or drain (?) */
56747c478bd9Sstevel@tonic-gate case MSG_SIMPLE_QTAG:
56757c478bd9Sstevel@tonic-gate case MSG_ORDERED_QTAG:
56767c478bd9Sstevel@tonic-gate case MSG_HEAD_QTAG:
56777c478bd9Sstevel@tonic-gate msgout = MSG_ABORT;
56787c478bd9Sstevel@tonic-gate reason = CMD_TAG_REJECT;
56797c478bd9Sstevel@tonic-gate break;
56807c478bd9Sstevel@tonic-gate case MSG_DEVICE_RESET:
56817c478bd9Sstevel@tonic-gate reason = CMD_BDR_FAIL;
56827c478bd9Sstevel@tonic-gate msgout = -ACTION_ABORT_CURCMD;
56837c478bd9Sstevel@tonic-gate break;
56847c478bd9Sstevel@tonic-gate case MSG_ABORT:
56857c478bd9Sstevel@tonic-gate case MSG_ABORT_TAG:
56867c478bd9Sstevel@tonic-gate /*
56877c478bd9Sstevel@tonic-gate * If an RESET/ABORT OPERATION message is rejected
56887c478bd9Sstevel@tonic-gate * it is time to yank the chain on the bus...
56897c478bd9Sstevel@tonic-gate */
56907c478bd9Sstevel@tonic-gate reason = CMD_ABORT_FAIL;
56917c478bd9Sstevel@tonic-gate msgout = -ACTION_ABORT_CURCMD;
56927c478bd9Sstevel@tonic-gate break;
56937c478bd9Sstevel@tonic-gate default:
56947c478bd9Sstevel@tonic-gate if (IS_IDENTIFY_MSG(lastmsg)) {
56957c478bd9Sstevel@tonic-gate if (TAGGED(tgt)) {
56967c478bd9Sstevel@tonic-gate /*
56977c478bd9Sstevel@tonic-gate * this often happens when the
56987c478bd9Sstevel@tonic-gate * target rejected our tag
56997c478bd9Sstevel@tonic-gate */
57007c478bd9Sstevel@tonic-gate reason = CMD_TAG_REJECT;
57017c478bd9Sstevel@tonic-gate } else {
57027c478bd9Sstevel@tonic-gate reason = CMD_ID_FAIL;
57037c478bd9Sstevel@tonic-gate }
57047c478bd9Sstevel@tonic-gate } else {
57057c478bd9Sstevel@tonic-gate reason = CMD_TRAN_ERR;
57067c478bd9Sstevel@tonic-gate msgout = -ACTION_ABORT_CURCMD;
57077c478bd9Sstevel@tonic-gate }
57087c478bd9Sstevel@tonic-gate
57097c478bd9Sstevel@tonic-gate break;
57107c478bd9Sstevel@tonic-gate }
57117c478bd9Sstevel@tonic-gate
57127c478bd9Sstevel@tonic-gate if (msgout) {
57137c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
57147c478bd9Sstevel@tonic-gate "Target %d rejects our message '%s'",
57157c478bd9Sstevel@tonic-gate tgt, scsi_mname(lastmsg));
57167c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, reason, 0);
57177c478bd9Sstevel@tonic-gate }
57187c478bd9Sstevel@tonic-gate
57197c478bd9Sstevel@tonic-gate break;
57207c478bd9Sstevel@tonic-gate }
57217c478bd9Sstevel@tonic-gate case MSG_RESTORE_PTRS:
57227c478bd9Sstevel@tonic-gate sp->cmd_cdbp = sp->cmd_pkt->pkt_cdbp;
57237c478bd9Sstevel@tonic-gate if (sp->cmd_data_count != sp->cmd_saved_data_count) {
57247c478bd9Sstevel@tonic-gate if (fas_restore_pointers(fas, sp)) {
57257c478bd9Sstevel@tonic-gate msgout = -ACTION_ABORT_CURCMD;
57267c478bd9Sstevel@tonic-gate } else if ((sp->cmd_pkt->pkt_reason & CMD_TRAN_ERR) &&
572719397407SSherry Moore (sp->cmd_pkt->pkt_statistics & STAT_PERR) &&
572819397407SSherry Moore (sp->cmd_cur_win == 0) &&
572919397407SSherry Moore (sp->cmd_data_count == 0)) {
57307c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason &= ~CMD_TRAN_ERR;
57317c478bd9Sstevel@tonic-gate }
57327c478bd9Sstevel@tonic-gate }
57337c478bd9Sstevel@tonic-gate break;
57347c478bd9Sstevel@tonic-gate
57357c478bd9Sstevel@tonic-gate case MSG_SAVE_DATA_PTR:
57367c478bd9Sstevel@tonic-gate sp->cmd_saved_data_count = sp->cmd_data_count;
57377c478bd9Sstevel@tonic-gate sp->cmd_saved_win = sp->cmd_cur_win;
57387c478bd9Sstevel@tonic-gate sp->cmd_saved_cur_addr = sp->cmd_cur_addr;
57397c478bd9Sstevel@tonic-gate break;
57407c478bd9Sstevel@tonic-gate
57417c478bd9Sstevel@tonic-gate /* These don't make sense for us, and */
57427c478bd9Sstevel@tonic-gate /* will be rejected */
57437c478bd9Sstevel@tonic-gate /* case MSG_INITIATOR_ERROR */
57447c478bd9Sstevel@tonic-gate /* case MSG_ABORT */
57457c478bd9Sstevel@tonic-gate /* case MSG_MSG_PARITY */
57467c478bd9Sstevel@tonic-gate /* case MSG_DEVICE_RESET */
57477c478bd9Sstevel@tonic-gate default:
57487c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
57497c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
57507c478bd9Sstevel@tonic-gate "Rejecting message '%s' from Target %d",
57517c478bd9Sstevel@tonic-gate scsi_mname(msgin), tgt);
57527c478bd9Sstevel@tonic-gate break;
57537c478bd9Sstevel@tonic-gate }
57547c478bd9Sstevel@tonic-gate
57557c478bd9Sstevel@tonic-gate EPRINTF1("Message in: %s\n", scsi_mname(msgin));
57567c478bd9Sstevel@tonic-gate
57577c478bd9Sstevel@tonic-gate return (msgout);
57587c478bd9Sstevel@tonic-gate }
57597c478bd9Sstevel@tonic-gate
57607c478bd9Sstevel@tonic-gate /*
57617c478bd9Sstevel@tonic-gate * phase handlers that are rarely used
57627c478bd9Sstevel@tonic-gate */
57637c478bd9Sstevel@tonic-gate static int
fas_handle_cmd_start(struct fas * fas)57647c478bd9Sstevel@tonic-gate fas_handle_cmd_start(struct fas *fas)
57657c478bd9Sstevel@tonic-gate {
57667c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
57677c478bd9Sstevel@tonic-gate volatile uchar_t *tp = fas->f_cmdarea;
57687c478bd9Sstevel@tonic-gate int i;
57697c478bd9Sstevel@tonic-gate int amt = sp->cmd_cdblen;
57707c478bd9Sstevel@tonic-gate
57717c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CMD_START_START,
57727c478bd9Sstevel@tonic-gate "fas_handle_cmd_start_start");
57737c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_cmd: send cmd\n");
57747c478bd9Sstevel@tonic-gate
57757c478bd9Sstevel@tonic-gate for (i = 0; i < amt; i++) {
57767c478bd9Sstevel@tonic-gate *tp++ = sp->cmd_cdbp[i];
57777c478bd9Sstevel@tonic-gate }
57787c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
57797c478bd9Sstevel@tonic-gate
57807c478bd9Sstevel@tonic-gate FAS_DMA_READ(fas, amt, fas->f_dmacookie.dmac_address, amt,
57817c478bd9Sstevel@tonic-gate CMD_TRAN_INFO|CMD_DMA);
57827c478bd9Sstevel@tonic-gate fas->f_lastcount = amt;
57837c478bd9Sstevel@tonic-gate
57847c478bd9Sstevel@tonic-gate New_state(fas, ACTS_CMD_DONE);
57857c478bd9Sstevel@tonic-gate
57867c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CMD_START_END,
57877c478bd9Sstevel@tonic-gate "fas_handle_cmd_start_end");
57887c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
57897c478bd9Sstevel@tonic-gate }
57907c478bd9Sstevel@tonic-gate
57917c478bd9Sstevel@tonic-gate static int
fas_handle_cmd_done(struct fas * fas)57927c478bd9Sstevel@tonic-gate fas_handle_cmd_done(struct fas *fas)
57937c478bd9Sstevel@tonic-gate {
57947c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
57957c478bd9Sstevel@tonic-gate uchar_t intr = fas->f_intr;
57967c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma;
57977c478bd9Sstevel@tonic-gate
57987c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CMD_DONE_START,
57997c478bd9Sstevel@tonic-gate "fas_handle_cmd_done_start");
58007c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_cmd_done\n");
58017c478bd9Sstevel@tonic-gate
58027c478bd9Sstevel@tonic-gate /*
58037c478bd9Sstevel@tonic-gate * We should have gotten a BUS SERVICE interrupt.
58047c478bd9Sstevel@tonic-gate * If it isn't that, and it isn't a DISCONNECT
58057c478bd9Sstevel@tonic-gate * interrupt, we have a "cannot happen" situation.
58067c478bd9Sstevel@tonic-gate */
58077c478bd9Sstevel@tonic-gate if ((intr & FAS_INT_BUS) == 0) {
58087c478bd9Sstevel@tonic-gate if ((intr & FAS_INT_DISCON) == 0) {
58097c478bd9Sstevel@tonic-gate fas_printstate(fas, "cmd transmission error");
58107c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
58117c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_CMD_DONE_ABORT1_END,
58127c478bd9Sstevel@tonic-gate "fas_handle_cmd_done_end (abort1)");
58137c478bd9Sstevel@tonic-gate return (ACTION_ABORT_CURCMD);
58147c478bd9Sstevel@tonic-gate }
58157c478bd9Sstevel@tonic-gate } else {
58167c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state |= STATE_SENT_CMD;
58177c478bd9Sstevel@tonic-gate }
58187c478bd9Sstevel@tonic-gate
58197c478bd9Sstevel@tonic-gate fas->f_dma_csr = fas_dma_reg_read(fas, &dmar->dma_csr);
58207c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA(fas);
58217c478bd9Sstevel@tonic-gate
58227c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
58237c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_CMD_DONE_END,
58247c478bd9Sstevel@tonic-gate "fas_handle_cmd_done_end");
58257c478bd9Sstevel@tonic-gate return (fas_handle_unknown(fas));
58267c478bd9Sstevel@tonic-gate }
58277c478bd9Sstevel@tonic-gate
58287c478bd9Sstevel@tonic-gate /*
58297c478bd9Sstevel@tonic-gate * Begin to send a message out
58307c478bd9Sstevel@tonic-gate */
58317c478bd9Sstevel@tonic-gate static int
fas_handle_msg_out_start(struct fas * fas)58327c478bd9Sstevel@tonic-gate fas_handle_msg_out_start(struct fas *fas)
58337c478bd9Sstevel@tonic-gate {
58347c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
58357c478bd9Sstevel@tonic-gate uchar_t *msgout = fas->f_cur_msgout;
58367c478bd9Sstevel@tonic-gate uchar_t amt = fas->f_omsglen;
58377c478bd9Sstevel@tonic-gate
58387c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_OUT_START,
58397c478bd9Sstevel@tonic-gate "fas_handle_msg_out_start");
58407c478bd9Sstevel@tonic-gate EPRINTF("fas_handle_msg_out_start\n");
58417c478bd9Sstevel@tonic-gate
58427c478bd9Sstevel@tonic-gate /*
58437c478bd9Sstevel@tonic-gate * Check to make *sure* that we are really
58447c478bd9Sstevel@tonic-gate * in MESSAGE OUT phase. If the last state
58457c478bd9Sstevel@tonic-gate * was ACTS_MSG_OUT_DONE, then we are trying
58467c478bd9Sstevel@tonic-gate * to resend a message that the target stated
58477c478bd9Sstevel@tonic-gate * had a parity error in it.
58487c478bd9Sstevel@tonic-gate *
58497c478bd9Sstevel@tonic-gate * If this is the case, and mark completion reason as CMD_NOMSGOUT.
58507c478bd9Sstevel@tonic-gate * XXX: Right now, we just *drive* on. Should we abort the command?
58517c478bd9Sstevel@tonic-gate */
58527c478bd9Sstevel@tonic-gate if ((fas->f_stat & FAS_PHASE_MASK) != FAS_PHASE_MSG_OUT &&
58537c478bd9Sstevel@tonic-gate fas->f_laststate == ACTS_MSG_OUT_DONE) {
58547c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
58557c478bd9Sstevel@tonic-gate "Target %d refused message resend", Tgt(sp));
58567c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_NOMSGOUT, 0);
58577c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
58587c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_OUT_PHASEMANAGE_END,
58597c478bd9Sstevel@tonic-gate "fas_handle_msg_out_end (ACTION_PHASEMANAGE)");
58607c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
58617c478bd9Sstevel@tonic-gate }
58627c478bd9Sstevel@tonic-gate
58637c478bd9Sstevel@tonic-gate /*
58647c478bd9Sstevel@tonic-gate * Clean the fifo.
58657c478bd9Sstevel@tonic-gate */
58667c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
58677c478bd9Sstevel@tonic-gate
58687c478bd9Sstevel@tonic-gate if (amt == 0) {
58697c478bd9Sstevel@tonic-gate /*
58707c478bd9Sstevel@tonic-gate * no msg to send
58717c478bd9Sstevel@tonic-gate */
58727c478bd9Sstevel@tonic-gate *msgout = MSG_NOP;
58737c478bd9Sstevel@tonic-gate amt = fas->f_omsglen = 1;
58747c478bd9Sstevel@tonic-gate }
58757c478bd9Sstevel@tonic-gate
58767c478bd9Sstevel@tonic-gate /*
58777c478bd9Sstevel@tonic-gate * If msg only 1 byte, just dump it in the fifo and go. For
58787c478bd9Sstevel@tonic-gate * multi-byte msgs, dma them to save time. If we have no
58797c478bd9Sstevel@tonic-gate * msg to send and we're in msg out phase, send a NOP.
58807c478bd9Sstevel@tonic-gate */
58817c478bd9Sstevel@tonic-gate fas->f_last_msgout = *msgout;
58827c478bd9Sstevel@tonic-gate
58837c478bd9Sstevel@tonic-gate /*
58847c478bd9Sstevel@tonic-gate * There is a bug in the fas366 that occasionaly
58857c478bd9Sstevel@tonic-gate * deasserts the ATN signal prematurely when we send
58867c478bd9Sstevel@tonic-gate * the sync/wide negotiation bytes out using DMA. The
58877c478bd9Sstevel@tonic-gate * workaround here is to send the negotiation bytes out
58887c478bd9Sstevel@tonic-gate * using PIO
58897c478bd9Sstevel@tonic-gate */
58907c478bd9Sstevel@tonic-gate fas_write_fifo(fas, msgout, fas->f_omsglen, 1);
58917c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_TRAN_INFO);
58927c478bd9Sstevel@tonic-gate
58937c478bd9Sstevel@tonic-gate EPRINTF2("amt=%x, last_msgout=%x\n", amt, fas->f_last_msgout);
58947c478bd9Sstevel@tonic-gate
58957c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_OUT_DONE);
58967c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_OUT_END,
58977c478bd9Sstevel@tonic-gate "fas_handle_msg_out_end");
58987c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
58997c478bd9Sstevel@tonic-gate }
59007c478bd9Sstevel@tonic-gate
59017c478bd9Sstevel@tonic-gate static int
fas_handle_msg_out_done(struct fas * fas)59027c478bd9Sstevel@tonic-gate fas_handle_msg_out_done(struct fas *fas)
59037c478bd9Sstevel@tonic-gate {
59047c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
59057c478bd9Sstevel@tonic-gate uchar_t msgout, phase;
59067c478bd9Sstevel@tonic-gate int target = Tgt(sp);
59077c478bd9Sstevel@tonic-gate int amt = fas->f_omsglen;
59087c478bd9Sstevel@tonic-gate int action;
59097c478bd9Sstevel@tonic-gate
59107c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_OUT_DONE_START,
59117c478bd9Sstevel@tonic-gate "fas_handle_msg_out_done_start");
59127c478bd9Sstevel@tonic-gate msgout = fas->f_cur_msgout[0];
59137c478bd9Sstevel@tonic-gate if ((msgout == MSG_HEAD_QTAG) || (msgout == MSG_SIMPLE_QTAG)) {
59147c478bd9Sstevel@tonic-gate msgout = fas->f_cur_msgout[2];
59157c478bd9Sstevel@tonic-gate }
59167c478bd9Sstevel@tonic-gate EPRINTF4("msgout: %x %x %x, last_msgout=%x\n",
591719397407SSherry Moore fas->f_cur_msgout[0], fas->f_cur_msgout[1],
591819397407SSherry Moore fas->f_cur_msgout[2], fas->f_last_msgout);
59197c478bd9Sstevel@tonic-gate
59207c478bd9Sstevel@tonic-gate EPRINTF1("fas_handle_msgout_done: msgout=%x\n", msgout);
59217c478bd9Sstevel@tonic-gate
59227c478bd9Sstevel@tonic-gate /*
59237c478bd9Sstevel@tonic-gate * flush fifo, just in case some bytes were not sent
59247c478bd9Sstevel@tonic-gate */
59257c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
59267c478bd9Sstevel@tonic-gate
59277c478bd9Sstevel@tonic-gate /*
59287c478bd9Sstevel@tonic-gate * If the FAS disconnected, then the message we sent caused
59297c478bd9Sstevel@tonic-gate * the target to decide to drop BSY* and clear the bus.
59307c478bd9Sstevel@tonic-gate */
59317c478bd9Sstevel@tonic-gate if (fas->f_intr == FAS_INT_DISCON) {
59327c478bd9Sstevel@tonic-gate if (msgout == MSG_DEVICE_RESET || msgout == MSG_ABORT ||
59337c478bd9Sstevel@tonic-gate msgout == MSG_ABORT_TAG) {
59347c478bd9Sstevel@tonic-gate /*
59357c478bd9Sstevel@tonic-gate * If we sent a device reset msg, then we need to do
59367c478bd9Sstevel@tonic-gate * a synch negotiate again unless we have already
59377c478bd9Sstevel@tonic-gate * inhibited synch.
59387c478bd9Sstevel@tonic-gate */
59397c478bd9Sstevel@tonic-gate if (msgout == MSG_ABORT || msgout == MSG_ABORT_TAG) {
59407c478bd9Sstevel@tonic-gate fas->f_abort_msg_sent++;
59417c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
59427c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp,
59437c478bd9Sstevel@tonic-gate CMD_ABORTED, STAT_ABORTED);
59447c478bd9Sstevel@tonic-gate }
59457c478bd9Sstevel@tonic-gate } else if (msgout == MSG_DEVICE_RESET) {
59467c478bd9Sstevel@tonic-gate fas->f_reset_msg_sent++;
59477c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
59487c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp,
59497c478bd9Sstevel@tonic-gate CMD_RESET, STAT_DEV_RESET);
59507c478bd9Sstevel@tonic-gate }
59517c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, Tgt(sp));
59527c478bd9Sstevel@tonic-gate }
59537c478bd9Sstevel@tonic-gate EPRINTF2("Successful %s message to target %d\n",
59547c478bd9Sstevel@tonic-gate scsi_mname(msgout), target);
59557c478bd9Sstevel@tonic-gate
59567c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CMDPROXY) {
59577c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] = TRUE;
59587c478bd9Sstevel@tonic-gate }
59597c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
59607c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MSG_OUT_DONE_FINISH_END,
59617c478bd9Sstevel@tonic-gate "fas_handle_msg_out_done_end (ACTION_FINISH)");
59627c478bd9Sstevel@tonic-gate return (ACTION_FINISH);
59637c478bd9Sstevel@tonic-gate }
59647c478bd9Sstevel@tonic-gate /*
59657c478bd9Sstevel@tonic-gate * If the target dropped busy on any other message, it
59667c478bd9Sstevel@tonic-gate * wasn't expected. We will let the code in fas_phasemanage()
59677c478bd9Sstevel@tonic-gate * handle this unexpected bus free event.
59687c478bd9Sstevel@tonic-gate */
59697c478bd9Sstevel@tonic-gate goto out;
59707c478bd9Sstevel@tonic-gate }
59717c478bd9Sstevel@tonic-gate
59727c478bd9Sstevel@tonic-gate /*
59737c478bd9Sstevel@tonic-gate * What phase have we transitioned to?
59747c478bd9Sstevel@tonic-gate */
59757c478bd9Sstevel@tonic-gate phase = fas->f_stat & FAS_PHASE_MASK;
59767c478bd9Sstevel@tonic-gate
59777c478bd9Sstevel@tonic-gate /*
59787c478bd9Sstevel@tonic-gate * If we finish sending a message out, and we are
59797c478bd9Sstevel@tonic-gate * still in message out phase, then the target has
59807c478bd9Sstevel@tonic-gate * detected one or more parity errors in the message
59817c478bd9Sstevel@tonic-gate * we just sent and it is asking us to resend the
59827c478bd9Sstevel@tonic-gate * previous message.
59837c478bd9Sstevel@tonic-gate */
59847c478bd9Sstevel@tonic-gate if ((fas->f_intr & FAS_INT_BUS) && phase == FAS_PHASE_MSG_OUT) {
59857c478bd9Sstevel@tonic-gate /*
59867c478bd9Sstevel@tonic-gate * As per SCSI-2 specification, if the message to
59877c478bd9Sstevel@tonic-gate * be re-sent is greater than one byte, then we
59887c478bd9Sstevel@tonic-gate * have to set ATN*.
59897c478bd9Sstevel@tonic-gate */
59907c478bd9Sstevel@tonic-gate if (amt > 1) {
59917c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
59927c478bd9Sstevel@tonic-gate }
59937c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
59947c478bd9Sstevel@tonic-gate "SCSI bus MESSAGE OUT phase parity error");
59957c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= STAT_PERR;
59967c478bd9Sstevel@tonic-gate New_state(fas, ACTS_MSG_OUT);
59977c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS,
59987c478bd9Sstevel@tonic-gate TR_FAS_HANDLE_MSG_OUT_DONE_PHASEMANAGE_END,
59997c478bd9Sstevel@tonic-gate "fas_handle_msg_out_done_end (ACTION_PHASEMANAGE)");
60007c478bd9Sstevel@tonic-gate return (ACTION_PHASEMANAGE);
60017c478bd9Sstevel@tonic-gate }
60027c478bd9Sstevel@tonic-gate
60037c478bd9Sstevel@tonic-gate
60047c478bd9Sstevel@tonic-gate out:
60057c478bd9Sstevel@tonic-gate fas->f_last_msgout = msgout;
60067c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
60077c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
60087c478bd9Sstevel@tonic-gate action = fas_handle_unknown(fas);
60097c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_HANDLE_MSG_OUT_DONE_END,
60107c478bd9Sstevel@tonic-gate "fas_handle_msg_out_done_end");
60117c478bd9Sstevel@tonic-gate return (action);
60127c478bd9Sstevel@tonic-gate }
60137c478bd9Sstevel@tonic-gate
60147c478bd9Sstevel@tonic-gate static int
fas_twobyte_msg(struct fas * fas)60157c478bd9Sstevel@tonic-gate fas_twobyte_msg(struct fas *fas)
60167c478bd9Sstevel@tonic-gate {
60177c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
60187c478bd9Sstevel@tonic-gate
60197c478bd9Sstevel@tonic-gate if ((fas->f_imsgarea[0] == MSG_IGNORE_WIDE_RESID) &&
60207c478bd9Sstevel@tonic-gate (fas->f_imsgarea[1] == 1)) {
60217c478bd9Sstevel@tonic-gate int xfer_amt;
60227c478bd9Sstevel@tonic-gate
60237c478bd9Sstevel@tonic-gate /*
60247c478bd9Sstevel@tonic-gate * Knock off one byte if there
60257c478bd9Sstevel@tonic-gate * is a last transfer and is even number of bytes
60267c478bd9Sstevel@tonic-gate */
60277c478bd9Sstevel@tonic-gate xfer_amt = sp->cmd_data_count - sp->cmd_saved_data_count;
60287c478bd9Sstevel@tonic-gate if (xfer_amt && (!(xfer_amt & 1))) {
60297c478bd9Sstevel@tonic-gate ASSERT(sp->cmd_data_count > 0);
60307c478bd9Sstevel@tonic-gate sp->cmd_data_count--;
60317c478bd9Sstevel@tonic-gate sp->cmd_cur_addr--;
60327c478bd9Sstevel@tonic-gate }
60337c478bd9Sstevel@tonic-gate IPRINTF1("ignore wide resid %d\n", fas->f_imsgarea[1]);
60347c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
60357c478bd9Sstevel@tonic-gate return (0);
60367c478bd9Sstevel@tonic-gate }
60377c478bd9Sstevel@tonic-gate
60387c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
60397c478bd9Sstevel@tonic-gate "Two byte message '%s' 0x%x rejected",
60407c478bd9Sstevel@tonic-gate scsi_mname(fas->f_imsgarea[0]), fas->f_imsgarea[1]);
60417c478bd9Sstevel@tonic-gate return (MSG_REJECT);
60427c478bd9Sstevel@tonic-gate }
60437c478bd9Sstevel@tonic-gate
60447c478bd9Sstevel@tonic-gate /*
60457c478bd9Sstevel@tonic-gate * handle receiving extended messages
60467c478bd9Sstevel@tonic-gate */
60477c478bd9Sstevel@tonic-gate static int
fas_multibyte_msg(struct fas * fas)60487c478bd9Sstevel@tonic-gate fas_multibyte_msg(struct fas *fas)
60497c478bd9Sstevel@tonic-gate {
60507c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
60517c478bd9Sstevel@tonic-gate static char *mbs =
60527c478bd9Sstevel@tonic-gate "Target %d now Synchronous at %d.%d MB/s max transmit rate\n";
60537c478bd9Sstevel@tonic-gate static char *mbs1 =
60547c478bd9Sstevel@tonic-gate "Target %d now Synchronous at %d.0%d MB/s max transmit rate\n";
60557c478bd9Sstevel@tonic-gate static char *mbs2 =
60567c478bd9Sstevel@tonic-gate "Target %d now Synchronous at %d.00%d MB/s max transmit rate\n";
60577c478bd9Sstevel@tonic-gate #endif
60587c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
60597c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
60607c478bd9Sstevel@tonic-gate uchar_t emsg = fas->f_imsgarea[2];
60617c478bd9Sstevel@tonic-gate int tgt = Tgt(sp);
60627c478bd9Sstevel@tonic-gate int msgout = 0;
60637c478bd9Sstevel@tonic-gate
60647c478bd9Sstevel@tonic-gate EPRINTF("fas_multibyte_msg:\n");
60657c478bd9Sstevel@tonic-gate
60667c478bd9Sstevel@tonic-gate if (emsg == MSG_SYNCHRONOUS) {
60677c478bd9Sstevel@tonic-gate uint_t period, offset, regval;
60687c478bd9Sstevel@tonic-gate uint_t minsync, maxsync, clockval;
60697c478bd9Sstevel@tonic-gate uint_t xfer_freq, xfer_div, xfer_mod, xfer_rate;
60707c478bd9Sstevel@tonic-gate
60717c478bd9Sstevel@tonic-gate period = fas->f_imsgarea[3] & 0xff;
60727c478bd9Sstevel@tonic-gate offset = fas->f_imsgarea[4] & 0xff;
60737c478bd9Sstevel@tonic-gate minsync = MIN_SYNC_PERIOD(fas);
60747c478bd9Sstevel@tonic-gate maxsync = MAX_SYNC_PERIOD(fas);
60757c478bd9Sstevel@tonic-gate DPRINTF5("sync msg received: %x %x %x %x %x\n",
60767c478bd9Sstevel@tonic-gate fas->f_imsgarea[0], fas->f_imsgarea[1],
60777c478bd9Sstevel@tonic-gate fas->f_imsgarea[2], fas->f_imsgarea[3],
60787c478bd9Sstevel@tonic-gate fas->f_imsgarea[4]);
60797c478bd9Sstevel@tonic-gate DPRINTF3("received period %d offset %d from tgt %d\n",
60807c478bd9Sstevel@tonic-gate period, offset, tgt);
60817c478bd9Sstevel@tonic-gate DPRINTF3("calculated minsync %d, maxsync %d for tgt %d\n",
60827c478bd9Sstevel@tonic-gate minsync, maxsync, tgt);
60837c478bd9Sstevel@tonic-gate DPRINTF2("sync period %d, neg period %d\n",
60847c478bd9Sstevel@tonic-gate fas->f_sync_period[tgt], fas->f_neg_period[tgt]);
60857c478bd9Sstevel@tonic-gate
60867c478bd9Sstevel@tonic-gate if ((++(fas->f_sdtr_sent)) & 1) {
60877c478bd9Sstevel@tonic-gate /*
60887c478bd9Sstevel@tonic-gate * In cases where the target negotiates synchronous
60897c478bd9Sstevel@tonic-gate * mode before we do, and we either have sync mode
60907c478bd9Sstevel@tonic-gate * disabled, or this target is known to be a weak
60917c478bd9Sstevel@tonic-gate * signal target, we send back a message indicating
60927c478bd9Sstevel@tonic-gate * a desire to stay in asynchronous mode (the SCSI-2
60937c478bd9Sstevel@tonic-gate * spec states that if we have synchronous capability
60947c478bd9Sstevel@tonic-gate * then we cannot reject a SYNCHRONOUS DATA TRANSFER
60957c478bd9Sstevel@tonic-gate * REQUEST message).
60967c478bd9Sstevel@tonic-gate */
60977c478bd9Sstevel@tonic-gate IPRINTF1("SYNC negotiation initiated by target %d\n",
60987c478bd9Sstevel@tonic-gate tgt);
60997c478bd9Sstevel@tonic-gate
61007c478bd9Sstevel@tonic-gate msgout = MSG_EXTENDED;
61017c478bd9Sstevel@tonic-gate
61027c478bd9Sstevel@tonic-gate period =
61037c478bd9Sstevel@tonic-gate period ? max(period, MIN_SYNC_PERIOD(fas)) : 0;
61047c478bd9Sstevel@tonic-gate
61057c478bd9Sstevel@tonic-gate if (fas->f_backoff & (1<<tgt)) {
61067c478bd9Sstevel@tonic-gate period = period ?
61077c478bd9Sstevel@tonic-gate max(period, fas->f_neg_period[tgt]) : 0;
61087c478bd9Sstevel@tonic-gate }
61097c478bd9Sstevel@tonic-gate offset = min(offset, fas_default_offset);
61107c478bd9Sstevel@tonic-gate }
61117c478bd9Sstevel@tonic-gate xfer_freq = regval = 0;
61127c478bd9Sstevel@tonic-gate
61137c478bd9Sstevel@tonic-gate /*
61147c478bd9Sstevel@tonic-gate * If the target's offset is bigger than ours,
61157c478bd9Sstevel@tonic-gate * the target has violated the scsi protocol.
61167c478bd9Sstevel@tonic-gate */
61177c478bd9Sstevel@tonic-gate if (offset > fas_default_offset) {
61187c478bd9Sstevel@tonic-gate period = offset = 0;
61197c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
61207c478bd9Sstevel@tonic-gate }
61217c478bd9Sstevel@tonic-gate
61227c478bd9Sstevel@tonic-gate if (offset && (period > maxsync)) {
61237c478bd9Sstevel@tonic-gate /*
61247c478bd9Sstevel@tonic-gate * We cannot transmit data in synchronous
61257c478bd9Sstevel@tonic-gate * mode this slow, so convert to asynchronous
61267c478bd9Sstevel@tonic-gate * mode.
61277c478bd9Sstevel@tonic-gate */
61287c478bd9Sstevel@tonic-gate msgout = MSG_EXTENDED;
61297c478bd9Sstevel@tonic-gate period = offset = 0;
61307c478bd9Sstevel@tonic-gate
61317c478bd9Sstevel@tonic-gate } else if (offset && (period < minsync)) {
61327c478bd9Sstevel@tonic-gate /*
61337c478bd9Sstevel@tonic-gate * If the target's period is less than ours,
61347c478bd9Sstevel@tonic-gate * the target has violated the scsi protocol.
61357c478bd9Sstevel@tonic-gate */
61367c478bd9Sstevel@tonic-gate period = offset = 0;
61377c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
61387c478bd9Sstevel@tonic-gate
61397c478bd9Sstevel@tonic-gate } else if (offset) {
61407c478bd9Sstevel@tonic-gate /*
61417c478bd9Sstevel@tonic-gate * Conversion method for received PERIOD value
61427c478bd9Sstevel@tonic-gate * to the number of input clock ticks to the FAS.
61437c478bd9Sstevel@tonic-gate *
61447c478bd9Sstevel@tonic-gate * We adjust the input period value such that
61457c478bd9Sstevel@tonic-gate * we always will transmit data *not* faster
61467c478bd9Sstevel@tonic-gate * than the period value received.
61477c478bd9Sstevel@tonic-gate */
61487c478bd9Sstevel@tonic-gate
61497c478bd9Sstevel@tonic-gate clockval = fas->f_clock_cycle / 1000;
61507c478bd9Sstevel@tonic-gate regval = (((period << 2) + clockval - 1) / clockval);
61517c478bd9Sstevel@tonic-gate
61527c478bd9Sstevel@tonic-gate /*
61537c478bd9Sstevel@tonic-gate * correction if xfer rate <= 5MB/sec
61547c478bd9Sstevel@tonic-gate * XXX do we need this?
61557c478bd9Sstevel@tonic-gate */
61567c478bd9Sstevel@tonic-gate if (regval && (period >= FASTSCSI_THRESHOLD)) {
61577c478bd9Sstevel@tonic-gate regval--;
61587c478bd9Sstevel@tonic-gate }
61597c478bd9Sstevel@tonic-gate }
61607c478bd9Sstevel@tonic-gate
61617c478bd9Sstevel@tonic-gate fas->f_offset[tgt] = offset;
61627c478bd9Sstevel@tonic-gate fas->f_neg_period[tgt] = period;
61637c478bd9Sstevel@tonic-gate
61647c478bd9Sstevel@tonic-gate /*
61657c478bd9Sstevel@tonic-gate * Is is now safe to produce a responce to a target
61667c478bd9Sstevel@tonic-gate * initiated sdtr. period and offset have been checked.
61677c478bd9Sstevel@tonic-gate */
61687c478bd9Sstevel@tonic-gate if (msgout == MSG_EXTENDED) {
61697c478bd9Sstevel@tonic-gate fas_make_sdtr(fas, 0, tgt);
61707c478bd9Sstevel@tonic-gate period = fas->f_neg_period[tgt];
61717c478bd9Sstevel@tonic-gate offset = (fas->f_offset[tgt] & 0xf);
61727c478bd9Sstevel@tonic-gate }
61737c478bd9Sstevel@tonic-gate
61747c478bd9Sstevel@tonic-gate if (offset) {
61757c478bd9Sstevel@tonic-gate fas->f_sync_period[tgt] = regval & SYNC_PERIOD_MASK;
61767c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_sync_period,
61777c478bd9Sstevel@tonic-gate fas->f_sync_period[tgt]);
61787c478bd9Sstevel@tonic-gate
61797c478bd9Sstevel@tonic-gate fas->f_offset[tgt] = offset | fas->f_req_ack_delay;
61807c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_sync_offset,
618119397407SSherry Moore fas->f_offset[tgt]);
61827c478bd9Sstevel@tonic-gate
61837c478bd9Sstevel@tonic-gate /*
61847c478bd9Sstevel@tonic-gate * if transferring > 5 MB/sec then enable
61857c478bd9Sstevel@tonic-gate * fastscsi in conf3
61867c478bd9Sstevel@tonic-gate */
61877c478bd9Sstevel@tonic-gate if (period < FASTSCSI_THRESHOLD) {
61887c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt] |= FAS_CONF3_FASTSCSI;
61897c478bd9Sstevel@tonic-gate } else {
61907c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt] &= ~FAS_CONF3_FASTSCSI;
61917c478bd9Sstevel@tonic-gate }
61927c478bd9Sstevel@tonic-gate
61937c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_conf3,
61947c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt]);
61957c478bd9Sstevel@tonic-gate
61967c478bd9Sstevel@tonic-gate DPRINTF4("period %d (%d), offset %d to tgt %d\n",
619719397407SSherry Moore period,
619819397407SSherry Moore fas->f_sync_period[tgt] & SYNC_PERIOD_MASK,
619919397407SSherry Moore fas->f_offset[tgt] & 0xf, tgt);
62007c478bd9Sstevel@tonic-gate DPRINTF1("req/ack delay = %x\n", fas->f_req_ack_delay);
62017c478bd9Sstevel@tonic-gate DPRINTF1("conf3 = %x\n", fas->f_fasconf3[tgt]);
62027c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
62037c478bd9Sstevel@tonic-gate /*
62047c478bd9Sstevel@tonic-gate * Convert input clock cycle per
62057c478bd9Sstevel@tonic-gate * byte to nanoseconds per byte.
62067c478bd9Sstevel@tonic-gate * (ns/b), and convert that to
62077c478bd9Sstevel@tonic-gate * k-bytes/second.
62087c478bd9Sstevel@tonic-gate */
62097c478bd9Sstevel@tonic-gate xfer_freq = FAS_SYNC_KBPS((regval *
621019397407SSherry Moore fas->f_clock_cycle) / 1000);
62117c478bd9Sstevel@tonic-gate xfer_rate = ((fas->f_nowide & (1<<tgt))? 1 : 2) *
621219397407SSherry Moore xfer_freq;
62137c478bd9Sstevel@tonic-gate xfer_div = xfer_rate / 1000;
62147c478bd9Sstevel@tonic-gate xfer_mod = xfer_rate % 1000;
62157c478bd9Sstevel@tonic-gate
62167c478bd9Sstevel@tonic-gate
62177c478bd9Sstevel@tonic-gate if (xfer_mod > 99) {
62187c478bd9Sstevel@tonic-gate IPRINTF3(mbs, tgt, xfer_div, xfer_mod);
62197c478bd9Sstevel@tonic-gate } else if (xfer_mod > 9) {
62207c478bd9Sstevel@tonic-gate IPRINTF3(mbs1, tgt, xfer_div, xfer_mod);
62217c478bd9Sstevel@tonic-gate } else {
62227c478bd9Sstevel@tonic-gate IPRINTF3(mbs2, tgt, xfer_div, xfer_mod);
62237c478bd9Sstevel@tonic-gate }
62247c478bd9Sstevel@tonic-gate #endif
62257c478bd9Sstevel@tonic-gate fas->f_sync_enabled |= (1<<tgt);
62267c478bd9Sstevel@tonic-gate
62277c478bd9Sstevel@tonic-gate } else {
62287c478bd9Sstevel@tonic-gate /*
62297c478bd9Sstevel@tonic-gate * We are converting back to async mode.
62307c478bd9Sstevel@tonic-gate */
62317c478bd9Sstevel@tonic-gate fas_revert_to_async(fas, tgt);
62327c478bd9Sstevel@tonic-gate }
62337c478bd9Sstevel@tonic-gate
62347c478bd9Sstevel@tonic-gate /*
62357c478bd9Sstevel@tonic-gate * If this target violated the scsi spec, reject the
62367c478bd9Sstevel@tonic-gate * sdtr msg and don't negotiate sdtr again.
62377c478bd9Sstevel@tonic-gate */
62387c478bd9Sstevel@tonic-gate if (msgout == MSG_REJECT) {
62397c478bd9Sstevel@tonic-gate fas->f_nosync |= (1<<tgt);
62407c478bd9Sstevel@tonic-gate }
62417c478bd9Sstevel@tonic-gate
62427c478bd9Sstevel@tonic-gate fas->f_props_update |= (1<<tgt);
62437c478bd9Sstevel@tonic-gate
62447c478bd9Sstevel@tonic-gate } else if (emsg == MSG_WIDE_DATA_XFER) {
62457c478bd9Sstevel@tonic-gate uchar_t width = fas->f_imsgarea[3] & 0xff;
62467c478bd9Sstevel@tonic-gate
62477c478bd9Sstevel@tonic-gate DPRINTF4("wide msg received: %x %x %x %x\n",
62487c478bd9Sstevel@tonic-gate fas->f_imsgarea[0], fas->f_imsgarea[1],
62497c478bd9Sstevel@tonic-gate fas->f_imsgarea[2], fas->f_imsgarea[3]);
62507c478bd9Sstevel@tonic-gate
62517c478bd9Sstevel@tonic-gate /* always renegotiate sync after wide */
62527c478bd9Sstevel@tonic-gate msgout = MSG_EXTENDED;
62537c478bd9Sstevel@tonic-gate
62547c478bd9Sstevel@tonic-gate if ((++(fas->f_wdtr_sent)) & 1) {
62557c478bd9Sstevel@tonic-gate IPRINTF1("Wide negotiation initiated by target %d\n",
62567c478bd9Sstevel@tonic-gate tgt);
62577c478bd9Sstevel@tonic-gate /*
62587c478bd9Sstevel@tonic-gate * allow wide neg even if the target driver hasn't
62597c478bd9Sstevel@tonic-gate * enabled wide yet.
62607c478bd9Sstevel@tonic-gate */
62617c478bd9Sstevel@tonic-gate fas->f_nowide &= ~(1<<tgt);
62627c478bd9Sstevel@tonic-gate fas_make_wdtr(fas, 0, tgt, width);
62637c478bd9Sstevel@tonic-gate IPRINTF1("sending wide sync %d back\n", width);
62647c478bd9Sstevel@tonic-gate /*
62657c478bd9Sstevel@tonic-gate * Let us go back to async mode(SCSI spec)
62667c478bd9Sstevel@tonic-gate * and depend on target to do sync
62677c478bd9Sstevel@tonic-gate * after wide negotiations.
62687c478bd9Sstevel@tonic-gate * If target does not do a sync neg and enters
62697c478bd9Sstevel@tonic-gate * async mode we will negotiate sync on next command
62707c478bd9Sstevel@tonic-gate */
62717c478bd9Sstevel@tonic-gate fas_revert_to_async(fas, tgt);
62727c478bd9Sstevel@tonic-gate fas->f_sync_known &= ~(1<<tgt);
62737c478bd9Sstevel@tonic-gate } else {
62747c478bd9Sstevel@tonic-gate /*
62757c478bd9Sstevel@tonic-gate * renegotiate sync after wide
62767c478bd9Sstevel@tonic-gate */
62777c478bd9Sstevel@tonic-gate fas_set_wide_conf3(fas, tgt, width);
62787c478bd9Sstevel@tonic-gate ASSERT(width <= 1);
62797c478bd9Sstevel@tonic-gate fas->f_wdtr_sent = 0;
62807c478bd9Sstevel@tonic-gate if ((fas->f_nosync & (1<<tgt)) == 0) {
62817c478bd9Sstevel@tonic-gate fas_make_sdtr(fas, 0, tgt);
62827c478bd9Sstevel@tonic-gate } else {
62837c478bd9Sstevel@tonic-gate msgout = 0;
62847c478bd9Sstevel@tonic-gate }
62857c478bd9Sstevel@tonic-gate }
62867c478bd9Sstevel@tonic-gate
62877c478bd9Sstevel@tonic-gate fas->f_props_update |= (1<<tgt);
62887c478bd9Sstevel@tonic-gate
62897c478bd9Sstevel@tonic-gate } else if (emsg == MSG_MODIFY_DATA_PTR) {
62907c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
62917c478bd9Sstevel@tonic-gate } else {
62927c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
62937c478bd9Sstevel@tonic-gate "Rejecting message %s 0x%x from Target %d",
62947c478bd9Sstevel@tonic-gate scsi_mname(MSG_EXTENDED), emsg, tgt);
62957c478bd9Sstevel@tonic-gate msgout = MSG_REJECT;
62967c478bd9Sstevel@tonic-gate }
62977c478bd9Sstevel@tonic-gate out:
62987c478bd9Sstevel@tonic-gate New_state(fas, ACTS_UNKNOWN);
62997c478bd9Sstevel@tonic-gate return (msgout);
63007c478bd9Sstevel@tonic-gate }
63017c478bd9Sstevel@tonic-gate
63027c478bd9Sstevel@tonic-gate /*
63037c478bd9Sstevel@tonic-gate * Back off sync negotiation
63047c478bd9Sstevel@tonic-gate * and got to async mode
63057c478bd9Sstevel@tonic-gate */
63067c478bd9Sstevel@tonic-gate static void
fas_revert_to_async(struct fas * fas,int tgt)63077c478bd9Sstevel@tonic-gate fas_revert_to_async(struct fas *fas, int tgt)
63087c478bd9Sstevel@tonic-gate {
63097c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
63107c478bd9Sstevel@tonic-gate
63117c478bd9Sstevel@tonic-gate fas->f_sync_period[tgt] = 0;
63127c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_sync_period, 0);
63137c478bd9Sstevel@tonic-gate fas->f_offset[tgt] = 0;
63147c478bd9Sstevel@tonic-gate fas_reg_write(fas, (uchar_t *)&fasreg->fas_sync_offset, 0);
63157c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt] &= ~FAS_CONF3_FASTSCSI;
63167c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_conf3, fas->f_fasconf3[tgt]);
63177c478bd9Sstevel@tonic-gate fas->f_sync_enabled &= ~(1<<tgt);
63187c478bd9Sstevel@tonic-gate }
63197c478bd9Sstevel@tonic-gate
63207c478bd9Sstevel@tonic-gate /*
63217c478bd9Sstevel@tonic-gate * handle an unexpected selection attempt
63227c478bd9Sstevel@tonic-gate * XXX look for better way: msg reject, drop off the bus
63237c478bd9Sstevel@tonic-gate */
63247c478bd9Sstevel@tonic-gate static int
fas_handle_selection(struct fas * fas)63257c478bd9Sstevel@tonic-gate fas_handle_selection(struct fas *fas)
63267c478bd9Sstevel@tonic-gate {
63277c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_DISCONNECT);
63287c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
63297c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_EN_RESEL);
63307c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
63317c478bd9Sstevel@tonic-gate }
63327c478bd9Sstevel@tonic-gate
63337c478bd9Sstevel@tonic-gate /*
63347c478bd9Sstevel@tonic-gate * dma window handling
63357c478bd9Sstevel@tonic-gate */
63367c478bd9Sstevel@tonic-gate static int
fas_restore_pointers(struct fas * fas,struct fas_cmd * sp)63377c478bd9Sstevel@tonic-gate fas_restore_pointers(struct fas *fas, struct fas_cmd *sp)
63387c478bd9Sstevel@tonic-gate {
63397c478bd9Sstevel@tonic-gate if (sp->cmd_data_count != sp->cmd_saved_data_count) {
63407c478bd9Sstevel@tonic-gate sp->cmd_data_count = sp->cmd_saved_data_count;
63417c478bd9Sstevel@tonic-gate sp->cmd_cur_addr = sp->cmd_saved_cur_addr;
63427c478bd9Sstevel@tonic-gate
63437c478bd9Sstevel@tonic-gate if (sp->cmd_cur_win != sp->cmd_saved_win) {
63447c478bd9Sstevel@tonic-gate sp->cmd_cur_win = sp->cmd_saved_win;
63457c478bd9Sstevel@tonic-gate if (fas_set_new_window(fas, sp)) {
63467c478bd9Sstevel@tonic-gate return (-1);
63477c478bd9Sstevel@tonic-gate }
63487c478bd9Sstevel@tonic-gate }
63497c478bd9Sstevel@tonic-gate DPRINTF1("curaddr=%x\n", sp->cmd_cur_addr);
63507c478bd9Sstevel@tonic-gate }
63517c478bd9Sstevel@tonic-gate return (0);
63527c478bd9Sstevel@tonic-gate }
63537c478bd9Sstevel@tonic-gate
63547c478bd9Sstevel@tonic-gate static int
fas_set_new_window(struct fas * fas,struct fas_cmd * sp)63557c478bd9Sstevel@tonic-gate fas_set_new_window(struct fas *fas, struct fas_cmd *sp)
63567c478bd9Sstevel@tonic-gate {
63577c478bd9Sstevel@tonic-gate off_t offset;
63587c478bd9Sstevel@tonic-gate size_t len;
63597c478bd9Sstevel@tonic-gate uint_t count;
63607c478bd9Sstevel@tonic-gate
63617c478bd9Sstevel@tonic-gate if (ddi_dma_getwin(sp->cmd_dmahandle, sp->cmd_cur_win,
63627c478bd9Sstevel@tonic-gate &offset, &len, &sp->cmd_dmacookie, &count) != DDI_SUCCESS) {
63637c478bd9Sstevel@tonic-gate return (-1);
63647c478bd9Sstevel@tonic-gate }
63657c478bd9Sstevel@tonic-gate
63667c478bd9Sstevel@tonic-gate DPRINTF4("new window %x: off=%lx, len=%lx, count=%x\n",
63677c478bd9Sstevel@tonic-gate sp->cmd_cur_win, offset, len, count);
63687c478bd9Sstevel@tonic-gate
63697c478bd9Sstevel@tonic-gate ASSERT(count == 1);
63707c478bd9Sstevel@tonic-gate return (0);
63717c478bd9Sstevel@tonic-gate }
63727c478bd9Sstevel@tonic-gate
63737c478bd9Sstevel@tonic-gate static int
fas_next_window(struct fas * fas,struct fas_cmd * sp,uint64_t end)63747c478bd9Sstevel@tonic-gate fas_next_window(struct fas *fas, struct fas_cmd *sp, uint64_t end)
63757c478bd9Sstevel@tonic-gate {
63767c478bd9Sstevel@tonic-gate
63777c478bd9Sstevel@tonic-gate /* are there more windows? */
63787c478bd9Sstevel@tonic-gate if (sp->cmd_nwin == 0) {
63797c478bd9Sstevel@tonic-gate uint_t nwin = 0;
63807c478bd9Sstevel@tonic-gate (void) ddi_dma_numwin(sp->cmd_dmahandle, &nwin);
63817c478bd9Sstevel@tonic-gate sp->cmd_nwin = (uchar_t)nwin;
63827c478bd9Sstevel@tonic-gate }
63837c478bd9Sstevel@tonic-gate
63847c478bd9Sstevel@tonic-gate DPRINTF5(
63857c478bd9Sstevel@tonic-gate "cmd_data_count=%x, dmacount=%x, curaddr=%x, end=%lx, nwin=%x\n",
63867c478bd9Sstevel@tonic-gate sp->cmd_data_count, sp->cmd_dmacount, sp->cmd_cur_addr, end,
63877c478bd9Sstevel@tonic-gate sp->cmd_nwin);
63887c478bd9Sstevel@tonic-gate
63897c478bd9Sstevel@tonic-gate if (sp->cmd_cur_win < sp->cmd_nwin) {
63907c478bd9Sstevel@tonic-gate sp->cmd_cur_win++;
63917c478bd9Sstevel@tonic-gate if (fas_set_new_window(fas, sp)) {
63927c478bd9Sstevel@tonic-gate fas_printstate(fas, "cannot set new window");
63937c478bd9Sstevel@tonic-gate sp->cmd_cur_win--;
63947c478bd9Sstevel@tonic-gate return (-1);
63957c478bd9Sstevel@tonic-gate }
63967c478bd9Sstevel@tonic-gate /*
63977c478bd9Sstevel@tonic-gate * if there are no more windows, we have a data overrun condition
63987c478bd9Sstevel@tonic-gate */
63997c478bd9Sstevel@tonic-gate } else {
64007c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
64017c478bd9Sstevel@tonic-gate
64027c478bd9Sstevel@tonic-gate fas_printstate(fas, "data transfer overrun");
64037c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_DATA_OVR, 0);
64047c478bd9Sstevel@tonic-gate
64057c478bd9Sstevel@tonic-gate /*
64067c478bd9Sstevel@tonic-gate * if we get data transfer overruns, assume we have
64077c478bd9Sstevel@tonic-gate * a weak scsi bus. Note that this won't catch consistent
64087c478bd9Sstevel@tonic-gate * underruns or other noise related syndromes.
64097c478bd9Sstevel@tonic-gate */
64107c478bd9Sstevel@tonic-gate fas_sync_wide_backoff(fas, sp, slot);
64117c478bd9Sstevel@tonic-gate return (-1);
64127c478bd9Sstevel@tonic-gate }
64137c478bd9Sstevel@tonic-gate sp->cmd_cur_addr = sp->cmd_dmacookie.dmac_address;
64147c478bd9Sstevel@tonic-gate DPRINTF1("cur_addr=%x\n", sp->cmd_cur_addr);
64157c478bd9Sstevel@tonic-gate return (0);
64167c478bd9Sstevel@tonic-gate }
64177c478bd9Sstevel@tonic-gate
64187c478bd9Sstevel@tonic-gate /*
64197c478bd9Sstevel@tonic-gate * dma error handler
64207c478bd9Sstevel@tonic-gate */
64217c478bd9Sstevel@tonic-gate static int
fas_check_dma_error(struct fas * fas)64227c478bd9Sstevel@tonic-gate fas_check_dma_error(struct fas *fas)
64237c478bd9Sstevel@tonic-gate {
64247c478bd9Sstevel@tonic-gate /*
64257c478bd9Sstevel@tonic-gate * was there a dma error that caused fas_intr_svc() to be called?
64267c478bd9Sstevel@tonic-gate */
64277c478bd9Sstevel@tonic-gate if (fas->f_dma->dma_csr & DMA_ERRPEND) {
64287c478bd9Sstevel@tonic-gate /*
64297c478bd9Sstevel@tonic-gate * It would be desirable to set the ATN* line and attempt to
64307c478bd9Sstevel@tonic-gate * do the whole schmear of INITIATOR DETECTED ERROR here,
64317c478bd9Sstevel@tonic-gate * but that is too hard to do at present.
64327c478bd9Sstevel@tonic-gate */
64337c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "Unrecoverable DMA error");
64347c478bd9Sstevel@tonic-gate fas_printstate(fas, "dma error");
64357c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, fas->f_current_sp, CMD_TRAN_ERR, 0);
64367c478bd9Sstevel@tonic-gate return (-1);
64377c478bd9Sstevel@tonic-gate }
64387c478bd9Sstevel@tonic-gate return (0);
64397c478bd9Sstevel@tonic-gate }
64407c478bd9Sstevel@tonic-gate
64417c478bd9Sstevel@tonic-gate /*
64427c478bd9Sstevel@tonic-gate * check for gross error or spurious interrupt
64437c478bd9Sstevel@tonic-gate */
64447c478bd9Sstevel@tonic-gate static int
fas_handle_gross_err(struct fas * fas)64457c478bd9Sstevel@tonic-gate fas_handle_gross_err(struct fas *fas)
64467c478bd9Sstevel@tonic-gate {
64477c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
64487c478bd9Sstevel@tonic-gate
64497c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
645019397407SSherry Moore "gross error in fas status (%x)", fas->f_stat);
64517c478bd9Sstevel@tonic-gate
64527c478bd9Sstevel@tonic-gate IPRINTF5("fas_cmd=%x, stat=%x, intr=%x, step=%x, fifoflag=%x\n",
645319397407SSherry Moore fasreg->fas_cmd, fas->f_stat, fas->f_intr, fasreg->fas_step,
645419397407SSherry Moore fasreg->fas_fifo_flag);
64557c478bd9Sstevel@tonic-gate
64567c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, fas->f_current_sp, CMD_TRAN_ERR, 0);
64577c478bd9Sstevel@tonic-gate
64587c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_FAS);
64597c478bd9Sstevel@tonic-gate return (ACTION_RESET);
64607c478bd9Sstevel@tonic-gate }
64617c478bd9Sstevel@tonic-gate
64627c478bd9Sstevel@tonic-gate
64637c478bd9Sstevel@tonic-gate /*
64647c478bd9Sstevel@tonic-gate * handle illegal cmd interrupt or (external) bus reset cleanup
64657c478bd9Sstevel@tonic-gate */
64667c478bd9Sstevel@tonic-gate static int
fas_illegal_cmd_or_bus_reset(struct fas * fas)64677c478bd9Sstevel@tonic-gate fas_illegal_cmd_or_bus_reset(struct fas *fas)
64687c478bd9Sstevel@tonic-gate {
64697c478bd9Sstevel@tonic-gate /*
64707c478bd9Sstevel@tonic-gate * If we detect a SCSI reset, we blow away the current
64717c478bd9Sstevel@tonic-gate * command (if there is one) and all disconnected commands
64727c478bd9Sstevel@tonic-gate * because we now don't know the state of them at all.
64737c478bd9Sstevel@tonic-gate */
64747c478bd9Sstevel@tonic-gate ASSERT(fas->f_intr & (FAS_INT_ILLEGAL | FAS_INT_RESET));
64757c478bd9Sstevel@tonic-gate
64767c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_RESET) {
64777c478bd9Sstevel@tonic-gate return (ACTION_FINRST);
64787c478bd9Sstevel@tonic-gate }
64797c478bd9Sstevel@tonic-gate
64807c478bd9Sstevel@tonic-gate /*
64817c478bd9Sstevel@tonic-gate * Illegal cmd to fas:
64827c478bd9Sstevel@tonic-gate * This should not happen. The one situation where
64837c478bd9Sstevel@tonic-gate * we can get an ILLEGAL COMMAND interrupt is due to
64847c478bd9Sstevel@tonic-gate * a bug in the FAS366 during reselection which we
64857c478bd9Sstevel@tonic-gate * should be handling in fas_reconnect().
64867c478bd9Sstevel@tonic-gate */
64877c478bd9Sstevel@tonic-gate if (fas->f_intr & FAS_INT_ILLEGAL) {
64887c478bd9Sstevel@tonic-gate IPRINTF1("lastcmd=%x\n", fas->f_reg->fas_cmd);
64897c478bd9Sstevel@tonic-gate fas_printstate(fas, "ILLEGAL bit set");
64907c478bd9Sstevel@tonic-gate return (ACTION_RESET);
64917c478bd9Sstevel@tonic-gate }
64927c478bd9Sstevel@tonic-gate /*NOTREACHED*/
6493360e6f5eSmathue return (ACTION_RETURN);
64947c478bd9Sstevel@tonic-gate }
64957c478bd9Sstevel@tonic-gate
64967c478bd9Sstevel@tonic-gate /*
64977c478bd9Sstevel@tonic-gate * set throttles for all luns of this target
64987c478bd9Sstevel@tonic-gate */
64997c478bd9Sstevel@tonic-gate static void
fas_set_throttles(struct fas * fas,int slot,int n,int what)65007c478bd9Sstevel@tonic-gate fas_set_throttles(struct fas *fas, int slot, int n, int what)
65017c478bd9Sstevel@tonic-gate {
65027c478bd9Sstevel@tonic-gate int i;
65037c478bd9Sstevel@tonic-gate
65047c478bd9Sstevel@tonic-gate /*
65057c478bd9Sstevel@tonic-gate * if the bus is draining/quiesced, no changes to the throttles
65067c478bd9Sstevel@tonic-gate * are allowed. Not allowing change of throttles during draining
65077c478bd9Sstevel@tonic-gate * limits error recovery but will reduce draining time
65087c478bd9Sstevel@tonic-gate *
65097c478bd9Sstevel@tonic-gate * all throttles should have been set to HOLD_THROTTLE
65107c478bd9Sstevel@tonic-gate */
65117c478bd9Sstevel@tonic-gate if (fas->f_softstate & (FAS_SS_QUIESCED | FAS_SS_DRAINING)) {
65127c478bd9Sstevel@tonic-gate return;
65137c478bd9Sstevel@tonic-gate }
65147c478bd9Sstevel@tonic-gate
65157c478bd9Sstevel@tonic-gate ASSERT((n == 1) || (n == N_SLOTS) || (n == NLUNS_PER_TARGET));
65167c478bd9Sstevel@tonic-gate ASSERT((slot + n) <= N_SLOTS);
65177c478bd9Sstevel@tonic-gate if (n == NLUNS_PER_TARGET) {
65187c478bd9Sstevel@tonic-gate slot &= ~(NLUNS_PER_TARGET - 1);
65197c478bd9Sstevel@tonic-gate }
65207c478bd9Sstevel@tonic-gate
65217c478bd9Sstevel@tonic-gate for (i = slot; i < (slot + n); i++) {
65227c478bd9Sstevel@tonic-gate if (what == HOLD_THROTTLE) {
65237c478bd9Sstevel@tonic-gate fas->f_throttle[i] = HOLD_THROTTLE;
65247c478bd9Sstevel@tonic-gate } else if ((fas->f_reset_delay[i/NLUNS_PER_TARGET]) == 0) {
65257c478bd9Sstevel@tonic-gate if (what == MAX_THROTTLE) {
65267c478bd9Sstevel@tonic-gate int tshift = 1 << (i/NLUNS_PER_TARGET);
65277c478bd9Sstevel@tonic-gate fas->f_throttle[i] = (short)
652819397407SSherry Moore ((fas->f_notag & tshift)? 1 : what);
65297c478bd9Sstevel@tonic-gate } else {
65307c478bd9Sstevel@tonic-gate fas->f_throttle[i] = what;
65317c478bd9Sstevel@tonic-gate }
65327c478bd9Sstevel@tonic-gate }
65337c478bd9Sstevel@tonic-gate }
65347c478bd9Sstevel@tonic-gate }
65357c478bd9Sstevel@tonic-gate
65367c478bd9Sstevel@tonic-gate static void
fas_set_all_lun_throttles(struct fas * fas,int slot,int what)65377c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(struct fas *fas, int slot, int what)
65387c478bd9Sstevel@tonic-gate {
65397c478bd9Sstevel@tonic-gate /*
65407c478bd9Sstevel@tonic-gate * fas_set_throttle will adjust slot to starting at LUN 0
65417c478bd9Sstevel@tonic-gate */
65427c478bd9Sstevel@tonic-gate fas_set_throttles(fas, slot, NLUNS_PER_TARGET, what);
65437c478bd9Sstevel@tonic-gate }
65447c478bd9Sstevel@tonic-gate
65457c478bd9Sstevel@tonic-gate static void
fas_full_throttle(struct fas * fas,int slot)65467c478bd9Sstevel@tonic-gate fas_full_throttle(struct fas *fas, int slot)
65477c478bd9Sstevel@tonic-gate {
65487c478bd9Sstevel@tonic-gate fas_set_throttles(fas, slot, 1, MAX_THROTTLE);
65497c478bd9Sstevel@tonic-gate }
65507c478bd9Sstevel@tonic-gate
65517c478bd9Sstevel@tonic-gate /*
65527c478bd9Sstevel@tonic-gate * run a polled cmd
65537c478bd9Sstevel@tonic-gate */
65547c478bd9Sstevel@tonic-gate static void
fas_runpoll(struct fas * fas,short slot,struct fas_cmd * sp)65557c478bd9Sstevel@tonic-gate fas_runpoll(struct fas *fas, short slot, struct fas_cmd *sp)
65567c478bd9Sstevel@tonic-gate {
65577c478bd9Sstevel@tonic-gate int limit, i, n;
65587c478bd9Sstevel@tonic-gate int timeout = 0;
65597c478bd9Sstevel@tonic-gate
65607c478bd9Sstevel@tonic-gate DPRINTF4("runpoll: slot=%x, cmd=%x, current_sp=0x%p, tcmds=%x\n",
656119397407SSherry Moore slot, *((uchar_t *)sp->cmd_pkt->pkt_cdbp),
656219397407SSherry Moore (void *)fas->f_current_sp, fas->f_tcmds[slot]);
65637c478bd9Sstevel@tonic-gate
65647c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_RUNPOLL_START, "fas_runpoll_start");
65657c478bd9Sstevel@tonic-gate
65667c478bd9Sstevel@tonic-gate /*
65677c478bd9Sstevel@tonic-gate * wait for cmd to complete
65687c478bd9Sstevel@tonic-gate * don't start new cmds so set throttles to HOLD_THROTTLE
65697c478bd9Sstevel@tonic-gate */
65707c478bd9Sstevel@tonic-gate while ((sp->cmd_flags & CFLAG_COMPLETED) == 0) {
65717c478bd9Sstevel@tonic-gate if (!(sp->cmd_flags & CFLAG_CMDPROXY)) {
65727c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, HOLD_THROTTLE);
65737c478bd9Sstevel@tonic-gate }
65747c478bd9Sstevel@tonic-gate if ((fas->f_state != STATE_FREE) || INTPENDING(fas)) {
65757c478bd9Sstevel@tonic-gate if (fas_dopoll(fas, POLL_TIMEOUT) <= 0) {
65767c478bd9Sstevel@tonic-gate IPRINTF("runpoll: timeout on draining\n");
65777c478bd9Sstevel@tonic-gate goto bad;
65787c478bd9Sstevel@tonic-gate }
65797c478bd9Sstevel@tonic-gate }
65807c478bd9Sstevel@tonic-gate
65817c478bd9Sstevel@tonic-gate ASSERT(fas->f_state == STATE_FREE);
65827c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp == NULL);
65837c478bd9Sstevel@tonic-gate
65847c478bd9Sstevel@tonic-gate /*
65857c478bd9Sstevel@tonic-gate * if this is not a proxy cmd, don't start the cmd
65867c478bd9Sstevel@tonic-gate * without draining the active cmd(s)
65877c478bd9Sstevel@tonic-gate * for proxy cmds, we zap the active cmd and assume
65887c478bd9Sstevel@tonic-gate * that the caller will take care of this
65897c478bd9Sstevel@tonic-gate * For tagged cmds, wait with submitting a non-tagged
65907c478bd9Sstevel@tonic-gate * cmd until the queue has been drained
65917c478bd9Sstevel@tonic-gate * If the cmd is a request sense, then draining won't
65927c478bd9Sstevel@tonic-gate * help since we are in contingence allegiance condition
65937c478bd9Sstevel@tonic-gate */
65947c478bd9Sstevel@tonic-gate if (!(sp->cmd_flags & CFLAG_CMDPROXY)) {
65957c478bd9Sstevel@tonic-gate uchar_t *cmdp = (uchar_t *)sp->cmd_pkt->pkt_cdbp;
65967c478bd9Sstevel@tonic-gate
65977c478bd9Sstevel@tonic-gate if ((fas->f_tcmds[slot]) &&
65987c478bd9Sstevel@tonic-gate (NOTAG(Tgt(sp)) ||
65997c478bd9Sstevel@tonic-gate (((sp->cmd_pkt_flags & FLAG_TAGMASK) == 0) &&
66007c478bd9Sstevel@tonic-gate (*cmdp != SCMD_REQUEST_SENSE)))) {
66017c478bd9Sstevel@tonic-gate if (timeout < POLL_TIMEOUT) {
66027c478bd9Sstevel@tonic-gate timeout += 100;
66037c478bd9Sstevel@tonic-gate drv_usecwait(100);
66047c478bd9Sstevel@tonic-gate continue;
66057c478bd9Sstevel@tonic-gate } else {
66067c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
66077c478bd9Sstevel@tonic-gate "polled cmd failed (target busy)");
66087c478bd9Sstevel@tonic-gate goto cleanup;
66097c478bd9Sstevel@tonic-gate }
66107c478bd9Sstevel@tonic-gate }
66117c478bd9Sstevel@tonic-gate }
66127c478bd9Sstevel@tonic-gate
66137c478bd9Sstevel@tonic-gate /*
66147c478bd9Sstevel@tonic-gate * If the draining of active commands killed the
66157c478bd9Sstevel@tonic-gate * the current polled command, we're done..
66167c478bd9Sstevel@tonic-gate */
66177c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_COMPLETED) {
66187c478bd9Sstevel@tonic-gate break;
66197c478bd9Sstevel@tonic-gate }
66207c478bd9Sstevel@tonic-gate
66217c478bd9Sstevel@tonic-gate /*
66227c478bd9Sstevel@tonic-gate * ensure we are not accessing a target too quickly
66237c478bd9Sstevel@tonic-gate * after a reset. the throttles get set back later
66247c478bd9Sstevel@tonic-gate * by the reset delay watch; hopefully, we don't go
66257c478bd9Sstevel@tonic-gate * thru this loop more than once
66267c478bd9Sstevel@tonic-gate */
66277c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[slot/NLUNS_PER_TARGET]) {
66287c478bd9Sstevel@tonic-gate IPRINTF1("reset delay set for slot %x\n", slot);
66297c478bd9Sstevel@tonic-gate drv_usecwait(fas->f_scsi_reset_delay * 1000);
66307c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
66317c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[i]) {
66327c478bd9Sstevel@tonic-gate int s = i * NLUNS_PER_TARGET;
66337c478bd9Sstevel@tonic-gate int e = s + NLUNS_PER_TARGET;
66347c478bd9Sstevel@tonic-gate fas->f_reset_delay[i] = 0;
66357c478bd9Sstevel@tonic-gate for (; s < e; s++) {
66367c478bd9Sstevel@tonic-gate fas_full_throttle(fas, s);
66377c478bd9Sstevel@tonic-gate }
66387c478bd9Sstevel@tonic-gate }
66397c478bd9Sstevel@tonic-gate }
66407c478bd9Sstevel@tonic-gate }
66417c478bd9Sstevel@tonic-gate
66427c478bd9Sstevel@tonic-gate /*
66437c478bd9Sstevel@tonic-gate * fas_startcmd() will return false if preempted
66447c478bd9Sstevel@tonic-gate * or draining
66457c478bd9Sstevel@tonic-gate */
66467c478bd9Sstevel@tonic-gate if (fas_startcmd(fas, sp) != TRUE) {
66477c478bd9Sstevel@tonic-gate IPRINTF("runpoll: cannot start new cmds\n");
66487c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp != sp);
66497c478bd9Sstevel@tonic-gate continue;
66507c478bd9Sstevel@tonic-gate }
66517c478bd9Sstevel@tonic-gate
66527c478bd9Sstevel@tonic-gate /*
66537c478bd9Sstevel@tonic-gate * We're now 'running' this command.
66547c478bd9Sstevel@tonic-gate *
66557c478bd9Sstevel@tonic-gate * fas_dopoll will always return when
66567c478bd9Sstevel@tonic-gate * fas->f_state is STATE_FREE, and
66577c478bd9Sstevel@tonic-gate */
66587c478bd9Sstevel@tonic-gate limit = sp->cmd_pkt->pkt_time * 1000000;
66597c478bd9Sstevel@tonic-gate if (limit == 0) {
66607c478bd9Sstevel@tonic-gate limit = POLL_TIMEOUT;
66617c478bd9Sstevel@tonic-gate }
66627c478bd9Sstevel@tonic-gate
66637c478bd9Sstevel@tonic-gate /*
66647c478bd9Sstevel@tonic-gate * if the cmd disconnected, the first call to fas_dopoll
66657c478bd9Sstevel@tonic-gate * will return with bus free; we go thru the loop one more
66667c478bd9Sstevel@tonic-gate * time and wait limit usec for the target to reconnect
66677c478bd9Sstevel@tonic-gate */
66687c478bd9Sstevel@tonic-gate for (i = 0; i <= POLL_TIMEOUT; i += 100) {
66697c478bd9Sstevel@tonic-gate
66707c478bd9Sstevel@tonic-gate if ((n = fas_dopoll(fas, limit)) <= 0) {
66717c478bd9Sstevel@tonic-gate IPRINTF("runpoll: timeout on polling\n");
66727c478bd9Sstevel@tonic-gate goto bad;
66737c478bd9Sstevel@tonic-gate }
66747c478bd9Sstevel@tonic-gate
66757c478bd9Sstevel@tonic-gate /*
66767c478bd9Sstevel@tonic-gate * If a preemption occurred that caused this
66777c478bd9Sstevel@tonic-gate * command to actually not start, go around
66787c478bd9Sstevel@tonic-gate * the loop again. If CFLAG_COMPLETED is set, the
66797c478bd9Sstevel@tonic-gate * command completed
66807c478bd9Sstevel@tonic-gate */
66817c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_COMPLETED) ||
66827c478bd9Sstevel@tonic-gate (sp->cmd_pkt->pkt_state == 0)) {
66837c478bd9Sstevel@tonic-gate break;
66847c478bd9Sstevel@tonic-gate }
66857c478bd9Sstevel@tonic-gate
66867c478bd9Sstevel@tonic-gate /*
66877c478bd9Sstevel@tonic-gate * the bus may have gone free because the target
66887c478bd9Sstevel@tonic-gate * disconnected; go thru the loop again
66897c478bd9Sstevel@tonic-gate */
66907c478bd9Sstevel@tonic-gate ASSERT(fas->f_state == STATE_FREE);
66917c478bd9Sstevel@tonic-gate if (n == 0) {
66927c478bd9Sstevel@tonic-gate /*
66937c478bd9Sstevel@tonic-gate * bump i, we have waited limit usecs in
66947c478bd9Sstevel@tonic-gate * fas_dopoll
66957c478bd9Sstevel@tonic-gate */
66967c478bd9Sstevel@tonic-gate i += limit - 100;
66977c478bd9Sstevel@tonic-gate }
66987c478bd9Sstevel@tonic-gate }
66997c478bd9Sstevel@tonic-gate
67007c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_COMPLETED) == 0) {
67017c478bd9Sstevel@tonic-gate
67027c478bd9Sstevel@tonic-gate if (i > POLL_TIMEOUT) {
67037c478bd9Sstevel@tonic-gate IPRINTF("polled timeout on disc. cmd\n");
67047c478bd9Sstevel@tonic-gate goto bad;
67057c478bd9Sstevel@tonic-gate }
67067c478bd9Sstevel@tonic-gate
67077c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_state) {
67087c478bd9Sstevel@tonic-gate /*
67097c478bd9Sstevel@tonic-gate * don't go thru the loop again; the cmd
67107c478bd9Sstevel@tonic-gate * was already started
67117c478bd9Sstevel@tonic-gate */
67127c478bd9Sstevel@tonic-gate IPRINTF("fas_runpoll: cmd started??\n");
67137c478bd9Sstevel@tonic-gate goto bad;
67147c478bd9Sstevel@tonic-gate }
67157c478bd9Sstevel@tonic-gate }
67167c478bd9Sstevel@tonic-gate }
67177c478bd9Sstevel@tonic-gate
67187c478bd9Sstevel@tonic-gate /*
67197c478bd9Sstevel@tonic-gate * blindly restore throttles which is preferable over
67207c478bd9Sstevel@tonic-gate * leaving throttle hanging at 0 and noone to clear it
67217c478bd9Sstevel@tonic-gate */
67227c478bd9Sstevel@tonic-gate if (!(sp->cmd_flags & CFLAG_CMDPROXY)) {
67237c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, MAX_THROTTLE);
67247c478bd9Sstevel@tonic-gate }
67257c478bd9Sstevel@tonic-gate
67267c478bd9Sstevel@tonic-gate /*
67277c478bd9Sstevel@tonic-gate * ensure that the cmd is completely removed
67287c478bd9Sstevel@tonic-gate */
67297c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, 0);
67307c478bd9Sstevel@tonic-gate
67317c478bd9Sstevel@tonic-gate /*
67327c478bd9Sstevel@tonic-gate * If we stored up commands to do, start them off now.
67337c478bd9Sstevel@tonic-gate */
67347c478bd9Sstevel@tonic-gate if ((fas->f_state == STATE_FREE) &&
67357c478bd9Sstevel@tonic-gate (!(sp->cmd_flags & CFLAG_CMDPROXY))) {
67367c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
67377c478bd9Sstevel@tonic-gate }
67387c478bd9Sstevel@tonic-gate exit:
67397c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_RUNPOLL_END, "fas_runpoll_end");
67407c478bd9Sstevel@tonic-gate return;
67417c478bd9Sstevel@tonic-gate
67427c478bd9Sstevel@tonic-gate bad:
67437c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "Polled cmd failed");
67447c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
67457c478bd9Sstevel@tonic-gate fas_printstate(fas, "fas_runpoll: polled cmd failed");
67467c478bd9Sstevel@tonic-gate #endif /* FASDEBUG */
67477c478bd9Sstevel@tonic-gate
67487c478bd9Sstevel@tonic-gate cleanup:
67497c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, MAX_THROTTLE);
67507c478bd9Sstevel@tonic-gate
67517c478bd9Sstevel@tonic-gate /*
67527c478bd9Sstevel@tonic-gate * clean up all traces of this sp because fas_runpoll will return
67537c478bd9Sstevel@tonic-gate * before fas_reset_recovery() cleans up
67547c478bd9Sstevel@tonic-gate */
67557c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
67567c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
67577c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
67587c478bd9Sstevel@tonic-gate
67597c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_CMDPROXY) == 0) {
67607c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
67617c478bd9Sstevel@tonic-gate }
67627c478bd9Sstevel@tonic-gate goto exit;
67637c478bd9Sstevel@tonic-gate }
67647c478bd9Sstevel@tonic-gate
67657c478bd9Sstevel@tonic-gate /*
67667c478bd9Sstevel@tonic-gate * Poll for command completion (i.e., no interrupts)
67677c478bd9Sstevel@tonic-gate * limit is in usec (and will not be very accurate)
67687c478bd9Sstevel@tonic-gate *
67697c478bd9Sstevel@tonic-gate * the assumption is that we only run polled cmds in interrupt context
67707c478bd9Sstevel@tonic-gate * as scsi_transport will filter out FLAG_NOINTR
67717c478bd9Sstevel@tonic-gate */
67727c478bd9Sstevel@tonic-gate static int
fas_dopoll(struct fas * fas,int limit)67737c478bd9Sstevel@tonic-gate fas_dopoll(struct fas *fas, int limit)
67747c478bd9Sstevel@tonic-gate {
67757c478bd9Sstevel@tonic-gate int i, n;
67767c478bd9Sstevel@tonic-gate
67777c478bd9Sstevel@tonic-gate /*
67787c478bd9Sstevel@tonic-gate * timeout is not very accurate since we don't know how
67797c478bd9Sstevel@tonic-gate * long the poll takes
67807c478bd9Sstevel@tonic-gate * also if the packet gets started fairly late, we may
67817c478bd9Sstevel@tonic-gate * timeout prematurely
67827c478bd9Sstevel@tonic-gate * fas_dopoll always returns if e_state transitions to STATE_FREE
67837c478bd9Sstevel@tonic-gate */
67847c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_DOPOLL_START, "fas_dopoll_start");
67857c478bd9Sstevel@tonic-gate
67867c478bd9Sstevel@tonic-gate if (limit == 0) {
67877c478bd9Sstevel@tonic-gate limit = POLL_TIMEOUT;
67887c478bd9Sstevel@tonic-gate }
67897c478bd9Sstevel@tonic-gate
67907c478bd9Sstevel@tonic-gate for (n = i = 0; i < limit; i += 100) {
67917c478bd9Sstevel@tonic-gate if (INTPENDING(fas)) {
67927c478bd9Sstevel@tonic-gate fas->f_polled_intr = 1;
67937c478bd9Sstevel@tonic-gate n++;
67947c478bd9Sstevel@tonic-gate (void) fas_intr_svc(fas);
67957c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE)
67967c478bd9Sstevel@tonic-gate break;
67977c478bd9Sstevel@tonic-gate }
67987c478bd9Sstevel@tonic-gate drv_usecwait(100);
67997c478bd9Sstevel@tonic-gate }
68007c478bd9Sstevel@tonic-gate
68017c478bd9Sstevel@tonic-gate if (i >= limit && fas->f_state != STATE_FREE) {
68027c478bd9Sstevel@tonic-gate fas_printstate(fas, "polled command timeout");
68037c478bd9Sstevel@tonic-gate n = -1;
68047c478bd9Sstevel@tonic-gate }
68057c478bd9Sstevel@tonic-gate TRACE_1(TR_FAC_SCSI_FAS, TR_FAS_DOPOLL_END,
680619397407SSherry Moore "fas_dopoll_end: rval %x", n);
68077c478bd9Sstevel@tonic-gate return (n);
68087c478bd9Sstevel@tonic-gate }
68097c478bd9Sstevel@tonic-gate
68107c478bd9Sstevel@tonic-gate /*
68117c478bd9Sstevel@tonic-gate * prepare a sync negotiation message
68127c478bd9Sstevel@tonic-gate */
68137c478bd9Sstevel@tonic-gate static void
fas_make_sdtr(struct fas * fas,int msgout_offset,int target)68147c478bd9Sstevel@tonic-gate fas_make_sdtr(struct fas *fas, int msgout_offset, int target)
68157c478bd9Sstevel@tonic-gate {
68167c478bd9Sstevel@tonic-gate uchar_t *p = fas->f_cur_msgout + msgout_offset;
68177c478bd9Sstevel@tonic-gate ushort_t tshift = 1<<target;
68187c478bd9Sstevel@tonic-gate uchar_t period = MIN_SYNC_PERIOD(fas);
68197c478bd9Sstevel@tonic-gate uchar_t offset = fas_default_offset;
68207c478bd9Sstevel@tonic-gate
68217c478bd9Sstevel@tonic-gate /*
68227c478bd9Sstevel@tonic-gate * If this target experienced a sync backoff use the
68237c478bd9Sstevel@tonic-gate * target's sync speed that was adjusted in
68247c478bd9Sstevel@tonic-gate * fas_sync_wide_backoff. For second sync backoff,
68257c478bd9Sstevel@tonic-gate * offset will be ajusted below in sanity checks.
68267c478bd9Sstevel@tonic-gate */
68277c478bd9Sstevel@tonic-gate if (fas->f_backoff & tshift) {
68287c478bd9Sstevel@tonic-gate period = fas->f_neg_period[target];
68297c478bd9Sstevel@tonic-gate }
68307c478bd9Sstevel@tonic-gate
68317c478bd9Sstevel@tonic-gate /*
68327c478bd9Sstevel@tonic-gate * If this is a responce to a target initiated sdtr,
68337c478bd9Sstevel@tonic-gate * use the agreed upon values.
68347c478bd9Sstevel@tonic-gate */
68357c478bd9Sstevel@tonic-gate if (fas->f_sdtr_sent & 1) {
68367c478bd9Sstevel@tonic-gate period = fas->f_neg_period[target];
68377c478bd9Sstevel@tonic-gate offset = fas->f_offset[target];
68387c478bd9Sstevel@tonic-gate }
68397c478bd9Sstevel@tonic-gate
68407c478bd9Sstevel@tonic-gate /*
68417c478bd9Sstevel@tonic-gate * If the target driver disabled
68427c478bd9Sstevel@tonic-gate * sync then make offset = 0
68437c478bd9Sstevel@tonic-gate */
68447c478bd9Sstevel@tonic-gate if (fas->f_force_async & tshift) {
68457c478bd9Sstevel@tonic-gate offset = 0;
68467c478bd9Sstevel@tonic-gate }
68477c478bd9Sstevel@tonic-gate
68487c478bd9Sstevel@tonic-gate /*
68497c478bd9Sstevel@tonic-gate * sanity check of period and offset
68507c478bd9Sstevel@tonic-gate */
68517c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[target] & SCSI_OPTIONS_FAST) {
68527c478bd9Sstevel@tonic-gate if (period < (uchar_t)(DEFAULT_FASTSYNC_PERIOD/4)) {
68537c478bd9Sstevel@tonic-gate period = (uchar_t)(DEFAULT_FASTSYNC_PERIOD/4);
68547c478bd9Sstevel@tonic-gate }
68557c478bd9Sstevel@tonic-gate } else if (fas->f_target_scsi_options[target] & SCSI_OPTIONS_SYNC) {
68567c478bd9Sstevel@tonic-gate if (period < (uchar_t)(DEFAULT_SYNC_PERIOD/4)) {
68577c478bd9Sstevel@tonic-gate period = (uchar_t)(DEFAULT_SYNC_PERIOD/4);
68587c478bd9Sstevel@tonic-gate }
68597c478bd9Sstevel@tonic-gate } else {
68607c478bd9Sstevel@tonic-gate fas->f_nosync |= tshift;
68617c478bd9Sstevel@tonic-gate }
68627c478bd9Sstevel@tonic-gate
68637c478bd9Sstevel@tonic-gate if (fas->f_nosync & tshift) {
68647c478bd9Sstevel@tonic-gate offset = 0;
68657c478bd9Sstevel@tonic-gate }
68667c478bd9Sstevel@tonic-gate
68677c478bd9Sstevel@tonic-gate if ((uchar_t)(offset & 0xf) > fas_default_offset) {
68687c478bd9Sstevel@tonic-gate offset = fas_default_offset | fas->f_req_ack_delay;
68697c478bd9Sstevel@tonic-gate }
68707c478bd9Sstevel@tonic-gate
68717c478bd9Sstevel@tonic-gate fas->f_neg_period[target] = (uchar_t)period;
68727c478bd9Sstevel@tonic-gate fas->f_offset[target] = (uchar_t)offset;
68737c478bd9Sstevel@tonic-gate
68747c478bd9Sstevel@tonic-gate *p++ = (uchar_t)MSG_EXTENDED;
68757c478bd9Sstevel@tonic-gate *p++ = (uchar_t)3;
68767c478bd9Sstevel@tonic-gate *p++ = (uchar_t)MSG_SYNCHRONOUS;
68777c478bd9Sstevel@tonic-gate *p++ = period;
68787c478bd9Sstevel@tonic-gate *p++ = offset & 0xf;
68797c478bd9Sstevel@tonic-gate fas->f_omsglen = 5 + msgout_offset;
68807c478bd9Sstevel@tonic-gate
68817c478bd9Sstevel@tonic-gate IPRINTF2("fas_make_sdtr: period = %x, offset = %x\n",
688219397407SSherry Moore period, offset);
68837c478bd9Sstevel@tonic-gate /*
68847c478bd9Sstevel@tonic-gate * increment sdtr flag, odd value indicates that we initiated
68857c478bd9Sstevel@tonic-gate * the negotiation
68867c478bd9Sstevel@tonic-gate */
68877c478bd9Sstevel@tonic-gate fas->f_sdtr_sent++;
68887c478bd9Sstevel@tonic-gate
68897c478bd9Sstevel@tonic-gate /*
68907c478bd9Sstevel@tonic-gate * the target may reject the optional sync message so
68917c478bd9Sstevel@tonic-gate * to avoid negotiating on every cmd, set sync known here
68927c478bd9Sstevel@tonic-gate * we should not negotiate wide after sync again
68937c478bd9Sstevel@tonic-gate */
68947c478bd9Sstevel@tonic-gate fas->f_sync_known |= 1<<target;
68957c478bd9Sstevel@tonic-gate fas->f_wide_known |= 1<<target;
68967c478bd9Sstevel@tonic-gate }
68977c478bd9Sstevel@tonic-gate
68987c478bd9Sstevel@tonic-gate /*
68997c478bd9Sstevel@tonic-gate * prepare a wide negotiation message
69007c478bd9Sstevel@tonic-gate */
69017c478bd9Sstevel@tonic-gate static void
fas_make_wdtr(struct fas * fas,int msgout_offset,int target,int width)69027c478bd9Sstevel@tonic-gate fas_make_wdtr(struct fas *fas, int msgout_offset, int target, int width)
69037c478bd9Sstevel@tonic-gate {
69047c478bd9Sstevel@tonic-gate uchar_t *p = fas->f_cur_msgout + msgout_offset;
69057c478bd9Sstevel@tonic-gate
69067c478bd9Sstevel@tonic-gate if (((fas->f_target_scsi_options[target] & SCSI_OPTIONS_WIDE) == 0) ||
69077c478bd9Sstevel@tonic-gate (fas->f_nowide & (1<<target))) {
69087c478bd9Sstevel@tonic-gate fas->f_nowide |= 1<<target;
69097c478bd9Sstevel@tonic-gate width = 0;
69107c478bd9Sstevel@tonic-gate }
69117c478bd9Sstevel@tonic-gate if (fas->f_force_narrow & (1<<target)) {
69127c478bd9Sstevel@tonic-gate width = 0;
69137c478bd9Sstevel@tonic-gate }
69147c478bd9Sstevel@tonic-gate width = min(FAS_XFER_WIDTH, width);
69157c478bd9Sstevel@tonic-gate
69167c478bd9Sstevel@tonic-gate *p++ = (uchar_t)MSG_EXTENDED;
69177c478bd9Sstevel@tonic-gate *p++ = (uchar_t)2;
69187c478bd9Sstevel@tonic-gate *p++ = (uchar_t)MSG_WIDE_DATA_XFER;
69197c478bd9Sstevel@tonic-gate *p++ = (uchar_t)width;
69207c478bd9Sstevel@tonic-gate fas->f_omsglen = 4 + msgout_offset;
69217c478bd9Sstevel@tonic-gate IPRINTF1("fas_make_wdtr: width=%x\n", width);
69227c478bd9Sstevel@tonic-gate
69237c478bd9Sstevel@tonic-gate /*
69247c478bd9Sstevel@tonic-gate * increment wdtr flag, odd value indicates that we initiated
69257c478bd9Sstevel@tonic-gate * the negotiation
69267c478bd9Sstevel@tonic-gate */
69277c478bd9Sstevel@tonic-gate fas->f_wdtr_sent++;
69287c478bd9Sstevel@tonic-gate
69297c478bd9Sstevel@tonic-gate /*
69307c478bd9Sstevel@tonic-gate * the target may reject the optional wide message so
69317c478bd9Sstevel@tonic-gate * to avoid negotiating on every cmd, set wide known here
69327c478bd9Sstevel@tonic-gate */
69337c478bd9Sstevel@tonic-gate fas->f_wide_known |= 1<<target;
69347c478bd9Sstevel@tonic-gate
69357c478bd9Sstevel@tonic-gate fas_set_wide_conf3(fas, target, width);
69367c478bd9Sstevel@tonic-gate }
69377c478bd9Sstevel@tonic-gate
69387c478bd9Sstevel@tonic-gate /*
69397c478bd9Sstevel@tonic-gate * auto request sense support
69407c478bd9Sstevel@tonic-gate * create or destroy an auto request sense packet
69417c478bd9Sstevel@tonic-gate */
69427c478bd9Sstevel@tonic-gate static int
fas_create_arq_pkt(struct fas * fas,struct scsi_address * ap)69437c478bd9Sstevel@tonic-gate fas_create_arq_pkt(struct fas *fas, struct scsi_address *ap)
69447c478bd9Sstevel@tonic-gate {
69457c478bd9Sstevel@tonic-gate /*
69467c478bd9Sstevel@tonic-gate * Allocate a request sense packet using get_pktiopb
69477c478bd9Sstevel@tonic-gate */
69487c478bd9Sstevel@tonic-gate struct fas_cmd *rqpktp;
69497c478bd9Sstevel@tonic-gate uchar_t slot = ap->a_target * NLUNS_PER_TARGET | ap->a_lun;
69507c478bd9Sstevel@tonic-gate struct buf *bp;
69517c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data;
69527c478bd9Sstevel@tonic-gate
69537c478bd9Sstevel@tonic-gate /*
69547c478bd9Sstevel@tonic-gate * if one exists, don't create another
69557c478bd9Sstevel@tonic-gate */
69567c478bd9Sstevel@tonic-gate if (fas->f_arq_pkt[slot] != 0) {
69577c478bd9Sstevel@tonic-gate return (0);
69587c478bd9Sstevel@tonic-gate }
69597c478bd9Sstevel@tonic-gate
69607c478bd9Sstevel@tonic-gate /*
69617c478bd9Sstevel@tonic-gate * it would be nicer if we could allow the target driver
69627c478bd9Sstevel@tonic-gate * to specify the size but this is easier and OK for most
69637c478bd9Sstevel@tonic-gate * drivers to use SENSE_LENGTH
69647c478bd9Sstevel@tonic-gate * Allocate a request sense packet.
69657c478bd9Sstevel@tonic-gate */
69667c478bd9Sstevel@tonic-gate bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL,
69677c478bd9Sstevel@tonic-gate SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL);
69687c478bd9Sstevel@tonic-gate rqpktp = PKT2CMD(scsi_init_pkt(ap,
69697c478bd9Sstevel@tonic-gate NULL, bp, CDB_GROUP0, 1, PKT_PRIV_LEN,
69707c478bd9Sstevel@tonic-gate PKT_CONSISTENT, SLEEP_FUNC, NULL));
69717c478bd9Sstevel@tonic-gate arq_data =
69727c478bd9Sstevel@tonic-gate (struct arq_private_data *)(rqpktp->cmd_pkt->pkt_private);
69737c478bd9Sstevel@tonic-gate arq_data->arq_save_bp = bp;
69747c478bd9Sstevel@tonic-gate
69757c478bd9Sstevel@tonic-gate RQ_MAKECOM_G0((CMD2PKT(rqpktp)),
69767c478bd9Sstevel@tonic-gate FLAG_SENSING | FLAG_HEAD | FLAG_NODISCON,
69777c478bd9Sstevel@tonic-gate (char)SCMD_REQUEST_SENSE, 0, (char)SENSE_LENGTH);
69787c478bd9Sstevel@tonic-gate rqpktp->cmd_flags |= CFLAG_CMDARQ;
69797c478bd9Sstevel@tonic-gate rqpktp->cmd_slot = slot;
69807c478bd9Sstevel@tonic-gate rqpktp->cmd_pkt->pkt_ha_private = rqpktp;
69817c478bd9Sstevel@tonic-gate fas->f_arq_pkt[slot] = rqpktp;
69827c478bd9Sstevel@tonic-gate
69837c478bd9Sstevel@tonic-gate /*
69847c478bd9Sstevel@tonic-gate * we need a function ptr here so abort/reset can
69857c478bd9Sstevel@tonic-gate * defer callbacks; fas_call_pkt_comp() calls
69867c478bd9Sstevel@tonic-gate * fas_complete_arq_pkt() directly without releasing the lock
69877c478bd9Sstevel@tonic-gate * However, since we are not calling back directly thru
69887c478bd9Sstevel@tonic-gate * pkt_comp, don't check this with warlock
69897c478bd9Sstevel@tonic-gate */
69907c478bd9Sstevel@tonic-gate #ifndef __lock_lint
69917c478bd9Sstevel@tonic-gate rqpktp->cmd_pkt->pkt_comp =
699219397407SSherry Moore (void (*)(struct scsi_pkt *))fas_complete_arq_pkt;
69937c478bd9Sstevel@tonic-gate #endif
69947c478bd9Sstevel@tonic-gate return (0);
69957c478bd9Sstevel@tonic-gate }
69967c478bd9Sstevel@tonic-gate
69977c478bd9Sstevel@tonic-gate static int
fas_delete_arq_pkt(struct fas * fas,struct scsi_address * ap)69987c478bd9Sstevel@tonic-gate fas_delete_arq_pkt(struct fas *fas, struct scsi_address *ap)
69997c478bd9Sstevel@tonic-gate {
70007c478bd9Sstevel@tonic-gate struct fas_cmd *rqpktp;
70017c478bd9Sstevel@tonic-gate int slot = ap->a_target * NLUNS_PER_TARGET | ap->a_lun;
70027c478bd9Sstevel@tonic-gate
70037c478bd9Sstevel@tonic-gate /*
70047c478bd9Sstevel@tonic-gate * if there is still a pkt saved or no rqpkt
70057c478bd9Sstevel@tonic-gate * then we cannot deallocate or there is nothing to do
70067c478bd9Sstevel@tonic-gate */
70077c478bd9Sstevel@tonic-gate if ((rqpktp = fas->f_arq_pkt[slot]) != NULL) {
70087c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data =
70097c478bd9Sstevel@tonic-gate (struct arq_private_data *)(rqpktp->cmd_pkt->pkt_private);
70107c478bd9Sstevel@tonic-gate struct buf *bp = arq_data->arq_save_bp;
70117c478bd9Sstevel@tonic-gate /*
70127c478bd9Sstevel@tonic-gate * is arq pkt in use?
70137c478bd9Sstevel@tonic-gate */
70147c478bd9Sstevel@tonic-gate if (arq_data->arq_save_sp) {
70157c478bd9Sstevel@tonic-gate return (-1);
70167c478bd9Sstevel@tonic-gate }
70177c478bd9Sstevel@tonic-gate
70187c478bd9Sstevel@tonic-gate scsi_destroy_pkt(CMD2PKT(rqpktp));
70197c478bd9Sstevel@tonic-gate scsi_free_consistent_buf(bp);
70207c478bd9Sstevel@tonic-gate fas->f_arq_pkt[slot] = 0;
70217c478bd9Sstevel@tonic-gate }
70227c478bd9Sstevel@tonic-gate return (0);
70237c478bd9Sstevel@tonic-gate }
70247c478bd9Sstevel@tonic-gate
70257c478bd9Sstevel@tonic-gate /*
70267c478bd9Sstevel@tonic-gate * complete an arq packet by copying over transport info and the actual
70277c478bd9Sstevel@tonic-gate * request sense data; called with mutex held from fas_call_pkt_comp()
70287c478bd9Sstevel@tonic-gate */
70297c478bd9Sstevel@tonic-gate void
fas_complete_arq_pkt(struct scsi_pkt * pkt)70307c478bd9Sstevel@tonic-gate fas_complete_arq_pkt(struct scsi_pkt *pkt)
70317c478bd9Sstevel@tonic-gate {
70327c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(&pkt->pkt_address);
70337c478bd9Sstevel@tonic-gate struct fas_cmd *sp = pkt->pkt_ha_private;
70347c478bd9Sstevel@tonic-gate struct scsi_arq_status *arqstat;
70357c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data =
703619397407SSherry Moore (struct arq_private_data *)sp->cmd_pkt->pkt_private;
70377c478bd9Sstevel@tonic-gate struct fas_cmd *ssp = arq_data->arq_save_sp;
70387c478bd9Sstevel@tonic-gate struct buf *bp = arq_data->arq_save_bp;
70397c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
70407c478bd9Sstevel@tonic-gate
70417c478bd9Sstevel@tonic-gate DPRINTF1("completing arq pkt sp=0x%p\n", (void *)sp);
70427c478bd9Sstevel@tonic-gate ASSERT(sp == fas->f_arq_pkt[slot]);
70437c478bd9Sstevel@tonic-gate ASSERT(arq_data->arq_save_sp != NULL);
70447c478bd9Sstevel@tonic-gate ASSERT(ssp != fas->f_active[sp->cmd_slot]->f_slot[sp->cmd_tag[1]]);
70457c478bd9Sstevel@tonic-gate
70467c478bd9Sstevel@tonic-gate arqstat = (struct scsi_arq_status *)(ssp->cmd_pkt->pkt_scbp);
70477c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_status = *((struct scsi_status *)
704819397407SSherry Moore (sp->cmd_pkt->pkt_scbp));
70497c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_reason = sp->cmd_pkt->pkt_reason;
70507c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_state = sp->cmd_pkt->pkt_state;
70517c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_statistics = sp->cmd_pkt->pkt_statistics;
70527c478bd9Sstevel@tonic-gate arqstat->sts_rqpkt_resid = sp->cmd_pkt->pkt_resid;
70537c478bd9Sstevel@tonic-gate arqstat->sts_sensedata =
70547c478bd9Sstevel@tonic-gate *((struct scsi_extended_sense *)bp->b_un.b_addr);
70557c478bd9Sstevel@tonic-gate ssp->cmd_pkt->pkt_state |= STATE_ARQ_DONE;
70567c478bd9Sstevel@tonic-gate arq_data->arq_save_sp = NULL;
70577c478bd9Sstevel@tonic-gate
70587c478bd9Sstevel@tonic-gate /*
70597c478bd9Sstevel@tonic-gate * ASC=0x47 is parity error
70607c478bd9Sstevel@tonic-gate */
70617c478bd9Sstevel@tonic-gate if (arqstat->sts_sensedata.es_key == KEY_ABORTED_COMMAND &&
706219397407SSherry Moore arqstat->sts_sensedata.es_add_code == 0x47) {
70637c478bd9Sstevel@tonic-gate fas_sync_wide_backoff(fas, sp, slot);
70647c478bd9Sstevel@tonic-gate }
70657c478bd9Sstevel@tonic-gate
70667c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, ssp);
70677c478bd9Sstevel@tonic-gate }
70687c478bd9Sstevel@tonic-gate
70697c478bd9Sstevel@tonic-gate /*
70707c478bd9Sstevel@tonic-gate * handle check condition and start an arq packet
70717c478bd9Sstevel@tonic-gate */
70727c478bd9Sstevel@tonic-gate static int
fas_handle_sts_chk(struct fas * fas,struct fas_cmd * sp)70737c478bd9Sstevel@tonic-gate fas_handle_sts_chk(struct fas *fas, struct fas_cmd *sp)
70747c478bd9Sstevel@tonic-gate {
70757c478bd9Sstevel@tonic-gate struct fas_cmd *arqsp = fas->f_arq_pkt[sp->cmd_slot];
70767c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data;
70777c478bd9Sstevel@tonic-gate struct buf *bp;
70787c478bd9Sstevel@tonic-gate
70797c478bd9Sstevel@tonic-gate if ((arqsp == NULL) || (arqsp == sp) ||
70807c478bd9Sstevel@tonic-gate (sp->cmd_scblen < sizeof (struct scsi_arq_status))) {
70817c478bd9Sstevel@tonic-gate IPRINTF("no arq packet or cannot arq on arq pkt\n");
70827c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
70837c478bd9Sstevel@tonic-gate return (0);
70847c478bd9Sstevel@tonic-gate }
70857c478bd9Sstevel@tonic-gate
70867c478bd9Sstevel@tonic-gate arq_data = (struct arq_private_data *)arqsp->cmd_pkt->pkt_private;
70877c478bd9Sstevel@tonic-gate bp = arq_data->arq_save_bp;
70887c478bd9Sstevel@tonic-gate
70897c478bd9Sstevel@tonic-gate ASSERT(sp->cmd_flags & CFLAG_FINISHED);
70907c478bd9Sstevel@tonic-gate ASSERT(sp != fas->f_active[sp->cmd_slot]->f_slot[sp->cmd_tag[1]]);
70917c478bd9Sstevel@tonic-gate DPRINTF3("start arq for slot=%x, arqsp=0x%p, rqpkt=0x%p\n",
70927c478bd9Sstevel@tonic-gate sp->cmd_slot, (void *)arqsp, (void *)fas->f_arq_pkt[sp->cmd_slot]);
70937c478bd9Sstevel@tonic-gate if (arq_data->arq_save_sp != NULL) {
70947c478bd9Sstevel@tonic-gate IPRINTF("auto request sense already in progress\n");
70957c478bd9Sstevel@tonic-gate goto fail;
70967c478bd9Sstevel@tonic-gate }
70977c478bd9Sstevel@tonic-gate
70987c478bd9Sstevel@tonic-gate arq_data->arq_save_sp = sp;
70997c478bd9Sstevel@tonic-gate
71007c478bd9Sstevel@tonic-gate bzero(bp->b_un.b_addr, sizeof (struct scsi_extended_sense));
71017c478bd9Sstevel@tonic-gate
71027c478bd9Sstevel@tonic-gate /*
71037c478bd9Sstevel@tonic-gate * copy the timeout from the original packet by lack of a better
71047c478bd9Sstevel@tonic-gate * value
71057c478bd9Sstevel@tonic-gate * we could take the residue of the timeout but that could cause
71067c478bd9Sstevel@tonic-gate * premature timeouts perhaps
71077c478bd9Sstevel@tonic-gate */
71087c478bd9Sstevel@tonic-gate arqsp->cmd_pkt->pkt_time = sp->cmd_pkt->pkt_time;
71097c478bd9Sstevel@tonic-gate arqsp->cmd_flags &= ~CFLAG_TRANFLAG;
71107c478bd9Sstevel@tonic-gate ASSERT(arqsp->cmd_pkt->pkt_comp != NULL);
71117c478bd9Sstevel@tonic-gate
71127c478bd9Sstevel@tonic-gate /*
71137c478bd9Sstevel@tonic-gate * make sure that auto request sense always goes out
71147c478bd9Sstevel@tonic-gate * after queue full and after throttle was set to draining
71157c478bd9Sstevel@tonic-gate */
71167c478bd9Sstevel@tonic-gate fas_full_throttle(fas, sp->cmd_slot);
71177c478bd9Sstevel@tonic-gate (void) fas_accept_pkt(fas, arqsp, NO_TRAN_BUSY);
71187c478bd9Sstevel@tonic-gate return (0);
71197c478bd9Sstevel@tonic-gate
71207c478bd9Sstevel@tonic-gate fail:
71217c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_TRAN_ERR, 0);
71227c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "auto request sense failed\n");
71237c478bd9Sstevel@tonic-gate fas_dump_cmd(fas, sp);
71247c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
71257c478bd9Sstevel@tonic-gate return (-1);
71267c478bd9Sstevel@tonic-gate }
71277c478bd9Sstevel@tonic-gate
71287c478bd9Sstevel@tonic-gate
71297c478bd9Sstevel@tonic-gate /*
71307c478bd9Sstevel@tonic-gate * handle qfull condition
71317c478bd9Sstevel@tonic-gate */
71327c478bd9Sstevel@tonic-gate static void
fas_handle_qfull(struct fas * fas,struct fas_cmd * sp)71337c478bd9Sstevel@tonic-gate fas_handle_qfull(struct fas *fas, struct fas_cmd *sp)
71347c478bd9Sstevel@tonic-gate {
71357c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
71367c478bd9Sstevel@tonic-gate
71377c478bd9Sstevel@tonic-gate if ((++sp->cmd_qfull_retries > fas->f_qfull_retries[Tgt(sp)]) ||
713819397407SSherry Moore (fas->f_qfull_retries[Tgt(sp)] == 0)) {
71397c478bd9Sstevel@tonic-gate /*
71407c478bd9Sstevel@tonic-gate * We have exhausted the retries on QFULL, or,
71417c478bd9Sstevel@tonic-gate * the target driver has indicated that it
71427c478bd9Sstevel@tonic-gate * wants to handle QFULL itself by setting
71437c478bd9Sstevel@tonic-gate * qfull-retries capability to 0. In either case
71447c478bd9Sstevel@tonic-gate * we want the target driver's QFULL handling
71457c478bd9Sstevel@tonic-gate * to kick in. We do this by having pkt_reason
71467c478bd9Sstevel@tonic-gate * as CMD_CMPLT and pkt_scbp as STATUS_QFULL.
71477c478bd9Sstevel@tonic-gate */
71487c478bd9Sstevel@tonic-gate IPRINTF2("%d.%d: status queue full, retries over\n",
714919397407SSherry Moore Tgt(sp), Lun(sp));
71507c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, DRAIN_THROTTLE);
71517c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
71527c478bd9Sstevel@tonic-gate } else {
71537c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[Tgt(sp)] == 0) {
71547c478bd9Sstevel@tonic-gate fas->f_throttle[slot] =
71557c478bd9Sstevel@tonic-gate max((fas->f_tcmds[slot] - 2), 0);
71567c478bd9Sstevel@tonic-gate }
71577c478bd9Sstevel@tonic-gate IPRINTF3("%d.%d: status queue full, new throttle = %d, "
715819397407SSherry Moore "retrying\n", Tgt(sp), Lun(sp), fas->f_throttle[slot]);
71597c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_flags |= FLAG_HEAD;
71607c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_TRANFLAG;
71617c478bd9Sstevel@tonic-gate (void) fas_accept_pkt(fas, sp, NO_TRAN_BUSY);
71627c478bd9Sstevel@tonic-gate
71637c478bd9Sstevel@tonic-gate /*
71647c478bd9Sstevel@tonic-gate * when target gives queue full status with no commands
71657c478bd9Sstevel@tonic-gate * outstanding (f_tcmds[] == 0), throttle is set to 0
71667c478bd9Sstevel@tonic-gate * (HOLD_THROTTLE), and the queue full handling starts
71677c478bd9Sstevel@tonic-gate * (see psarc/1994/313); if there are commands outstanding,
71687c478bd9Sstevel@tonic-gate * the throttle is set to (f_tcmds[] - 2)
71697c478bd9Sstevel@tonic-gate */
71707c478bd9Sstevel@tonic-gate if (fas->f_throttle[slot] == HOLD_THROTTLE) {
71717c478bd9Sstevel@tonic-gate /*
71727c478bd9Sstevel@tonic-gate * By setting throttle to QFULL_THROTTLE, we
71737c478bd9Sstevel@tonic-gate * avoid submitting new commands and in
71747c478bd9Sstevel@tonic-gate * fas_restart_cmd find out slots which need
71757c478bd9Sstevel@tonic-gate * their throttles to be cleared.
71767c478bd9Sstevel@tonic-gate */
71777c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, QFULL_THROTTLE);
71787c478bd9Sstevel@tonic-gate if (fas->f_restart_cmd_timeid == 0) {
71797c478bd9Sstevel@tonic-gate fas->f_restart_cmd_timeid =
71807c478bd9Sstevel@tonic-gate timeout(fas_restart_cmd, fas,
71817c478bd9Sstevel@tonic-gate fas->f_qfull_retry_interval[Tgt(sp)]);
71827c478bd9Sstevel@tonic-gate }
71837c478bd9Sstevel@tonic-gate }
71847c478bd9Sstevel@tonic-gate }
71857c478bd9Sstevel@tonic-gate }
71867c478bd9Sstevel@tonic-gate
71877c478bd9Sstevel@tonic-gate /*
71887c478bd9Sstevel@tonic-gate * invoked from timeout() to restart qfull cmds with throttle == 0
71897c478bd9Sstevel@tonic-gate */
71907c478bd9Sstevel@tonic-gate static void
fas_restart_cmd(void * fas_arg)71917c478bd9Sstevel@tonic-gate fas_restart_cmd(void *fas_arg)
71927c478bd9Sstevel@tonic-gate {
71937c478bd9Sstevel@tonic-gate struct fas *fas = fas_arg;
71947c478bd9Sstevel@tonic-gate int i;
71957c478bd9Sstevel@tonic-gate
71967c478bd9Sstevel@tonic-gate IPRINTF("fas_restart_cmd:\n");
71977c478bd9Sstevel@tonic-gate
71987c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
71997c478bd9Sstevel@tonic-gate fas->f_restart_cmd_timeid = 0;
72007c478bd9Sstevel@tonic-gate
72017c478bd9Sstevel@tonic-gate for (i = 0; i < N_SLOTS; i += NLUNS_PER_TARGET) {
72027c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[i/NLUNS_PER_TARGET] == 0) {
72037c478bd9Sstevel@tonic-gate if (fas->f_throttle[i] == QFULL_THROTTLE) {
72047c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas,
720519397407SSherry Moore i, MAX_THROTTLE);
72067c478bd9Sstevel@tonic-gate }
72077c478bd9Sstevel@tonic-gate }
72087c478bd9Sstevel@tonic-gate }
72097c478bd9Sstevel@tonic-gate
72107c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
72117c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
72127c478bd9Sstevel@tonic-gate }
72137c478bd9Sstevel@tonic-gate
72147c478bd9Sstevel@tonic-gate /*
72157c478bd9Sstevel@tonic-gate * Timeout handling:
72167c478bd9Sstevel@tonic-gate * Command watchdog routines
72177c478bd9Sstevel@tonic-gate */
72187c478bd9Sstevel@tonic-gate
72197c478bd9Sstevel@tonic-gate /*ARGSUSED*/
72207c478bd9Sstevel@tonic-gate static void
fas_watch(void * arg)72217c478bd9Sstevel@tonic-gate fas_watch(void *arg)
72227c478bd9Sstevel@tonic-gate {
72237c478bd9Sstevel@tonic-gate struct fas *fas;
72247c478bd9Sstevel@tonic-gate ushort_t props_update = 0;
72257c478bd9Sstevel@tonic-gate
72267c478bd9Sstevel@tonic-gate rw_enter(&fas_global_rwlock, RW_READER);
72277c478bd9Sstevel@tonic-gate
72287c478bd9Sstevel@tonic-gate for (fas = fas_head; fas != (struct fas *)NULL; fas = fas->f_next) {
72297c478bd9Sstevel@tonic-gate
72307c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
72317c478bd9Sstevel@tonic-gate IPRINTF2("ncmds=%x, ndisc=%x\n", fas->f_ncmds, fas->f_ndisc);
72327c478bd9Sstevel@tonic-gate
72337c478bd9Sstevel@tonic-gate #ifdef FAS_PIO_COUNTS
72347c478bd9Sstevel@tonic-gate if (fas->f_total_cmds) {
72357c478bd9Sstevel@tonic-gate int n = fas->f_total_cmds;
72367c478bd9Sstevel@tonic-gate
72377c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE,
72387c478bd9Sstevel@tonic-gate "total=%d, cmds=%d fas-rd=%d, fas-wrt=%d, dma-rd=%d, dma-wrt=%d\n",
723919397407SSherry Moore fas->f_total_cmds,
724019397407SSherry Moore fas->f_reg_cmds/n,
724119397407SSherry Moore fas->f_reg_reads/n, fas->f_reg_writes/n,
724219397407SSherry Moore fas->f_reg_dma_reads/n, fas->f_reg_dma_writes/n);
72437c478bd9Sstevel@tonic-gate
72447c478bd9Sstevel@tonic-gate fas->f_reg_reads = fas->f_reg_writes =
724519397407SSherry Moore fas->f_reg_dma_reads = fas->f_reg_dma_writes =
724619397407SSherry Moore fas->f_reg_cmds = fas->f_total_cmds = 0;
72477c478bd9Sstevel@tonic-gate }
72487c478bd9Sstevel@tonic-gate #endif
72497c478bd9Sstevel@tonic-gate if (fas->f_ncmds) {
72507c478bd9Sstevel@tonic-gate int i;
72517c478bd9Sstevel@tonic-gate fas_watchsubr(fas);
72527c478bd9Sstevel@tonic-gate
72537c478bd9Sstevel@tonic-gate /*
72547c478bd9Sstevel@tonic-gate * reset throttle. the throttle may have been
72557c478bd9Sstevel@tonic-gate * too low if queue full was caused by
72567c478bd9Sstevel@tonic-gate * another initiator
72577c478bd9Sstevel@tonic-gate * Only reset throttle if no cmd active in slot 0
72587c478bd9Sstevel@tonic-gate * (untagged cmd)
72597c478bd9Sstevel@tonic-gate */
72607c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
72617c478bd9Sstevel@tonic-gate if (fas_enable_untagged) {
72627c478bd9Sstevel@tonic-gate fas_test_untagged++;
72637c478bd9Sstevel@tonic-gate }
72647c478bd9Sstevel@tonic-gate #endif
72657c478bd9Sstevel@tonic-gate for (i = 0; i < N_SLOTS; i++) {
72667c478bd9Sstevel@tonic-gate if ((fas->f_throttle[i] > HOLD_THROTTLE) &&
72677c478bd9Sstevel@tonic-gate (fas->f_active[i] &&
72687c478bd9Sstevel@tonic-gate (fas->f_active[i]->f_slot[0] == NULL))) {
72697c478bd9Sstevel@tonic-gate fas_full_throttle(fas, i);
72707c478bd9Sstevel@tonic-gate }
72717c478bd9Sstevel@tonic-gate }
72727c478bd9Sstevel@tonic-gate }
72737c478bd9Sstevel@tonic-gate
72747c478bd9Sstevel@tonic-gate if (fas->f_props_update) {
72757c478bd9Sstevel@tonic-gate int i;
72767c478bd9Sstevel@tonic-gate /*
72777c478bd9Sstevel@tonic-gate * f_mutex will be released and reentered in
72787c478bd9Sstevel@tonic-gate * fas_props_update().
72797c478bd9Sstevel@tonic-gate * Hence we save the fas->f_props_update now and
72807c478bd9Sstevel@tonic-gate * set to 0 indicating that property has been
72817c478bd9Sstevel@tonic-gate * updated. This will avoid a race condition with
72827c478bd9Sstevel@tonic-gate * any thread that runs in interrupt context that
72837c478bd9Sstevel@tonic-gate * attempts to set the f_props_update to non-zero value
72847c478bd9Sstevel@tonic-gate */
72857c478bd9Sstevel@tonic-gate props_update = fas->f_props_update;
72867c478bd9Sstevel@tonic-gate fas->f_props_update = 0;
72877c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
72887c478bd9Sstevel@tonic-gate if (props_update & (1<<i)) {
72897c478bd9Sstevel@tonic-gate fas_update_props(fas, i);
72907c478bd9Sstevel@tonic-gate }
72917c478bd9Sstevel@tonic-gate }
72927c478bd9Sstevel@tonic-gate }
72937c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
72947c478bd9Sstevel@tonic-gate
72957c478bd9Sstevel@tonic-gate }
72967c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
72977c478bd9Sstevel@tonic-gate
72987c478bd9Sstevel@tonic-gate again:
72997c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
73007c478bd9Sstevel@tonic-gate if (fas_timeout_initted && fas_timeout_id) {
73017c478bd9Sstevel@tonic-gate fas_timeout_id = timeout(fas_watch, NULL, fas_tick);
73027c478bd9Sstevel@tonic-gate }
73037c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
73047c478bd9Sstevel@tonic-gate TRACE_0(TR_FAC_SCSI_FAS, TR_FAS_WATCH_END, "fas_watch_end");
73057c478bd9Sstevel@tonic-gate }
73067c478bd9Sstevel@tonic-gate
73077c478bd9Sstevel@tonic-gate static void
fas_watchsubr(struct fas * fas)73087c478bd9Sstevel@tonic-gate fas_watchsubr(struct fas *fas)
73097c478bd9Sstevel@tonic-gate {
73107c478bd9Sstevel@tonic-gate short slot;
73117c478bd9Sstevel@tonic-gate int d = ((fas->f_dslot == 0)? 1 : fas->f_dslot);
73127c478bd9Sstevel@tonic-gate struct f_slots *tag_slots;
73137c478bd9Sstevel@tonic-gate
73147c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot += d) {
73157c478bd9Sstevel@tonic-gate
73167c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
73177c478bd9Sstevel@tonic-gate if (fas_btest) {
73187c478bd9Sstevel@tonic-gate fas_btest = 0;
73197c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
73207c478bd9Sstevel@tonic-gate return;
73217c478bd9Sstevel@tonic-gate }
73227c478bd9Sstevel@tonic-gate if (fas_force_timeout && fas->f_tcmds[slot]) {
73237c478bd9Sstevel@tonic-gate fas_cmd_timeout(fas, slot);
73247c478bd9Sstevel@tonic-gate fas_force_timeout = 0;
73257c478bd9Sstevel@tonic-gate return;
73267c478bd9Sstevel@tonic-gate }
73277c478bd9Sstevel@tonic-gate fas_test_reset(fas, slot);
73287c478bd9Sstevel@tonic-gate fas_test_abort(fas, slot);
73297c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
73307c478bd9Sstevel@tonic-gate
73317c478bd9Sstevel@tonic-gate /*
73327c478bd9Sstevel@tonic-gate * check tagged cmds first
73337c478bd9Sstevel@tonic-gate */
73347c478bd9Sstevel@tonic-gate tag_slots = fas->f_active[slot];
73357c478bd9Sstevel@tonic-gate DPRINTF3(
73367c478bd9Sstevel@tonic-gate "fas_watchsubr: slot %x: tcmds=%x, timeout=%x\n",
733719397407SSherry Moore slot, fas->f_tcmds[slot], tag_slots->f_timeout);
73387c478bd9Sstevel@tonic-gate
73397c478bd9Sstevel@tonic-gate if ((fas->f_tcmds[slot] > 0) && (tag_slots->f_timebase)) {
73407c478bd9Sstevel@tonic-gate
73417c478bd9Sstevel@tonic-gate if (tag_slots->f_timebase <=
73427c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick) {
73437c478bd9Sstevel@tonic-gate tag_slots->f_timebase +=
73447c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick;
73457c478bd9Sstevel@tonic-gate continue;
73467c478bd9Sstevel@tonic-gate }
73477c478bd9Sstevel@tonic-gate
73487c478bd9Sstevel@tonic-gate tag_slots->f_timeout -= fas_scsi_watchdog_tick;
73497c478bd9Sstevel@tonic-gate
73507c478bd9Sstevel@tonic-gate if (tag_slots->f_timeout < 0) {
73517c478bd9Sstevel@tonic-gate fas_cmd_timeout(fas, slot);
73527c478bd9Sstevel@tonic-gate return;
73537c478bd9Sstevel@tonic-gate }
73547c478bd9Sstevel@tonic-gate if ((tag_slots->f_timeout) <=
73557c478bd9Sstevel@tonic-gate fas_scsi_watchdog_tick) {
73567c478bd9Sstevel@tonic-gate IPRINTF1("pending timeout on slot=%x\n",
735719397407SSherry Moore slot);
73587c478bd9Sstevel@tonic-gate IPRINTF("draining all queues\n");
73597c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS,
736019397407SSherry Moore DRAIN_THROTTLE);
73617c478bd9Sstevel@tonic-gate }
73627c478bd9Sstevel@tonic-gate }
73637c478bd9Sstevel@tonic-gate }
73647c478bd9Sstevel@tonic-gate }
73657c478bd9Sstevel@tonic-gate
73667c478bd9Sstevel@tonic-gate /*
73677c478bd9Sstevel@tonic-gate * timeout recovery
73687c478bd9Sstevel@tonic-gate */
73697c478bd9Sstevel@tonic-gate static void
fas_cmd_timeout(struct fas * fas,int slot)73707c478bd9Sstevel@tonic-gate fas_cmd_timeout(struct fas *fas, int slot)
73717c478bd9Sstevel@tonic-gate {
73727c478bd9Sstevel@tonic-gate int d = ((fas->f_dslot == 0)? 1 : fas->f_dslot);
73737c478bd9Sstevel@tonic-gate int target, lun, i, n, tag, ncmds;
73747c478bd9Sstevel@tonic-gate struct fas_cmd *sp = NULL;
73757c478bd9Sstevel@tonic-gate struct fas_cmd *ssp;
73767c478bd9Sstevel@tonic-gate
73777c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot]);
73787c478bd9Sstevel@tonic-gate
73797c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
73807c478bd9Sstevel@tonic-gate if (fas_test_stop) {
73817c478bd9Sstevel@tonic-gate debug_enter("timeout");
73827c478bd9Sstevel@tonic-gate }
73837c478bd9Sstevel@tonic-gate #endif
73847c478bd9Sstevel@tonic-gate
73857c478bd9Sstevel@tonic-gate /*
73867c478bd9Sstevel@tonic-gate * set throttle back; no more draining necessary
73877c478bd9Sstevel@tonic-gate */
73887c478bd9Sstevel@tonic-gate for (i = 0; i < N_SLOTS; i += d) {
73897c478bd9Sstevel@tonic-gate if (fas->f_throttle[i] == DRAIN_THROTTLE) {
73907c478bd9Sstevel@tonic-gate fas_full_throttle(fas, i);
73917c478bd9Sstevel@tonic-gate }
73927c478bd9Sstevel@tonic-gate }
73937c478bd9Sstevel@tonic-gate
73947c478bd9Sstevel@tonic-gate if (NOTAG(slot/NLUNS_PER_TARGET)) {
73957c478bd9Sstevel@tonic-gate sp = fas->f_active[slot]->f_slot[0];
73967c478bd9Sstevel@tonic-gate }
73977c478bd9Sstevel@tonic-gate
73987c478bd9Sstevel@tonic-gate /*
73997c478bd9Sstevel@tonic-gate * if no interrupt pending for next second then the current
74007c478bd9Sstevel@tonic-gate * cmd must be stuck; switch slot and sp to current slot and cmd
74017c478bd9Sstevel@tonic-gate */
74027c478bd9Sstevel@tonic-gate if (fas->f_current_sp && fas->f_state != STATE_FREE) {
74037c478bd9Sstevel@tonic-gate for (i = 0; (i < 10000) && (INTPENDING(fas) == 0); i++) {
74047c478bd9Sstevel@tonic-gate drv_usecwait(100);
74057c478bd9Sstevel@tonic-gate }
74067c478bd9Sstevel@tonic-gate if (INTPENDING(fas) == 0) {
74077c478bd9Sstevel@tonic-gate slot = fas->f_current_sp->cmd_slot;
74087c478bd9Sstevel@tonic-gate sp = fas->f_current_sp;
74097c478bd9Sstevel@tonic-gate }
74107c478bd9Sstevel@tonic-gate }
74117c478bd9Sstevel@tonic-gate
74127c478bd9Sstevel@tonic-gate target = slot / NLUNS_PER_TARGET;
74137c478bd9Sstevel@tonic-gate lun = slot % NLUNS_PER_TARGET;
74147c478bd9Sstevel@tonic-gate
74157c478bd9Sstevel@tonic-gate /*
74167c478bd9Sstevel@tonic-gate * update all outstanding pkts for this slot
74177c478bd9Sstevel@tonic-gate */
74187c478bd9Sstevel@tonic-gate n = fas->f_active[slot]->f_n_slots;
74197c478bd9Sstevel@tonic-gate for (ncmds = tag = 0; tag < n; tag++) {
74207c478bd9Sstevel@tonic-gate ssp = fas->f_active[slot]->f_slot[tag];
74217c478bd9Sstevel@tonic-gate if (ssp && ssp->cmd_pkt->pkt_time) {
74227c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, ssp, CMD_TIMEOUT,
742319397407SSherry Moore STAT_TIMEOUT | STAT_ABORTED);
74247c478bd9Sstevel@tonic-gate fas_short_dump_cmd(fas, ssp);
74257c478bd9Sstevel@tonic-gate ncmds++;
74267c478bd9Sstevel@tonic-gate }
74277c478bd9Sstevel@tonic-gate }
74287c478bd9Sstevel@tonic-gate
74297c478bd9Sstevel@tonic-gate /*
74307c478bd9Sstevel@tonic-gate * no timed-out cmds here?
74317c478bd9Sstevel@tonic-gate */
74327c478bd9Sstevel@tonic-gate if (ncmds == 0) {
74337c478bd9Sstevel@tonic-gate return;
74347c478bd9Sstevel@tonic-gate }
74357c478bd9Sstevel@tonic-gate
74367c478bd9Sstevel@tonic-gate /*
74377c478bd9Sstevel@tonic-gate * dump all we know about this timeout
74387c478bd9Sstevel@tonic-gate */
74397c478bd9Sstevel@tonic-gate if (sp) {
74407c478bd9Sstevel@tonic-gate if (sp->cmd_flags & CFLAG_CMDDISC) {
74417c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
74427c478bd9Sstevel@tonic-gate "Disconnected command timeout for Target %d.%d",
74437c478bd9Sstevel@tonic-gate target, lun);
74447c478bd9Sstevel@tonic-gate } else {
74457c478bd9Sstevel@tonic-gate ASSERT(sp == fas->f_current_sp);
74467c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
74477c478bd9Sstevel@tonic-gate "Connected command timeout for Target %d.%d",
74487c478bd9Sstevel@tonic-gate target, lun);
74497c478bd9Sstevel@tonic-gate /*
74507c478bd9Sstevel@tonic-gate * Current command timeout appears to relate often
74517c478bd9Sstevel@tonic-gate * to noisy SCSI in synchronous mode.
74527c478bd9Sstevel@tonic-gate */
74537c478bd9Sstevel@tonic-gate if (fas->f_state == ACTS_DATA_DONE) {
74547c478bd9Sstevel@tonic-gate fas_sync_wide_backoff(fas, sp, slot);
74557c478bd9Sstevel@tonic-gate }
74567c478bd9Sstevel@tonic-gate }
74577c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
74587c478bd9Sstevel@tonic-gate fas_printstate(fas, "timeout");
74597c478bd9Sstevel@tonic-gate #endif
74607c478bd9Sstevel@tonic-gate } else {
74617c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
74627c478bd9Sstevel@tonic-gate "Disconnected tagged cmd(s) (%d) timeout for Target %d.%d",
74637c478bd9Sstevel@tonic-gate fas->f_tcmds[slot], target, lun);
74647c478bd9Sstevel@tonic-gate }
74657c478bd9Sstevel@tonic-gate
74667c478bd9Sstevel@tonic-gate if (fas_abort_cmd(fas, sp, slot) == ACTION_SEARCH) {
74677c478bd9Sstevel@tonic-gate (void) fas_istart(fas);
74687c478bd9Sstevel@tonic-gate }
74697c478bd9Sstevel@tonic-gate }
74707c478bd9Sstevel@tonic-gate
74717c478bd9Sstevel@tonic-gate /*
74727c478bd9Sstevel@tonic-gate * fas_sync_wide_backoff() increases sync period and enables slow
74737c478bd9Sstevel@tonic-gate * cable mode.
74747c478bd9Sstevel@tonic-gate * the second time, we revert back to narrow/async
74757c478bd9Sstevel@tonic-gate * we count on a bus reset to disable wide in the target and will
74767c478bd9Sstevel@tonic-gate * never renegotiate wide again
74777c478bd9Sstevel@tonic-gate */
74787c478bd9Sstevel@tonic-gate static void
fas_sync_wide_backoff(struct fas * fas,struct fas_cmd * sp,int slot)74797c478bd9Sstevel@tonic-gate fas_sync_wide_backoff(struct fas *fas, struct fas_cmd *sp,
74807c478bd9Sstevel@tonic-gate int slot)
74817c478bd9Sstevel@tonic-gate {
74827c478bd9Sstevel@tonic-gate char phase;
74837c478bd9Sstevel@tonic-gate ushort_t state = fas->f_state;
74847c478bd9Sstevel@tonic-gate uchar_t tgt = slot / NLUNS_PER_TARGET;
74857c478bd9Sstevel@tonic-gate uint_t tshift = 1 << tgt;
74867c478bd9Sstevel@tonic-gate
74877c478bd9Sstevel@tonic-gate phase = fas_reg_read(fas, &fas->f_reg->fas_stat);
74887c478bd9Sstevel@tonic-gate phase &= FAS_PHASE_MASK;
74897c478bd9Sstevel@tonic-gate
74907c478bd9Sstevel@tonic-gate IPRINTF4(
74917c478bd9Sstevel@tonic-gate "fas_sync_wide_backoff: target %d: state=%x, phase=%x, sp=0x%p\n",
74927c478bd9Sstevel@tonic-gate tgt, state, phase, (void *)sp);
74937c478bd9Sstevel@tonic-gate
74947c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
74957c478bd9Sstevel@tonic-gate if (fas_no_sync_wide_backoff) {
74967c478bd9Sstevel@tonic-gate return;
74977c478bd9Sstevel@tonic-gate }
74987c478bd9Sstevel@tonic-gate #endif
74997c478bd9Sstevel@tonic-gate
75007c478bd9Sstevel@tonic-gate /*
75017c478bd9Sstevel@tonic-gate * if this not the first time or sync is disabled
75027c478bd9Sstevel@tonic-gate * thru scsi_options then disable wide
75037c478bd9Sstevel@tonic-gate */
75047c478bd9Sstevel@tonic-gate if ((fas->f_backoff & tshift) ||
75057c478bd9Sstevel@tonic-gate (fas->f_nosync & tshift)) {
75067c478bd9Sstevel@tonic-gate /*
75077c478bd9Sstevel@tonic-gate * disable wide for just this target
75087c478bd9Sstevel@tonic-gate */
75097c478bd9Sstevel@tonic-gate if ((fas->f_nowide & tshift) == 0) {
75107c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
75117c478bd9Sstevel@tonic-gate "Target %d disabled wide SCSI mode", tgt);
75127c478bd9Sstevel@tonic-gate }
75137c478bd9Sstevel@tonic-gate /*
75147c478bd9Sstevel@tonic-gate * do not reset the bit in f_nowide because that
75157c478bd9Sstevel@tonic-gate * would not force a renegotiation of wide
75167c478bd9Sstevel@tonic-gate * and do not change any register value yet because
75177c478bd9Sstevel@tonic-gate * we may have reconnects before the renegotiations
75187c478bd9Sstevel@tonic-gate */
75197c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[tgt] &= ~SCSI_OPTIONS_WIDE;
75207c478bd9Sstevel@tonic-gate }
75217c478bd9Sstevel@tonic-gate
75227c478bd9Sstevel@tonic-gate /*
75237c478bd9Sstevel@tonic-gate * reduce xfer rate. if this is the first time, reduce by
75247c478bd9Sstevel@tonic-gate * 100%. second time, disable sync and wide.
75257c478bd9Sstevel@tonic-gate */
75267c478bd9Sstevel@tonic-gate if (fas->f_offset[tgt] != 0) {
75277c478bd9Sstevel@tonic-gate /*
75287c478bd9Sstevel@tonic-gate * do not reset the bit in f_nosync because that
75297c478bd9Sstevel@tonic-gate * would not force a renegotiation of sync
75307c478bd9Sstevel@tonic-gate */
75317c478bd9Sstevel@tonic-gate if (fas->f_backoff & tshift) {
75327c478bd9Sstevel@tonic-gate if ((fas->f_nosync & tshift) == 0) {
75337c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
75347c478bd9Sstevel@tonic-gate "Target %d reverting to async. mode",
75357c478bd9Sstevel@tonic-gate tgt);
75367c478bd9Sstevel@tonic-gate }
75377c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[tgt] &=
753819397407SSherry Moore ~(SCSI_OPTIONS_SYNC | SCSI_OPTIONS_FAST);
75397c478bd9Sstevel@tonic-gate } else {
75407c478bd9Sstevel@tonic-gate /* increase period by 100% */
75417c478bd9Sstevel@tonic-gate fas->f_neg_period[tgt] *= 2;
75427c478bd9Sstevel@tonic-gate
75437c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN,
75447c478bd9Sstevel@tonic-gate "Target %d reducing sync. transfer rate", tgt);
75457c478bd9Sstevel@tonic-gate }
75467c478bd9Sstevel@tonic-gate }
75477c478bd9Sstevel@tonic-gate fas->f_backoff |= tshift;
75487c478bd9Sstevel@tonic-gate
75497c478bd9Sstevel@tonic-gate /*
75507c478bd9Sstevel@tonic-gate * always enable slow cable mode, if not already enabled
75517c478bd9Sstevel@tonic-gate */
75527c478bd9Sstevel@tonic-gate if ((fas->f_fasconf & FAS_CONF_SLOWMODE) == 0) {
75537c478bd9Sstevel@tonic-gate fas->f_fasconf |= FAS_CONF_SLOWMODE;
75547c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fas->f_reg->fas_conf, fas->f_fasconf);
75557c478bd9Sstevel@tonic-gate IPRINTF("Reverting to slow SCSI cable mode\n");
75567c478bd9Sstevel@tonic-gate }
75577c478bd9Sstevel@tonic-gate
75587c478bd9Sstevel@tonic-gate /*
75597c478bd9Sstevel@tonic-gate * Force sync renegotiation and update properties
75607c478bd9Sstevel@tonic-gate */
75617c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, tgt);
75627c478bd9Sstevel@tonic-gate fas->f_props_update |= (1<<tgt);
75637c478bd9Sstevel@tonic-gate }
75647c478bd9Sstevel@tonic-gate
75657c478bd9Sstevel@tonic-gate /*
75667c478bd9Sstevel@tonic-gate * handle failed negotiations (either reject or bus free condition)
75677c478bd9Sstevel@tonic-gate */
75687c478bd9Sstevel@tonic-gate static void
fas_reset_sync_wide(struct fas * fas)75697c478bd9Sstevel@tonic-gate fas_reset_sync_wide(struct fas *fas)
75707c478bd9Sstevel@tonic-gate {
75717c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
75727c478bd9Sstevel@tonic-gate int tgt = Tgt(sp);
75737c478bd9Sstevel@tonic-gate
75747c478bd9Sstevel@tonic-gate if (fas->f_wdtr_sent) {
75757c478bd9Sstevel@tonic-gate IPRINTF("wide neg message rejected or bus free\n");
75767c478bd9Sstevel@tonic-gate fas->f_nowide |= (1<<tgt);
75777c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt] &= ~FAS_CONF3_WIDE;
75787c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fas->f_reg->fas_conf3,
757919397407SSherry Moore fas->f_fasconf3[tgt]);
75807c478bd9Sstevel@tonic-gate /*
75817c478bd9Sstevel@tonic-gate * clear offset just in case it goes to
75827c478bd9Sstevel@tonic-gate * data phase
75837c478bd9Sstevel@tonic-gate */
75847c478bd9Sstevel@tonic-gate fas_reg_write(fas,
75857c478bd9Sstevel@tonic-gate (uchar_t *)&fas->f_reg->fas_sync_offset, 0);
75867c478bd9Sstevel@tonic-gate } else if (fas->f_sdtr_sent) {
75877c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg =
758819397407SSherry Moore fas->f_reg;
75897c478bd9Sstevel@tonic-gate IPRINTF("sync neg message rejected or bus free\n");
75907c478bd9Sstevel@tonic-gate fas->f_nosync |= (1<<tgt);
75917c478bd9Sstevel@tonic-gate fas->f_offset[tgt] = 0;
75927c478bd9Sstevel@tonic-gate fas->f_sync_period[tgt] = 0;
75937c478bd9Sstevel@tonic-gate fas_reg_write(fas,
75947c478bd9Sstevel@tonic-gate (uchar_t *)&fasreg->fas_sync_period, 0);
75957c478bd9Sstevel@tonic-gate fas_reg_write(fas,
75967c478bd9Sstevel@tonic-gate (uchar_t *)&fasreg->fas_sync_offset, 0);
75977c478bd9Sstevel@tonic-gate fas->f_offset[tgt] = 0;
75987c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt] &= ~FAS_CONF3_FASTSCSI;
75997c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fasreg->fas_conf3,
76007c478bd9Sstevel@tonic-gate fas->f_fasconf3[tgt]);
76017c478bd9Sstevel@tonic-gate }
76027c478bd9Sstevel@tonic-gate
76037c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, tgt);
76047c478bd9Sstevel@tonic-gate }
76057c478bd9Sstevel@tonic-gate
76067c478bd9Sstevel@tonic-gate /*
76077c478bd9Sstevel@tonic-gate * force wide and sync renegotiation
76087c478bd9Sstevel@tonic-gate */
76097c478bd9Sstevel@tonic-gate static void
fas_force_renegotiation(struct fas * fas,int target)76107c478bd9Sstevel@tonic-gate fas_force_renegotiation(struct fas *fas, int target)
76117c478bd9Sstevel@tonic-gate {
76127c478bd9Sstevel@tonic-gate ushort_t tshift = 1<<target;
76137c478bd9Sstevel@tonic-gate fas->f_sync_known &= ~tshift;
76147c478bd9Sstevel@tonic-gate fas->f_sync_enabled &= ~tshift;
76157c478bd9Sstevel@tonic-gate fas->f_wide_known &= ~tshift;
76167c478bd9Sstevel@tonic-gate fas->f_wide_enabled &= ~tshift;
76177c478bd9Sstevel@tonic-gate }
76187c478bd9Sstevel@tonic-gate
76197c478bd9Sstevel@tonic-gate /*
76207c478bd9Sstevel@tonic-gate * update conf3 register for wide negotiation
76217c478bd9Sstevel@tonic-gate */
76227c478bd9Sstevel@tonic-gate static void
fas_set_wide_conf3(struct fas * fas,int target,int width)76237c478bd9Sstevel@tonic-gate fas_set_wide_conf3(struct fas *fas, int target, int width)
76247c478bd9Sstevel@tonic-gate {
76257c478bd9Sstevel@tonic-gate ASSERT(width <= 1);
76267c478bd9Sstevel@tonic-gate switch (width) {
76277c478bd9Sstevel@tonic-gate case 0:
76287c478bd9Sstevel@tonic-gate fas->f_fasconf3[target] &= ~FAS_CONF3_WIDE;
76297c478bd9Sstevel@tonic-gate break;
76307c478bd9Sstevel@tonic-gate case 1:
76317c478bd9Sstevel@tonic-gate fas->f_fasconf3[target] |= FAS_CONF3_WIDE;
76327c478bd9Sstevel@tonic-gate fas->f_wide_enabled |= (1<<target);
76337c478bd9Sstevel@tonic-gate break;
76347c478bd9Sstevel@tonic-gate }
76357c478bd9Sstevel@tonic-gate
76367c478bd9Sstevel@tonic-gate fas_reg_write(fas, &fas->f_reg->fas_conf3, fas->f_fasconf3[target]);
76377c478bd9Sstevel@tonic-gate fas->f_fasconf3_reg_last = fas->f_fasconf3[target];
76387c478bd9Sstevel@tonic-gate }
76397c478bd9Sstevel@tonic-gate
76407c478bd9Sstevel@tonic-gate /*
76417c478bd9Sstevel@tonic-gate * Abort command handling
76427c478bd9Sstevel@tonic-gate *
76437c478bd9Sstevel@tonic-gate * abort current cmd, either by device reset or immediately with bus reset
76447c478bd9Sstevel@tonic-gate * (usually an abort msg doesn't completely solve the problem, therefore
76457c478bd9Sstevel@tonic-gate * a device or bus reset is recommended)
76467c478bd9Sstevel@tonic-gate */
76477c478bd9Sstevel@tonic-gate static int
fas_abort_curcmd(struct fas * fas)76487c478bd9Sstevel@tonic-gate fas_abort_curcmd(struct fas *fas)
76497c478bd9Sstevel@tonic-gate {
76507c478bd9Sstevel@tonic-gate if (fas->f_current_sp) {
76517c478bd9Sstevel@tonic-gate return (fas_abort_cmd(fas, fas->f_current_sp,
765219397407SSherry Moore fas->f_current_sp->cmd_slot));
76537c478bd9Sstevel@tonic-gate } else {
76547c478bd9Sstevel@tonic-gate return (fas_reset_bus(fas));
76557c478bd9Sstevel@tonic-gate }
76567c478bd9Sstevel@tonic-gate }
76577c478bd9Sstevel@tonic-gate
76587c478bd9Sstevel@tonic-gate static int
fas_abort_cmd(struct fas * fas,struct fas_cmd * sp,int slot)76597c478bd9Sstevel@tonic-gate fas_abort_cmd(struct fas *fas, struct fas_cmd *sp, int slot)
76607c478bd9Sstevel@tonic-gate {
76617c478bd9Sstevel@tonic-gate struct scsi_address ap;
76627c478bd9Sstevel@tonic-gate
76637c478bd9Sstevel@tonic-gate ap.a_hba_tran = fas->f_tran;
76647c478bd9Sstevel@tonic-gate ap.a_target = slot / NLUNS_PER_TARGET;
76657c478bd9Sstevel@tonic-gate ap.a_lun = slot % NLUNS_PER_TARGET;
76667c478bd9Sstevel@tonic-gate
76677c478bd9Sstevel@tonic-gate IPRINTF1("abort cmd 0x%p\n", (void *)sp);
76687c478bd9Sstevel@tonic-gate
76697c478bd9Sstevel@tonic-gate /*
76707c478bd9Sstevel@tonic-gate * attempting to abort a connected cmd is usually fruitless, so
76717c478bd9Sstevel@tonic-gate * only try disconnected cmds
76727c478bd9Sstevel@tonic-gate * a reset is preferable over an abort (see 1161701)
76737c478bd9Sstevel@tonic-gate */
76747c478bd9Sstevel@tonic-gate if ((fas->f_current_sp && (fas->f_current_sp->cmd_slot != slot)) ||
76757c478bd9Sstevel@tonic-gate (fas->f_state == STATE_FREE)) {
76767c478bd9Sstevel@tonic-gate IPRINTF2("attempting to reset target %d.%d\n",
76777c478bd9Sstevel@tonic-gate ap.a_target, ap.a_lun);
76787c478bd9Sstevel@tonic-gate if (fas_do_scsi_reset(&ap, RESET_TARGET)) {
76797c478bd9Sstevel@tonic-gate return (ACTION_SEARCH);
76807c478bd9Sstevel@tonic-gate }
76817c478bd9Sstevel@tonic-gate }
76827c478bd9Sstevel@tonic-gate
76837c478bd9Sstevel@tonic-gate /*
76847c478bd9Sstevel@tonic-gate * if the target won't listen, then a retry is useless
76857c478bd9Sstevel@tonic-gate * there is also the possibility that the cmd still completed while
76867c478bd9Sstevel@tonic-gate * we were trying to reset and the target driver may have done a
76877c478bd9Sstevel@tonic-gate * device reset which has blown away this sp.
76887c478bd9Sstevel@tonic-gate * well, we've tried, now pull the chain
76897c478bd9Sstevel@tonic-gate */
76907c478bd9Sstevel@tonic-gate IPRINTF("aborting all cmds by bus reset\n");
76917c478bd9Sstevel@tonic-gate return (fas_reset_bus(fas));
76927c478bd9Sstevel@tonic-gate }
76937c478bd9Sstevel@tonic-gate
76947c478bd9Sstevel@tonic-gate /*
76957c478bd9Sstevel@tonic-gate * fas_do_scsi_abort() assumes that we already have the mutex.
76967c478bd9Sstevel@tonic-gate * during the abort, we hold the mutex and prevent callbacks by setting
76977c478bd9Sstevel@tonic-gate * completion pointer to NULL. this will also avoid that a target driver
76987c478bd9Sstevel@tonic-gate * attempts to do a scsi_abort/reset while we are aborting.
76997c478bd9Sstevel@tonic-gate * because the completion pointer is NULL we can still update the
77007c478bd9Sstevel@tonic-gate * packet after completion
77017c478bd9Sstevel@tonic-gate * the throttle for this slot is cleared either by fas_abort_connected_cmd
77027c478bd9Sstevel@tonic-gate * or fas_runpoll which prevents new cmds from starting while aborting
77037c478bd9Sstevel@tonic-gate */
77047c478bd9Sstevel@tonic-gate static int
fas_do_scsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)77057c478bd9Sstevel@tonic-gate fas_do_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
77067c478bd9Sstevel@tonic-gate {
77077c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
77087c478bd9Sstevel@tonic-gate struct fas_cmd *sp;
77097c478bd9Sstevel@tonic-gate int rval = FALSE;
77107c478bd9Sstevel@tonic-gate short slot;
77117c478bd9Sstevel@tonic-gate struct fas_cmd *cur_sp = fas->f_current_sp;
77127c478bd9Sstevel@tonic-gate void (*cur_savec)(), (*sp_savec)();
77137c478bd9Sstevel@tonic-gate int sp_tagged_flag, abort_msg;
77147c478bd9Sstevel@tonic-gate
77157c478bd9Sstevel@tonic-gate if (pkt) {
77167c478bd9Sstevel@tonic-gate sp = PKT2CMD(pkt);
77177c478bd9Sstevel@tonic-gate slot = sp->cmd_slot;
77187c478bd9Sstevel@tonic-gate ASSERT(slot == ((ap->a_target * NLUNS_PER_TARGET) | ap->a_lun));
77197c478bd9Sstevel@tonic-gate } else {
77207c478bd9Sstevel@tonic-gate sp = NULL;
77217c478bd9Sstevel@tonic-gate slot = (ap->a_target * NLUNS_PER_TARGET) | ap->a_lun;
77227c478bd9Sstevel@tonic-gate }
77237c478bd9Sstevel@tonic-gate
77247c478bd9Sstevel@tonic-gate fas_move_waitQ_to_readyQ(fas);
77257c478bd9Sstevel@tonic-gate
77267c478bd9Sstevel@tonic-gate /*
77277c478bd9Sstevel@tonic-gate * If no specific command was passed, all cmds here will be aborted
77287c478bd9Sstevel@tonic-gate * If a specific command was passed as an argument (to be aborted)
77297c478bd9Sstevel@tonic-gate * only the specified command will be aborted
77307c478bd9Sstevel@tonic-gate */
77317c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
77327c478bd9Sstevel@tonic-gate IPRINTF4("fas_scsi_abort for slot %x, "
77337c478bd9Sstevel@tonic-gate "sp=0x%p, pkt_flags=%x, cur_sp=0x%p\n",
77347c478bd9Sstevel@tonic-gate slot, (void *)sp, (sp? sp->cmd_pkt_flags : 0), (void *)cur_sp);
77357c478bd9Sstevel@tonic-gate
77367c478bd9Sstevel@tonic-gate /*
77377c478bd9Sstevel@tonic-gate * first check if the cmd is in the ready queue or
77387c478bd9Sstevel@tonic-gate * in the active queue
77397c478bd9Sstevel@tonic-gate */
77407c478bd9Sstevel@tonic-gate if (sp) {
77417c478bd9Sstevel@tonic-gate IPRINTF3("aborting one command 0x%p for %d.%d\n",
77427c478bd9Sstevel@tonic-gate (void *)sp, ap->a_target, ap->a_lun);
77437c478bd9Sstevel@tonic-gate rval = fas_remove_from_readyQ(fas, sp, slot);
77447c478bd9Sstevel@tonic-gate if (rval) {
77457c478bd9Sstevel@tonic-gate IPRINTF("aborted one ready cmd\n");
77467c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_ABORTED, STAT_ABORTED);
77477c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
77487c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
77497c478bd9Sstevel@tonic-gate goto exit;
77507c478bd9Sstevel@tonic-gate
77517c478bd9Sstevel@tonic-gate } else if ((sp !=
77527c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_slot[sp->cmd_tag[1]])) {
77537c478bd9Sstevel@tonic-gate IPRINTF("cmd doesn't exist here\n");
77547c478bd9Sstevel@tonic-gate rval = TRUE;
77557c478bd9Sstevel@tonic-gate goto exit;
77567c478bd9Sstevel@tonic-gate }
77577c478bd9Sstevel@tonic-gate }
77587c478bd9Sstevel@tonic-gate
77597c478bd9Sstevel@tonic-gate /*
77607c478bd9Sstevel@tonic-gate * hold off any new commands while attempting to abort
77617c478bd9Sstevel@tonic-gate * an active cmd
77627c478bd9Sstevel@tonic-gate */
77637c478bd9Sstevel@tonic-gate fas_set_throttles(fas, slot, 1, HOLD_THROTTLE);
77647c478bd9Sstevel@tonic-gate
77657c478bd9Sstevel@tonic-gate if (cur_sp) {
77667c478bd9Sstevel@tonic-gate /*
77677c478bd9Sstevel@tonic-gate * prevent completion on current cmd
77687c478bd9Sstevel@tonic-gate */
77697c478bd9Sstevel@tonic-gate cur_savec = cur_sp->cmd_pkt->pkt_comp;
77707c478bd9Sstevel@tonic-gate cur_sp->cmd_pkt->pkt_comp = NULL;
77717c478bd9Sstevel@tonic-gate }
77727c478bd9Sstevel@tonic-gate
77737c478bd9Sstevel@tonic-gate if (sp) {
77747c478bd9Sstevel@tonic-gate /*
77757c478bd9Sstevel@tonic-gate * the cmd exists here. is it connected or disconnected?
77767c478bd9Sstevel@tonic-gate * if connected but still selecting then can't abort now.
77777c478bd9Sstevel@tonic-gate * prevent completion on this cmd
77787c478bd9Sstevel@tonic-gate */
77797c478bd9Sstevel@tonic-gate sp_tagged_flag = (sp->cmd_pkt_flags & FLAG_TAGMASK);
77807c478bd9Sstevel@tonic-gate abort_msg = (sp_tagged_flag? MSG_ABORT_TAG : MSG_ABORT);
77817c478bd9Sstevel@tonic-gate sp_savec = sp->cmd_pkt->pkt_comp;
77827c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_comp = NULL;
77837c478bd9Sstevel@tonic-gate
77847c478bd9Sstevel@tonic-gate /* connected but not selecting? */
77857c478bd9Sstevel@tonic-gate if ((sp == cur_sp) && (fas->f_state != STATE_FREE) &&
77867c478bd9Sstevel@tonic-gate (sp->cmd_pkt->pkt_state)) {
77877c478bd9Sstevel@tonic-gate rval = fas_abort_connected_cmd(fas, sp, abort_msg);
77887c478bd9Sstevel@tonic-gate }
77897c478bd9Sstevel@tonic-gate
77907c478bd9Sstevel@tonic-gate /* if abort connected cmd failed, try abort disconnected */
77917c478bd9Sstevel@tonic-gate if ((rval == 0) &&
77927c478bd9Sstevel@tonic-gate (sp->cmd_flags & CFLAG_CMDDISC) &&
77937c478bd9Sstevel@tonic-gate ((sp->cmd_flags & CFLAG_COMPLETED) == 0)) {
77947c478bd9Sstevel@tonic-gate rval = fas_abort_disconnected_cmd(fas, ap, sp,
779519397407SSherry Moore abort_msg, slot);
77967c478bd9Sstevel@tonic-gate }
77977c478bd9Sstevel@tonic-gate
77987c478bd9Sstevel@tonic-gate if (rval) {
77997c478bd9Sstevel@tonic-gate sp->cmd_flags |= CFLAG_COMPLETED;
78007c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, CMD_ABORTED, STAT_ABORTED);
78017c478bd9Sstevel@tonic-gate }
78027c478bd9Sstevel@tonic-gate
78037c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_comp = sp_savec;
78047c478bd9Sstevel@tonic-gate
78057c478bd9Sstevel@tonic-gate } else {
78067c478bd9Sstevel@tonic-gate IPRINTF2("aborting all commands for %d.%d\n",
78077c478bd9Sstevel@tonic-gate ap->a_target, ap->a_lun);
78087c478bd9Sstevel@tonic-gate abort_msg = MSG_ABORT;
78097c478bd9Sstevel@tonic-gate
78107c478bd9Sstevel@tonic-gate /* active and not selecting ? */
78117c478bd9Sstevel@tonic-gate if (cur_sp && (fas->f_state != STATE_FREE) &&
78127c478bd9Sstevel@tonic-gate (cur_sp->cmd_slot == slot) &&
78137c478bd9Sstevel@tonic-gate cur_sp->cmd_pkt->pkt_state) {
78147c478bd9Sstevel@tonic-gate rval = fas_abort_connected_cmd(fas, cur_sp,
781519397407SSherry Moore abort_msg);
78167c478bd9Sstevel@tonic-gate }
78177c478bd9Sstevel@tonic-gate if (rval == 0) {
78187c478bd9Sstevel@tonic-gate rval = fas_abort_disconnected_cmd(fas, ap,
781919397407SSherry Moore NULL, abort_msg, slot);
78207c478bd9Sstevel@tonic-gate }
78217c478bd9Sstevel@tonic-gate }
78227c478bd9Sstevel@tonic-gate
78237c478bd9Sstevel@tonic-gate done:
78247c478bd9Sstevel@tonic-gate /* complete the current sp */
78257c478bd9Sstevel@tonic-gate if (cur_sp) {
78267c478bd9Sstevel@tonic-gate cur_sp->cmd_pkt->pkt_comp = cur_savec;
78277c478bd9Sstevel@tonic-gate if (cur_sp->cmd_flags & CFLAG_COMPLETED) {
78287c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, cur_sp, NEW_TIMEOUT);
78297c478bd9Sstevel@tonic-gate cur_sp->cmd_flags &= ~CFLAG_COMPLETED;
78307c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, cur_sp);
78317c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, cur_sp);
78327c478bd9Sstevel@tonic-gate }
78337c478bd9Sstevel@tonic-gate }
78347c478bd9Sstevel@tonic-gate
78357c478bd9Sstevel@tonic-gate /* complete the sp passed as 2nd arg */
78367c478bd9Sstevel@tonic-gate if (sp && (sp != cur_sp) && (sp->cmd_flags & CFLAG_COMPLETED)) {
78377c478bd9Sstevel@tonic-gate sp->cmd_flags &= ~CFLAG_COMPLETED;
78387c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
78397c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
78407c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
78417c478bd9Sstevel@tonic-gate }
78427c478bd9Sstevel@tonic-gate
78437c478bd9Sstevel@tonic-gate /* clean up all cmds for this slot */
78447c478bd9Sstevel@tonic-gate if (rval && (abort_msg == MSG_ABORT)) {
78457c478bd9Sstevel@tonic-gate /*
78467c478bd9Sstevel@tonic-gate * mark all commands here as aborted
78477c478bd9Sstevel@tonic-gate * abort msg has been accepted, now cleanup queues;
78487c478bd9Sstevel@tonic-gate */
78497c478bd9Sstevel@tonic-gate fas_mark_packets(fas, slot, CMD_ABORTED, STAT_ABORTED);
78507c478bd9Sstevel@tonic-gate fas_flush_tagQ(fas, slot);
78517c478bd9Sstevel@tonic-gate fas_flush_readyQ(fas, slot);
78527c478bd9Sstevel@tonic-gate }
78537c478bd9Sstevel@tonic-gate fas_set_throttles(fas, slot, 1, MAX_THROTTLE);
78547c478bd9Sstevel@tonic-gate
78557c478bd9Sstevel@tonic-gate exit:
78567c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE) {
78577c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
78587c478bd9Sstevel@tonic-gate }
78597c478bd9Sstevel@tonic-gate
78607c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
78617c478bd9Sstevel@tonic-gate
78627c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
78637c478bd9Sstevel@tonic-gate if (rval && fas_test_stop) {
78647c478bd9Sstevel@tonic-gate debug_enter("abort succeeded");
78657c478bd9Sstevel@tonic-gate }
78667c478bd9Sstevel@tonic-gate #endif
78677c478bd9Sstevel@tonic-gate return (rval);
78687c478bd9Sstevel@tonic-gate }
78697c478bd9Sstevel@tonic-gate
78707c478bd9Sstevel@tonic-gate /*
78717c478bd9Sstevel@tonic-gate * mark all packets with new reason and update statistics
78727c478bd9Sstevel@tonic-gate */
78737c478bd9Sstevel@tonic-gate static void
fas_mark_packets(struct fas * fas,int slot,uchar_t reason,uint_t stat)78747c478bd9Sstevel@tonic-gate fas_mark_packets(struct fas *fas, int slot, uchar_t reason, uint_t stat)
78757c478bd9Sstevel@tonic-gate {
78767c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_readyf[slot];
78777c478bd9Sstevel@tonic-gate
78787c478bd9Sstevel@tonic-gate while (sp != 0) {
78797c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, reason, STAT_ABORTED);
78807c478bd9Sstevel@tonic-gate sp = sp->cmd_forw;
78817c478bd9Sstevel@tonic-gate }
78827c478bd9Sstevel@tonic-gate if (fas->f_tcmds[slot]) {
78837c478bd9Sstevel@tonic-gate int n = 0;
78847c478bd9Sstevel@tonic-gate ushort_t tag;
78857c478bd9Sstevel@tonic-gate
78867c478bd9Sstevel@tonic-gate for (tag = 0; tag < fas->f_active[slot]->f_n_slots; tag++) {
78877c478bd9Sstevel@tonic-gate if ((sp = fas->f_active[slot]->f_slot[tag]) != 0) {
78887c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, reason, stat);
78897c478bd9Sstevel@tonic-gate n++;
78907c478bd9Sstevel@tonic-gate }
78917c478bd9Sstevel@tonic-gate }
78927c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] == n);
78937c478bd9Sstevel@tonic-gate }
78947c478bd9Sstevel@tonic-gate }
78957c478bd9Sstevel@tonic-gate
78967c478bd9Sstevel@tonic-gate /*
78977c478bd9Sstevel@tonic-gate * set pkt_reason and OR in pkt_statistics flag
78987c478bd9Sstevel@tonic-gate */
78997c478bd9Sstevel@tonic-gate static void
fas_set_pkt_reason(struct fas * fas,struct fas_cmd * sp,uchar_t reason,uint_t stat)79007c478bd9Sstevel@tonic-gate fas_set_pkt_reason(struct fas *fas, struct fas_cmd *sp, uchar_t reason,
79017c478bd9Sstevel@tonic-gate uint_t stat)
79027c478bd9Sstevel@tonic-gate {
79037c478bd9Sstevel@tonic-gate if (sp) {
79047c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_reason == CMD_CMPLT) {
79057c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason = reason;
79067c478bd9Sstevel@tonic-gate }
79077c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics |= stat;
79087c478bd9Sstevel@tonic-gate IPRINTF3("sp=0x%p, pkt_reason=%x, pkt_stat=%x\n",
79097c478bd9Sstevel@tonic-gate (void *)sp, reason, sp->cmd_pkt->pkt_statistics);
79107c478bd9Sstevel@tonic-gate }
79117c478bd9Sstevel@tonic-gate }
79127c478bd9Sstevel@tonic-gate
79137c478bd9Sstevel@tonic-gate /*
79147c478bd9Sstevel@tonic-gate * delete specified cmd from the ready queue
79157c478bd9Sstevel@tonic-gate */
79167c478bd9Sstevel@tonic-gate static int
fas_remove_from_readyQ(struct fas * fas,struct fas_cmd * sp,int slot)79177c478bd9Sstevel@tonic-gate fas_remove_from_readyQ(struct fas *fas, struct fas_cmd *sp, int slot)
79187c478bd9Sstevel@tonic-gate {
79197c478bd9Sstevel@tonic-gate struct fas_cmd *ssp, *psp;
79207c478bd9Sstevel@tonic-gate
79217c478bd9Sstevel@tonic-gate /*
79227c478bd9Sstevel@tonic-gate * command has not been started yet and is still in the ready queue
79237c478bd9Sstevel@tonic-gate */
79247c478bd9Sstevel@tonic-gate if (sp) {
79257c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds > 0);
79267c478bd9Sstevel@tonic-gate /*
79277c478bd9Sstevel@tonic-gate * find packet on the ready queue and remove it
79287c478bd9Sstevel@tonic-gate */
79297c478bd9Sstevel@tonic-gate for (psp = NULL, ssp = fas->f_readyf[slot]; ssp != NULL;
79307c478bd9Sstevel@tonic-gate psp = ssp, ssp = ssp->cmd_forw) {
79317c478bd9Sstevel@tonic-gate if (ssp == sp) {
79327c478bd9Sstevel@tonic-gate if (fas->f_readyf[slot] == sp) {
79337c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = sp->cmd_forw;
79347c478bd9Sstevel@tonic-gate } else {
79357c478bd9Sstevel@tonic-gate psp->cmd_forw = sp->cmd_forw;
79367c478bd9Sstevel@tonic-gate }
79377c478bd9Sstevel@tonic-gate if (fas->f_readyb[slot] == sp) {
79387c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = psp;
79397c478bd9Sstevel@tonic-gate }
79407c478bd9Sstevel@tonic-gate return (TRUE);
79417c478bd9Sstevel@tonic-gate }
79427c478bd9Sstevel@tonic-gate }
79437c478bd9Sstevel@tonic-gate }
79447c478bd9Sstevel@tonic-gate return (FALSE);
79457c478bd9Sstevel@tonic-gate }
79467c478bd9Sstevel@tonic-gate
79477c478bd9Sstevel@tonic-gate /*
79487c478bd9Sstevel@tonic-gate * add cmd to to head of the readyQ
79497c478bd9Sstevel@tonic-gate * due to tag allocation failure or preemption we have to return
79507c478bd9Sstevel@tonic-gate * this cmd to the readyQ
79517c478bd9Sstevel@tonic-gate */
79527c478bd9Sstevel@tonic-gate static void
fas_head_of_readyQ(struct fas * fas,struct fas_cmd * sp)79537c478bd9Sstevel@tonic-gate fas_head_of_readyQ(struct fas *fas, struct fas_cmd *sp)
79547c478bd9Sstevel@tonic-gate {
79557c478bd9Sstevel@tonic-gate /*
79567c478bd9Sstevel@tonic-gate * never return a NOINTR pkt to the readyQ
79577c478bd9Sstevel@tonic-gate * (fas_runpoll will resubmit)
79587c478bd9Sstevel@tonic-gate */
79597c478bd9Sstevel@tonic-gate if ((sp->cmd_pkt_flags & FLAG_NOINTR) == 0) {
79607c478bd9Sstevel@tonic-gate struct fas_cmd *dp;
79617c478bd9Sstevel@tonic-gate int slot = sp->cmd_slot;
79627c478bd9Sstevel@tonic-gate
79637c478bd9Sstevel@tonic-gate dp = fas->f_readyf[slot];
79647c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = sp;
79657c478bd9Sstevel@tonic-gate sp->cmd_forw = dp;
79667c478bd9Sstevel@tonic-gate if (fas->f_readyb[slot] == NULL) {
79677c478bd9Sstevel@tonic-gate fas->f_readyb[slot] = sp;
79687c478bd9Sstevel@tonic-gate }
79697c478bd9Sstevel@tonic-gate }
79707c478bd9Sstevel@tonic-gate }
79717c478bd9Sstevel@tonic-gate
79727c478bd9Sstevel@tonic-gate /*
79737c478bd9Sstevel@tonic-gate * flush cmds in ready queue
79747c478bd9Sstevel@tonic-gate */
79757c478bd9Sstevel@tonic-gate static void
fas_flush_readyQ(struct fas * fas,int slot)79767c478bd9Sstevel@tonic-gate fas_flush_readyQ(struct fas *fas, int slot)
79777c478bd9Sstevel@tonic-gate {
79787c478bd9Sstevel@tonic-gate if (fas->f_readyf[slot]) {
79797c478bd9Sstevel@tonic-gate struct fas_cmd *sp, *nsp;
79807c478bd9Sstevel@tonic-gate
79817c478bd9Sstevel@tonic-gate IPRINTF1("flushing ready queue, slot=%x\n", slot);
79827c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds > 0);
79837c478bd9Sstevel@tonic-gate
79847c478bd9Sstevel@tonic-gate sp = fas->f_readyf[slot];
79857c478bd9Sstevel@tonic-gate fas->f_readyf[slot] = fas->f_readyb[slot] = NULL;
79867c478bd9Sstevel@tonic-gate
79877c478bd9Sstevel@tonic-gate while (sp != 0) {
79887c478bd9Sstevel@tonic-gate /*
79897c478bd9Sstevel@tonic-gate * save the forward pointer before calling
79907c478bd9Sstevel@tonic-gate * the completion routine
79917c478bd9Sstevel@tonic-gate */
79927c478bd9Sstevel@tonic-gate nsp = sp->cmd_forw;
79937c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_FREE) == 0);
79947c478bd9Sstevel@tonic-gate ASSERT(Tgt(sp) == slot/NLUNS_PER_TARGET);
79957c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
79967c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
79977c478bd9Sstevel@tonic-gate sp = nsp;
79987c478bd9Sstevel@tonic-gate }
79997c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
80007c478bd9Sstevel@tonic-gate }
80017c478bd9Sstevel@tonic-gate }
80027c478bd9Sstevel@tonic-gate
80037c478bd9Sstevel@tonic-gate /*
80047c478bd9Sstevel@tonic-gate * cleanup the tag queue
80057c478bd9Sstevel@tonic-gate * preserve some order by starting with the oldest tag
80067c478bd9Sstevel@tonic-gate */
80077c478bd9Sstevel@tonic-gate static void
fas_flush_tagQ(struct fas * fas,int slot)80087c478bd9Sstevel@tonic-gate fas_flush_tagQ(struct fas *fas, int slot)
80097c478bd9Sstevel@tonic-gate {
80107c478bd9Sstevel@tonic-gate ushort_t tag, starttag;
80117c478bd9Sstevel@tonic-gate struct fas_cmd *sp;
80127c478bd9Sstevel@tonic-gate struct f_slots *tagque = fas->f_active[slot];
80137c478bd9Sstevel@tonic-gate
80147c478bd9Sstevel@tonic-gate if (tagque == NULL) {
80157c478bd9Sstevel@tonic-gate return;
80167c478bd9Sstevel@tonic-gate }
80177c478bd9Sstevel@tonic-gate
80187c478bd9Sstevel@tonic-gate DPRINTF2("flushing entire tag queue, slot=%x, tcmds=%x\n",
80197c478bd9Sstevel@tonic-gate slot, fas->f_tcmds[slot]);
80207c478bd9Sstevel@tonic-gate
80217c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
80227c478bd9Sstevel@tonic-gate {
80237c478bd9Sstevel@tonic-gate int n = 0;
80247c478bd9Sstevel@tonic-gate for (tag = 0; tag < fas->f_active[slot]->f_n_slots; tag++) {
80257c478bd9Sstevel@tonic-gate if ((sp = tagque->f_slot[tag]) != 0) {
80267c478bd9Sstevel@tonic-gate n++;
80277c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_FREE) == 0);
80287c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_reason == CMD_CMPLT) {
80297c478bd9Sstevel@tonic-gate if ((sp->cmd_flags & CFLAG_FINISHED) ==
80307c478bd9Sstevel@tonic-gate 0) {
80317c478bd9Sstevel@tonic-gate debug_enter("fas_flush_tagQ");
80327c478bd9Sstevel@tonic-gate }
80337c478bd9Sstevel@tonic-gate }
80347c478bd9Sstevel@tonic-gate }
80357c478bd9Sstevel@tonic-gate }
80367c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] == n);
80377c478bd9Sstevel@tonic-gate }
80387c478bd9Sstevel@tonic-gate #endif
80397c478bd9Sstevel@tonic-gate tag = starttag = fas->f_active[slot]->f_tags;
80407c478bd9Sstevel@tonic-gate
80417c478bd9Sstevel@tonic-gate do {
80427c478bd9Sstevel@tonic-gate if ((sp = tagque->f_slot[tag]) != 0) {
80437c478bd9Sstevel@tonic-gate fas_flush_cmd(fas, sp, 0, 0);
80447c478bd9Sstevel@tonic-gate }
80457c478bd9Sstevel@tonic-gate tag = ((ushort_t)(tag + 1)) %
80467c478bd9Sstevel@tonic-gate (ushort_t)fas->f_active[slot]->f_n_slots;
80477c478bd9Sstevel@tonic-gate } while (tag != starttag);
80487c478bd9Sstevel@tonic-gate
80497c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[slot] == 0);
80507c478bd9Sstevel@tonic-gate EPRINTF2("ncmds = %x, ndisc=%x\n", fas->f_ncmds, fas->f_ndisc);
80517c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
80527c478bd9Sstevel@tonic-gate }
80537c478bd9Sstevel@tonic-gate
80547c478bd9Sstevel@tonic-gate /*
80557c478bd9Sstevel@tonic-gate * cleanup one active command
80567c478bd9Sstevel@tonic-gate */
80577c478bd9Sstevel@tonic-gate static void
fas_flush_cmd(struct fas * fas,struct fas_cmd * sp,uchar_t reason,uint_t stat)80587c478bd9Sstevel@tonic-gate fas_flush_cmd(struct fas *fas, struct fas_cmd *sp, uchar_t reason,
80597c478bd9Sstevel@tonic-gate uint_t stat)
80607c478bd9Sstevel@tonic-gate {
80617c478bd9Sstevel@tonic-gate short slot = sp->cmd_slot;
80627c478bd9Sstevel@tonic-gate
80637c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds > 0);
80647c478bd9Sstevel@tonic-gate ASSERT((sp->cmd_flags & CFLAG_FREE) == 0);
80657c478bd9Sstevel@tonic-gate ASSERT(sp == fas->f_active[slot]->f_slot[sp->cmd_tag[1]]);
80667c478bd9Sstevel@tonic-gate
80677c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, sp, NEW_TIMEOUT);
80687c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, sp);
80697c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, sp, reason, stat);
80707c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, sp);
80717c478bd9Sstevel@tonic-gate
80727c478bd9Sstevel@tonic-gate EPRINTF2("ncmds = %x, ndisc=%x\n", fas->f_ncmds, fas->f_ndisc);
80737c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
80747c478bd9Sstevel@tonic-gate }
80757c478bd9Sstevel@tonic-gate
80767c478bd9Sstevel@tonic-gate /*
80777c478bd9Sstevel@tonic-gate * prepare a proxy cmd (a cmd sent on behalf of the target driver,
80787c478bd9Sstevel@tonic-gate * usually for error recovery or abort/reset)
80797c478bd9Sstevel@tonic-gate */
80807c478bd9Sstevel@tonic-gate static void
fas_makeproxy_cmd(struct fas_cmd * sp,struct scsi_address * ap,struct scsi_pkt * pkt,int nmsgs,...)80817c478bd9Sstevel@tonic-gate fas_makeproxy_cmd(struct fas_cmd *sp, struct scsi_address *ap,
80827c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt, int nmsgs, ...)
80837c478bd9Sstevel@tonic-gate {
80847c478bd9Sstevel@tonic-gate va_list vap;
80857c478bd9Sstevel@tonic-gate int i;
80867c478bd9Sstevel@tonic-gate
80877c478bd9Sstevel@tonic-gate ASSERT(nmsgs <= (CDB_GROUP5 - CDB_GROUP0 - 3));
80887c478bd9Sstevel@tonic-gate
80897c478bd9Sstevel@tonic-gate bzero(sp, sizeof (*sp));
8090602ca9eaScth bzero(pkt, scsi_pkt_size());
80917c478bd9Sstevel@tonic-gate
80927c478bd9Sstevel@tonic-gate pkt->pkt_address = *ap;
80937c478bd9Sstevel@tonic-gate pkt->pkt_cdbp = (opaque_t)&sp->cmd_cdb[0];
80947c478bd9Sstevel@tonic-gate pkt->pkt_scbp = (opaque_t)&sp->cmd_scb;
80957c478bd9Sstevel@tonic-gate pkt->pkt_ha_private = (opaque_t)sp;
80967c478bd9Sstevel@tonic-gate sp->cmd_pkt = pkt;
80977c478bd9Sstevel@tonic-gate sp->cmd_scblen = 1;
80987c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags = pkt->pkt_flags = FLAG_NOINTR;
80997c478bd9Sstevel@tonic-gate sp->cmd_flags = CFLAG_CMDPROXY;
81007c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_TYPE] = FAS_PROXY_SNDMSG;
81017c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] = FALSE;
81027c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA] = (char)nmsgs;
81037c478bd9Sstevel@tonic-gate
81047c478bd9Sstevel@tonic-gate va_start(vap, nmsgs);
81057c478bd9Sstevel@tonic-gate for (i = 0; i < nmsgs; i++) {
81067c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_DATA + 1 + i] = (uchar_t)va_arg(vap, int);
81077c478bd9Sstevel@tonic-gate }
81087c478bd9Sstevel@tonic-gate va_end(vap);
81097c478bd9Sstevel@tonic-gate }
81107c478bd9Sstevel@tonic-gate
81117c478bd9Sstevel@tonic-gate /*
81127c478bd9Sstevel@tonic-gate * send a proxy cmd and check the result
81137c478bd9Sstevel@tonic-gate */
81147c478bd9Sstevel@tonic-gate static int
fas_do_proxy_cmd(struct fas * fas,struct fas_cmd * sp,struct scsi_address * ap,char * what)81157c478bd9Sstevel@tonic-gate fas_do_proxy_cmd(struct fas *fas, struct fas_cmd *sp,
81167c478bd9Sstevel@tonic-gate struct scsi_address *ap, char *what)
81177c478bd9Sstevel@tonic-gate {
81187c478bd9Sstevel@tonic-gate int rval;
81197c478bd9Sstevel@tonic-gate
81207c478bd9Sstevel@tonic-gate IPRINTF3("Sending proxy %s message to %d.%d\n", what,
81217c478bd9Sstevel@tonic-gate ap->a_target, ap->a_lun);
81227c478bd9Sstevel@tonic-gate if (fas_accept_pkt(fas, sp, TRAN_BUSY_OK) == TRAN_ACCEPT &&
81237c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason == CMD_CMPLT &&
81247c478bd9Sstevel@tonic-gate sp->cmd_cdb[FAS_PROXY_RESULT] == TRUE) {
81257c478bd9Sstevel@tonic-gate IPRINTF3("Proxy %s succeeded for %d.%d\n", what,
81267c478bd9Sstevel@tonic-gate ap->a_target, ap->a_lun);
81277c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp != sp);
81287c478bd9Sstevel@tonic-gate rval = TRUE;
81297c478bd9Sstevel@tonic-gate } else {
81307c478bd9Sstevel@tonic-gate IPRINTF5(
81317c478bd9Sstevel@tonic-gate "Proxy %s failed for %d.%d, result=%x, reason=%x\n", what,
81327c478bd9Sstevel@tonic-gate ap->a_target, ap->a_lun, sp->cmd_cdb[FAS_PROXY_RESULT],
81337c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_reason);
81347c478bd9Sstevel@tonic-gate ASSERT(fas->f_current_sp != sp);
81357c478bd9Sstevel@tonic-gate rval = FALSE;
81367c478bd9Sstevel@tonic-gate }
81377c478bd9Sstevel@tonic-gate return (rval);
81387c478bd9Sstevel@tonic-gate }
81397c478bd9Sstevel@tonic-gate
81407c478bd9Sstevel@tonic-gate /*
81417c478bd9Sstevel@tonic-gate * abort a connected command by sending an abort msg; hold off on
81427c478bd9Sstevel@tonic-gate * starting new cmds by setting throttles to HOLD_THROTTLE
81437c478bd9Sstevel@tonic-gate */
81447c478bd9Sstevel@tonic-gate static int
fas_abort_connected_cmd(struct fas * fas,struct fas_cmd * sp,uchar_t msg)81457c478bd9Sstevel@tonic-gate fas_abort_connected_cmd(struct fas *fas, struct fas_cmd *sp, uchar_t msg)
81467c478bd9Sstevel@tonic-gate {
81477c478bd9Sstevel@tonic-gate int rval = FALSE;
81487c478bd9Sstevel@tonic-gate int flags = sp->cmd_pkt_flags;
81497c478bd9Sstevel@tonic-gate
81507c478bd9Sstevel@tonic-gate /*
81517c478bd9Sstevel@tonic-gate * if reset delay active we cannot access the target.
81527c478bd9Sstevel@tonic-gate */
81537c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[Tgt(sp)]) {
81547c478bd9Sstevel@tonic-gate return (rval);
81557c478bd9Sstevel@tonic-gate }
81567c478bd9Sstevel@tonic-gate
81577c478bd9Sstevel@tonic-gate /*
81587c478bd9Sstevel@tonic-gate * only abort while in data phase; otherwise we mess up msg phase
81597c478bd9Sstevel@tonic-gate */
81607c478bd9Sstevel@tonic-gate if (!((fas->f_state == ACTS_DATA) ||
81617c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_DATA_DONE))) {
81627c478bd9Sstevel@tonic-gate return (rval);
81637c478bd9Sstevel@tonic-gate }
81647c478bd9Sstevel@tonic-gate
81657c478bd9Sstevel@tonic-gate
81667c478bd9Sstevel@tonic-gate IPRINTF3("Sending abort message %s to connected %d.%d\n",
81677c478bd9Sstevel@tonic-gate scsi_mname(msg), Tgt(sp), Lun(sp));
81687c478bd9Sstevel@tonic-gate
81697c478bd9Sstevel@tonic-gate
81707c478bd9Sstevel@tonic-gate fas->f_abort_msg_sent = 0;
81717c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
81727c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = msg;
81737c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags |= FLAG_NOINTR;
81747c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
81757c478bd9Sstevel@tonic-gate
81767c478bd9Sstevel@tonic-gate (void) fas_dopoll(fas, SHORT_POLL_TIMEOUT);
81777c478bd9Sstevel@tonic-gate
81787c478bd9Sstevel@tonic-gate /*
81797c478bd9Sstevel@tonic-gate * now check if the msg was taken
81807c478bd9Sstevel@tonic-gate * e_abort is set in fas_handle_msg_out_done when the abort
81817c478bd9Sstevel@tonic-gate * msg has actually gone out (ie. msg out phase occurred
81827c478bd9Sstevel@tonic-gate */
81837c478bd9Sstevel@tonic-gate if (fas->f_abort_msg_sent && (sp->cmd_flags & CFLAG_COMPLETED)) {
81847c478bd9Sstevel@tonic-gate IPRINTF2("target %d.%d aborted\n",
818519397407SSherry Moore Tgt(sp), Lun(sp));
81867c478bd9Sstevel@tonic-gate rval = TRUE;
81877c478bd9Sstevel@tonic-gate } else {
81887c478bd9Sstevel@tonic-gate IPRINTF2("target %d.%d did not abort\n",
818919397407SSherry Moore Tgt(sp), Lun(sp));
81907c478bd9Sstevel@tonic-gate }
81917c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags = flags;
81927c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
81937c478bd9Sstevel@tonic-gate return (rval);
81947c478bd9Sstevel@tonic-gate }
81957c478bd9Sstevel@tonic-gate
81967c478bd9Sstevel@tonic-gate /*
81977c478bd9Sstevel@tonic-gate * abort a disconnected command; if it is a tagged command, we need
81987c478bd9Sstevel@tonic-gate * to include the tag
81997c478bd9Sstevel@tonic-gate */
82007c478bd9Sstevel@tonic-gate static int
fas_abort_disconnected_cmd(struct fas * fas,struct scsi_address * ap,struct fas_cmd * sp,uchar_t msg,int slot)82017c478bd9Sstevel@tonic-gate fas_abort_disconnected_cmd(struct fas *fas, struct scsi_address *ap,
82027c478bd9Sstevel@tonic-gate struct fas_cmd *sp, uchar_t msg, int slot)
82037c478bd9Sstevel@tonic-gate {
8204602ca9eaScth auto struct fas_cmd local;
8205602ca9eaScth struct fas_cmd *proxy_cmdp = &local;
8206602ca9eaScth struct scsi_pkt *pkt;
8207602ca9eaScth int rval;
8208602ca9eaScth int target = ap->a_target;
82097c478bd9Sstevel@tonic-gate
82107c478bd9Sstevel@tonic-gate /*
82117c478bd9Sstevel@tonic-gate * if reset delay is active, we cannot start a selection
82127c478bd9Sstevel@tonic-gate * and there shouldn't be a cmd outstanding
82137c478bd9Sstevel@tonic-gate */
82147c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[target] != 0) {
82157c478bd9Sstevel@tonic-gate return (FALSE);
82167c478bd9Sstevel@tonic-gate }
82177c478bd9Sstevel@tonic-gate
82187c478bd9Sstevel@tonic-gate if (sp)
82197c478bd9Sstevel@tonic-gate ASSERT(sp->cmd_slot == slot);
82207c478bd9Sstevel@tonic-gate
82217c478bd9Sstevel@tonic-gate IPRINTF1("aborting disconnected tagged cmd(s) with %s\n",
822219397407SSherry Moore scsi_mname(msg));
8223602ca9eaScth pkt = kmem_alloc(scsi_pkt_size(), KM_SLEEP);
82247c478bd9Sstevel@tonic-gate if (sp && (TAGGED(target) && (msg == MSG_ABORT_TAG))) {
82257c478bd9Sstevel@tonic-gate int tag = sp->cmd_tag[1];
82267c478bd9Sstevel@tonic-gate ASSERT(sp == fas->f_active[slot]->f_slot[tag]);
8227602ca9eaScth fas_makeproxy_cmd(proxy_cmdp, ap, pkt, 3,
82287c478bd9Sstevel@tonic-gate MSG_SIMPLE_QTAG, tag, msg);
82297c478bd9Sstevel@tonic-gate } else {
8230602ca9eaScth fas_makeproxy_cmd(proxy_cmdp, ap, pkt, 1, msg);
82317c478bd9Sstevel@tonic-gate }
82327c478bd9Sstevel@tonic-gate
8233602ca9eaScth rval = fas_do_proxy_cmd(fas, proxy_cmdp, ap, scsi_mname(msg));
8234602ca9eaScth kmem_free(pkt, scsi_pkt_size());
8235602ca9eaScth return (rval);
82367c478bd9Sstevel@tonic-gate }
82377c478bd9Sstevel@tonic-gate
82387c478bd9Sstevel@tonic-gate /*
82397c478bd9Sstevel@tonic-gate * reset handling:
82407c478bd9Sstevel@tonic-gate * fas_do_scsi_reset assumes that we have already entered the mutex
82417c478bd9Sstevel@tonic-gate */
82427c478bd9Sstevel@tonic-gate static int
fas_do_scsi_reset(struct scsi_address * ap,int level)82437c478bd9Sstevel@tonic-gate fas_do_scsi_reset(struct scsi_address *ap, int level)
82447c478bd9Sstevel@tonic-gate {
82457c478bd9Sstevel@tonic-gate int rval = FALSE;
82467c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
82477c478bd9Sstevel@tonic-gate short slot = (ap->a_target * NLUNS_PER_TARGET) | ap->a_lun;
82487c478bd9Sstevel@tonic-gate
82497c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
82507c478bd9Sstevel@tonic-gate IPRINTF3("fas_scsi_reset for slot %x, level=%x, tcmds=%x\n",
825119397407SSherry Moore slot, level, fas->f_tcmds[slot]);
82527c478bd9Sstevel@tonic-gate
82537c478bd9Sstevel@tonic-gate fas_move_waitQ_to_readyQ(fas);
82547c478bd9Sstevel@tonic-gate
82557c478bd9Sstevel@tonic-gate if (level == RESET_ALL) {
82567c478bd9Sstevel@tonic-gate /*
82577c478bd9Sstevel@tonic-gate * We know that fas_reset_bus() returns ACTION_RETURN.
82587c478bd9Sstevel@tonic-gate */
82597c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
82607c478bd9Sstevel@tonic-gate
82617c478bd9Sstevel@tonic-gate /*
82627c478bd9Sstevel@tonic-gate * Now call fas_dopoll() to field the reset interrupt
82637c478bd9Sstevel@tonic-gate * which will then call fas_reset_recovery which will
82647c478bd9Sstevel@tonic-gate * call the completion function for all commands.
82657c478bd9Sstevel@tonic-gate */
82667c478bd9Sstevel@tonic-gate if (fas_dopoll(fas, SHORT_POLL_TIMEOUT) <= 0) {
82677c478bd9Sstevel@tonic-gate /*
82687c478bd9Sstevel@tonic-gate * reset fas
82697c478bd9Sstevel@tonic-gate */
82707c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_FAS);
82717c478bd9Sstevel@tonic-gate (void) fas_reset_bus(fas);
82727c478bd9Sstevel@tonic-gate if (fas_dopoll(fas, SHORT_POLL_TIMEOUT) <= 0) {
82737c478bd9Sstevel@tonic-gate fas_log(fas,
82747c478bd9Sstevel@tonic-gate CE_WARN, "reset scsi bus failed");
82757c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
82767c478bd9Sstevel@tonic-gate } else {
82777c478bd9Sstevel@tonic-gate rval = TRUE;
82787c478bd9Sstevel@tonic-gate }
82797c478bd9Sstevel@tonic-gate } else {
82807c478bd9Sstevel@tonic-gate rval = TRUE;
82817c478bd9Sstevel@tonic-gate }
82827c478bd9Sstevel@tonic-gate
82837c478bd9Sstevel@tonic-gate } else {
82847c478bd9Sstevel@tonic-gate struct fas_cmd *cur_sp = fas->f_current_sp;
82857c478bd9Sstevel@tonic-gate void (*savec)() = NULL;
82867c478bd9Sstevel@tonic-gate
82877c478bd9Sstevel@tonic-gate /*
82887c478bd9Sstevel@tonic-gate * prevent new commands from starting
82897c478bd9Sstevel@tonic-gate */
82907c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, HOLD_THROTTLE);
82917c478bd9Sstevel@tonic-gate
82927c478bd9Sstevel@tonic-gate /*
82937c478bd9Sstevel@tonic-gate * zero pkt_comp so it won't complete during the reset and
82947c478bd9Sstevel@tonic-gate * we can still update the packet after the reset.
82957c478bd9Sstevel@tonic-gate */
82967c478bd9Sstevel@tonic-gate if (cur_sp) {
82977c478bd9Sstevel@tonic-gate savec = cur_sp->cmd_pkt->pkt_comp;
82987c478bd9Sstevel@tonic-gate cur_sp->cmd_pkt->pkt_comp = NULL;
82997c478bd9Sstevel@tonic-gate }
83007c478bd9Sstevel@tonic-gate
83017c478bd9Sstevel@tonic-gate /*
83027c478bd9Sstevel@tonic-gate * is this a connected cmd but not selecting?
83037c478bd9Sstevel@tonic-gate */
83047c478bd9Sstevel@tonic-gate if (cur_sp && (fas->f_state != STATE_FREE) &&
83057c478bd9Sstevel@tonic-gate (cur_sp->cmd_pkt->pkt_state != 0) &&
83067c478bd9Sstevel@tonic-gate (ap->a_target == (Tgt(cur_sp)))) {
83077c478bd9Sstevel@tonic-gate rval = fas_reset_connected_cmd(fas, ap);
83087c478bd9Sstevel@tonic-gate }
83097c478bd9Sstevel@tonic-gate
83107c478bd9Sstevel@tonic-gate /*
83117c478bd9Sstevel@tonic-gate * if not connected or fas_reset_connected_cmd() failed,
83127c478bd9Sstevel@tonic-gate * attempt a reset_disconnected_cmd
83137c478bd9Sstevel@tonic-gate */
83147c478bd9Sstevel@tonic-gate if (rval == FALSE) {
83157c478bd9Sstevel@tonic-gate rval = fas_reset_disconnected_cmd(fas, ap);
83167c478bd9Sstevel@tonic-gate }
83177c478bd9Sstevel@tonic-gate
83187c478bd9Sstevel@tonic-gate /*
83197c478bd9Sstevel@tonic-gate * cleanup if reset was successful
83207c478bd9Sstevel@tonic-gate * complete the current sp first.
83217c478bd9Sstevel@tonic-gate */
83227c478bd9Sstevel@tonic-gate if (cur_sp) {
83237c478bd9Sstevel@tonic-gate cur_sp->cmd_pkt->pkt_comp = savec;
83247c478bd9Sstevel@tonic-gate if (cur_sp->cmd_flags & CFLAG_COMPLETED) {
83257c478bd9Sstevel@tonic-gate if (ap->a_target == (Tgt(cur_sp))) {
83267c478bd9Sstevel@tonic-gate fas_set_pkt_reason(fas, cur_sp,
83277c478bd9Sstevel@tonic-gate CMD_RESET, STAT_DEV_RESET);
83287c478bd9Sstevel@tonic-gate }
83297c478bd9Sstevel@tonic-gate fas_remove_cmd(fas, cur_sp, NEW_TIMEOUT);
83307c478bd9Sstevel@tonic-gate cur_sp->cmd_flags &= ~CFLAG_COMPLETED;
83317c478bd9Sstevel@tonic-gate fas_decrement_ncmds(fas, cur_sp);
83327c478bd9Sstevel@tonic-gate fas_call_pkt_comp(fas, cur_sp);
83337c478bd9Sstevel@tonic-gate }
83347c478bd9Sstevel@tonic-gate }
83357c478bd9Sstevel@tonic-gate
83367c478bd9Sstevel@tonic-gate if (rval == TRUE) {
83377c478bd9Sstevel@tonic-gate fas_reset_cleanup(fas, slot);
83387c478bd9Sstevel@tonic-gate } else {
83397c478bd9Sstevel@tonic-gate IPRINTF1("fas_scsi_reset failed for slot %x\n", slot);
83407c478bd9Sstevel@tonic-gate
83417c478bd9Sstevel@tonic-gate /*
83427c478bd9Sstevel@tonic-gate * restore throttles to max throttle, regardless
83437c478bd9Sstevel@tonic-gate * of what it was (fas_set_throttles() will deal
83447c478bd9Sstevel@tonic-gate * with reset delay active)
83457c478bd9Sstevel@tonic-gate * restoring to the old throttle is not
83467c478bd9Sstevel@tonic-gate * a such a good idea
83477c478bd9Sstevel@tonic-gate */
83487c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, MAX_THROTTLE);
83497c478bd9Sstevel@tonic-gate
83507c478bd9Sstevel@tonic-gate }
83517c478bd9Sstevel@tonic-gate
83527c478bd9Sstevel@tonic-gate if (fas->f_state == STATE_FREE) {
83537c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
83547c478bd9Sstevel@tonic-gate }
83557c478bd9Sstevel@tonic-gate }
83567c478bd9Sstevel@tonic-gate exit:
83577c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
83587c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
83597c478bd9Sstevel@tonic-gate
83607c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
83617c478bd9Sstevel@tonic-gate if (rval && fas_test_stop) {
83627c478bd9Sstevel@tonic-gate debug_enter("reset succeeded");
83637c478bd9Sstevel@tonic-gate }
83647c478bd9Sstevel@tonic-gate #endif
83657c478bd9Sstevel@tonic-gate return (rval);
83667c478bd9Sstevel@tonic-gate }
83677c478bd9Sstevel@tonic-gate
83687c478bd9Sstevel@tonic-gate /*
83697c478bd9Sstevel@tonic-gate * reset delay is handled by a separate watchdog; this ensures that
83707c478bd9Sstevel@tonic-gate * regardless of fas_scsi_watchdog_tick, the reset delay will not change
83717c478bd9Sstevel@tonic-gate */
83727c478bd9Sstevel@tonic-gate static void
fas_start_watch_reset_delay(struct fas * fas)83737c478bd9Sstevel@tonic-gate fas_start_watch_reset_delay(struct fas *fas)
83747c478bd9Sstevel@tonic-gate {
83757c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
83767c478bd9Sstevel@tonic-gate if ((fas_reset_watch == 0) && FAS_CAN_SCHED) {
83777c478bd9Sstevel@tonic-gate fas_reset_watch = timeout(fas_watch_reset_delay, NULL,
83787c478bd9Sstevel@tonic-gate drv_usectohz((clock_t)FAS_WATCH_RESET_DELAY_TICK * 1000));
83797c478bd9Sstevel@tonic-gate }
83807c478bd9Sstevel@tonic-gate ASSERT((fas_reset_watch != 0) || (fas->f_flags & FAS_FLG_NOTIMEOUTS));
83817c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
83827c478bd9Sstevel@tonic-gate }
83837c478bd9Sstevel@tonic-gate
83847c478bd9Sstevel@tonic-gate /*
83857c478bd9Sstevel@tonic-gate * set throttles to HOLD and set reset_delay for all target/luns
83867c478bd9Sstevel@tonic-gate */
83877c478bd9Sstevel@tonic-gate static void
fas_setup_reset_delay(struct fas * fas)83887c478bd9Sstevel@tonic-gate fas_setup_reset_delay(struct fas *fas)
83897c478bd9Sstevel@tonic-gate {
83907c478bd9Sstevel@tonic-gate if (!ddi_in_panic()) {
83917c478bd9Sstevel@tonic-gate int i;
83927c478bd9Sstevel@tonic-gate
83937c478bd9Sstevel@tonic-gate fas_set_throttles(fas, 0, N_SLOTS, HOLD_THROTTLE);
83947c478bd9Sstevel@tonic-gate for (i = 0; i < NTARGETS_WIDE; i++) {
83957c478bd9Sstevel@tonic-gate fas->f_reset_delay[i] = fas->f_scsi_reset_delay;
83967c478bd9Sstevel@tonic-gate }
83977c478bd9Sstevel@tonic-gate fas_start_watch_reset_delay(fas);
83987c478bd9Sstevel@tonic-gate } else {
83997c478bd9Sstevel@tonic-gate drv_usecwait(fas->f_scsi_reset_delay * 1000);
84007c478bd9Sstevel@tonic-gate }
84017c478bd9Sstevel@tonic-gate }
84027c478bd9Sstevel@tonic-gate
84037c478bd9Sstevel@tonic-gate /*
84047c478bd9Sstevel@tonic-gate * fas_watch_reset_delay(_subr) is invoked by timeout() and checks every
84057c478bd9Sstevel@tonic-gate * fas instance for active reset delays
84067c478bd9Sstevel@tonic-gate */
84077c478bd9Sstevel@tonic-gate /*ARGSUSED*/
84087c478bd9Sstevel@tonic-gate static void
fas_watch_reset_delay(void * arg)84097c478bd9Sstevel@tonic-gate fas_watch_reset_delay(void *arg)
84107c478bd9Sstevel@tonic-gate {
84117c478bd9Sstevel@tonic-gate struct fas *fas;
84127c478bd9Sstevel@tonic-gate struct fas *lfas; /* last not_done fas */
84137c478bd9Sstevel@tonic-gate int not_done = 0;
84147c478bd9Sstevel@tonic-gate
84157c478bd9Sstevel@tonic-gate mutex_enter(&fas_global_mutex);
84167c478bd9Sstevel@tonic-gate fas_reset_watch = 0;
84177c478bd9Sstevel@tonic-gate mutex_exit(&fas_global_mutex);
84187c478bd9Sstevel@tonic-gate
84197c478bd9Sstevel@tonic-gate rw_enter(&fas_global_rwlock, RW_READER);
84207c478bd9Sstevel@tonic-gate for (fas = fas_head; fas != (struct fas *)NULL; fas = fas->f_next) {
84217c478bd9Sstevel@tonic-gate if (fas->f_tran == 0) {
84227c478bd9Sstevel@tonic-gate continue;
84237c478bd9Sstevel@tonic-gate }
84247c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
84257c478bd9Sstevel@tonic-gate not_done += fas_watch_reset_delay_subr(fas);
84267c478bd9Sstevel@tonic-gate lfas = fas;
84277c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
84287c478bd9Sstevel@tonic-gate }
84297c478bd9Sstevel@tonic-gate rw_exit(&fas_global_rwlock);
84307c478bd9Sstevel@tonic-gate if (not_done) {
84317c478bd9Sstevel@tonic-gate ASSERT(lfas != NULL);
84327c478bd9Sstevel@tonic-gate fas_start_watch_reset_delay(lfas);
84337c478bd9Sstevel@tonic-gate }
84347c478bd9Sstevel@tonic-gate }
84357c478bd9Sstevel@tonic-gate
84367c478bd9Sstevel@tonic-gate static int
fas_watch_reset_delay_subr(struct fas * fas)84377c478bd9Sstevel@tonic-gate fas_watch_reset_delay_subr(struct fas *fas)
84387c478bd9Sstevel@tonic-gate {
84397c478bd9Sstevel@tonic-gate short slot, s;
84407c478bd9Sstevel@tonic-gate int start_slot = -1;
84417c478bd9Sstevel@tonic-gate int done = 0;
84427c478bd9Sstevel@tonic-gate
84437c478bd9Sstevel@tonic-gate for (slot = 0; slot < N_SLOTS; slot += NLUNS_PER_TARGET) {
84447c478bd9Sstevel@tonic-gate
84457c478bd9Sstevel@tonic-gate /*
84467c478bd9Sstevel@tonic-gate * check if a reset delay is active; if so back to full throttle
84477c478bd9Sstevel@tonic-gate * which will unleash the cmds in the ready Q
84487c478bd9Sstevel@tonic-gate */
84497c478bd9Sstevel@tonic-gate s = slot/NLUNS_PER_TARGET;
84507c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[s] != 0) {
84517c478bd9Sstevel@tonic-gate EPRINTF2("target%d: reset delay=%d\n", s,
84527c478bd9Sstevel@tonic-gate fas->f_reset_delay[s]);
84537c478bd9Sstevel@tonic-gate fas->f_reset_delay[s] -= FAS_WATCH_RESET_DELAY_TICK;
84547c478bd9Sstevel@tonic-gate if (fas->f_reset_delay[s] <= 0) {
84557c478bd9Sstevel@tonic-gate /*
84567c478bd9Sstevel@tonic-gate * clear throttle for all luns on this target
84577c478bd9Sstevel@tonic-gate */
84587c478bd9Sstevel@tonic-gate fas->f_reset_delay[s] = 0;
84597c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas,
84607c478bd9Sstevel@tonic-gate slot, MAX_THROTTLE);
84617c478bd9Sstevel@tonic-gate IPRINTF1("reset delay completed, slot=%x\n",
84627c478bd9Sstevel@tonic-gate slot);
84637c478bd9Sstevel@tonic-gate if (start_slot == -1) {
84647c478bd9Sstevel@tonic-gate start_slot = slot;
84657c478bd9Sstevel@tonic-gate }
84667c478bd9Sstevel@tonic-gate } else {
84677c478bd9Sstevel@tonic-gate done = -1;
84687c478bd9Sstevel@tonic-gate }
84697c478bd9Sstevel@tonic-gate }
84707c478bd9Sstevel@tonic-gate }
84717c478bd9Sstevel@tonic-gate
84727c478bd9Sstevel@tonic-gate /*
84737c478bd9Sstevel@tonic-gate * start a cmd if a reset delay expired
84747c478bd9Sstevel@tonic-gate */
84757c478bd9Sstevel@tonic-gate if (start_slot != -1 && fas->f_state == STATE_FREE) {
84767c478bd9Sstevel@tonic-gate (void) fas_ustart(fas);
84777c478bd9Sstevel@tonic-gate }
84787c478bd9Sstevel@tonic-gate return (done);
84797c478bd9Sstevel@tonic-gate }
84807c478bd9Sstevel@tonic-gate
84817c478bd9Sstevel@tonic-gate /*
84827c478bd9Sstevel@tonic-gate * cleanup after a device reset. this affects all target's luns
84837c478bd9Sstevel@tonic-gate */
84847c478bd9Sstevel@tonic-gate static void
fas_reset_cleanup(struct fas * fas,int slot)84857c478bd9Sstevel@tonic-gate fas_reset_cleanup(struct fas *fas, int slot)
84867c478bd9Sstevel@tonic-gate {
84877c478bd9Sstevel@tonic-gate /*
84887c478bd9Sstevel@tonic-gate * reset msg has been accepted, now cleanup queues;
84897c478bd9Sstevel@tonic-gate * for all luns of this target
84907c478bd9Sstevel@tonic-gate */
84917c478bd9Sstevel@tonic-gate int i, start, end;
84927c478bd9Sstevel@tonic-gate int target = slot/NLUNS_PER_TARGET;
84937c478bd9Sstevel@tonic-gate
84947c478bd9Sstevel@tonic-gate start = slot & ~(NLUNS_PER_TARGET-1);
84957c478bd9Sstevel@tonic-gate end = start + NLUNS_PER_TARGET;
84967c478bd9Sstevel@tonic-gate IPRINTF4("fas_reset_cleanup: slot %x, start=%x, end=%x, tcmds=%x\n",
84977c478bd9Sstevel@tonic-gate slot, start, end, fas->f_tcmds[slot]);
84987c478bd9Sstevel@tonic-gate
84997c478bd9Sstevel@tonic-gate ASSERT(!(fas->f_current_sp &&
850019397407SSherry Moore (fas->f_current_sp->cmd_slot == slot) &&
850119397407SSherry Moore (fas->f_state & STATE_SELECTING)));
85027c478bd9Sstevel@tonic-gate
85037c478bd9Sstevel@tonic-gate /*
85047c478bd9Sstevel@tonic-gate * if we are not in panic set up a reset delay for this target,
85057c478bd9Sstevel@tonic-gate * a zero throttle forces all new requests into the ready Q
85067c478bd9Sstevel@tonic-gate */
85077c478bd9Sstevel@tonic-gate if (!ddi_in_panic()) {
85087c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, start, HOLD_THROTTLE);
85097c478bd9Sstevel@tonic-gate fas->f_reset_delay[target] = fas->f_scsi_reset_delay;
85107c478bd9Sstevel@tonic-gate fas_start_watch_reset_delay(fas);
85117c478bd9Sstevel@tonic-gate } else {
85127c478bd9Sstevel@tonic-gate drv_usecwait(fas->f_scsi_reset_delay * 1000);
85137c478bd9Sstevel@tonic-gate }
85147c478bd9Sstevel@tonic-gate
85157c478bd9Sstevel@tonic-gate for (i = start; i < end; i++) {
85167c478bd9Sstevel@tonic-gate fas_mark_packets(fas, i, CMD_RESET, STAT_DEV_RESET);
85177c478bd9Sstevel@tonic-gate fas_flush_tagQ(fas, i);
85187c478bd9Sstevel@tonic-gate fas_flush_readyQ(fas, i);
85197c478bd9Sstevel@tonic-gate if (fas->f_arq_pkt[i]) {
85207c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_arq_pkt[i];
85217c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data =
852219397407SSherry Moore (struct arq_private_data *)
852319397407SSherry Moore (sp->cmd_pkt->pkt_private);
85247c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_comp) {
85257c478bd9Sstevel@tonic-gate ASSERT(arq_data->arq_save_sp == NULL);
85267c478bd9Sstevel@tonic-gate }
85277c478bd9Sstevel@tonic-gate }
85287c478bd9Sstevel@tonic-gate ASSERT(fas->f_tcmds[i] == 0);
85297c478bd9Sstevel@tonic-gate }
85307c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
85317c478bd9Sstevel@tonic-gate
85327c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, target);
85337c478bd9Sstevel@tonic-gate }
85347c478bd9Sstevel@tonic-gate
85357c478bd9Sstevel@tonic-gate /*
85367c478bd9Sstevel@tonic-gate * reset a currently disconnected target
85377c478bd9Sstevel@tonic-gate */
85387c478bd9Sstevel@tonic-gate static int
fas_reset_disconnected_cmd(struct fas * fas,struct scsi_address * ap)85397c478bd9Sstevel@tonic-gate fas_reset_disconnected_cmd(struct fas *fas, struct scsi_address *ap)
85407c478bd9Sstevel@tonic-gate {
8541602ca9eaScth auto struct fas_cmd local;
8542602ca9eaScth struct fas_cmd *sp = &local;
8543602ca9eaScth struct scsi_pkt *pkt;
8544602ca9eaScth int rval;
8545602ca9eaScth
8546602ca9eaScth pkt = kmem_alloc(scsi_pkt_size(), KM_SLEEP);
8547602ca9eaScth fas_makeproxy_cmd(sp, ap, pkt, 1, MSG_DEVICE_RESET);
8548602ca9eaScth rval = fas_do_proxy_cmd(fas, sp, ap, scsi_mname(MSG_DEVICE_RESET));
8549602ca9eaScth kmem_free(pkt, scsi_pkt_size());
8550602ca9eaScth return (rval);
85517c478bd9Sstevel@tonic-gate }
85527c478bd9Sstevel@tonic-gate
85537c478bd9Sstevel@tonic-gate /*
85547c478bd9Sstevel@tonic-gate * reset a target with a currently connected command
85557c478bd9Sstevel@tonic-gate * Assert ATN and send MSG_DEVICE_RESET, zero throttles temporarily
85567c478bd9Sstevel@tonic-gate * to prevent new cmds from starting regardless of the outcome
85577c478bd9Sstevel@tonic-gate */
85587c478bd9Sstevel@tonic-gate static int
fas_reset_connected_cmd(struct fas * fas,struct scsi_address * ap)85597c478bd9Sstevel@tonic-gate fas_reset_connected_cmd(struct fas *fas, struct scsi_address *ap)
85607c478bd9Sstevel@tonic-gate {
85617c478bd9Sstevel@tonic-gate int rval = FALSE;
85627c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
85637c478bd9Sstevel@tonic-gate int flags = sp->cmd_pkt_flags;
85647c478bd9Sstevel@tonic-gate
85657c478bd9Sstevel@tonic-gate /*
85667c478bd9Sstevel@tonic-gate * only attempt to reset in data phase; during other phases
85677c478bd9Sstevel@tonic-gate * asserting ATN may just cause confusion
85687c478bd9Sstevel@tonic-gate */
85697c478bd9Sstevel@tonic-gate if (!((fas->f_state == ACTS_DATA) ||
85707c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_DATA_DONE))) {
85717c478bd9Sstevel@tonic-gate return (rval);
85727c478bd9Sstevel@tonic-gate }
85737c478bd9Sstevel@tonic-gate
85747c478bd9Sstevel@tonic-gate IPRINTF2("Sending reset message to connected %d.%d\n",
85757c478bd9Sstevel@tonic-gate ap->a_target, ap->a_lun);
85767c478bd9Sstevel@tonic-gate fas->f_reset_msg_sent = 0;
85777c478bd9Sstevel@tonic-gate fas->f_omsglen = 1;
85787c478bd9Sstevel@tonic-gate fas->f_cur_msgout[0] = MSG_DEVICE_RESET;
85797c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags |= FLAG_NOINTR;
85807c478bd9Sstevel@tonic-gate
85817c478bd9Sstevel@tonic-gate fas_assert_atn(fas);
85827c478bd9Sstevel@tonic-gate
85837c478bd9Sstevel@tonic-gate /*
85847c478bd9Sstevel@tonic-gate * poll for interrupts until bus free
85857c478bd9Sstevel@tonic-gate */
85867c478bd9Sstevel@tonic-gate (void) fas_dopoll(fas, SHORT_POLL_TIMEOUT);
85877c478bd9Sstevel@tonic-gate
85887c478bd9Sstevel@tonic-gate /*
85897c478bd9Sstevel@tonic-gate * now check if the msg was taken
85907c478bd9Sstevel@tonic-gate * f_reset is set in fas_handle_msg_out_done when
85917c478bd9Sstevel@tonic-gate * msg has actually gone out (ie. msg out phase occurred)
85927c478bd9Sstevel@tonic-gate */
85937c478bd9Sstevel@tonic-gate if (fas->f_reset_msg_sent && (sp->cmd_flags & CFLAG_COMPLETED)) {
85947c478bd9Sstevel@tonic-gate IPRINTF2("target %d.%d reset\n", ap->a_target, ap->a_lun);
85957c478bd9Sstevel@tonic-gate rval = TRUE;
85967c478bd9Sstevel@tonic-gate } else {
85977c478bd9Sstevel@tonic-gate IPRINTF2("target %d.%d did not reset\n",
859819397407SSherry Moore ap->a_target, ap->a_lun);
85997c478bd9Sstevel@tonic-gate }
86007c478bd9Sstevel@tonic-gate sp->cmd_pkt_flags = flags;
86017c478bd9Sstevel@tonic-gate fas->f_omsglen = 0;
86027c478bd9Sstevel@tonic-gate
86037c478bd9Sstevel@tonic-gate return (rval);
86047c478bd9Sstevel@tonic-gate }
86057c478bd9Sstevel@tonic-gate
86067c478bd9Sstevel@tonic-gate /*
86077c478bd9Sstevel@tonic-gate * reset the scsi bus to blow all commands away
86087c478bd9Sstevel@tonic-gate */
86097c478bd9Sstevel@tonic-gate static int
fas_reset_bus(struct fas * fas)86107c478bd9Sstevel@tonic-gate fas_reset_bus(struct fas *fas)
86117c478bd9Sstevel@tonic-gate {
86127c478bd9Sstevel@tonic-gate IPRINTF("fas_reset_bus:\n");
86137c478bd9Sstevel@tonic-gate New_state(fas, ACTS_RESET);
86147c478bd9Sstevel@tonic-gate
86157c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_SCSIBUS);
86167c478bd9Sstevel@tonic-gate
86177c478bd9Sstevel@tonic-gate /*
86187c478bd9Sstevel@tonic-gate * Now that we've reset the SCSI bus, we'll take a SCSI RESET
86197c478bd9Sstevel@tonic-gate * interrupt and use that to clean up the state of things.
86207c478bd9Sstevel@tonic-gate */
86217c478bd9Sstevel@tonic-gate return (ACTION_RETURN);
86227c478bd9Sstevel@tonic-gate }
86237c478bd9Sstevel@tonic-gate
86247c478bd9Sstevel@tonic-gate /*
86257c478bd9Sstevel@tonic-gate * fas_reset_recovery is called on the reset interrupt and cleans
86267c478bd9Sstevel@tonic-gate * up all cmds (active or waiting)
86277c478bd9Sstevel@tonic-gate */
86287c478bd9Sstevel@tonic-gate static int
fas_reset_recovery(struct fas * fas)86297c478bd9Sstevel@tonic-gate fas_reset_recovery(struct fas *fas)
86307c478bd9Sstevel@tonic-gate {
86317c478bd9Sstevel@tonic-gate short slot, start_slot;
86327c478bd9Sstevel@tonic-gate int i;
86337c478bd9Sstevel@tonic-gate int rval = ACTION_SEARCH;
86347c478bd9Sstevel@tonic-gate int max_loop = 0;
86357c478bd9Sstevel@tonic-gate
86367c478bd9Sstevel@tonic-gate IPRINTF("fas_reset_recovery:\n");
86377c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
86387c478bd9Sstevel@tonic-gate
86397c478bd9Sstevel@tonic-gate /*
86407c478bd9Sstevel@tonic-gate * renegotiate wide and sync for all targets
86417c478bd9Sstevel@tonic-gate */
86427c478bd9Sstevel@tonic-gate fas->f_sync_known = fas->f_wide_known = 0;
86437c478bd9Sstevel@tonic-gate
86447c478bd9Sstevel@tonic-gate /*
86457c478bd9Sstevel@tonic-gate * reset dma engine
86467c478bd9Sstevel@tonic-gate */
86477c478bd9Sstevel@tonic-gate FAS_FLUSH_DMA_HARD(fas);
86487c478bd9Sstevel@tonic-gate
86497c478bd9Sstevel@tonic-gate /*
86507c478bd9Sstevel@tonic-gate * set throttles and reset delay
86517c478bd9Sstevel@tonic-gate */
86527c478bd9Sstevel@tonic-gate fas_setup_reset_delay(fas);
86537c478bd9Sstevel@tonic-gate
86547c478bd9Sstevel@tonic-gate /*
86557c478bd9Sstevel@tonic-gate * clear interrupts until they go away
86567c478bd9Sstevel@tonic-gate */
86577c478bd9Sstevel@tonic-gate while (INTPENDING(fas) && (max_loop < FAS_RESET_SPIN_MAX_LOOP)) {
86587c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
86597c478bd9Sstevel@tonic-gate fas->f_stat = fas_reg_read(fas, &fasreg->fas_stat);
86607c478bd9Sstevel@tonic-gate fas->f_stat2 = fas_reg_read(fas, &fasreg->fas_stat2);
86617c478bd9Sstevel@tonic-gate fas->f_step = fas_reg_read(fas, &fasreg->fas_step);
86627c478bd9Sstevel@tonic-gate fas->f_intr = fas_reg_read(fas, &fasreg->fas_intr);
86637c478bd9Sstevel@tonic-gate drv_usecwait(FAS_RESET_SPIN_DELAY_USEC);
86647c478bd9Sstevel@tonic-gate max_loop++;
86657c478bd9Sstevel@tonic-gate }
86667c478bd9Sstevel@tonic-gate
86677c478bd9Sstevel@tonic-gate if (max_loop >= FAS_RESET_SPIN_MAX_LOOP) {
86687c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "Resetting SCSI bus failed");
86697c478bd9Sstevel@tonic-gate }
86707c478bd9Sstevel@tonic-gate
86717c478bd9Sstevel@tonic-gate fas_reg_cmd_write(fas, CMD_FLUSH);
86727c478bd9Sstevel@tonic-gate
86737c478bd9Sstevel@tonic-gate /*
86747c478bd9Sstevel@tonic-gate * reset the chip, this shouldn't be necessary but sometimes
86757c478bd9Sstevel@tonic-gate * we get a hang in the next data in phase
86767c478bd9Sstevel@tonic-gate */
86777c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_FAS);
86787c478bd9Sstevel@tonic-gate
86797c478bd9Sstevel@tonic-gate /*
86807c478bd9Sstevel@tonic-gate * reset was expected? if not, it must be external bus reset
86817c478bd9Sstevel@tonic-gate */
86827c478bd9Sstevel@tonic-gate if (fas->f_state != ACTS_RESET) {
86837c478bd9Sstevel@tonic-gate if (fas->f_ncmds) {
86847c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "external SCSI bus reset");
86857c478bd9Sstevel@tonic-gate }
86867c478bd9Sstevel@tonic-gate }
86877c478bd9Sstevel@tonic-gate
86887c478bd9Sstevel@tonic-gate if (fas->f_ncmds == 0) {
86897c478bd9Sstevel@tonic-gate rval = ACTION_RETURN;
86907c478bd9Sstevel@tonic-gate goto done;
86917c478bd9Sstevel@tonic-gate }
86927c478bd9Sstevel@tonic-gate
86937c478bd9Sstevel@tonic-gate /*
86947c478bd9Sstevel@tonic-gate * completely reset the state of the softc data.
86957c478bd9Sstevel@tonic-gate */
86967c478bd9Sstevel@tonic-gate fas_internal_reset(fas, FAS_RESET_SOFTC);
86977c478bd9Sstevel@tonic-gate
86987c478bd9Sstevel@tonic-gate /*
86997c478bd9Sstevel@tonic-gate * Hold the state of the host adapter open
87007c478bd9Sstevel@tonic-gate */
87017c478bd9Sstevel@tonic-gate New_state(fas, ACTS_FROZEN);
87027c478bd9Sstevel@tonic-gate
87037c478bd9Sstevel@tonic-gate /*
87047c478bd9Sstevel@tonic-gate * for right now just claim that all
87057c478bd9Sstevel@tonic-gate * commands have been destroyed by a SCSI reset
87067c478bd9Sstevel@tonic-gate * and let already set reason fields or callers
87077c478bd9Sstevel@tonic-gate * decide otherwise for specific commands.
87087c478bd9Sstevel@tonic-gate */
87097c478bd9Sstevel@tonic-gate start_slot = fas->f_next_slot;
87107c478bd9Sstevel@tonic-gate slot = start_slot;
87117c478bd9Sstevel@tonic-gate do {
87127c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
87137c478bd9Sstevel@tonic-gate fas_mark_packets(fas, slot, CMD_RESET, STAT_BUS_RESET);
87147c478bd9Sstevel@tonic-gate fas_flush_tagQ(fas, slot);
87157c478bd9Sstevel@tonic-gate fas_flush_readyQ(fas, slot);
87167c478bd9Sstevel@tonic-gate if (fas->f_arq_pkt[slot]) {
87177c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_arq_pkt[slot];
87187c478bd9Sstevel@tonic-gate struct arq_private_data *arq_data =
871919397407SSherry Moore (struct arq_private_data *)
872019397407SSherry Moore (sp->cmd_pkt->pkt_private);
87217c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_comp) {
87227c478bd9Sstevel@tonic-gate ASSERT(arq_data->arq_save_sp == NULL);
87237c478bd9Sstevel@tonic-gate }
87247c478bd9Sstevel@tonic-gate }
87257c478bd9Sstevel@tonic-gate slot = NEXTSLOT(slot, fas->f_dslot);
87267c478bd9Sstevel@tonic-gate } while (slot != start_slot);
87277c478bd9Sstevel@tonic-gate
87287c478bd9Sstevel@tonic-gate fas_check_ncmds(fas);
87297c478bd9Sstevel@tonic-gate
87307c478bd9Sstevel@tonic-gate /*
87317c478bd9Sstevel@tonic-gate * reset timeouts
87327c478bd9Sstevel@tonic-gate */
87337c478bd9Sstevel@tonic-gate for (i = 0; i < N_SLOTS; i++) {
87347c478bd9Sstevel@tonic-gate if (fas->f_active[i]) {
87357c478bd9Sstevel@tonic-gate fas->f_active[i]->f_timebase = 0;
87367c478bd9Sstevel@tonic-gate fas->f_active[i]->f_timeout = 0;
87377c478bd9Sstevel@tonic-gate fas->f_active[i]->f_dups = 0;
87387c478bd9Sstevel@tonic-gate }
87397c478bd9Sstevel@tonic-gate }
87407c478bd9Sstevel@tonic-gate
87417c478bd9Sstevel@tonic-gate done:
87427c478bd9Sstevel@tonic-gate /*
87437c478bd9Sstevel@tonic-gate * Move the state back to free...
87447c478bd9Sstevel@tonic-gate */
87457c478bd9Sstevel@tonic-gate New_state(fas, STATE_FREE);
87467c478bd9Sstevel@tonic-gate ASSERT(fas->f_ncmds >= fas->f_ndisc);
87477c478bd9Sstevel@tonic-gate
87487c478bd9Sstevel@tonic-gate /*
87497c478bd9Sstevel@tonic-gate * perform the reset notification callbacks that are registered.
87507c478bd9Sstevel@tonic-gate */
87517c478bd9Sstevel@tonic-gate (void) scsi_hba_reset_notify_callback(&fas->f_mutex,
875219397407SSherry Moore &fas->f_reset_notify_listf);
87537c478bd9Sstevel@tonic-gate
87547c478bd9Sstevel@tonic-gate /*
87557c478bd9Sstevel@tonic-gate * if reset delay is still active a search is meaningless
87567c478bd9Sstevel@tonic-gate * but do it anyway
87577c478bd9Sstevel@tonic-gate */
87587c478bd9Sstevel@tonic-gate return (rval);
87597c478bd9Sstevel@tonic-gate }
87607c478bd9Sstevel@tonic-gate
87617c478bd9Sstevel@tonic-gate /*
87627c478bd9Sstevel@tonic-gate * hba_tran ops for quiesce and unquiesce
87637c478bd9Sstevel@tonic-gate */
87647c478bd9Sstevel@tonic-gate static int
fas_scsi_quiesce(dev_info_t * dip)87657c478bd9Sstevel@tonic-gate fas_scsi_quiesce(dev_info_t *dip)
87667c478bd9Sstevel@tonic-gate {
87677c478bd9Sstevel@tonic-gate struct fas *fas;
87687c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran;
87697c478bd9Sstevel@tonic-gate
87707c478bd9Sstevel@tonic-gate tran = ddi_get_driver_private(dip);
87717c478bd9Sstevel@tonic-gate if ((tran == NULL) || ((fas = TRAN2FAS(tran)) == NULL)) {
87727c478bd9Sstevel@tonic-gate return (-1);
87737c478bd9Sstevel@tonic-gate }
87747c478bd9Sstevel@tonic-gate
87757c478bd9Sstevel@tonic-gate return (fas_quiesce_bus(fas));
87767c478bd9Sstevel@tonic-gate }
87777c478bd9Sstevel@tonic-gate
87787c478bd9Sstevel@tonic-gate static int
fas_scsi_unquiesce(dev_info_t * dip)87797c478bd9Sstevel@tonic-gate fas_scsi_unquiesce(dev_info_t *dip)
87807c478bd9Sstevel@tonic-gate {
87817c478bd9Sstevel@tonic-gate struct fas *fas;
87827c478bd9Sstevel@tonic-gate scsi_hba_tran_t *tran;
87837c478bd9Sstevel@tonic-gate
87847c478bd9Sstevel@tonic-gate tran = ddi_get_driver_private(dip);
87857c478bd9Sstevel@tonic-gate if ((tran == NULL) || ((fas = TRAN2FAS(tran)) == NULL)) {
87867c478bd9Sstevel@tonic-gate return (-1);
87877c478bd9Sstevel@tonic-gate }
87887c478bd9Sstevel@tonic-gate
87897c478bd9Sstevel@tonic-gate return (fas_unquiesce_bus(fas));
87907c478bd9Sstevel@tonic-gate }
87917c478bd9Sstevel@tonic-gate
87927c478bd9Sstevel@tonic-gate #ifdef FAS_TEST
87937c478bd9Sstevel@tonic-gate /*
87947c478bd9Sstevel@tonic-gate * torture test functions
87957c478bd9Sstevel@tonic-gate */
87967c478bd9Sstevel@tonic-gate static void
fas_test_reset(struct fas * fas,int slot)87977c478bd9Sstevel@tonic-gate fas_test_reset(struct fas *fas, int slot)
87987c478bd9Sstevel@tonic-gate {
87997c478bd9Sstevel@tonic-gate struct scsi_address ap;
88007c478bd9Sstevel@tonic-gate char target = slot/NLUNS_PER_TARGET;
88017c478bd9Sstevel@tonic-gate
88027c478bd9Sstevel@tonic-gate if (fas_rtest & (1 << target)) {
88037c478bd9Sstevel@tonic-gate ap.a_hba_tran = fas->f_tran;
88047c478bd9Sstevel@tonic-gate ap.a_target = target;
88057c478bd9Sstevel@tonic-gate ap.a_lun = 0;
88067c478bd9Sstevel@tonic-gate if ((fas_rtest_type == 1) &&
88077c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_DATA_DONE)) {
88087c478bd9Sstevel@tonic-gate if (fas_do_scsi_reset(&ap, RESET_TARGET)) {
88097c478bd9Sstevel@tonic-gate fas_rtest = 0;
88107c478bd9Sstevel@tonic-gate }
88117c478bd9Sstevel@tonic-gate } else if ((fas_rtest_type == 2) &&
88127c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_DATA_DONE)) {
88137c478bd9Sstevel@tonic-gate if (fas_do_scsi_reset(&ap, RESET_ALL)) {
88147c478bd9Sstevel@tonic-gate fas_rtest = 0;
88157c478bd9Sstevel@tonic-gate }
88167c478bd9Sstevel@tonic-gate } else {
88177c478bd9Sstevel@tonic-gate if (fas_do_scsi_reset(&ap, RESET_TARGET)) {
88187c478bd9Sstevel@tonic-gate fas_rtest = 0;
88197c478bd9Sstevel@tonic-gate }
88207c478bd9Sstevel@tonic-gate }
88217c478bd9Sstevel@tonic-gate }
88227c478bd9Sstevel@tonic-gate }
88237c478bd9Sstevel@tonic-gate
88247c478bd9Sstevel@tonic-gate static void
fas_test_abort(struct fas * fas,int slot)88257c478bd9Sstevel@tonic-gate fas_test_abort(struct fas *fas, int slot)
88267c478bd9Sstevel@tonic-gate {
88277c478bd9Sstevel@tonic-gate struct fas_cmd *sp = fas->f_current_sp;
88287c478bd9Sstevel@tonic-gate struct scsi_address ap;
88297c478bd9Sstevel@tonic-gate char target = slot/NLUNS_PER_TARGET;
88307c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = NULL;
88317c478bd9Sstevel@tonic-gate
88327c478bd9Sstevel@tonic-gate if (fas_atest & (1 << target)) {
88337c478bd9Sstevel@tonic-gate ap.a_hba_tran = fas->f_tran;
88347c478bd9Sstevel@tonic-gate ap.a_target = target;
88357c478bd9Sstevel@tonic-gate ap.a_lun = 0;
88367c478bd9Sstevel@tonic-gate
88377c478bd9Sstevel@tonic-gate if ((fas_atest_disc == 0) && sp &&
88387c478bd9Sstevel@tonic-gate (sp->cmd_slot == slot) &&
88397c478bd9Sstevel@tonic-gate ((sp->cmd_flags & CFLAG_CMDDISC) == 0)) {
88407c478bd9Sstevel@tonic-gate pkt = sp->cmd_pkt;
88417c478bd9Sstevel@tonic-gate } else if ((fas_atest_disc == 1) && NOTAG(target)) {
88427c478bd9Sstevel@tonic-gate sp = fas->f_active[slot]->f_slot[0];
88437c478bd9Sstevel@tonic-gate if (sp && (sp->cmd_flags & CFLAG_CMDDISC)) {
88447c478bd9Sstevel@tonic-gate pkt = sp->cmd_pkt;
88457c478bd9Sstevel@tonic-gate }
88467c478bd9Sstevel@tonic-gate } else if ((fas_atest_disc == 1) && (sp == 0) &&
88477c478bd9Sstevel@tonic-gate TAGGED(target) &&
88487c478bd9Sstevel@tonic-gate (fas->f_tcmds[slot] != 0)) {
88497c478bd9Sstevel@tonic-gate int tag;
88507c478bd9Sstevel@tonic-gate /*
88517c478bd9Sstevel@tonic-gate * find the oldest tag
88527c478bd9Sstevel@tonic-gate */
88537c478bd9Sstevel@tonic-gate for (tag = NTAGS-1; tag >= 0; tag--) {
885419397407SSherry Moore if ((sp = fas->f_active[slot]->f_slot[tag])
885519397407SSherry Moore != 0)
88567c478bd9Sstevel@tonic-gate break;
88577c478bd9Sstevel@tonic-gate }
88587c478bd9Sstevel@tonic-gate if (sp) {
88597c478bd9Sstevel@tonic-gate pkt = sp->cmd_pkt;
88607c478bd9Sstevel@tonic-gate ASSERT(sp->cmd_slot == slot);
88617c478bd9Sstevel@tonic-gate } else {
88627c478bd9Sstevel@tonic-gate return;
88637c478bd9Sstevel@tonic-gate }
88647c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 2 && (sp == 0) &&
88657c478bd9Sstevel@tonic-gate (fas->f_tcmds[slot] != 0)) {
88667c478bd9Sstevel@tonic-gate pkt = NULL;
88677c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 2 && NOTAG(target)) {
88687c478bd9Sstevel@tonic-gate pkt = NULL;
88697c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 3 && fas->f_readyf[slot]) {
88707c478bd9Sstevel@tonic-gate pkt = fas->f_readyf[slot]->cmd_pkt;
88717c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 4 &&
88727c478bd9Sstevel@tonic-gate fas->f_readyf[slot] && fas->f_readyf[slot]->cmd_forw) {
88737c478bd9Sstevel@tonic-gate pkt = fas->f_readyf[slot]->cmd_forw->cmd_pkt;
88747c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 5 && fas->f_readyb[slot]) {
88757c478bd9Sstevel@tonic-gate pkt = fas->f_readyb[slot]->cmd_pkt;
88767c478bd9Sstevel@tonic-gate } else if ((fas_atest_disc == 6) && sp &&
88777c478bd9Sstevel@tonic-gate (sp->cmd_slot == slot) &&
88787c478bd9Sstevel@tonic-gate (fas->f_state == ACTS_DATA_DONE)) {
88797c478bd9Sstevel@tonic-gate pkt = sp->cmd_pkt;
88807c478bd9Sstevel@tonic-gate } else if (fas_atest_disc == 7) {
88817c478bd9Sstevel@tonic-gate if (fas_do_scsi_abort(&ap, NULL)) {
88827c478bd9Sstevel@tonic-gate if (fas_do_scsi_abort(&ap, NULL)) {
88837c478bd9Sstevel@tonic-gate if (fas_do_scsi_reset(&ap,
88847c478bd9Sstevel@tonic-gate RESET_TARGET)) {
88857c478bd9Sstevel@tonic-gate fas_atest = 0;
88867c478bd9Sstevel@tonic-gate }
88877c478bd9Sstevel@tonic-gate }
88887c478bd9Sstevel@tonic-gate }
88897c478bd9Sstevel@tonic-gate return;
88907c478bd9Sstevel@tonic-gate } else {
88917c478bd9Sstevel@tonic-gate return;
88927c478bd9Sstevel@tonic-gate }
88937c478bd9Sstevel@tonic-gate
88947c478bd9Sstevel@tonic-gate fas_log(fas, CE_NOTE, "aborting pkt=0x%p state=%x\n",
889519397407SSherry Moore (void *)pkt, (pkt != NULL? pkt->pkt_state : 0));
88967c478bd9Sstevel@tonic-gate if (fas_do_scsi_abort(&ap, pkt)) {
88977c478bd9Sstevel@tonic-gate fas_atest = 0;
88987c478bd9Sstevel@tonic-gate }
88997c478bd9Sstevel@tonic-gate }
89007c478bd9Sstevel@tonic-gate }
89017c478bd9Sstevel@tonic-gate #endif /* FAS_TEST */
89027c478bd9Sstevel@tonic-gate
89037c478bd9Sstevel@tonic-gate /*
89047c478bd9Sstevel@tonic-gate * capability interface
89057c478bd9Sstevel@tonic-gate */
89067c478bd9Sstevel@tonic-gate static int
fas_commoncap(struct scsi_address * ap,char * cap,int val,int tgtonly,int doset)89077c478bd9Sstevel@tonic-gate fas_commoncap(struct scsi_address *ap, char *cap, int val,
89087c478bd9Sstevel@tonic-gate int tgtonly, int doset)
89097c478bd9Sstevel@tonic-gate {
89107c478bd9Sstevel@tonic-gate struct fas *fas = ADDR2FAS(ap);
89117c478bd9Sstevel@tonic-gate int cidx;
89127c478bd9Sstevel@tonic-gate int target = ap->a_target;
89137c478bd9Sstevel@tonic-gate ushort_t tshift = (1<<target);
89147c478bd9Sstevel@tonic-gate ushort_t ntshift = ~tshift;
89157c478bd9Sstevel@tonic-gate int rval = FALSE;
89167c478bd9Sstevel@tonic-gate
89177c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
89187c478bd9Sstevel@tonic-gate
89197c478bd9Sstevel@tonic-gate if (cap == (char *)0) {
89207c478bd9Sstevel@tonic-gate goto exit;
89217c478bd9Sstevel@tonic-gate }
89227c478bd9Sstevel@tonic-gate
89237c478bd9Sstevel@tonic-gate cidx = scsi_hba_lookup_capstr(cap);
89247c478bd9Sstevel@tonic-gate if (cidx == -1) {
89257c478bd9Sstevel@tonic-gate rval = UNDEFINED;
89267c478bd9Sstevel@tonic-gate } else if (doset) {
89277c478bd9Sstevel@tonic-gate /*
89287c478bd9Sstevel@tonic-gate * we usually don't allow setting capabilities for
89297c478bd9Sstevel@tonic-gate * other targets!
89307c478bd9Sstevel@tonic-gate */
89317c478bd9Sstevel@tonic-gate if (!tgtonly) {
89327c478bd9Sstevel@tonic-gate goto exit;
89337c478bd9Sstevel@tonic-gate }
89347c478bd9Sstevel@tonic-gate switch (cidx) {
89357c478bd9Sstevel@tonic-gate case SCSI_CAP_DMA_MAX:
89367c478bd9Sstevel@tonic-gate case SCSI_CAP_MSG_OUT:
89377c478bd9Sstevel@tonic-gate case SCSI_CAP_PARITY:
89387c478bd9Sstevel@tonic-gate case SCSI_CAP_INITIATOR_ID:
89397c478bd9Sstevel@tonic-gate case SCSI_CAP_LINKED_CMDS:
89407c478bd9Sstevel@tonic-gate case SCSI_CAP_UNTAGGED_QING:
89417c478bd9Sstevel@tonic-gate case SCSI_CAP_RESET_NOTIFICATION:
89427c478bd9Sstevel@tonic-gate /*
89437c478bd9Sstevel@tonic-gate * None of these are settable via
89447c478bd9Sstevel@tonic-gate * the capability interface.
89457c478bd9Sstevel@tonic-gate */
89467c478bd9Sstevel@tonic-gate break;
89477c478bd9Sstevel@tonic-gate
89487c478bd9Sstevel@tonic-gate case SCSI_CAP_DISCONNECT:
89497c478bd9Sstevel@tonic-gate if (val)
89507c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[ap->a_target] |=
89517c478bd9Sstevel@tonic-gate SCSI_OPTIONS_DR;
89527c478bd9Sstevel@tonic-gate else
89537c478bd9Sstevel@tonic-gate fas->f_target_scsi_options[ap->a_target] &=
89547c478bd9Sstevel@tonic-gate ~SCSI_OPTIONS_DR;
89557c478bd9Sstevel@tonic-gate
89567c478bd9Sstevel@tonic-gate break;
89577c478bd9Sstevel@tonic-gate
89587c478bd9Sstevel@tonic-gate case SCSI_CAP_SYNCHRONOUS:
89597c478bd9Sstevel@tonic-gate if (val) {
89607c478bd9Sstevel@tonic-gate fas->f_force_async &= ~tshift;
89617c478bd9Sstevel@tonic-gate } else {
89627c478bd9Sstevel@tonic-gate fas->f_force_async |= tshift;
89637c478bd9Sstevel@tonic-gate }
89647c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, target);
89657c478bd9Sstevel@tonic-gate rval = TRUE;
89667c478bd9Sstevel@tonic-gate break;
89677c478bd9Sstevel@tonic-gate
89687c478bd9Sstevel@tonic-gate case SCSI_CAP_TAGGED_QING:
89697c478bd9Sstevel@tonic-gate {
89707c478bd9Sstevel@tonic-gate int slot = target * NLUNS_PER_TARGET | ap->a_lun;
89717c478bd9Sstevel@tonic-gate ushort_t old_notag = fas->f_notag;
89727c478bd9Sstevel@tonic-gate
89737c478bd9Sstevel@tonic-gate /* do not allow with active tgt */
89747c478bd9Sstevel@tonic-gate if (fas->f_tcmds[slot]) {
89757c478bd9Sstevel@tonic-gate break;
89767c478bd9Sstevel@tonic-gate }
89777c478bd9Sstevel@tonic-gate
89787c478bd9Sstevel@tonic-gate slot = target * NLUNS_PER_TARGET | ap->a_lun;
89797c478bd9Sstevel@tonic-gate
89807c478bd9Sstevel@tonic-gate if (val) {
89817c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[target] &
89827c478bd9Sstevel@tonic-gate SCSI_OPTIONS_TAG) {
89837c478bd9Sstevel@tonic-gate IPRINTF1("target %d: TQ enabled\n",
89847c478bd9Sstevel@tonic-gate target);
89857c478bd9Sstevel@tonic-gate fas->f_notag &= ntshift;
89867c478bd9Sstevel@tonic-gate } else {
89877c478bd9Sstevel@tonic-gate break;
89887c478bd9Sstevel@tonic-gate }
89897c478bd9Sstevel@tonic-gate } else {
89907c478bd9Sstevel@tonic-gate IPRINTF1("target %d: TQ disabled\n",
89917c478bd9Sstevel@tonic-gate target);
89927c478bd9Sstevel@tonic-gate fas->f_notag |= tshift;
89937c478bd9Sstevel@tonic-gate }
89947c478bd9Sstevel@tonic-gate
89957c478bd9Sstevel@tonic-gate if (val && fas_alloc_active_slots(fas, slot,
89967c478bd9Sstevel@tonic-gate KM_NOSLEEP)) {
89977c478bd9Sstevel@tonic-gate fas->f_notag = old_notag;
89987c478bd9Sstevel@tonic-gate break;
89997c478bd9Sstevel@tonic-gate }
90007c478bd9Sstevel@tonic-gate
90017c478bd9Sstevel@tonic-gate fas_set_all_lun_throttles(fas, slot, MAX_THROTTLE);
90027c478bd9Sstevel@tonic-gate
90037c478bd9Sstevel@tonic-gate fas_update_props(fas, target);
90047c478bd9Sstevel@tonic-gate rval = TRUE;
90057c478bd9Sstevel@tonic-gate break;
90067c478bd9Sstevel@tonic-gate }
90077c478bd9Sstevel@tonic-gate
90087c478bd9Sstevel@tonic-gate case SCSI_CAP_WIDE_XFER:
90097c478bd9Sstevel@tonic-gate if (val) {
90107c478bd9Sstevel@tonic-gate if (fas->f_target_scsi_options[target] &
90117c478bd9Sstevel@tonic-gate SCSI_OPTIONS_WIDE) {
90127c478bd9Sstevel@tonic-gate fas->f_nowide &= ntshift;
90137c478bd9Sstevel@tonic-gate fas->f_force_narrow &= ~tshift;
90147c478bd9Sstevel@tonic-gate } else {
90157c478bd9Sstevel@tonic-gate break;
90167c478bd9Sstevel@tonic-gate }
90177c478bd9Sstevel@tonic-gate } else {
90187c478bd9Sstevel@tonic-gate fas->f_force_narrow |= tshift;
90197c478bd9Sstevel@tonic-gate }
90207c478bd9Sstevel@tonic-gate fas_force_renegotiation(fas, target);
90217c478bd9Sstevel@tonic-gate rval = TRUE;
90227c478bd9Sstevel@tonic-gate break;
90237c478bd9Sstevel@tonic-gate
90247c478bd9Sstevel@tonic-gate case SCSI_CAP_ARQ:
90257c478bd9Sstevel@tonic-gate if (val) {
90267c478bd9Sstevel@tonic-gate if (fas_create_arq_pkt(fas, ap)) {
90277c478bd9Sstevel@tonic-gate break;
90287c478bd9Sstevel@tonic-gate }
90297c478bd9Sstevel@tonic-gate } else {
90307c478bd9Sstevel@tonic-gate if (fas_delete_arq_pkt(fas, ap)) {
90317c478bd9Sstevel@tonic-gate break;
90327c478bd9Sstevel@tonic-gate }
90337c478bd9Sstevel@tonic-gate }
90347c478bd9Sstevel@tonic-gate rval = TRUE;
90357c478bd9Sstevel@tonic-gate break;
90367c478bd9Sstevel@tonic-gate
90377c478bd9Sstevel@tonic-gate case SCSI_CAP_QFULL_RETRIES:
90387c478bd9Sstevel@tonic-gate fas->f_qfull_retries[target] = (uchar_t)val;
90397c478bd9Sstevel@tonic-gate rval = TRUE;
90407c478bd9Sstevel@tonic-gate break;
90417c478bd9Sstevel@tonic-gate
90427c478bd9Sstevel@tonic-gate case SCSI_CAP_QFULL_RETRY_INTERVAL:
90437c478bd9Sstevel@tonic-gate fas->f_qfull_retry_interval[target] =
904419397407SSherry Moore drv_usectohz(val * 1000);
90457c478bd9Sstevel@tonic-gate rval = TRUE;
90467c478bd9Sstevel@tonic-gate break;
90477c478bd9Sstevel@tonic-gate
90487c478bd9Sstevel@tonic-gate default:
90497c478bd9Sstevel@tonic-gate rval = UNDEFINED;
90507c478bd9Sstevel@tonic-gate break;
90517c478bd9Sstevel@tonic-gate }
90527c478bd9Sstevel@tonic-gate
90537c478bd9Sstevel@tonic-gate } else if (doset == 0) {
90547c478bd9Sstevel@tonic-gate int slot = target * NLUNS_PER_TARGET | ap->a_lun;
90557c478bd9Sstevel@tonic-gate
90567c478bd9Sstevel@tonic-gate switch (cidx) {
90577c478bd9Sstevel@tonic-gate case SCSI_CAP_DMA_MAX:
90587c478bd9Sstevel@tonic-gate /* very high limit because of multiple dma windows */
90597c478bd9Sstevel@tonic-gate rval = 1<<30;
90607c478bd9Sstevel@tonic-gate break;
90617c478bd9Sstevel@tonic-gate case SCSI_CAP_MSG_OUT:
90627c478bd9Sstevel@tonic-gate rval = TRUE;
90637c478bd9Sstevel@tonic-gate break;
90647c478bd9Sstevel@tonic-gate case SCSI_CAP_DISCONNECT:
90657c478bd9Sstevel@tonic-gate if (tgtonly &&
90667c478bd9Sstevel@tonic-gate (fas->f_target_scsi_options[target] &
906719397407SSherry Moore SCSI_OPTIONS_DR)) {
90687c478bd9Sstevel@tonic-gate rval = TRUE;
90697c478bd9Sstevel@tonic-gate }
90707c478bd9Sstevel@tonic-gate break;
90717c478bd9Sstevel@tonic-gate case SCSI_CAP_SYNCHRONOUS:
90727c478bd9Sstevel@tonic-gate if (tgtonly && fas->f_offset[target]) {
90737c478bd9Sstevel@tonic-gate rval = TRUE;
90747c478bd9Sstevel@tonic-gate }
90757c478bd9Sstevel@tonic-gate break;
90767c478bd9Sstevel@tonic-gate case SCSI_CAP_PARITY:
90777c478bd9Sstevel@tonic-gate rval = TRUE;
90787c478bd9Sstevel@tonic-gate break;
90797c478bd9Sstevel@tonic-gate case SCSI_CAP_INITIATOR_ID:
90807c478bd9Sstevel@tonic-gate rval = MY_ID(fas);
90817c478bd9Sstevel@tonic-gate break;
90827c478bd9Sstevel@tonic-gate case SCSI_CAP_TAGGED_QING:
90837c478bd9Sstevel@tonic-gate if (tgtonly && ((fas->f_notag & tshift) == 0)) {
90847c478bd9Sstevel@tonic-gate rval = TRUE;
90857c478bd9Sstevel@tonic-gate }
90867c478bd9Sstevel@tonic-gate break;
90877c478bd9Sstevel@tonic-gate case SCSI_CAP_WIDE_XFER:
90887c478bd9Sstevel@tonic-gate if ((tgtonly && (fas->f_nowide & tshift) == 0)) {
90897c478bd9Sstevel@tonic-gate rval = TRUE;
90907c478bd9Sstevel@tonic-gate }
90917c478bd9Sstevel@tonic-gate break;
90927c478bd9Sstevel@tonic-gate case SCSI_CAP_UNTAGGED_QING:
90937c478bd9Sstevel@tonic-gate rval = TRUE;
90947c478bd9Sstevel@tonic-gate break;
90957c478bd9Sstevel@tonic-gate case SCSI_CAP_ARQ:
90967c478bd9Sstevel@tonic-gate if (tgtonly && fas->f_arq_pkt[slot]) {
90977c478bd9Sstevel@tonic-gate rval = TRUE;
90987c478bd9Sstevel@tonic-gate }
90997c478bd9Sstevel@tonic-gate break;
91007c478bd9Sstevel@tonic-gate case SCSI_CAP_LINKED_CMDS:
91017c478bd9Sstevel@tonic-gate break;
91027c478bd9Sstevel@tonic-gate case SCSI_CAP_RESET_NOTIFICATION:
91037c478bd9Sstevel@tonic-gate rval = TRUE;
91047c478bd9Sstevel@tonic-gate break;
91057c478bd9Sstevel@tonic-gate case SCSI_CAP_QFULL_RETRIES:
91067c478bd9Sstevel@tonic-gate rval = fas->f_qfull_retries[target];
91077c478bd9Sstevel@tonic-gate break;
91087c478bd9Sstevel@tonic-gate case SCSI_CAP_QFULL_RETRY_INTERVAL:
91097c478bd9Sstevel@tonic-gate rval = drv_hztousec(
911019397407SSherry Moore fas->f_qfull_retry_interval[target]) /
911119397407SSherry Moore 1000;
91127c478bd9Sstevel@tonic-gate break;
91137c478bd9Sstevel@tonic-gate
91147c478bd9Sstevel@tonic-gate default:
91157c478bd9Sstevel@tonic-gate rval = UNDEFINED;
91167c478bd9Sstevel@tonic-gate break;
91177c478bd9Sstevel@tonic-gate }
91187c478bd9Sstevel@tonic-gate }
91197c478bd9Sstevel@tonic-gate exit:
91207c478bd9Sstevel@tonic-gate if (val && tgtonly) {
91217c478bd9Sstevel@tonic-gate fas_update_props(fas, target);
91227c478bd9Sstevel@tonic-gate }
91237c478bd9Sstevel@tonic-gate fas_check_waitQ_and_mutex_exit(fas);
91247c478bd9Sstevel@tonic-gate
91257c478bd9Sstevel@tonic-gate if (doset) {
91267c478bd9Sstevel@tonic-gate IPRINTF6(
91277c478bd9Sstevel@tonic-gate "fas_commoncap:tgt=%x,cap=%s,tgtonly=%x,doset=%x,val=%x,rval=%x\n",
912819397407SSherry Moore target, cap, tgtonly, doset, val, rval);
91297c478bd9Sstevel@tonic-gate }
91307c478bd9Sstevel@tonic-gate return (rval);
91317c478bd9Sstevel@tonic-gate }
91327c478bd9Sstevel@tonic-gate
91337c478bd9Sstevel@tonic-gate /*
91347c478bd9Sstevel@tonic-gate * property management
91357c478bd9Sstevel@tonic-gate * fas_update_props:
91367c478bd9Sstevel@tonic-gate * create/update sync/wide/TQ/scsi-options properties for this target
91377c478bd9Sstevel@tonic-gate */
91387c478bd9Sstevel@tonic-gate static void
fas_update_props(struct fas * fas,int tgt)91397c478bd9Sstevel@tonic-gate fas_update_props(struct fas *fas, int tgt)
91407c478bd9Sstevel@tonic-gate {
91417c478bd9Sstevel@tonic-gate char property[32];
91427c478bd9Sstevel@tonic-gate uint_t xfer_speed = 0;
91437c478bd9Sstevel@tonic-gate uint_t xfer_rate = 0;
91447c478bd9Sstevel@tonic-gate int wide_enabled, tq_enabled;
91457c478bd9Sstevel@tonic-gate uint_t regval = fas->f_sync_period[tgt];
91467c478bd9Sstevel@tonic-gate int offset = fas->f_offset[tgt];
91477c478bd9Sstevel@tonic-gate
91487c478bd9Sstevel@tonic-gate wide_enabled = ((fas->f_nowide & (1<<tgt)) == 0);
91497c478bd9Sstevel@tonic-gate if (offset && regval) {
91507c478bd9Sstevel@tonic-gate xfer_speed =
915119397407SSherry Moore FAS_SYNC_KBPS((regval * fas->f_clock_cycle) / 1000);
91527c478bd9Sstevel@tonic-gate xfer_rate = ((wide_enabled)? 2 : 1) * xfer_speed;
91537c478bd9Sstevel@tonic-gate }
91547c478bd9Sstevel@tonic-gate (void) sprintf(property, "target%x-sync-speed", tgt);
91557c478bd9Sstevel@tonic-gate fas_update_this_prop(fas, property, xfer_rate);
91567c478bd9Sstevel@tonic-gate
91577c478bd9Sstevel@tonic-gate (void) sprintf(property, "target%x-wide", tgt);
91587c478bd9Sstevel@tonic-gate fas_update_this_prop(fas, property, wide_enabled);
91597c478bd9Sstevel@tonic-gate
91607c478bd9Sstevel@tonic-gate (void) sprintf(property, "target%x-TQ", tgt);
91617c478bd9Sstevel@tonic-gate tq_enabled = ((fas->f_notag & (1<<tgt))? 0 : 1);
91627c478bd9Sstevel@tonic-gate fas_update_this_prop(fas, property, tq_enabled);
91637c478bd9Sstevel@tonic-gate
91647c478bd9Sstevel@tonic-gate }
91657c478bd9Sstevel@tonic-gate
91667c478bd9Sstevel@tonic-gate static void
fas_update_this_prop(struct fas * fas,char * property,int value)91677c478bd9Sstevel@tonic-gate fas_update_this_prop(struct fas *fas, char *property, int value)
91687c478bd9Sstevel@tonic-gate {
91697c478bd9Sstevel@tonic-gate dev_info_t *dip = fas->f_dev;
91707c478bd9Sstevel@tonic-gate
91717c478bd9Sstevel@tonic-gate IPRINTF2("update prop: %s value=%x\n", property, value);
91727c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(FAS_MUTEX(fas)));
91737c478bd9Sstevel@tonic-gate /*
91747c478bd9Sstevel@tonic-gate * We cannot hold any mutex at this point because the call to
91757c478bd9Sstevel@tonic-gate * ddi_prop_update_int() may block.
91767c478bd9Sstevel@tonic-gate */
91777c478bd9Sstevel@tonic-gate mutex_exit(FAS_MUTEX(fas));
91787c478bd9Sstevel@tonic-gate if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
91797c478bd9Sstevel@tonic-gate property, value) != DDI_PROP_SUCCESS) {
91807c478bd9Sstevel@tonic-gate IPRINTF1("cannot modify/create %s property\n", property);
91817c478bd9Sstevel@tonic-gate }
91827c478bd9Sstevel@tonic-gate mutex_enter(FAS_MUTEX(fas));
91837c478bd9Sstevel@tonic-gate }
91847c478bd9Sstevel@tonic-gate
91857c478bd9Sstevel@tonic-gate /*
91867c478bd9Sstevel@tonic-gate * allocate active slots array, size is dependent on whether tagQ enabled
91877c478bd9Sstevel@tonic-gate */
91887c478bd9Sstevel@tonic-gate static int
fas_alloc_active_slots(struct fas * fas,int slot,int flag)91897c478bd9Sstevel@tonic-gate fas_alloc_active_slots(struct fas *fas, int slot, int flag)
91907c478bd9Sstevel@tonic-gate {
91917c478bd9Sstevel@tonic-gate int target = slot / NLUNS_PER_TARGET;
91927c478bd9Sstevel@tonic-gate struct f_slots *old_active = fas->f_active[slot];
91937c478bd9Sstevel@tonic-gate struct f_slots *new_active;
91947c478bd9Sstevel@tonic-gate ushort_t size;
91957c478bd9Sstevel@tonic-gate int rval = -1;
91967c478bd9Sstevel@tonic-gate
91977c478bd9Sstevel@tonic-gate if (fas->f_tcmds[slot]) {
91987c478bd9Sstevel@tonic-gate IPRINTF("cannot change size of active slots array\n");
91997c478bd9Sstevel@tonic-gate return (rval);
92007c478bd9Sstevel@tonic-gate }
92017c478bd9Sstevel@tonic-gate
92027c478bd9Sstevel@tonic-gate size = ((NOTAG(target)) ? FAS_F_SLOT_SIZE : FAS_F_SLOTS_SIZE_TQ);
92037c478bd9Sstevel@tonic-gate EPRINTF4(
92047c478bd9Sstevel@tonic-gate "fas_alloc_active_slots: target=%x size=%x, old=0x%p, oldsize=%x\n",
920519397407SSherry Moore target, size, (void *)old_active,
920619397407SSherry Moore ((old_active == NULL) ? -1 : old_active->f_size));
92077c478bd9Sstevel@tonic-gate
92087c478bd9Sstevel@tonic-gate new_active = kmem_zalloc(size, flag);
92097c478bd9Sstevel@tonic-gate if (new_active == NULL) {
92107c478bd9Sstevel@tonic-gate IPRINTF("new active alloc failed\n");
92117c478bd9Sstevel@tonic-gate } else {
92127c478bd9Sstevel@tonic-gate fas->f_active[slot] = new_active;
92137c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_n_slots = (NOTAG(target) ? 1 : NTAGS);
92147c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_size = size;
92157c478bd9Sstevel@tonic-gate /*
92167c478bd9Sstevel@tonic-gate * reserve tag 0 for non-tagged cmds to tagged targets
92177c478bd9Sstevel@tonic-gate */
92187c478bd9Sstevel@tonic-gate if (TAGGED(target)) {
92197c478bd9Sstevel@tonic-gate fas->f_active[slot]->f_tags = 1;
92207c478bd9Sstevel@tonic-gate }
92217c478bd9Sstevel@tonic-gate if (old_active) {
92227c478bd9Sstevel@tonic-gate kmem_free((caddr_t)old_active, old_active->f_size);
92237c478bd9Sstevel@tonic-gate }
92247c478bd9Sstevel@tonic-gate rval = 0;
92257c478bd9Sstevel@tonic-gate }
92267c478bd9Sstevel@tonic-gate return (rval);
92277c478bd9Sstevel@tonic-gate }
92287c478bd9Sstevel@tonic-gate
92297c478bd9Sstevel@tonic-gate /*
92307c478bd9Sstevel@tonic-gate * Error logging, printing, and debug print routines
92317c478bd9Sstevel@tonic-gate */
92327c478bd9Sstevel@tonic-gate static char *fas_label = "fas";
92337c478bd9Sstevel@tonic-gate
92347c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/
92357c478bd9Sstevel@tonic-gate static void
fas_log(struct fas * fas,int level,const char * fmt,...)92367c478bd9Sstevel@tonic-gate fas_log(struct fas *fas, int level, const char *fmt, ...)
92377c478bd9Sstevel@tonic-gate {
92387c478bd9Sstevel@tonic-gate dev_info_t *dev;
92397c478bd9Sstevel@tonic-gate va_list ap;
92407c478bd9Sstevel@tonic-gate
92417c478bd9Sstevel@tonic-gate if (fas) {
92427c478bd9Sstevel@tonic-gate dev = fas->f_dev;
92437c478bd9Sstevel@tonic-gate } else {
92447c478bd9Sstevel@tonic-gate dev = 0;
92457c478bd9Sstevel@tonic-gate }
92467c478bd9Sstevel@tonic-gate
92477c478bd9Sstevel@tonic-gate mutex_enter(&fas_log_mutex);
92487c478bd9Sstevel@tonic-gate
92497c478bd9Sstevel@tonic-gate va_start(ap, fmt);
92507c478bd9Sstevel@tonic-gate (void) vsprintf(fas_log_buf, fmt, ap);
92517c478bd9Sstevel@tonic-gate va_end(ap);
92527c478bd9Sstevel@tonic-gate
92537c478bd9Sstevel@tonic-gate if (level == CE_CONT) {
92547c478bd9Sstevel@tonic-gate scsi_log(dev, fas_label, level, "%s\n", fas_log_buf);
92557c478bd9Sstevel@tonic-gate } else {
92567c478bd9Sstevel@tonic-gate scsi_log(dev, fas_label, level, "%s", fas_log_buf);
92577c478bd9Sstevel@tonic-gate }
92587c478bd9Sstevel@tonic-gate
92597c478bd9Sstevel@tonic-gate mutex_exit(&fas_log_mutex);
92607c478bd9Sstevel@tonic-gate }
92617c478bd9Sstevel@tonic-gate
92627c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/
92637c478bd9Sstevel@tonic-gate static void
fas_printf(struct fas * fas,const char * fmt,...)92647c478bd9Sstevel@tonic-gate fas_printf(struct fas *fas, const char *fmt, ...)
92657c478bd9Sstevel@tonic-gate {
92667c478bd9Sstevel@tonic-gate dev_info_t *dev = 0;
92677c478bd9Sstevel@tonic-gate va_list ap;
92687c478bd9Sstevel@tonic-gate int level = CE_CONT;
92697c478bd9Sstevel@tonic-gate
92707c478bd9Sstevel@tonic-gate mutex_enter(&fas_log_mutex);
92717c478bd9Sstevel@tonic-gate
92727c478bd9Sstevel@tonic-gate va_start(ap, fmt);
92737c478bd9Sstevel@tonic-gate (void) vsprintf(fas_log_buf, fmt, ap);
92747c478bd9Sstevel@tonic-gate va_end(ap);
92757c478bd9Sstevel@tonic-gate
92767c478bd9Sstevel@tonic-gate if (fas) {
92777c478bd9Sstevel@tonic-gate dev = fas->f_dev;
92787c478bd9Sstevel@tonic-gate level = CE_NOTE;
92797c478bd9Sstevel@tonic-gate scsi_log(dev, fas_label, level, "%s", fas_log_buf);
92807c478bd9Sstevel@tonic-gate } else {
92817c478bd9Sstevel@tonic-gate scsi_log(dev, fas_label, level, "%s\n", fas_log_buf);
92827c478bd9Sstevel@tonic-gate }
92837c478bd9Sstevel@tonic-gate
92847c478bd9Sstevel@tonic-gate mutex_exit(&fas_log_mutex);
92857c478bd9Sstevel@tonic-gate }
92867c478bd9Sstevel@tonic-gate
92877c478bd9Sstevel@tonic-gate #ifdef FASDEBUG
92887c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/
92897c478bd9Sstevel@tonic-gate void
fas_dprintf(struct fas * fas,const char * fmt,...)92907c478bd9Sstevel@tonic-gate fas_dprintf(struct fas *fas, const char *fmt, ...)
92917c478bd9Sstevel@tonic-gate {
92927c478bd9Sstevel@tonic-gate dev_info_t *dev = 0;
92937c478bd9Sstevel@tonic-gate va_list ap;
92947c478bd9Sstevel@tonic-gate
92957c478bd9Sstevel@tonic-gate if (fas) {
92967c478bd9Sstevel@tonic-gate dev = fas->f_dev;
92977c478bd9Sstevel@tonic-gate }
92987c478bd9Sstevel@tonic-gate
92997c478bd9Sstevel@tonic-gate mutex_enter(&fas_log_mutex);
93007c478bd9Sstevel@tonic-gate
93017c478bd9Sstevel@tonic-gate va_start(ap, fmt);
93027c478bd9Sstevel@tonic-gate (void) vsprintf(fas_log_buf, fmt, ap);
93037c478bd9Sstevel@tonic-gate va_end(ap);
93047c478bd9Sstevel@tonic-gate
93057c478bd9Sstevel@tonic-gate scsi_log(dev, fas_label, SCSI_DEBUG, "%s", fas_log_buf);
93067c478bd9Sstevel@tonic-gate
93077c478bd9Sstevel@tonic-gate mutex_exit(&fas_log_mutex);
93087c478bd9Sstevel@tonic-gate }
93097c478bd9Sstevel@tonic-gate #endif
93107c478bd9Sstevel@tonic-gate
93117c478bd9Sstevel@tonic-gate
93127c478bd9Sstevel@tonic-gate static void
fas_printstate(struct fas * fas,char * msg)93137c478bd9Sstevel@tonic-gate fas_printstate(struct fas *fas, char *msg)
93147c478bd9Sstevel@tonic-gate {
93157c478bd9Sstevel@tonic-gate volatile struct fasreg *fasreg = fas->f_reg;
93167c478bd9Sstevel@tonic-gate volatile struct dma *dmar = fas->f_dma;
93177c478bd9Sstevel@tonic-gate uint_t csr = fas_dma_reg_read(fas, &dmar->dma_csr);
93187c478bd9Sstevel@tonic-gate uint_t count = fas_dma_reg_read(fas, &dmar->dma_count);
93197c478bd9Sstevel@tonic-gate uint_t addr = fas_dma_reg_read(fas, &dmar->dma_addr);
93207c478bd9Sstevel@tonic-gate uint_t test = fas_dma_reg_read(fas, &dmar->dma_test);
93217c478bd9Sstevel@tonic-gate uint_t fas_cnt;
93227c478bd9Sstevel@tonic-gate
93237c478bd9Sstevel@tonic-gate fas_log(fas, CE_WARN, "%s: current fas state:", msg);
93247c478bd9Sstevel@tonic-gate fas_printf(NULL, "Latched stat=0x%b intr=0x%b",
93257c478bd9Sstevel@tonic-gate fas->f_stat, FAS_STAT_BITS, fas->f_intr, FAS_INT_BITS);
93267c478bd9Sstevel@tonic-gate fas_printf(NULL, "last msgout: %s, last msgin: %s",
93277c478bd9Sstevel@tonic-gate scsi_mname(fas->f_last_msgout), scsi_mname(fas->f_last_msgin));
93287c478bd9Sstevel@tonic-gate fas_printf(NULL, "DMA csr=0x%b", csr, dma_bits);
93297c478bd9Sstevel@tonic-gate fas_printf(NULL,
93307c478bd9Sstevel@tonic-gate "addr=%x dmacnt=%x test=%x last=%x last_cnt=%x",
93317c478bd9Sstevel@tonic-gate addr, count, test, fas->f_lastdma, fas->f_lastcount);
93327c478bd9Sstevel@tonic-gate
93337c478bd9Sstevel@tonic-gate GET_FAS_COUNT(fasreg, fas_cnt);
93347c478bd9Sstevel@tonic-gate fas_printf(NULL, "fas state:");
93357c478bd9Sstevel@tonic-gate fas_printf(NULL, "\tcount(32)=%x cmd=%x stat=%x stat2=%x intr=%x",
93367c478bd9Sstevel@tonic-gate fas_cnt, fasreg->fas_cmd, fasreg->fas_stat, fasreg->fas_stat2,
93377c478bd9Sstevel@tonic-gate fasreg->fas_intr);
93387c478bd9Sstevel@tonic-gate fas_printf(NULL,
93397c478bd9Sstevel@tonic-gate "\tstep=%x fifoflag=%x conf=%x test=%x conf2=%x conf3=%x",
93407c478bd9Sstevel@tonic-gate fasreg->fas_step, fasreg->fas_fifo_flag, fasreg->fas_conf,
93417c478bd9Sstevel@tonic-gate fasreg->fas_test, fasreg->fas_conf2, fasreg->fas_conf3);
93427c478bd9Sstevel@tonic-gate
93437c478bd9Sstevel@tonic-gate if (fas->f_current_sp) {
93447c478bd9Sstevel@tonic-gate fas_dump_cmd(fas, fas->f_current_sp);
93457c478bd9Sstevel@tonic-gate }
93467c478bd9Sstevel@tonic-gate }
93477c478bd9Sstevel@tonic-gate
93487c478bd9Sstevel@tonic-gate /*
93497c478bd9Sstevel@tonic-gate * dump all we know about a cmd
93507c478bd9Sstevel@tonic-gate */
93517c478bd9Sstevel@tonic-gate static void
fas_dump_cmd(struct fas * fas,struct fas_cmd * sp)93527c478bd9Sstevel@tonic-gate fas_dump_cmd(struct fas *fas, struct fas_cmd *sp)
93537c478bd9Sstevel@tonic-gate {
93547c478bd9Sstevel@tonic-gate int i;
93557c478bd9Sstevel@tonic-gate uchar_t *cp = (uchar_t *)sp->cmd_pkt->pkt_cdbp;
93567c478bd9Sstevel@tonic-gate auto char buf[128];
93577c478bd9Sstevel@tonic-gate
93587c478bd9Sstevel@tonic-gate buf[0] = '\0';
93597c478bd9Sstevel@tonic-gate fas_printf(NULL, "Cmd dump for Target %d Lun %d:",
93607c478bd9Sstevel@tonic-gate Tgt(sp), Lun(sp));
93617c478bd9Sstevel@tonic-gate (void) sprintf(&buf[0], " cdb=[");
93627c478bd9Sstevel@tonic-gate for (i = 0; i < (int)sp->cmd_actual_cdblen; i++) {
93637c478bd9Sstevel@tonic-gate (void) sprintf(&buf[strlen(buf)], " 0x%x", *cp++);
93647c478bd9Sstevel@tonic-gate }
93657c478bd9Sstevel@tonic-gate (void) sprintf(&buf[strlen(buf)], " ]");
93667c478bd9Sstevel@tonic-gate fas_printf(NULL, buf);
93677c478bd9Sstevel@tonic-gate fas_printf(NULL, "State=%s Last State=%s",
93687c478bd9Sstevel@tonic-gate fas_state_name(fas->f_state), fas_state_name(fas->f_laststate));
93697c478bd9Sstevel@tonic-gate fas_printf(NULL,
93707c478bd9Sstevel@tonic-gate "pkt_state=0x%b pkt_flags=0x%x pkt_statistics=0x%x",
93717c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_state, scsi_state_bits, sp->cmd_pkt_flags,
93727c478bd9Sstevel@tonic-gate sp->cmd_pkt->pkt_statistics);
93737c478bd9Sstevel@tonic-gate if (sp->cmd_pkt->pkt_state & STATE_GOT_STATUS) {
93747c478bd9Sstevel@tonic-gate fas_printf(NULL, "Status=0x%x\n", sp->cmd_pkt->pkt_scbp[0]);
93757c478bd9Sstevel@tonic-gate }
93767c478bd9Sstevel@tonic-gate }
93777c478bd9Sstevel@tonic-gate
93787c478bd9Sstevel@tonic-gate /*ARGSUSED*/
93797c478bd9Sstevel@tonic-gate static void
fas_short_dump_cmd(struct fas * fas,struct fas_cmd * sp)93807c478bd9Sstevel@tonic-gate fas_short_dump_cmd(struct fas *fas, struct fas_cmd *sp)
93817c478bd9Sstevel@tonic-gate {
93827c478bd9Sstevel@tonic-gate int i;
93837c478bd9Sstevel@tonic-gate uchar_t *cp = (uchar_t *)sp->cmd_pkt->pkt_cdbp;
93847c478bd9Sstevel@tonic-gate auto char buf[128];
93857c478bd9Sstevel@tonic-gate
93867c478bd9Sstevel@tonic-gate buf[0] = '\0';
93877c478bd9Sstevel@tonic-gate (void) sprintf(&buf[0], "?%d.%d: cdb=[", Tgt(sp), Lun(sp));
93887c478bd9Sstevel@tonic-gate for (i = 0; i < (int)sp->cmd_actual_cdblen; i++) {
93897c478bd9Sstevel@tonic-gate (void) sprintf(&buf[strlen(buf)], " 0x%x", *cp++);
93907c478bd9Sstevel@tonic-gate }
93917c478bd9Sstevel@tonic-gate (void) sprintf(&buf[strlen(buf)], " ]");
93927c478bd9Sstevel@tonic-gate fas_printf(NULL, buf);
93937c478bd9Sstevel@tonic-gate }
93947c478bd9Sstevel@tonic-gate
93957c478bd9Sstevel@tonic-gate /*
93967c478bd9Sstevel@tonic-gate * state decoding for error messages
93977c478bd9Sstevel@tonic-gate */
93987c478bd9Sstevel@tonic-gate static char *
fas_state_name(ushort_t state)93997c478bd9Sstevel@tonic-gate fas_state_name(ushort_t state)
94007c478bd9Sstevel@tonic-gate {
94017c478bd9Sstevel@tonic-gate if (state == STATE_FREE) {
94027c478bd9Sstevel@tonic-gate return ("FREE");
94037c478bd9Sstevel@tonic-gate } else if (state & STATE_SELECTING) {
94047c478bd9Sstevel@tonic-gate if (state == STATE_SELECT_NORMAL)
94057c478bd9Sstevel@tonic-gate return ("SELECT");
94067c478bd9Sstevel@tonic-gate else if (state == STATE_SELECT_N_STOP)
94077c478bd9Sstevel@tonic-gate return ("SEL&STOP");
94087c478bd9Sstevel@tonic-gate else if (state == STATE_SELECT_N_SENDMSG)
94097c478bd9Sstevel@tonic-gate return ("SELECT_SNDMSG");
94107c478bd9Sstevel@tonic-gate else
94117c478bd9Sstevel@tonic-gate return ("SEL_NO_ATN");
94127c478bd9Sstevel@tonic-gate } else {
94137c478bd9Sstevel@tonic-gate static struct {
94147c478bd9Sstevel@tonic-gate char *sname;
94157c478bd9Sstevel@tonic-gate char state;
94167c478bd9Sstevel@tonic-gate } names[] = {
94177c478bd9Sstevel@tonic-gate "CMD_START", ACTS_CMD_START,
94187c478bd9Sstevel@tonic-gate "CMD_DONE", ACTS_CMD_DONE,
94197c478bd9Sstevel@tonic-gate "MSG_OUT", ACTS_MSG_OUT,
94207c478bd9Sstevel@tonic-gate "MSG_OUT_DONE", ACTS_MSG_OUT_DONE,
94217c478bd9Sstevel@tonic-gate "MSG_IN", ACTS_MSG_IN,
94227c478bd9Sstevel@tonic-gate "MSG_IN_MORE", ACTS_MSG_IN_MORE,
94237c478bd9Sstevel@tonic-gate "MSG_IN_DONE", ACTS_MSG_IN_DONE,
94247c478bd9Sstevel@tonic-gate "CLEARING", ACTS_CLEARING,
94257c478bd9Sstevel@tonic-gate "DATA", ACTS_DATA,
94267c478bd9Sstevel@tonic-gate "DATA_DONE", ACTS_DATA_DONE,
94277c478bd9Sstevel@tonic-gate "CMD_CMPLT", ACTS_C_CMPLT,
94287c478bd9Sstevel@tonic-gate "UNKNOWN", ACTS_UNKNOWN,
94297c478bd9Sstevel@tonic-gate "RESEL", ACTS_RESEL,
94307c478bd9Sstevel@tonic-gate "ENDVEC", ACTS_ENDVEC,
94317c478bd9Sstevel@tonic-gate "RESET", ACTS_RESET,
94327c478bd9Sstevel@tonic-gate "ABORTING", ACTS_ABORTING,
94337c478bd9Sstevel@tonic-gate "FROZEN", ACTS_FROZEN,
94347c478bd9Sstevel@tonic-gate 0
94357c478bd9Sstevel@tonic-gate };
94367c478bd9Sstevel@tonic-gate int i;
94377c478bd9Sstevel@tonic-gate for (i = 0; names[i].sname; i++) {
94387c478bd9Sstevel@tonic-gate if (names[i].state == state)
94397c478bd9Sstevel@tonic-gate return (names[i].sname);
94407c478bd9Sstevel@tonic-gate }
94417c478bd9Sstevel@tonic-gate }
94427c478bd9Sstevel@tonic-gate return ("<BAD>");
94437c478bd9Sstevel@tonic-gate }
9444