xref: /illumos-gate/usr/src/uts/sun/io/scsi/adapters/fas.c (revision 89b43686)
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