linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ANNOUNCE] Adaptec SAS/SATA device driver [24/27]
@ 2005-02-17 17:37 Luben Tuikov
  0 siblings, 0 replies; only message in thread
From: Luben Tuikov @ 2005-02-17 17:37 UTC (permalink / raw)
  To: SCSI Mailing List

Communicating with the sequencers.  Part 1/2.

diff -Nru a/drivers/scsi/adp94xx/adp94xx_seq.c b/drivers/scsi/adp94xx/adp94xx_seq.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/scsi/adp94xx/adp94xx_seq.c	2005-02-16 16:08:12 -05:00
@@ -0,0 +1,1470 @@
+/*
+ * Adaptec ADP94xx SAS HBA device driver for Linux.
+ * Functions for interfacing with Sequencers.
+ *
+ * Written by : David Chaw <david_chaw@adaptec.com>
+ *   
+ * Copyright (c) 2004 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: //depot/razor/linux/src/adp94xx_seq.c#62 $
+ * 
+ */	
+
+#include "adp94xx_osm.h"
+#include "adp94xx_inline.h"
+#include "adp94xx_seq.h"
+
+/*
+ * Wrappers for which particular version of sequencer code and interrupt
+ * vectors  to use.
+ */
+#define ASD_USE_A1_CODE(softc)		\
+	(softc->hw_profile.rev_id == AIC9410_DEV_REV_A1 ? 1: 0)
+
+#define ASD_SEQ_VER(seq, ver)	(seq##ver)
+
+#define ASD_INT_VEC(vec, ver)	(vec##ver)
+	
+#ifdef ASD_DEBUG
+/*
+ * Registers dump state definitions (for debug purpose).
+ */
+static void	asd_hwi_dump_cseq_state(struct asd_softc *asd);
+static void	asd_hwi_dump_lseq_state(struct asd_softc *asd, u_int lseq_id);
+
+typedef struct lseq_cio_reqs {
+	uint8_t		name[24];
+	uint16_t	offset;
+	uint16_t	width;
+	uint16_t	mode;
+} lseq_cio_regs_t;
+
+#define MD(x)	(1 << x)
+
+static lseq_cio_regs_t	LSEQmCIOREGS[] =
+{
+   {"LmMODEPTR"     ,0x00, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmALTMODE"     ,0x01, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmFLAG"        ,0x04, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmARP2INTCTL"  ,0x05, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPRGMCNT"     ,0x08,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmARP2HALTCODE",0x15, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmCURRADDR"    ,0x16,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmLASTADDR"    ,0x18,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmNXTLADDR"    ,0x1A,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnSCBPTR"    ,0x20,16, MD(0)|MD(1)|MD(2)|MD(3)},
+   {"LmMnDDBPTR"    ,0x22,16, MD(0)|MD(1)|MD(2)|MD(3)},
+   {"LmREQMBX"      ,0x30,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmRSPMBX"      ,0x34,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnINT"       ,0x38,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnINTEN"     ,0x3C,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmXMTPRIMD"    ,0x40,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmXMTPRIMCS"   ,0x44, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmCONSTAT"     ,0x45, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnDMAERRS"   ,0x46, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnSGDMAERRS" ,0x47, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnSASALIGN"  ,0x48, 8, MD(1)},
+   {"LmMnSTPALIGN"  ,0x49, 8, MD(1)},
+   {"LmALIGNMODE"   ,0x4B, 8, MD(1)},
+   {"LmMnEXPRCVCNT" ,0x4C,32, MD(0)},
+   {"LmMnXMTCNT"    ,0x4C,32, MD(1)},
+   {"LmMnCURRTAG"   ,0x54,16, MD(0)},
+   {"LmMnPREVTAG"   ,0x56,16, MD(0)},
+   {"LmMnACKOFS"    ,0x58, 8, MD(1)},
+   {"LmMnXFRLVL"    ,0x59, 8, MD(0)|MD(1)},
+   {"LmMnSGDMACTL"  ,0x5A, 8, MD(0)|MD(1)},
+   {"LmMnSGDMASTAT" ,0x5B, 8, MD(0)|MD(1)},
+   {"LmMnDDMACTL"   ,0x5C, 8, MD(0)|MD(1)},
+   {"LmMnDDMASTAT"  ,0x5D, 8, MD(0)|MD(1)},
+   {"LmMnDDMAMODE"  ,0x5E,16, MD(0)|MD(1)},
+   {"LmMnPIPECTL"   ,0x61, 8, MD(0)|MD(1)},
+   {"LmMnACTSCB"    ,0x62,16, MD(0)|MD(1)},
+   {"LmMnSGBHADR"   ,0x64, 8, MD(0)|MD(1)},
+   {"LmMnSGBADR"    ,0x65, 8, MD(0)|MD(1)},
+   {"LmMnSGDCNT"    ,0x66, 8, MD(0)|MD(1)},
+   {"LmMnSGDMADR"   ,0x68,32, MD(0)|MD(1)},
+   {"LmMnSGDMADR"   ,0x6C,32, MD(0)|MD(1)},
+   {"LmMnXFRCNT"    ,0x70,32, MD(0)|MD(1)},
+   {"LmMnXMTCRC"    ,0x74,32, MD(1)},
+   {"LmCURRTAG"     ,0x74,16, MD(0)},
+   {"LmPREVTAG"     ,0x76,16, MD(0)},
+   {"LmDPSEL"       ,0x7B, 8, MD(0)|MD(1)},
+   {"LmDPTHSTAT"    ,0x7C, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnHOLDLVL"   ,0x7D, 8, MD(0)},
+   {"LmMnSATAFS"    ,0x7E, 8, MD(1)},
+   {"LmMnCMPLTSTAT" ,0x7F, 8, MD(0)|MD(1)},
+   {"LmPRMSTAT0"    ,0x80,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPRMSTAT1"    ,0x84,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmGPRMINT"     ,0x88, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnCURRSCB"   ,0x8A,16, MD(0)},
+   {"LmPRMICODE"    ,0x8C,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnRCVCNT"    ,0x90,16, MD(0)},
+   {"LmMnBUFSTAT"   ,0x92,16, MD(0)},
+   {"LmMnXMTHDRSIZE",0x92, 8, MD(1)},
+   {"LmMnXMTSIZE"   ,0x93, 8, MD(1)},
+   {"LmMnTGTXFRCNT" ,0x94,32, MD(0)},
+   {"LmMnEXPROFS"   ,0x98,32, MD(0)},
+   {"LmMnXMTROFS"   ,0x98,32, MD(1)},
+   {"LmMnRCVROFS"   ,0x9C,32, MD(0)},
+   {"LmCONCTL"      ,0xA0,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmBITLTIMER"   ,0xA2,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmWWNLOW"      ,0xA8,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmWWNHIGH"     ,0xAC,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnFRMERR"    ,0xB0,32, MD(0)},
+   {"LmMnFRMERREN"  ,0xB4,32, MD(0)},
+   {"LmAWTIMER"     ,0xB8,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmAWTCTL"      ,0xBA, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnHDRCMPS"   ,0xC0,32, MD(0)},
+   {"LmMnXMTSTAT"   ,0xC4, 8, MD(1)},
+   {"LmHWTSTATEN"   ,0xC5, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnRRDYRC"    ,0xC6, 8, MD(0)},
+   {"LmMnRRDYTC"    ,0xC6, 8, MD(1)},
+   {"LmHWTSTAT"     ,0xC7, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnDATABUFADR",0xC8,16, MD(0)|MD(1)},
+   {"LmDWSSTATUS"   ,0xCB, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmMnACTSTAT"   ,0xCE,16, MD(0)|MD(1)},
+   {"LmMnREQSCB"    ,0xD2,16, MD(0)|MD(1)},
+   {"LmXXXPRIM"     ,0xD4,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmRCVASTAT"    ,0xD9, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmINTDIS1"     ,0xDA, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPSTORESEL"   ,0xDB, 8, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPSTORE"      ,0xDC,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPRIMSTAT0EN" ,0xE0,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmPRIMSTAT1EN" ,0xE4,32, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"LmDONETCTL"    ,0xF2,16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4)|MD(5)|MD(6)|MD(7)},
+   {"", 0, 0, 0 }	/* Last entry should be NULL. */
+}; 
+
+static lseq_cio_regs_t	LSEQmOOBREGS[] =
+{
+   {"OOB_BFLTR"        ,0x100, 8, MD(5)},
+   {"OOB_INIT_MIN"     ,0x102,16, MD(5)},
+   {"OOB_INIT_MAX"     ,0x104,16, MD(5)},
+   {"OOB_INIT_NEG"     ,0x106,16, MD(5)},
+   {"OOB_SAS_MIN"      ,0x108,16, MD(5)},
+   {"OOB_SAS_MAX"      ,0x10A,16, MD(5)},
+   {"OOB_SAS_NEG"      ,0x10C,16, MD(5)},
+   {"OOB_WAKE_MIN"     ,0x10E,16, MD(5)},
+   {"OOB_WAKE_MAX"     ,0x110,16, MD(5)},
+   {"OOB_WAKE_NEG"     ,0x112,16, MD(5)},
+   {"OOB_IDLE_MAX"     ,0x114,16, MD(5)},
+   {"OOB_BURST_MAX"    ,0x116,16, MD(5)},
+   {"OOB_XMIT_BURST"   ,0x118, 8, MD(5)},
+   {"OOB_SEND_PAIRS"   ,0x119, 8, MD(5)},
+   {"OOB_INIT_IDLE"    ,0x11A, 8, MD(5)},
+   {"OOB_INIT_NEGO"    ,0x11C, 8, MD(5)},
+   {"OOB_SAS_IDLE"     ,0x11E, 8, MD(5)},
+   {"OOB_SAS_NEGO"     ,0x120, 8, MD(5)},
+   {"OOB_WAKE_IDLE"    ,0x122, 8, MD(5)},
+   {"OOB_WAKE_NEGO"    ,0x124, 8, MD(5)},
+   {"OOB_DATA_KBITS"   ,0x126, 8, MD(5)},
+   {"OOB_BURST_DATA"   ,0x128,32, MD(5)},
+   {"OOB_ALIGN_0_DATA" ,0x12C,32, MD(5)},
+   {"OOB_ALIGN_1_DATA" ,0x130,32, MD(5)},
+   {"OOB_SYNC_DATA"    ,0x134,32, MD(5)},
+   {"OOB_D10_2_DATA"   ,0x138,32, MD(5)},
+   {"OOB_PHY_RST_CNT"  ,0x13C,32, MD(5)},
+   {"OOB_SIG_GEN"      ,0x140, 8, MD(5)},
+   {"OOB_XMIT"         ,0x141, 8, MD(5)},
+   {"FUNCTION_MAKS"    ,0x142, 8, MD(5)},
+   {"OOB_MODE"         ,0x143, 8, MD(5)},
+   {"CURRENT_STATUS"   ,0x144, 8, MD(5)},
+   {"SPEED_MASK"       ,0x145, 8, MD(5)},
+   {"PRIM_COUNT"       ,0x146, 8, MD(5)},
+   {"OOB_SIGNALS"      ,0x148, 8, MD(5)},
+   {"OOB_DATA_DET"     ,0x149, 8, MD(5)},
+   {"OOB_TIME_OUT"     ,0x14C, 8, MD(5)},
+   {"OOB_TIMER_ENABLE" ,0x14D, 8, MD(5)},
+   {"OOB_STATUS"       ,0x14E, 8, MD(5)},
+   {"HOT_PLUG_DELAY"   ,0x150, 8, MD(5)},
+   {"RCD_DELAY"        ,0x151, 8, MD(5)},
+   {"COMSAS_TIMER"     ,0x152, 8, MD(5)},
+   {"SNTT_DELAY"       ,0x153, 8, MD(5)},
+   {"SPD_CHNG_DELAY"   ,0x154, 8, MD(5)},
+   {"SNLT_DELAY"       ,0x155, 8, MD(5)},
+   {"SNWT_DELAY"       ,0x156, 8, MD(5)},
+   {"ALIGN_DELAY"      ,0x157, 8, MD(5)},
+   {"INT_ENABLE_0"     ,0x158, 8, MD(5)},
+   {"INT_ENABLE_1"     ,0x159, 8, MD(5)},
+   {"INT_ENABLE_2"     ,0x15A, 8, MD(5)},
+   {"INT_ENABLE_3"     ,0x15B, 8, MD(5)},
+   {"OOB_TEST_REG"     ,0x15C, 8, MD(5)},
+   {"PHY_CONTROL_0"    ,0x160, 8, MD(5)},
+   {"PHY_CONTROL_1"    ,0x161, 8, MD(5)},
+   {"PHY_CONTROL_2"    ,0x162, 8, MD(5)},
+   {"PHY_CONTROL_3"    ,0x163, 8, MD(5)},
+   {"PHY_OOB_CAL_TX"   ,0x164, 8, MD(5)},
+   {"PHY_OOB_CAL_RX"   ,0x165, 8, MD(5)},
+   {"OOB_PHY_CAL_TX"   ,0x166, 8, MD(5)},
+   {"OOB_PHY_CAL_RX"   ,0x167, 8, MD(5)},
+   {"PHY_CONTROL_4"    ,0x168, 8, MD(5)},
+   {"PHY_TEST"         ,0x169, 8, MD(5)},
+   {"PHY_PWR_CTL"      ,0x16A, 8, MD(5)},
+   {"PHY_PWR_DELAY"    ,0x16B, 8, MD(5)},
+   {"OOB_SM_CON"       ,0x16C, 8, MD(5)},
+   {"ADDR_TRAP_1"      ,0x16D, 8, MD(5)},
+   {"ADDR_NEXT_1"      ,0x16E, 8, MD(5)},
+   {"NEXT_ST_1"        ,0x16F, 8, MD(5)},
+   {"OOB_SM_STATE"     ,0x170, 8, MD(5)},
+   {"ADDR_TRAP_2"      ,0x171, 8, MD(5)},
+   {"ADDR_NEXT_2"      ,0x172, 8, MD(5)},
+   {"NEXT_ST_2"        ,0x173, 8, MD(5)},
+   {"", 0, 0, 0 }	/* Last entry should be NULL. */
+};
+#endif /* ASD_DEBUG */
+
+/* Local functions' prototypes */
+static int	asd_hwi_verify_seqs(struct asd_softc *asd, uint8_t *code, 
+				    uint32_t code_size, uint8_t lseq_mask);
+static void	asd_hwi_init_cseq_scratch(struct asd_softc *asd);
+static void	asd_hwi_init_cseq_mip(struct asd_softc *asd);
+static void	asd_hwi_init_cseq_mdp(struct asd_softc *asd);
+static void	asd_hwi_init_cseq_cio(struct asd_softc *asd);
+static void 	asd_hwi_post_init_cseq(struct asd_softc *asd);
+static void	asd_hwi_init_scb_sites(struct asd_softc *asd);
+static void	asd_hwi_init_lseq_scratch(struct asd_softc *asd);
+static void	asd_hwi_init_lseq_mip(struct asd_softc *asd, u_int link_num);
+static void	asd_hwi_init_lseq_mdp(struct asd_softc *asd, u_int link_num);
+static void	asd_hwi_init_lseq_cio(struct asd_softc *asd, u_int link_num);
+static inline void
+		asd_swap_with_next_hscb(struct asd_softc *asd, struct scb *scb);
+      
+
+/* Sequencer misc. utilities. */
+static inline void asd_hwi_set_scbptr(struct asd_softc *asd, uint16_t val);
+static inline void asd_hwi_set_ddbptr(struct asd_softc *asd, uint16_t val);
+static inline void asd_hwi_set_scbsite_byte(struct asd_softc *asd,
+					    uint16_t site_offset, uint8_t val);
+static inline void asd_hwi_set_scbsite_word(struct asd_softc *asd,
+					    uint16_t site_offset, uint16_t val);
+static inline void asd_hwi_set_scbsite_dword(struct asd_softc *asd,
+					    uint16_t site_offset, uint32_t val);
+static inline uint8_t asd_hwi_get_scbsite_byte(struct asd_softc *asd, 
+					       uint16_t site_offset);
+static inline uint16_t asd_hwi_get_scbsite_word(struct asd_softc *asd, 
+					        uint16_t site_offset);
+static inline uint32_t asd_hwi_get_scbsite_dword(struct asd_softc *asd, 
+						 uint16_t site_offset);
+static inline void asd_hwi_set_ddbsite_byte(struct asd_softc *asd,
+					    uint16_t site_offset, uint8_t val);
+static inline void asd_hwi_set_ddbsite_word(struct asd_softc *asd,
+					    uint16_t site_offset, uint16_t val);
+static inline void asd_hwi_set_ddbsite_dword(struct asd_softc *asd,
+					    uint16_t site_offset, uint32_t val);
+static inline uint8_t asd_hwi_get_ddbsite_byte(struct asd_softc *asd, 
+					       uint16_t site_offset);
+static inline uint16_t asd_hwi_get_ddbsite_word(struct asd_softc *asd, 
+						uint16_t site_offset);
+static inline uint32_t asd_hwi_get_ddbsite_dword(struct asd_softc *asd, 
+						 uint16_t site_offset);
+
+
+/* 
+ * Function:
+ *	asd_hwi_set_scbptr()
+ *
+ * Description:
+ *      Program the SCBPTR. 
+ *	SCBPTR is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_scbptr(struct asd_softc *asd, uint16_t val)
+{
+	asd_hwi_swb_write_word(asd, CSEQm_CIO_REG(15, MnSCBPTR), val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_ddbptr()
+ *
+ * Description:
+ *      Program the DDBPTR. 
+ *	DDBPTR is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_ddbptr(struct asd_softc *asd, uint16_t val)
+{
+	asd_hwi_swb_write_word(asd, CSEQm_CIO_REG(15, MnDDBPTR), val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_scbsite_byte()
+ *
+ * Description:
+ *      Write an 8-bits value to the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_scbsite_byte(struct asd_softc *asd, uint16_t site_offset, 
+			 uint8_t val)
+{
+	asd_hwi_swb_write_byte(asd,
+			       CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset)),
+			       val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_scbsite_word()
+ *
+ * Description:
+ *      Write a 16-bits value to the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_scbsite_word(struct asd_softc *asd, uint16_t site_offset, 
+			 uint16_t val)
+{
+	asd_hwi_swb_write_word(asd,
+			       CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset)),
+			       val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_scbsite_dword()
+ *
+ * Description:
+ *      Write a 32-bits value to the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_scbsite_dword(struct asd_softc *asd, uint16_t site_offset, 
+			  uint32_t val)
+{
+	asd_hwi_swb_write_dword(asd,
+				CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset)),
+				val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_scbsite_byte()
+ *
+ * Description:
+ *      Read an 8-bits value from the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint8_t
+asd_hwi_get_scbsite_byte(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint8_t) asd_hwi_swb_read_byte(
+				asd,
+				CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_scbsite_word()
+ *
+ * Description:
+ *      Read a 16-bits value from the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint16_t
+asd_hwi_get_scbsite_word(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint16_t) asd_hwi_swb_read_word(
+				asd,
+				CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_scbsite_dword()
+ *
+ * Description:
+ *      Read a 32-bits value from the SCBSITE starting from the site_offset. 
+ *	SCBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint32_t
+asd_hwi_get_scbsite_dword(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint32_t) asd_hwi_swb_read_dword(
+				asd,
+				CSEQm_CIO_REG(15, (MnSCB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_ddbsite_byte()
+ *
+ * Description:
+ *      Write an 8-bits value to the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_ddbsite_byte(struct asd_softc *asd, uint16_t site_offset, 
+			 uint8_t val)
+{
+	asd_hwi_swb_write_byte(asd,
+			       CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset)),
+			       val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_ddbsite_word()
+ *
+ * Description:
+ *      Write a 16-bits value to the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_ddbsite_word(struct asd_softc *asd, uint16_t site_offset, 
+			 uint16_t val)
+{
+	asd_hwi_swb_write_word(asd,
+			       CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset)),
+			       val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_set_ddbsite_dword()
+ *
+ * Description:
+ *      Write a 32-bits value to the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline void
+asd_hwi_set_ddbsite_dword(struct asd_softc *asd, uint16_t site_offset, 
+			  uint32_t val)
+{
+	asd_hwi_swb_write_dword(asd,
+				CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset)),
+				val);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_ddbsite_byte()
+ *
+ * Description:
+ *      Read an 8-bits value from the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint8_t
+asd_hwi_get_ddbsite_byte(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint8_t) asd_hwi_swb_read_byte(
+				asd,
+			      	CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_ddbsite_word()
+ *
+ * Description:
+ *      Read a 16-bits value from the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint16_t
+asd_hwi_get_ddbsite_word(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint16_t) asd_hwi_swb_read_word(
+				asd,
+			      	CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_ddbsite_dword()
+ *
+ * Description:
+ *      Read a 32-bits value from the DDBSITE starting from the site_offset. 
+ *	DDBSITE is in Mode 15 of CSEQ CIO Bus Registers. 
+ */
+static inline uint32_t
+asd_hwi_get_ddbsite_dword(struct asd_softc *asd, uint16_t site_offset)
+{
+	return ((uint32_t) asd_hwi_swb_read_dword(
+				asd,
+			      	CSEQm_CIO_REG(15, (MnDDB_SITE + site_offset))));
+}
+
+/* 
+ * Function:
+ *	asd_hwi_pause_cseq()
+ *
+ * Description:
+ *      Pause the Central Sequencer. 
+ */
+int
+asd_hwi_pause_cseq(struct asd_softc *asd)
+{
+	uint32_t	arp2ctl_reg;
+	uint32_t	arp2ctl;
+	uint32_t	timer_tick;
+
+	timer_tick = ASD_REG_TIMEOUT_TICK;
+	arp2ctl_reg = (uint32_t) CARP2CTL;
+	arp2ctl = asd_hwi_swb_read_dword(asd, arp2ctl_reg);
+	
+	/* Check if the CSEQ is paused. */
+	if (!(arp2ctl & PAUSED)) {
+		/* CSEQ is running. Pause it. */
+		asd_hwi_swb_write_dword(asd, arp2ctl_reg, (arp2ctl | EPAUSE));
+		/* Verify that the CSEQ is paused. */
+		do { 
+			arp2ctl = asd_hwi_swb_read_dword(asd, arp2ctl_reg);
+			if (!(arp2ctl & PAUSED)) {
+				timer_tick--;		
+				asd_delay(ASD_DELAY_COUNT);	
+			} else
+				break;
+		} while (timer_tick != 0);					
+	}		 		
+	if (timer_tick == 0) {
+		asd_log(ASD_DBG_ERROR, "Timeout expired when pausing CSEQ.\n");
+		ASD_DUMP_REG(CARP2CTL);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_unpause_cseq()
+ *
+ * Description:
+ *      Unpause the Central Sequencer. 
+ */
+int
+asd_hwi_unpause_cseq(struct asd_softc *asd)
+{
+	uint32_t	arp2ctl;
+	uint32_t	timer_tick;
+
+	timer_tick = ASD_REG_TIMEOUT_TICK;
+	arp2ctl = asd_hwi_swb_read_dword(asd, CARP2CTL);
+
+	/* Check if the CSEQ is paused. */
+	if (arp2ctl & PAUSED) {
+		/* CSEQ is currently paused. Unpause it. */
+		asd_hwi_swb_write_dword(asd, CARP2CTL, (arp2ctl & ~EPAUSE));
+		/* Verify that the CSEQ is unpaused. */
+		do { 
+			arp2ctl = asd_hwi_swb_read_dword(asd, CARP2CTL);
+			if (arp2ctl & PAUSED) {
+				timer_tick--;		
+				asd_delay(ASD_DELAY_COUNT);	
+			} else
+				break;
+		} while (timer_tick != 0);	
+	}
+	if (timer_tick == 0) {
+		asd_log(ASD_DBG_ERROR, "Timeout expired when unpausing "
+					"CSEQ.\n");
+		ASD_DUMP_REG(CARP2CTL);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_pause_lseq()
+ *
+ * Description:
+ *      Pause the Central Sequencer. 
+ */
+int	
+asd_hwi_pause_lseq(struct asd_softc *asd, uint8_t lseq_mask)
+{
+	uint32_t	arp2ctl;
+	uint32_t	timer_tick;
+	uint8_t		temp_lseq_mask;	
+	uint8_t		phy_id;
+
+	phy_id = 0;
+	temp_lseq_mask = lseq_mask;
+	timer_tick = ASD_REG_TIMEOUT_TICK;	
+	while (temp_lseq_mask) {
+		do {
+			if (temp_lseq_mask & (1 << phy_id))  
+				break;
+			else 
+				phy_id++;		
+		} while (phy_id < asd->hw_profile.max_phys);
+		
+		arp2ctl = asd_hwi_swb_read_dword(asd, LmARP2CTL(phy_id));
+		
+		/* Check if the CSEQ is paused. */
+		if (!(arp2ctl & PAUSED)) {
+			/*
+		 	 * CSEQ is running. Pause it.
+		 	 */
+			asd_hwi_swb_write_dword(asd, LmARP2CTL(phy_id), 
+						(arp2ctl | EPAUSE));
+			/* Verify that the CSEQ is paused. */
+			do { 
+				arp2ctl = asd_hwi_swb_read_dword(asd, 
+							LmARP2CTL(phy_id));
+				if (!(arp2ctl & PAUSED)) {
+					timer_tick--;		
+					asd_delay(ASD_DELAY_COUNT);	
+				} else
+					break;
+			} while (timer_tick != 0);				
+		}
+		if (timer_tick == 0) {
+			asd_log(ASD_DBG_ERROR, "Timeout expired when pausing "
+				"LSEQ %d.\n", phy_id);
+			return (-1);
+		} 	
+		temp_lseq_mask &= (~(1 << phy_id));		
+		phy_id++;
+	}
+
+	return (0);						 
+}
+
+/* 
+ * Function:
+ *	asd_hwi_unpause_cseq()
+ *
+ * Description:
+ *      Unpause the requested Link Sequencer(s). 
+ */
+int
+asd_hwi_unpause_lseq(struct asd_softc *asd, uint8_t lseq_mask)
+{
+	uint32_t	arp2ctl;
+	uint32_t	timer_tick;
+	uint8_t		temp_lseq_mask;	
+	uint8_t		phy_id;
+	
+	phy_id = 0;
+	temp_lseq_mask = lseq_mask;
+	timer_tick = ASD_REG_TIMEOUT_TICK;	
+	while (temp_lseq_mask) {
+		do {
+			if (temp_lseq_mask & (1 << phy_id))  
+				break;
+			else 
+				phy_id++;		
+		} while (phy_id < asd->hw_profile.max_phys);
+		
+		arp2ctl = asd_hwi_swb_read_dword(asd, LmARP2CTL(phy_id));	
+
+		/* Check if the LSEQ is paused. */
+		if (arp2ctl & PAUSED) {
+			/* Unpause the LSEQ. */
+			asd_hwi_swb_write_dword(asd, LmARP2CTL(phy_id), 
+					       (arp2ctl & ~EPAUSE));
+			do { 
+				arp2ctl = asd_hwi_swb_read_dword(asd, 
+							LmARP2CTL(phy_id));
+				if (arp2ctl & PAUSED) {
+					timer_tick--;		
+					asd_delay(ASD_DELAY_COUNT);	
+				} else
+					break;
+			} while (timer_tick != 0);	
+
+		}
+		if (timer_tick == 0) {
+			asd_log(ASD_DBG_ERROR, "Timeout expired when unpausing "
+					"LSEQ %d.\n", phy_id);
+			return (-1);
+		}
+		temp_lseq_mask &= (~(1 << phy_id));		
+		phy_id++;
+	}
+
+	return (0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_download_seqs()
+ *
+ * Description:
+ *      Setup the Central and Link Sequencers.
+ *	Download the sequencers microcode.  
+ */
+int	
+asd_hwi_download_seqs(struct asd_softc *asd)
+{
+	int 	error;
+
+#define ASD_SET_SEQ_VER(asd, seq)	\
+	(ASD_USE_A1_CODE(asd) == 1 ? ASD_SEQ_VER(seq, a1) :		\
+	 ASD_SEQ_VER(seq, b0))
+
+#define ASD_SEQ_SIZE(asd, seq)		\
+	(ASD_USE_A1_CODE(asd) == 1 ? sizeof(ASD_SEQ_VER(seq, a1)) :	\
+	 sizeof(ASD_SEQ_VER(seq, b0)))
+
+	/* Download the Central Sequencer code. */
+	error = ASD_HWI_DOWNLOAD_SEQS(asd, ASD_SET_SEQ_VER(asd, Cs), 
+				      ASD_SEQ_SIZE(asd, Cs),
+				      0);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "CSEQ download failed.\n");
+		goto exit;
+	}
+	
+	/* 
+	 * Download the Link Sequencers code. All of the Link Sequencers 
+	 * microcode can be downloaded at the same time.
+	 */
+	error = ASD_HWI_DOWNLOAD_SEQS(asd, ASD_SET_SEQ_VER(asd, Ls), 
+				      ASD_SEQ_SIZE(asd, Ls),
+				      asd->hw_profile.enabled_phys); 
+	if (error != 0) {
+		uint8_t		i;
+		
+		/*
+		 * If we failed to load the LSEQ all at a time. 
+		 * Try to load them one at a time.
+		 */		 
+		for (i = 0; i < asd->hw_profile.max_phys; i++) {
+			error = ASD_HWI_DOWNLOAD_SEQS(
+					asd,
+					ASD_SET_SEQ_VER(asd, Ls),
+					ASD_SEQ_SIZE(asd, Ls),
+				      	(1 << i));
+			if (error != 0)
+				break;
+
+			asd->phy_list[i]->state = ASD_PHY_OFFLINE;
+		}
+		if (error) {
+			asd_log(ASD_DBG_ERROR, "LSEQs download failed.\n");
+			goto exit;
+		}
+	}
+
+exit:
+	return (error);		  
+}
+
+#if ASD_DMA_DOWNLOAD_SEQS
+/* 
+ * Function:
+ *	asd_hwi_dma_load_seqs()
+ *
+ * Description:
+ *	Download the sequencers using Host Overlay DMA mode.  
+ */
+int
+asd_hwi_dma_load_seqs(struct asd_softc *asd, uint8_t *code,
+		      uint32_t code_size, uint8_t lseq_mask)
+{
+	struct map_node	 buf_map;	
+	bus_dma_tag_t	 buf_dmat;
+	uint32_t	 buf_size;
+	uint32_t	 comstaten_val;
+	uint32_t	 instr_size;
+	uint32_t	 timer_tick;
+	uint16_t	 ovlyaddr;
+	uint16_t	 ovlydmactl_lo;
+	uint16_t	 ovlydmactl_hi;	
+	uint8_t		*instr;	
+	uint8_t		 nseg;
+	u_int		 i;
+	int		 error;
+	
+	error = 0;
+	if (code_size % 4) {
+		/*
+		 * For PIO mode, sequencer code size must be multiple of dword.
+		 */
+		asd_log(ASD_DBG_ERROR, "SEQ code size (%d) not multiple of "
+			"dword. \n", code_size);
+		return (-1);	
+	}
+	
+	/* Save the Interrupt Mask register value. */
+	comstaten_val = asd_read_dword(asd, COMSTATEN);
+	/* 
+	 * Disable Device Communication Status interrupt and clear any 
+	 * pending interrupts.
+	 */
+	asd_write_dword(asd, COMSTATEN, 0x0);
+	asd_write_dword(asd, COMSTAT, COMSTAT_MASK);
+		
+	/* Disable CHIM interrupt and clear any pending interrupts. */
+	asd_write_dword(asd, CHIMINTEN, RST_CHIMINTEN);
+	asd_write_dword(asd, CHIMINT, CHIMINT_MASK);
+	
+	/* 
+	 * Check the limit of HW DMA transfer size.
+	 * Limit the Overlay DMA transter size to code_size at a time.
+	 */ 
+	if ((asd->hw_profile.max_scbs * ASD_SCB_SIZE) >= code_size)
+		buf_size = code_size;
+	else
+		buf_size = (asd->hw_profile.max_scbs * ASD_SCB_SIZE);	
+
+	/* Allocate a dma tag for buffer to dma-ing the instruction codes. */
+	if (asd_dma_tag_create(asd, 4, buf_size, GFP_ATOMIC, &buf_dmat) != 0)
+		return (-ENOMEM);
+
+	if (asd_dmamem_alloc(asd, buf_dmat, (void **) &buf_map.vaddr,
+			     GFP_ATOMIC, &buf_map.dmamap,
+			     &buf_map.busaddr) != 0) {
+		asd_dma_tag_destroy(asd, buf_dmat);
+		return (-ENOMEM);
+	}
+
+	instr = (uint8_t *) buf_map.vaddr;
+	
+	/* Calculate number of DMA segments needed to transfer the code. */
+	nseg = (code_size + buf_size - 1) / buf_size;
+	instr_size = buf_size;
+	ovlydmactl_lo = ovlydmactl_hi = 0;
+	ovlyaddr = 0;
+	
+	asd_log(ASD_DBG_INFO, "Downloading %s (Overlay DMA Mode) ...\n", 
+		(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+	
+	for (i = 0; i < nseg; ) {
+		memcpy(instr, &code[ovlyaddr], instr_size);
+		
+		/* Program the DMA download size. */
+		asd_write_dword(asd, OVLYDMACNT, instr_size);
+		/* 
+		 * Program the 64-bit DMA address, OVLYDMAADDR register is 
+		 * 64-bit. 
+		 */
+		asd_write_dword(asd, OVLYDMAADR0, 
+				ASD_GET_PADR(buf_map.busaddr));
+		asd_write_dword(asd, OVLYDMAADR1,
+				ASD_GET_PUADR(buf_map.busaddr));
+		
+		/* lseq_mask tells us which sequencer(s) the code is for. */
+		if (lseq_mask == 0)
+			ovlydmactl_lo = (uint16_t) OVLYCSEQ;
+		else
+			ovlydmactl_lo = (uint16_t) (((uint16_t)lseq_mask) << 8);
+		/* 
+		 * Program the OVLYADR. It increments for each dword 
+		 * instruction overlaid. 
+		 */
+		ovlydmactl_hi = (ovlyaddr / 4);
+		/*
+		 * Reset the Overlay DMA counter and buffer pointers to zero.
+		 * Also, enabled the Overlay DMA engine.
+		 */
+	       ovlydmactl_lo |= (RESETOVLYDMA | STARTOVLYDMA | OVLYHALTERR);
+		/* 
+		 * Start the DMA. We need to set the higher two bytes of 
+		 * OVLYDMACTL register before programming the lower two bytes 
+		 * as the lowest byte of OVLYDMACTL register contains 
+		 * STARTOVLYDMA bit which once is written, will start the DMA.
+		 */
+		asd_write_word(asd, (OVLYDMACTL+2), ovlydmactl_hi);
+		asd_write_word(asd, OVLYDMACTL, ovlydmactl_lo);		
+			       
+	       	timer_tick = ASD_REG_TIMEOUT_TICK;
+		do {
+			/*
+			 * Check if the Overlay DMA is still active. 
+			 * It will get reset once the transfer is done.
+			 */
+			if (asd_read_dword(asd, OVLYDMACTL) & OVLYDMAACT) {
+				timer_tick--;		
+				asd_delay(ASD_DELAY_COUNT);
+			} else
+				break;
+		} while (timer_tick != 0);
+		
+		/*
+		 * Check if the DMA transfer has completed successfully.
+		 */
+		if ((timer_tick != 0) && 
+		    (asd_read_dword(asd, COMSTAT) & OVLYDMADONE) &&
+		    (!(asd_read_dword(asd, COMSTAT) & OVLYERR)) &&
+		    (!(asd_read_dword(asd, CHIMINT) & DEVEXCEPT_MASK))) {
+			/* DMA transfer completed successfully. */
+			ovlyaddr += instr_size;
+			/*
+			 * Sanity check when we doing the last segment, 
+			 * make sure that we only transfer the remaing code
+			 * size.
+			 */ 
+			if (++i == (nseg - 1))
+				instr_size = code_size - (i * instr_size);
+		} else {
+			/* DMA transfer failed. */
+			asd_log(ASD_DBG_ERROR, "%s download failed.\n",
+				(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+			error = -1;
+			goto exit;
+		}
+	} 
+
+	/* Restore the Interrupt Mask. */
+	asd_write_dword(asd, COMSTATEN, comstaten_val);	
+
+	/* Verify that the sequencer is downloaded properly. */
+	asd_log(ASD_DBG_INFO, "Verifying %s ...\n", 
+		(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+	if (asd_hwi_verify_seqs(asd, code, code_size, lseq_mask) != 0) {
+		asd_log(ASD_DBG_ERROR, "%s verify failed.\n",
+			(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+		error = -1;
+		goto exit;
+	}
+	asd_log(ASD_DBG_INFO, "%s verified successfully.\n", 
+		(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+
+exit:
+	asd_free_dma_mem(asd, buf_dmat, &buf_map);
+	
+	return (error);
+}
+
+#else
+
+/* 
+ * Function:
+ *	asd_hwi_pio_load_seqs()
+ *
+ * Description:
+ *	Download the sequencers using PIO mode.  
+ */
+int
+asd_hwi_pio_load_seqs(struct asd_softc *asd, uint8_t*code,
+		      uint32_t code_size, uint8_t lseq_mask)
+{
+	uint32_t	reg_val;
+	uint32_t	instr;
+	uint32_t	i;
+	
+	if (code_size % 4) {
+		/*
+		 * For PIO mode, sequencer code size must be multiple of dword.
+		 */
+		asd_log(ASD_DBG_ERROR, "SEQ code size (%d) not multiple of "
+			"dword. \n", code_size);	
+		return (-1);
+	}
+	/* Set to PIO Mode */
+	reg_val = PIOCMODE;
+		
+	/* lseq_mask tells us which sequencer(s) the code is for. */
+	if (lseq_mask != 0)
+		reg_val |= (uint32_t) (((uint16_t) lseq_mask) << 8);
+	else
+		reg_val |= OVLYCSEQ;
+
+	/*
+	 * Progam the sequencer RAM address, which sequencer(s) to load and the
+	 * download mode.
+	 */	
+	/* Program the download size. */
+	asd_write_dword(asd, OVLYDMACNT, code_size);
+	asd_write_dword(asd, OVLYDMACTL, reg_val); 	
+	
+	asd_log(ASD_DBG_INFO, "Downloading %s (PIO Mode) ...\n", 
+		(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+	/* Download the instr 4 bytes a time. */
+	for (i = 0; i < (code_size/4); i++) {
+		instr = *(uint32_t *) &code[i*4];
+		/* The sequencer is little-endian. */
+		instr = asd_htole32(instr);
+		asd_write_dword(asd, SPIODATA, instr);	
+	}
+	
+	/*
+	 * TBRV : Check Device Exception Error ??
+	 */
+	 
+	/* Clear the PIO mode bit and enabled Overlay Halt Error. */
+	reg_val = (reg_val & ~PIOCMODE) | OVLYHALTERR;
+	asd_write_dword(asd, OVLYDMACTL, reg_val);
+	
+	/* Verify that the sequencer is downloaded properly. */
+	asd_log(ASD_DBG_INFO,
+		"Verifying %s ...\n", (lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+	if (asd_hwi_verify_seqs(asd, code, code_size, lseq_mask) != 0) {
+		asd_log(ASD_DBG_ERROR, "%s verify failed.\n",
+			(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+		return (-1);
+	}
+	asd_log(ASD_DBG_INFO, "%s verified successfully.\n", 
+		(lseq_mask == 0 ? "CSEQ" : "LSEQ"));
+
+	return (0);
+}
+#endif
+
+/* 
+ * Function:
+ *	asd_hwi_verify_seqs()
+ *
+ * Description:
+ *	Verify the downloaded sequencer.  
+ */
+static int
+asd_hwi_verify_seqs(struct asd_softc *asd, uint8_t *code, 
+		    uint32_t code_size, uint8_t lseq_mask)
+{
+	int		error;
+	uint32_t	base_addr;
+	uint32_t	page_offset;
+	uint32_t	instr;
+	uint32_t	i;
+	uint8_t		temp_lseq_mask;	
+	uint8_t		phy_id;
+
+	error = 0;
+	phy_id = 0;
+	temp_lseq_mask = lseq_mask;	
+	do {	
+		if (temp_lseq_mask == 0) {
+			/* Get CSEQ Instruction RAM base addr. */
+			base_addr = CSEQ_RAM_REG_BASE_ADR;
+		} else {
+			for ( ; phy_id < asd->hw_profile.max_phys; phy_id++) {
+				if (temp_lseq_mask & (1 << phy_id)) {
+					temp_lseq_mask &= ~(1 << phy_id);
+					break;
+				}
+			}
+			
+			/* Get the LmSEQ Instruction RAM base addr. */
+			base_addr = (uint32_t) LmSEQRAM(phy_id);;
+			/*
+			 * Set the LmSEQ Instruction Memory Page to 0.
+			 * LmSEQRAM is mapped 4KB in internal memory space.
+			 */
+			asd_hwi_swb_write_dword(asd, LmBISTCTL1(phy_id), 0);
+		}	
+		
+		page_offset = 0;
+		for (i = 0; i < (code_size/4); i++) {
+			if ((base_addr != CSEQ_RAM_REG_BASE_ADR) && (i > 0) &&
+			    ((i % 1024) == 0)) {
+				/*
+			 	 * For LSEQ, we need to adjust the LmSEQ 
+				 * Instruction Memory page to the next 4KB page
+				 * once we past the page boundary.
+				 */
+				asd_hwi_swb_write_dword(asd, 
+							LmBISTCTL1(phy_id), 
+							((i / 1024) <<
+							 LmRAMPAGE_LSHIFT));
+				page_offset = 0;		 
+			}				  
+		
+			/* 
+		 	 * Compare dword at a time since Instruction RAM page is
+		 	 * dword accessible read only.
+		 	 */ 
+			instr = asd_htole32(*(uint32_t *)&code[i*4]);
+			if (instr != asd_hwi_swb_read_dword(asd, 
+					base_addr + page_offset)) {
+				/* Code doesn't match. */
+				error = -1;
+				break;
+			}
+			page_offset += 4;
+		}
+		/* Done verifing the sequencer(s). */
+		if (temp_lseq_mask == 0)
+			break;
+	} while (error == 0);
+
+	return (error);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_setup_seqs()
+ *
+ * Description:
+ *	Setup and initialize Central and Link sequencers.  
+ */
+void
+asd_hwi_setup_seqs(struct asd_softc *asd)
+{
+	int 		link_num;
+	uint8_t		enabled_phys;
+
+#define ASD_SET_INT_VEC(asd, vec)	\
+	(ASD_USE_A1_CODE(asd) == 1 ? ASD_INT_VEC(vec, A1) :	\
+	 ASD_INT_VEC(vec, B0))
+
+	/* Initialize CSEQ Scratch RAM registers. */
+	asd_hwi_init_cseq_scratch(asd);
+	
+	/* Initialize LmSEQ Scratch RAM registers. */
+	asd_hwi_init_lseq_scratch(asd);
+
+	/* Initialize SCB sites. */
+	asd_hwi_init_scb_sites(asd);
+
+	/* Initialize CSEQ CIO registers. */
+	asd_hwi_init_cseq_cio(asd);
+		
+	/* Initialize LmSEQ CIO registers. */
+	link_num = 0;
+	enabled_phys = asd->hw_profile.enabled_phys;
+
+	while (enabled_phys != 0) { 
+		for ( ; link_num < asd->hw_profile.max_phys; link_num++) {
+			if (enabled_phys & (1 << link_num)) {
+				enabled_phys &= ~(1 << link_num);
+				break;
+			} 
+		}
+		
+		asd_hwi_init_lseq_cio(asd, link_num);
+	}
+
+	asd_hwi_post_init_cseq(asd);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_cseq_init_scratch()
+ *
+ * Description:
+ *	Setup and initialize Central sequencers. Initialiaze the mode 
+ *	independent and dependent scratch page to the default settings.
+ */
+static void
+asd_hwi_init_cseq_scratch(struct asd_softc *asd)
+{
+	/* Reset SCBPRO count register. */
+	asd_write_dword(asd, SCBPRO, asd->qinfifonext);
+
+	/* Initialize CSEQ Mode Independent Page. */
+	asd_hwi_init_cseq_mip(asd);
+	/* Initialize CSEQ Mode Dependent Page. */
+	asd_hwi_init_cseq_mdp(asd);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_cseq_mip()
+ *
+ * Description:
+ *	Initialize CSEQ Mode Independent Pages 4-7.	 
+ */
+static void
+asd_hwi_init_cseq_mip(struct asd_softc *asd)
+{
+	uint8_t		free_scb_mask;
+	u_int		i;
+	
+	/* CSEQ Mode Independent , page 4 setup. */
+	asd_hwi_swb_write_word(asd, CSEQ_Q_EXE_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_EXE_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_DONE_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_DONE_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_SEND_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_SEND_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_DMA2CHIM_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_DMA2CHIM_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_COPY_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_COPY_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_REG0, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_REG1, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_REG2, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_REG3, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_LINK_CTL_Q_MAP, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_SCRATCH_FLAGS, 0x0);
+
+	/* CSEQ Mode Independent , page 5 setup. */
+	/* Calculate the free scb mask. */
+	free_scb_mask = (uint8_t) ((~(((asd->hw_profile.max_scbs * 
+					ASD_SCB_SIZE) / 128) - 1)) >> 8);
+
+	asd_hwi_swb_write_byte(asd, CSEQ_FREE_SCB_MASK, free_scb_mask);
+			       		       
+	/* 
+	 * Fill BUILTIN_FREE_SCB_HEAD with the first scb no. and 
+	 * BUILTIN_FREE_SCB_TAIL with the last scb no.
+	 */
+	asd_hwi_swb_write_word(asd, CSEQ_BUILTIN_FREE_SCB_HEAD, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_BUILTIN_FREE_SCB_TAIL,
+			      ((((ASD_MAX_SCB_SITES-1) & 0xFF) == 0xFF) ?
+			      (ASD_MAX_SCB_SITES-2) : (ASD_MAX_SCB_SITES-1))); 
+		
+	/* Extended SCB sites are not being used now. */
+	asd_hwi_swb_write_word(asd, CSEQ_EXTNDED_FREE_SCB_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_EXTNDED_FREE_SCB_TAIL, 0xFFFF);
+
+	/* CSEQ Mode Independent , page 6 setup. */
+	asd_hwi_swb_write_word(asd, CSEQ_INT_ROUT_RET_ADDR0, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_INT_ROUT_RET_ADDR1, 0x0);	
+	asd_hwi_swb_write_word(asd, CSEQ_INT_ROUT_SCBPTR, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_INT_ROUT_MODE, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_ISR_SCRATCH_FLAGS, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_ISR_SAVE_SINDEX, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_ISR_SAVE_DINDEX, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_SLS_SAVE_ACCUM, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_SLS_SAVE_SINDEX, 0x0);
+	
+	/* CSEQ Mode Independent , page 7 setup. */
+	for (i = 0; i < 8; i = i+4) {
+		asd_hwi_swb_write_dword(asd,
+				       (CSEQ_EMPTY_REQ_QUEUE + i),
+					0x0);
+		asd_hwi_swb_write_dword(asd,
+				       (CSEQ_EMPTY_REQ_COUNT + i),
+					0x0);	
+	}
+	/* Initialize Q_EMPTY_HEAD and Q_EMPTY_TAIL. */
+	asd_hwi_swb_write_word(asd, CSEQ_Q_EMPTY_HEAD, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_Q_EMPTY_TAIL, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_NEED_EMPTY_SCB, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_EMPTY_REQ_HEAD, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_EMPTY_REQ_TAIL, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_EMPTY_SCB_OFFSET, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_PRIMITIVE_DATA, 0x0);
+	asd_hwi_swb_write_dword(asd, CSEQ_TIMEOUT_CONSTANT, 0x0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_cseq_mdp()
+ *
+ * Description:
+ *	Initialize CSEQ Mode Dependent Pages.	 
+ */
+static void	
+asd_hwi_init_cseq_mdp(struct asd_softc *asd)
+{
+	u_int	i;
+	u_int	mode_offset; 
+	
+	mode_offset = CSEQ_PAGE_SIZE * 2;
+	
+	/* CSEQ Mode Dependent 0-7, page 0 setup. */
+	for (i = 0; i < 8; i++) {
+		asd_hwi_swb_write_word(asd, ((i * mode_offset) + 
+				       CSEQ_LRM_SAVE_SINDEX), 0x0);
+		asd_hwi_swb_write_word(asd, ((i * mode_offset) + 
+				       CSEQ_LRM_SAVE_SCBPTR), 0x0);
+		asd_hwi_swb_write_word(asd, ((i * mode_offset) + 
+				       CSEQ_Q_LINK_HEAD), 0xFFFF);
+		asd_hwi_swb_write_word(asd, ((i * mode_offset) +
+				       CSEQ_Q_LINK_TAIL), 0xFFFF);
+	}
+	
+	/* CSEQ Mode Dependent 0-7, page 1 and 2 shall be ignored. */
+
+	/* CSEQ Mode Dependent 8, page 0 setup. */
+	asd_hwi_swb_write_word(asd, CSEQ_RET_ADDR, 0xFFFF);
+	asd_hwi_swb_write_word(asd, CSEQ_RET_SCBPTR, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_SAVE_SCBPTR, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_EMPTY_TRANS_CTX, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_RESP_LEN, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_TMF_SCBPTR, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_GLOBAL_PREV_SCB, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_GLOBAL_HEAD, 0x0);
+	asd_hwi_swb_write_byte(asd, CSEQ_TMF_OPCODE, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_CLEAR_LU_HEAD, 0x0);
+	asd_hwi_swb_write_word(asd, CSEQ_FIRST_INV_SCB_SITE,
+			       ASD_MAX_SCB_SITES);
+	asd_hwi_swb_write_word(asd, CSEQ_FIRST_INV_DDB_SITE, ASD_MAX_DDBS);
+
+	/* CSEQ Mode Dependent 8, page 1 setup. */
+	asd_hwi_swb_write_dword(asd, CSEQ_LUN_TO_CLEAR, 0x0);
+	asd_hwi_swb_write_dword(asd, (CSEQ_LUN_TO_CLEAR + 4), 0x0);
+	asd_hwi_swb_write_dword(asd, CSEQ_LUN_TO_CHECK, 0x0);
+	asd_hwi_swb_write_dword(asd, (CSEQ_LUN_TO_CHECK + 4), 0x0);
+
+	/* CSEQ Mode Dependent 8, page 2 setup. */
+	/* Advertise the first SCB site address to the sequencer. */
+	asd_hwi_set_hw_addr(asd, CSEQ_Q_NEW_POINTER,
+			    asd->next_queued_hscb_busaddr);
+
+	/* Advertise the first Done List address to the sequencer.*/
+	asd_hwi_set_hw_addr(asd, CSEQ_Q_DONE_BASE,
+			    asd->shared_data_map.busaddr);
+
+	/* 
+	 * Initialize the Q_DONE_POINTER with the least significant bytes of 
+	 * the first Done List address.
+	 */
+	asd_hwi_swb_write_dword(asd, CSEQ_Q_DONE_POINTER,
+				ASD_GET_PADR(asd->shared_data_map.busaddr));
+
+	asd_hwi_swb_write_byte(asd, CSEQ_Q_DONE_PASS, ASD_QDONE_PASS_DEF);
+	
+	/* CSEQ Mode Dependent 8, page 3 shall be ignored. */
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_cseq_cio()
+ *
+ * Description:
+ *	Initialize CSEQ CIO Registers.	 
+ */
+static void
+asd_hwi_init_cseq_cio(struct asd_softc *asd)
+{
+	uint8_t		dl_bits;
+	u_int		i;
+	
+	/* Enabled ARP2HALTC (ARP2 Halted from Halt Code Write). */
+	asd_hwi_swb_write_byte(asd, CARP2INTEN, EN_ARP2HALTC);
+
+	/* Initialize CSEQ Scratch Page to 0x04. */
+	asd_hwi_swb_write_byte(asd, CSCRATCHPAGE, 0x04);
+	
+	/* Initialize CSEQ Mode[0-8] Dependent registers. */	
+	for (i = 0; i < 9; i++) 
+		/* Initialize Scratch Page to 0. */
+		asd_hwi_swb_write_byte(asd, CMnSCRATCHPAGE(i), 0x0);
+	
+	/* 
+	 * CSEQCOMINTEN, CSEQDLCTL, and CSEQDLOFFS are in Mode 8.
+	 */
+	asd_hwi_swb_write_byte(asd, CSEQCOMINTEN, 0x0);
+	
+	/*
+	 * Get the DONELISTSIZE bits by calculate number of DL entry 
+	 * available and set the value to the DoneList Control register.
+	 */
+	dl_bits = ffs(asd->dl_wrap_mask + 1);
+	
+	/* Minimum amount of done lists is 4. Reduce dl_bits by 3. */
+	dl_bits -= 3;
+	asd_hwi_swb_write_byte(asd, CSEQDLCTL, 
+		((asd_hwi_swb_read_byte(asd, CSEQDLCTL) & 
+		 ~DONELISTSIZE_MASK) | dl_bits));
+	
+	asd_hwi_swb_write_byte(asd, CSEQDLOFFS, 0);
+	asd_hwi_swb_write_byte(asd, (CSEQDLOFFS+1), 0);
+
+	/* Reset the Producer mailbox. */
+	asd_write_dword(asd, SCBPRO, 0x0);				
+
+	/* Intialize CSEQ Mode 11 Interrupt Vectors. */
+	asd_hwi_swb_write_word(asd, CM11INTVEC0, 
+			      ((ASD_SET_INT_VEC(asd, CSEQ_INT_VEC0)) / 4));
+	asd_hwi_swb_write_word(asd, CM11INTVEC1,
+			      ((ASD_SET_INT_VEC(asd, CSEQ_INT_VEC1)) / 4));
+	asd_hwi_swb_write_word(asd, CM11INTVEC2,
+			      ((ASD_SET_INT_VEC(asd, CSEQ_INT_VEC2)) / 4));
+
+	/* Reset the ARP2 Program Count. */
+	asd_hwi_swb_write_word(asd, CPRGMCNT, (CSEQ_IDLE_LOOP_ENTRY / 4));
+
+	for (i = 0; i < 8; i++) {
+		/* Intialize Mode n Link m Interrupt Enable. */
+		asd_hwi_swb_write_dword(asd, CMnINTEN(i), EN_CMnRSPMBXF);
+		/* Initialize Mode n Request Mailbox. */
+		asd_hwi_swb_write_dword(asd, CMnREQMBX(i), 0x0);	
+	}
+
+	/* Reset the Consumer mailbox. */
+	asd_hwi_swb_write_dword(asd, CSEQCON, 0x0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_scb_sites()
+ *
+ * Description:
+ *	Initialize HW SCB sites.	 
+ */
+static void
+asd_hwi_init_scb_sites(struct asd_softc *asd)
+{
+	uint16_t	site_no;
+	uint16_t	next_site_no;
+	u_int		i;
+ 
+	for (site_no = 0; site_no < ASD_MAX_SCB_SITES; site_no++) {
+		/* 
+		 * Adjust to the SCB site that we want to access in command
+		 * context memory.
+		 */	 
+		asd_hwi_set_scbptr(asd, site_no);
+		
+		/* Initialize all fields in the SCB site to 0. */
+		for (i = 0; i < ASD_SCB_SIZE; i += 4)
+			asd_hwi_set_scbsite_dword(asd, i, 0x0);
+
+		/*
+		 * Workaround needed by SEQ to fix a SATA issue is to skip
+	      	 * including scb sites that ended with FFh in the sequencer
+		 * free list.
+		 */
+		if ((site_no & 0xF0FF) == 0x00FF)
+			continue;
+		
+		/*
+		 * For every SCB site, we need to initialize the following 
+		 * fields: Q_NEXT, SCB_OPCODE, SCB_FLAGS, and SG Element Flag.
+		 */
+
+		next_site_no = ((((site_no+1) & 0xF0FF) == 0x00FF) ?
+				  (site_no+2) : (site_no+1));
+
+		/* 
+	 	 * Set the Q_NEXT of last usable SCB site to 0xFFFF.
+	 	 */
+		if (next_site_no >= ASD_MAX_SCB_SITES)
+			next_site_no = 0xFFFF;
+
+		/* 
+		 * Initialize Q_NEXT field to point to the next SCB site.
+		 */
+		asd_hwi_set_scbsite_word(asd, SCB_SITE_Q_NEXT, next_site_no);
+		
+		/* Initialize SCB Site Opcode field. */
+		asd_hwi_set_scbsite_byte(asd, 
+					 offsetof(struct hscb_header, opcode),
+					 0xFF);	
+
+		/* Initialize SCB Site Flags field. */
+		asd_hwi_set_scbsite_byte(asd, SCB_SITE_FLAGS, 0x01);	
+		/* 
+		 * Set the first SG Element flag to no data transfer.
+		 * Also set Domain Select to OffChip memory. 
+		 */
+		asd_hwi_set_scbsite_byte(asd, 
+					 offsetof(struct asd_ssp_task_hscb,
+					 	  sg_elements[0].flags),
+					 SG_NO_DATA);
+	}
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2005-02-17 17:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-17 17:37 [ANNOUNCE] Adaptec SAS/SATA device driver [24/27] Luben Tuikov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).