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

Hardware interface. Part 1/3.

diff -Nru a/drivers/scsi/adp94xx/adp94xx_hwi.c b/drivers/scsi/adp94xx/adp94xx_hwi.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/scsi/adp94xx/adp94xx_hwi.c	2005-02-16 16:08:12 -05:00
@@ -0,0 +1,1976 @@
+/*
+ * Adaptec ADP94xx SAS HBA device driver for Linux.
+ *
+ * Written by : David Chaw <david_chaw@adaptec.com>
+ * Modified by : Naveen Chandrasekaran <naveen_chandrasekaran@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_hwi.c#156 $
+ * 
+ */	
+
+#include "adp94xx_osm.h"
+#include "adp94xx_inline.h"
+#if KDB_ENABLE
+#include "linux/kdb.h"
+#endif
+
+/* Local functions' prototypes */
+static u_int	asd_sglist_allocsize(struct asd_softc *asd);
+static int	asd_hwi_init_scbdata(struct asd_softc *asd);
+static int	asd_hwi_reset_hw(struct asd_softc *asd);
+static void	asd_hwi_exit_hw(struct asd_softc *asd);
+static void	asd_hwi_setup_sw_bar(struct asd_softc *asd);
+static int	asd_hwi_init_phys(struct asd_softc *asd);
+static int	asd_hwi_init_ports(struct asd_softc *asd); 
+static void	asd_hwi_alloc_scbs(struct asd_softc *asd);
+static int	asd_hwi_init_sequencers(struct asd_softc *asd);
+static void	asd_hwi_process_dl(struct asd_softc *asd);
+static void	asd_hwi_build_id_frame(struct asd_phy *phy);
+static void	asd_hwi_build_smp_phy_req(struct asd_port *port, int req_type, 
+					  int phy_id, int ctx);
+
+/* SCB completion post routines. */
+static void	asd_hwi_process_phy_comp(struct asd_softc *asd, 
+					 struct scb *scb,
+					 struct control_phy_sb *cntrl_phy);
+static void	asd_hwi_process_edb(struct asd_softc *asd, 
+				    struct asd_done_list *dl_entry);
+static void	asd_hwi_process_prim_event(struct asd_softc *asd,
+					   struct asd_phy *phy,
+					   u_int reg_addr, u_int reg_content);
+static void	asd_hwi_process_phy_event(struct asd_softc *asd, 
+					  struct asd_phy *phy, u_int oob_status,
+					  u_int oob_mode);
+static void	asd_hwi_handle_link_rst_err(struct asd_softc *asd,
+					    struct asd_phy *phy);
+static void	asd_hwi_process_req_task(struct asd_softc *asd,
+					 uint8_t req_type, uint16_t index);
+
+/* Error Handling routines. */
+static void	asd_scb_eh_timeout(u_long arg);
+static void	asd_hwi_abort_scb(struct asd_softc *asd, 
+				  struct scb *scb_to_abort, struct scb *scb);
+static void	asd_hwi_reset_lu(struct asd_softc *asd, 
+				 struct scb *scb_to_reset, struct scb *scb);
+static void	asd_hwi_reset_device(struct asd_softc *asd, 
+				     struct scb *scb_to_reset, struct scb *scb);
+static void	asd_hwi_reset_end_device(struct asd_softc *asd,
+					 struct scb *scb);
+static void	asd_hwi_reset_exp_device(struct asd_softc *asd,
+					 struct scb *scb);
+static void	asd_hwi_report_phy_err_log(struct asd_softc *asd,
+					   struct scb *scb);
+static void	asd_hwi_dump_phy_err_log(struct asd_port *port,
+					 struct scb *scb);
+static void	asd_hwi_reset_port(struct asd_softc *asd, 
+				   struct scb *scb_to_reset, struct scb *scb);
+
+static asd_scb_post_t	asd_hwi_abort_scb_done;
+static asd_scb_post_t	asd_hwi_reset_lu_done;
+static asd_scb_post_t	asd_hwi_reset_device_done;
+static asd_scb_post_t	asd_hwi_reset_end_device_done;
+static asd_scb_post_t	asd_hwi_reset_exp_device_done;
+static asd_scb_post_t	asd_hwi_reset_port_done;
+static asd_scb_eh_post_t asd_hwi_req_task_done;
+
+/* Function prototypes for NVRAM access utilites. */
+#if NVRAM_SUPPORT
+
+static int	asd_hwi_poll_nvram(struct asd_softc *asd);
+static int	asd_hwi_chk_write_status(struct asd_softc *asd, 
+					uint32_t sector_addr, 
+					uint8_t erase_flag); 
+static int	asd_hwi_reset_nvram(struct asd_softc *asd);
+int	asd_hwi_search_nv_cookie(struct asd_softc *asd, 
+			uint32_t *addr,
+			struct asd_flash_dir_layout *pflash_dir_buf);
+
+static int	asd_hwi_erase_nv_sector(struct asd_softc *asd, 
+					uint32_t  sector_addr);
+static int 	asd_hwi_verify_nv_checksum(struct asd_softc *asd, 
+					   u_int segment_id,
+					   uint8_t *segment_ptr, 
+					   u_int bytes_to_read);
+static int	asd_hwi_get_nv_config(struct asd_softc *asd);
+
+static int	asd_hwi_search_nv_id(struct asd_softc *asd, 
+				     u_int setting_id, 
+				     void *dest, u_int *src_offset, 
+				     u_int bytes_to_read);
+static int 	asd_hwi_get_nv_phy_settings(struct asd_softc *asd);
+static int 	asd_hwi_map_lrate_from_sas(u_int sas_link_rate, 
+					   u_int *asd_link_rate);
+static int 	asd_hwi_get_nv_manuf_seg(struct asd_softc *asd, void *dest, 
+					 uint32_t bytes_to_read,
+					 uint32_t *src_offset,
+					 uint16_t signature); 
+static void	asd_hwi_get_nv_phy_params(struct asd_softc *asd);
+
+static int asd_hwi_check_flash(struct asd_softc *asd); 
+
+#endif /* NVRAM_SUPPORT */
+
+/* OCM access routines */
+static int 	asd_hwi_get_ocm_info(struct asd_softc *asd);
+static int 	asd_hwi_get_ocm_entry(struct asd_softc *asd, 
+				      uint32_t entry_type,
+				      struct asd_ocm_entry_format *pocm_de, 
+				      uint32_t *src_offset);
+static int 	asd_hwi_read_ocm_seg(struct asd_softc *asd, void *dest,
+				     uint32_t src_offset, u_int bytes_to_read,
+				     u_int *bytes_read);
+static int 	asd_hwi_set_speed_mask(u_int asd_link_rate, 
+				       uint8_t *asd_speed_mask);
+
+#if SAS_COMSTOCK_SUPPORT
+static void	asd_hwi_get_rtl_ver(struct asd_softc *asd);
+#endif
+
+#ifdef ASD_TEST
+static void 	asd_hwi_dump_phy(struct asd_phy *phy);
+static void 	asd_hwi_dump_phy_id_addr(struct asd_phy *phy);
+#endif
+
+/*
+ * Function:
+ *	asd_sglist_allocsize
+ *
+ * Description:
+ * 	Calculate the optimum S/G List allocation size.  S/G elements used
+ *	for a given transaction must be physically contiguous.  Assume the
+ *	OS will allocate full pages to us, so it doesn't make sense to request
+ *	less than a page.
+ */
+static u_int
+asd_sglist_allocsize(struct asd_softc *asd)
+{
+	uint32_t sg_list_increment;
+	uint32_t sg_list_size;
+	uint32_t max_list_size;
+	uint32_t best_list_size;
+
+	/* Start out with the minimum required for ASD_NSEG. */
+	sg_list_increment = asd_sglist_size(asd);
+	sg_list_size = sg_list_increment;
+
+	/* Get us as close as possible to a page in size. */
+	while ((sg_list_size + sg_list_increment) <= PAGE_SIZE)
+		sg_list_size += sg_list_increment;
+
+	/*
+	 * Try to reduce the amount of wastage by allocating
+	 * multiple pages.
+	 */
+	best_list_size = sg_list_size;
+	max_list_size = roundup(sg_list_increment, PAGE_SIZE);
+	if (max_list_size < 4 * PAGE_SIZE)
+		max_list_size = 4 * PAGE_SIZE;
+	if (max_list_size > (ASD_MAX_ALLOCATED_SCBS * sg_list_increment))
+		max_list_size = (ASD_MAX_ALLOCATED_SCBS * sg_list_increment);
+	while ((sg_list_size + sg_list_increment) <= max_list_size
+	   &&  (sg_list_size % PAGE_SIZE) != 0) {
+		uint32_t new_mod;
+		uint32_t best_mod;
+
+		sg_list_size += sg_list_increment;
+		new_mod = sg_list_size % PAGE_SIZE;
+		best_mod = best_list_size % PAGE_SIZE;
+		if (new_mod > best_mod || new_mod == 0) {
+			best_list_size = sg_list_size;
+		}
+	}
+	return (best_list_size);
+}
+
+static int
+asd_hwi_init_scbdata(struct asd_softc *asd)
+{
+	struct scb	*scb;
+	u_int		 scb_cnt;
+	int		 loop_cnt;
+
+	asd->scbindex = asd_alloc_mem(
+				asd->hw_profile.max_scbs * sizeof(struct scb *),
+				GFP_ATOMIC);
+	if (asd->scbindex == NULL)
+		return (-ENOMEM);
+	
+	memset(asd->scbindex, 0x0, 
+	       asd->hw_profile.max_scbs * sizeof(struct scb *));
+
+	asd->init_level++;
+
+	asd->qinfifo = asd_alloc_mem(
+			   asd->hw_profile.max_scbs * sizeof(*asd->qinfifo),
+			   GFP_ATOMIC);
+
+	if (asd->qinfifo == NULL) {
+		asd_free_mem(asd->scbindex);
+		return (-ENOMEM);
+	}
+
+	asd->init_level++;
+	/*
+	 * Create our DMA tags.  These tags define the kinds of device
+	 * accessible memory allocations and memory mappings we will
+	 * need to perform during normal operation.
+	 */
+	/* DMA tag for our hardware scb structures */
+	if (asd_dma_tag_create(asd, 1, PAGE_SIZE, GFP_ATOMIC, 
+			       &asd->hscb_dmat) != 0) {
+		asd_hwi_exit_hw(asd);
+		goto error_exit;
+	}
+	asd->init_level++;
+
+	/* DMA tag for our S/G structures. */
+	if (asd_dma_tag_create(asd, 8, asd_sglist_allocsize(asd),
+			       GFP_ATOMIC, &asd->sg_dmat) != 0) {
+		asd_hwi_exit_hw(asd);
+		goto error_exit;
+	}
+	asd->init_level++;
+
+	/* Perform initial SCB allocation */
+	asd_hwi_alloc_scbs(asd);
+
+	if (asd->numscbs == 0) {
+		asd_print("%s: Unable to allocate initial scbs\n", 
+			  asd_name(asd));
+		asd_hwi_exit_hw(asd);
+		goto error_exit;
+	}
+	/* 
+	 * Make sure we are able to allocate more than the reserved 
+	 * SCB requirements.
+	 */
+	loop_cnt = 0;
+	while (asd->numscbs < ASD_RSVD_SCBS) {
+		/* 
+		 * Allocate SCB until we have more than the reserved SCBs
+		 * requirement.
+		 */
+		asd_hwi_alloc_scbs(asd);
+		if (++loop_cnt > 4)
+			break;
+	}
+
+	if (asd->numscbs < ASD_RSVD_SCBS) {
+		asd_log(ASD_DBG_ERROR, "Failed to allocate reserved pool of "
+			"SCBs.\n");
+		asd_hwi_exit_hw(asd);
+		goto error_exit;
+	}
+
+	scb_cnt = 0;
+	/* Save certain amount of SCBs as reserved. */
+	while (!list_empty(&asd->free_scbs)) {
+		scb = list_entry(asd->free_scbs.next, struct scb, hwi_links);
+		list_del(&scb->hwi_links);
+		list_add_tail(&scb->hwi_links, &asd->rsvd_scbs);
+		if (++scb_cnt > ASD_RSVD_SCBS)
+			break;
+	}
+	
+	return (0); 
+
+error_exit:
+	return (-ENOMEM);
+}
+
+/* 
+ * Function:
+ *	asd_alloc_softc()
+ *
+ * Description:
+ *	Allocate a softc structure and setup necessary fields.
+ */
+struct asd_softc *
+asd_alloc_softc(asd_dev_t dev)
+{
+	struct asd_softc	*asd;
+	
+	asd = asd_alloc_mem(sizeof(*asd), GFP_KERNEL);
+	if (asd == NULL) {
+		asd_log(ASD_DBG_ERROR, "Unable to alloc softc.\n");
+		return (NULL);
+	}
+	
+	memset(asd, 0x0, sizeof(*asd));
+	INIT_LIST_HEAD(&asd->rsvd_scbs);
+	INIT_LIST_HEAD(&asd->free_scbs);
+	INIT_LIST_HEAD(&asd->pending_scbs);
+	INIT_LIST_HEAD(&asd->timedout_scbs);
+	INIT_LIST_HEAD(&asd->empty_scbs);
+	INIT_LIST_HEAD(&asd->hscb_maps);
+	INIT_LIST_HEAD(&asd->sg_maps);
+	asd->dev = dev;
+
+	if (asd_platform_alloc(asd) != 0) {
+		asd_free_softc(asd);
+		asd = NULL;
+	}
+	return (asd);
+}
+
+/* 
+ * Function:
+ *	asd_free_softc()
+ *
+ * Description:
+ *	Free the host structure and any memory allocated for its member fields.
+ *	Also perform cleanup for module unloading purpose.
+ */
+void
+asd_free_softc(struct asd_softc *asd)
+{
+	asd_platform_free(asd);
+
+	/* Free any internal data structures */
+	asd_hwi_exit_hw(asd);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && \
+    LINUX_VERSION_CODE  < KERNEL_VERSION(2,5,0)
+	if (asd->dev != NULL)
+		asd->dev->driver = NULL;
+#endif
+
+	asd_free_mem(asd);			
+}
+
+void
+asd_intr_enable(struct asd_softc *asd, int enable)
+{
+	asd_write_dword(asd, CHIMINTEN, enable ? SET_CHIMINTEN : 0);
+}
+
+static int
+asd_hwi_reset_hw(struct asd_softc *asd)
+{
+	u_int i;
+
+#define ASD_RESET_DELAY	10
+#define ASD_RESET_LOOP_COUNT (1 * 1000000 / ASD_RESET_DELAY)
+	asd_write_dword(asd, COMBIST, HARDRST);
+	for (i = 0; i < ASD_RESET_LOOP_COUNT; i++) {
+		asd_delay(ASD_RESET_DELAY);
+		if (asd_read_dword(asd, CHIMINT) & HARDRSTDET)
+			break;
+	}
+	if (i >= ASD_RESET_LOOP_COUNT) {
+		asd_log(ASD_DBG_ERROR, "Chip reset failed.\n");
+		return (-EIO);
+	}
+
+	return (0);
+} 
+
+/* 
+ * Function:
+ *	asd_hwi_init_hw()
+ *
+ * Description:
+ *      Perform controller specific initialization.
+ */
+int
+asd_hwi_init_hw(struct asd_softc  *asd)
+{
+	union hardware_scb 	*hscb;
+	struct scb		*scb;
+	uint8_t			*next_vaddr;
+	dma_addr_t	 	 next_baddr;
+	dma_addr_t	 	 edb_baddr;
+	size_t		 	 driver_data_size;
+	size_t		 	 dl_size;
+	uint8_t		 	 enabled_phys;
+	u_int		 	 num_edbs;
+	u_int		 	 num_escbs;
+	u_int		 	 i;
+	int		 	 error;
+	u_long			 flags;
+
+	/* Setup the Sliding Window BAR. */
+	asd_hwi_setup_sw_bar(asd);
+
+#if SAS_COMSTOCK_SUPPORT
+	/* 
+	 * Retrieve the RTL number. The RTL Number will be used to decide which
+	 * sequencer version to use.
+	 */  
+	asd_hwi_get_rtl_ver(asd);
+
+	/* No support for COMSTOCK RTL less than version 14. */
+	if (asd->hw_profile.rev_id < COMSTOCK_LATEST_RTL)
+		return (-ENODEV);
+#endif
+
+	/* Allocate SCB data */
+	if (asd_hwi_init_scbdata(asd) != 0)
+		return (-ENOMEM);
+	
+	/*
+	 * DMA tag for our done_list, empty buffers, empty hardware SCBs,
+	 * and sentinel hardware SCB.  These are data host memory structures
+	 * the controller must be able to access.
+	 *
+	 * The number of elements in our done list must be a powerof2
+	 * greater than or equal to 4 that is large enough to guarantee
+	 * it cannot overflow.  Since each done list entry is associated
+	 * with either an empty data buffer or an SCB, add the counts for
+	 * these two objects together and roundup to the next power of 2.
+	 * To ensure our sequencers don't stall, we need two empty buffers
+	 * per sequencer (1 sequencer per-phy plus central seq).  We round
+	 * this up to a multiple of the number of EDBs that we can fit in
+	 * a single empty SCB.
+	 */
+	num_edbs = roundup(2 * (asd->hw_profile.max_phys + 1),
+			   ASD_MAX_EDBS_PER_SCB);
+	/*
+	 * At minimum, allocate 2 empty SCBs so that the sequencers
+	 * always have empty buffers while we are trying to queue more.
+	 */
+	num_edbs = MAX(num_edbs, 2 * ASD_MAX_EDBS_PER_SCB);
+	num_escbs = num_edbs / ASD_MAX_EDBS_PER_SCB;
+	dl_size = asd->hw_profile.max_scbs + num_edbs;
+	dl_size = roundup_pow2(dl_size);
+	asd->dl_wrap_mask = dl_size - 1;
+	dl_size *= sizeof(*asd->done_list);
+
+	driver_data_size = dl_size + (ASD_SCB_SIZE) /* for sentinel */ + 
+			 + (num_escbs * ASD_SCB_SIZE)
+			 + (num_edbs * sizeof(union edb));
+
+	if (asd_dma_tag_create(asd, 8, driver_data_size, GFP_ATOMIC,
+			       &asd->shared_data_dmat) != 0) {
+		asd_hwi_exit_hw(asd);
+		return (-ENOMEM);
+	}
+
+	asd->init_level++;
+
+	/* Allocation of driver data */
+	if (asd_dmamem_alloc(asd, asd->shared_data_dmat,
+			     (void **)&asd->shared_data_map.vaddr, GFP_ATOMIC,
+			     &asd->shared_data_map.dmamap,
+			     &asd->shared_data_map.busaddr) != 0) {
+		asd_hwi_exit_hw(asd);
+		return (-ENOMEM);
+	}
+
+	asd->init_level++;
+
+	/*
+	 * Distribute the memory.
+	 */
+	memset(asd->shared_data_map.vaddr, 0, driver_data_size);
+	asd->done_list = (struct asd_done_list *)asd->shared_data_map.vaddr;
+	asd->dl_valid = ASD_QDONE_PASS_DEF;
+	next_vaddr = asd->shared_data_map.vaddr + dl_size;
+	next_baddr = asd->shared_data_map.busaddr + dl_size;
+
+	/*
+	 * We need one SCB to serve as the "next SCB".  Since the
+	 * tag identifier in this SCB will never be used, there is
+	 * no point in using a valid HSCB tag from an SCB pulled from
+	 * the standard free pool.  So, we allocate this "sentinel"
+	 * specially from the DMA safe memory chunk.
+	 */
+	asd->next_queued_hscb = (union hardware_scb *)next_vaddr;
+	asd->next_queued_hscb_map = &asd->shared_data_map;
+	asd->next_queued_hscb_busaddr = asd_htole64(next_baddr);
+	next_vaddr += ASD_SCB_SIZE;
+	next_baddr += ASD_SCB_SIZE;
+
+	/*
+	 * Since Empty SCBs do not require scatter gather lists
+	 * we also allocate them outside of asd_alloc_scbs().
+	 */
+	hscb = asd->next_queued_hscb + 1;
+	edb_baddr = next_baddr + (num_escbs * ASD_SCB_SIZE);
+	for (i = 0; i < num_escbs; i++, hscb++, next_baddr += ASD_SCB_SIZE) {
+		int j;
+
+		/*
+		 * Allocate ESCBs.
+		 */
+		scb = asd_alloc_mem(sizeof(*scb), GFP_ATOMIC);
+		if (scb == NULL) {
+			error = -ENOMEM;
+			goto exit;
+		}
+		memset(scb, 0, sizeof(*scb));
+		INIT_LIST_HEAD(&scb->hwi_links);
+		INIT_LIST_HEAD(&scb->owner_links);
+		scb->hscb = hscb;
+		scb->hscb_busaddr = asd_htole64(next_baddr);
+		scb->softc = asd;
+		scb->hscb_map = &asd->shared_data_map;
+		hscb->header.index = asd_htole16(asd->numscbs);
+		hscb->header.opcode = SCB_EMPTY_BUFFER;
+		asd->scbindex[asd->numscbs++] = scb;
+		hscb->empty_scb.num_valid_elems = ASD_MAX_EDBS_PER_SCB;
+		for (j = 0; j < ASD_MAX_EDBS_PER_SCB; j++) {
+			hscb->empty_scb.buf_elem[j].busaddr =
+			    asd_htole64(edb_baddr);
+			hscb->empty_scb.buf_elem[j].buffer_size =
+			    asd_htole32(sizeof(union edb));
+			hscb->empty_scb.buf_elem[j].elem_valid_ds =
+			    ELEM_BUFFER_VALID;
+			edb_baddr += sizeof(union edb);
+		}
+		list_add(&scb->hwi_links, &asd->empty_scbs);
+	}
+
+	asd->init_level++;
+
+	/* Allocate Free DDB bitmap. */
+	asd->ddb_bitmap_size = roundup(asd->hw_profile.max_ddbs, BITS_PER_LONG);
+	asd->ddb_bitmap_size /= BITS_PER_LONG;
+	asd->free_ddb_bitmap = asd_alloc_mem((asd->ddb_bitmap_size *
+					      sizeof(u_long)), GFP_ATOMIC);
+	if (asd->free_ddb_bitmap == NULL)
+		return (-ENOMEM);
+	memset(asd->free_ddb_bitmap, 0, asd->ddb_bitmap_size);
+
+	/*
+	 * DDB site 0 and 1 are reserved for the firmware for internal use.
+	 */
+	asd->free_ddb_bitmap[0] |= (3<<0);
+	asd->init_level++;
+
+	/* Retrieve OCM information */
+	if (asd_hwi_get_ocm_info(asd)) {
+		asd_log(ASD_DBG_ERROR, "Failed to retrieve OCM info.\n");
+		/* TBD: return -1; ? */
+	}
+#if NVRAM_SUPPORT
+	/* 
+	 * Retrieves controller NVRAM setting. 
+	 */
+	error = asd_hwi_get_nv_config(asd);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to retrieve NVRAM config.\n");
+	}
+#endif
+
+	/* Initialize the phy and port to default settings. */
+	error = asd_hwi_init_phys(asd);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to init the phys.\n");
+		asd_hwi_exit_hw(asd);
+		goto exit;
+	}
+
+	error = asd_hwi_init_ports(asd);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to init the ports.\n");
+		asd_hwi_exit_hw(asd);
+		goto exit;
+	}
+
+	if (asd_hwi_reset_hw(asd) != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to perform Chip reset.\n");
+		asd_hwi_exit_hw(asd);
+		goto exit;
+	}
+
+	asd_write_dword(asd, CHIMINT, PORRSTDET|HARDRSTDET);
+
+	/*
+	 * Reset the producer and consumer index to reflect
+	 * no outstanding SCBs.
+	 */
+	asd->qinfifonext = (asd_read_dword(asd, SCBPRO) & SCBCONS_MASK) >> 16;
+	asd_write_dword(asd, SCBPRO, asd->qinfifonext);
+
+	asd->qinfifonext = asd_read_word(asd, SCBPRO+2);
+
+	/* Disable the Host interrupts. */
+	asd_write_dword(asd, CHIMINTEN, RST_CHIMINTEN);
+
+	/* Initialize and setup the CSEQ and LSEQ. */
+	error = asd_hwi_init_sequencers(asd);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to init the SEQ.\n"); 
+		goto exit;
+	}		
+	
+	/* CSEQ should be ready to run. Start the CSEQ. */
+	error = asd_hwi_start_cseq(asd);
+	if (error != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to start the CSEQ.\n"); 
+		goto exit;
+	}		
+
+	/* Start the LSEQ(s). */
+	i = 0;
+	enabled_phys = asd->hw_profile.enabled_phys;
+	while (enabled_phys != 0) {
+		for ( ; i < asd->hw_profile.max_phys; i++) {
+			if (enabled_phys & (1U << i)) {
+				enabled_phys &= ~(1U << i);
+				break;
+			}
+		}
+		
+		error = asd_hwi_start_lseq(asd, i);
+		if (error != 0) {
+			asd_log(ASD_DBG_ERROR,
+				"Failed to start LSEQ %d.\n", i);
+			goto exit;
+		}		
+	}
+
+	asd_lock(asd, &flags);
+
+	/*
+	 * Post all of our empty scbs to the central sequencer.
+	 */
+	list_for_each_entry(scb, &asd->empty_scbs, hwi_links) {
+		asd_hwi_post_scb(asd, scb);
+	}
+
+	asd_unlock(asd, &flags);
+
+	/* Enabled all the phys. */
+	i = 0;
+	enabled_phys = asd->hw_profile.enabled_phys;
+	while (enabled_phys != 0) { 
+		for ( ; i < asd->hw_profile.max_phys; i++) {
+			if (enabled_phys & (1 << i)) {
+				enabled_phys &= ~(1 << i);
+				break;
+			} 
+		}
+	
+		error = asd_hwi_enable_phy(asd, asd->phy_list[i]);
+		if (error != 0) {
+			/*
+			 * TODO: This shouldn't happen.
+			 *       Need more thought on how to proceed.
+			 */
+			asd_log(ASD_DBG_ERROR, "Failed to enable phy %d.\n", i);
+			break;
+		}
+	}
+exit:
+	return (error);         
+}
+
+#if SAS_COMSTOCK_SUPPORT
+/*
+ * Function:
+ *	asd_hwi_get_rtl_ver()
+ *
+ * Description:
+ * 	Retrive the COMSTOCK rtl version.
+ */ 	  	 
+static void
+asd_hwi_get_rtl_ver(struct asd_softc *asd)
+{
+	uint32_t	exsi_base_addr;
+	uint32_t	reg_addr;
+	uint8_t		reg_data;
+
+	exsi_base_addr = EXSI_REG_BASE_ADR + XREGADDR;
+	reg_addr = asd_hwi_swb_read_dword(asd, exsi_base_addr);
+	asd_hwi_swb_write_dword(asd, exsi_base_addr, (uint32_t)
+				(reg_addr & ~(XRADDRINCEN | XREGADD_MASK)));
+	reg_data = asd_hwi_swb_read_byte(asd, EXSI_REG_BASE_ADR + XREGDATAR);
+	asd_hwi_swb_write_dword(asd, exsi_base_addr, reg_addr);
+	
+	asd->hw_profile.rev_id = reg_data;
+}
+#endif /* SAS_COMSTOCK_SUPPORT */
+
+/* 
+ * Function:
+ *	asd_hwi_exit_hw()
+ *
+ * Description:
+ *      Perform controller specific cleanup.
+ */
+static void
+asd_hwi_exit_hw(struct asd_softc *asd)
+{
+	struct scb	*scb;
+	struct map_node	*map;
+	struct asd_phy	*phy;
+	struct asd_port	*port;
+	u_int		 i;
+
+	/*
+	 * Reset the chip so that the sequencers do not
+	 * attempt to DMA data into buffers we are about
+	 * to remove or issue further interrupts.
+	 */
+	/* TBRV: This seems to fail all the time. */
+	//asd_hwi_reset_hw(asd);
+
+	/* Clean up the phy structures */
+	for (i = 0; i <	asd->hw_profile.max_phys; i++) {
+		if (asd->phy_list[i] != NULL) {
+			phy = asd->phy_list[i];
+			/* Free the ID ADDR Frame buffer. */
+			asd_free_dma_mem(asd, phy->id_addr_dmat,
+					 &phy->id_addr_map);
+			asd_free_mem(asd->phy_list[i]);
+		}
+	}
+
+	/* Clean up the port structures */
+	for (i = 0; i < asd->hw_profile.max_ports; i++) {
+		if (asd->port_list[i] != NULL) {
+			port = asd->port_list[i];
+
+			/*
+			 * Free SMP Request Frame buffer.
+			 */
+			asd_free_dma_mem(asd, 
+				port->dc.smp_req_dmat,
+				&port->dc.smp_req_map);
+
+			/*
+			 * Free SMP Response Frame buffer.
+			 */
+			asd_free_dma_mem(asd, 
+				port->dc.smp_resp_dmat,
+				&port->dc.smp_resp_map);
+
+			asd_free_mem(asd->port_list[i]);
+		}
+	}
+
+	/* Free up the SCBs */
+	while (!list_empty(&asd->pending_scbs)) {
+		scb = list_entry(asd->pending_scbs.next, struct scb, hwi_links);
+		list_del(&scb->hwi_links);
+		asd_free_scb_platform_data(asd, scb->platform_data);
+        	asd_free_mem(scb);
+	}
+	while (!list_empty(&asd->rsvd_scbs)) {
+		scb = list_entry(asd->rsvd_scbs.next, struct scb, hwi_links);
+		list_del(&scb->hwi_links);
+		asd_free_scb_platform_data(asd, scb->platform_data);
+        	asd_free_mem(scb);
+	}
+	while (!list_empty(&asd->free_scbs)) {
+		scb = list_entry(asd->free_scbs.next, struct scb, hwi_links);
+		list_del(&scb->hwi_links);
+		asd_free_scb_platform_data(asd, scb->platform_data);
+        	asd_free_mem(scb);
+	}
+	while (!list_empty(&asd->empty_scbs)) {
+		/*
+		 * Empties have no OSM data.
+		 */
+		scb = list_entry(asd->empty_scbs.next, struct scb, hwi_links);
+		list_del(&scb->hwi_links);
+        	asd_free_mem(scb);
+	}
+
+	/* Free up DMA safe memory shared with the controller */
+	while (!list_empty(&asd->hscb_maps)) {
+		map = list_entry(asd->hscb_maps.next, struct map_node, links);
+		list_del(&map->links);
+		asd_dmamem_free(asd, asd->hscb_dmat, map->vaddr, map->dmamap);
+		asd_free_mem(map);
+	}
+	while (!list_empty(&asd->sg_maps)) {
+		map = list_entry(asd->sg_maps.next, struct map_node, links);
+		list_del(&map->links);
+		asd_dmamem_free(asd, asd->sg_dmat, map->vaddr, map->dmamap);
+		asd_free_mem(map);
+	}
+
+	switch (asd->init_level) {
+	default:
+	case 7:
+		asd_free_mem(asd->free_ddb_bitmap);
+		/* FALLTHROUGH */
+	case 6:
+		asd_dmamem_free(asd, asd->shared_data_dmat,
+				asd->shared_data_map.vaddr,
+				asd->shared_data_map.dmamap);
+		/* FALLTHROUGH */
+	case 5:
+		asd_dma_tag_destroy(asd, asd->shared_data_dmat);
+		/* FALLTHROUGH */
+	case 4:
+		asd_dma_tag_destroy(asd, asd->sg_dmat);
+		/* FALLTHROUGH */
+	case 3:
+		asd_dma_tag_destroy(asd, asd->hscb_dmat);
+		/* FALLTHROUGH */
+	case 2:
+		asd_free_mem(asd->qinfifo);
+		/* FALLTHROUGH */
+	case 1:
+		asd_free_mem(asd->scbindex);
+		/* FALLTHROUGH */
+	case 0:
+		break;
+	}
+}
+
+/* 
+ * Function:
+ *	asd_hwi_setup_sw_bar()
+ *
+ * Description:
+ *      Setup the location of internal space where the Sliding Window will
+ *	point to.
+ */
+static void
+asd_hwi_setup_sw_bar(struct asd_softc *asd)
+{
+	/* Setup Sliding Window A and B to point to CHIM_REG_BASE_ADR. */
+	asd_write_dword(asd, PCIC_BASEA, CHIM_REG_BASE_ADR);
+	asd_write_dword(asd, PCIC_BASEB, CHIM_REG_BASE_ADR);
+	
+	asd->io_handle[0]->swb_base = (uint32_t) CHIM_REG_BASE_ADR;        
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_phys()
+ *
+ * Description:
+ *      Alllocate phy structures and intialize them to default settings. 
+ */
+static int
+asd_hwi_init_phys(struct asd_softc *asd)
+{
+	struct asd_phy	*phy;
+	u_int		 phy_id;
+
+        for (phy_id = 0; phy_id < asd->hw_profile.max_phys; phy_id++) {
+        	phy = asd_alloc_mem(sizeof(*phy), GFP_KERNEL);
+                if (phy == NULL) {
+                	asd_log(ASD_DBG_ERROR," Alloc Phy failed.\n");
+                        return (-ENOMEM);
+		}
+                
+		memset(phy, 0x0, sizeof(*phy));
+		
+		/* Fill in the default settings. */
+		phy->id = phy_id;
+		phy->max_link_rate = SAS_30GBPS_RATE;
+		phy->min_link_rate = SAS_15GBPS_RATE;       
+                /*
+                 * Set the phy attributes to support SSP, SMP and STP 
+                 * initiator mode. Target mode is not supported.
+                 */
+		phy->attr = (ASD_SSP_INITIATOR | ASD_SMP_INITIATOR | 
+			     ASD_STP_INITIATOR);
+		
+		/* 
+		 * By default, use the adapter WWN as the SAS address for 
+		 * the phy. 
+		 */
+                memcpy(phy->sas_addr, asd->hw_profile.wwn, SAS_ADDR_LEN);
+
+		/* Allocate buffer for IDENTIFY ADDRESS frame. */
+		if (asd_dma_tag_create(asd, 8, sizeof(struct sas_id_addr),
+				       GFP_ATOMIC, &phy->id_addr_dmat) != 0)
+			return (-ENOMEM);
+
+		if (asd_dmamem_alloc(asd, phy->id_addr_dmat, 
+				    (void **) &phy->id_addr_map.vaddr,
+				    GFP_ATOMIC,
+				    &phy->id_addr_map.dmamap,
+				    &phy->id_addr_map.busaddr) != 0) {
+			asd_dma_tag_destroy(asd, phy->id_addr_dmat);
+			return (-ENOMEM);
+		}
+
+		INIT_LIST_HEAD(&phy->pending_scbs);
+		phy->state = ASD_PHY_UNUSED;
+		phy->src_port = NULL;
+                phy->softc = (void *) asd;
+		INIT_LIST_HEAD(&phy->links);
+		phy->pat_gen = 0;
+		asd->phy_list[phy_id] = phy;
+	}
+	
+	asd_hwi_get_nv_phy_settings(asd);
+	asd_hwi_get_nv_phy_params(asd);
+
+	return (0);	
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_ports()
+ *
+ * Description:
+ *      Alllocate port structures and intialize them to default settings. 
+ */
+static int
+asd_hwi_init_ports(struct asd_softc *asd)
+{
+	struct asd_port	*port;
+	u_char		i;
+
+	for (i = 0; i < asd->hw_profile.max_ports; i++) {
+       		port = asd_alloc_mem(sizeof(*port), GFP_ATOMIC);
+		if (port == NULL) {
+			asd_log(ASD_DBG_ERROR," Alloc Port failed.\n");
+			return (-ENOMEM);
+		}			
+	
+		memset(port, 0x0, sizeof(*port));
+		INIT_LIST_HEAD(&port->phys_attached);
+		INIT_LIST_HEAD(&port->targets);
+		INIT_LIST_HEAD(&port->targets_to_validate);
+
+		if (asd_alloc_dma_mem(asd, sizeof(struct SMPRequest),
+			(void **)&port->dc.SMPRequestFrame,
+			&port->dc.SMPRequestBusAddr,
+			&port->dc.smp_req_dmat,
+			&port->dc.smp_req_map) != 0) {
+
+			return (-ENOMEM);
+		}
+
+		if (asd_alloc_dma_mem(asd, sizeof(struct SMPResponse),
+			(void **)&port->dc.SMPResponseFrame,
+			&port->dc.SMPResponseBusAddr,
+			&port->dc.smp_resp_dmat,
+			&port->dc.smp_resp_map) != 0) {
+
+			/*
+			 * If we get get the response, free the request.
+			 */
+			asd_free_dma_mem(asd, 
+				port->dc.smp_req_dmat,
+				&port->dc.smp_req_map);
+
+			return (-ENOMEM);
+		}
+
+		/*
+		 * The SASInfoFrame includes the length of the list as the 
+		 * first element.
+		 */
+		port->dc.sas_info_len = MAX(
+			(ASD_MAX_LUNS + 1) * sizeof(uint64_t),
+			PRODUCT_SERIAL_NUMBER_LEN);
+
+		if (asd_alloc_dma_mem(asd,
+			port->dc.sas_info_len,
+			(void **)&port->dc.SASInfoFrame,
+			&port->dc.SASInfoBusAddr,
+			&port->dc.sas_info_dmat,
+			&port->dc.sas_info_map) != 0) {
+
+			/*
+			 * If we get get the report luns ...
+			 */
+			asd_free_dma_mem(asd, 
+				port->dc.smp_req_dmat,
+				&port->dc.smp_req_map);
+
+			asd_free_dma_mem(asd, 
+				port->dc.smp_resp_dmat,
+				&port->dc.smp_resp_map);
+
+			return (-ENOMEM);
+		}
+
+		/* Fill in default settings. */
+		port->attr = (ASD_SSP_INITIATOR | ASD_SMP_INITIATOR |
+			      ASD_STP_INITIATOR);
+		port->softc = (void *) asd;
+		port->state = ASD_PORT_UNUSED;
+		port->events = ASD_IDLE;
+		port->link_type = ASD_LINK_UNKNOWN;
+		port->management_type = ASD_DEVICE_NONE;
+		port->id = i;
+		asd->port_list[i] = port;
+	}
+
+	return (0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_alloc_scbs()
+ *
+ * Description:
+ *      Allocate SCB buffers. 
+ */
+static void
+asd_hwi_alloc_scbs(struct asd_softc *asd)
+{
+	struct scb 		*next_scb;
+	union hardware_scb 	*hscb;
+	struct map_node 	*hscb_map;
+	struct map_node 	*sg_map;
+	uint8_t			*segs;
+	dma_addr_t	 	 hscb_busaddr;
+	dma_addr_t	 	 sg_busaddr;
+	int		 	 newcount;
+	int		 	 i;
+
+	if (asd->numscbs >= asd->hw_profile.max_scbs)
+		/* Can't allocate any more */
+		return;
+
+	if (asd->scbs_left != 0) {
+		int offset;
+
+		offset = (PAGE_SIZE / sizeof(*hscb)) - asd->scbs_left;
+		hscb_map = list_entry(asd->hscb_maps.next,
+				      struct map_node, links);
+		hscb = &((union hardware_scb *)hscb_map->vaddr)[offset];
+		hscb_busaddr = hscb_map->busaddr + (offset * sizeof(*hscb));
+	} else {
+		hscb_map = asd_alloc_mem(sizeof(*hscb_map), GFP_ATOMIC);
+
+		if (hscb_map == NULL)
+			return;
+
+		/* Allocate the next batch of hardware SCBs */
+		if (asd_dmamem_alloc(asd, asd->hscb_dmat,
+				     (void **) &hscb_map->vaddr, GFP_ATOMIC,
+				     &hscb_map->dmamap,
+				     &hscb_map->busaddr) != 0) {
+			asd_free_mem(hscb_map);
+			return;
+		}
+
+		list_add(&hscb_map->links, &asd->hscb_maps);
+		hscb = (union hardware_scb *)hscb_map->vaddr;
+		hscb_busaddr = hscb_map->busaddr;
+		asd->scbs_left = PAGE_SIZE / sizeof(*hscb);
+
+		asd_log(ASD_DBG_RUNTIME, "Mapped SCB data. %d SCBs left. "
+			"Total SCBs %d.\n", 
+			asd->scbs_left, asd->numscbs);
+	}
+
+	if (asd->sgs_left != 0) {
+		int offset;
+
+		offset = ((asd_sglist_allocsize(asd) / asd_sglist_size(asd))
+		     		- asd->sgs_left) * asd_sglist_size(asd);
+		sg_map = list_entry(asd->sg_maps.next,
+				    struct map_node, links);
+		segs = sg_map->vaddr + offset;
+		sg_busaddr = sg_map->busaddr + offset;
+	} else {
+		sg_map = asd_alloc_mem(sizeof(*sg_map), GFP_ATOMIC);
+
+		if (sg_map == NULL)
+			return;
+
+		/* Allocate the next batch of S/G lists */
+		if (asd_dmamem_alloc(asd, asd->sg_dmat,
+				     (void **) &sg_map->vaddr, GFP_ATOMIC,
+				     &sg_map->dmamap, &sg_map->busaddr) != 0) {
+			asd_free_mem(sg_map);
+			return;
+		}
+
+		list_add(&sg_map->links, &asd->sg_maps);
+		segs = sg_map->vaddr;
+		sg_busaddr = sg_map->busaddr;
+		asd->sgs_left =
+			asd_sglist_allocsize(asd) / asd_sglist_size(asd);
+	}
+
+	newcount = MIN(asd->scbs_left, asd->sgs_left);
+	newcount = MIN(newcount, (asd->hw_profile.max_scbs - asd->numscbs));
+
+	for (i = 0; i < newcount; i++) {
+		struct asd_scb_platform_data *pdata;
+
+		next_scb = (struct scb *) asd_alloc_mem(sizeof(*next_scb),
+							GFP_ATOMIC);
+		if (next_scb == NULL)
+			break;
+
+		memset(next_scb, 0, sizeof(*next_scb));
+		INIT_LIST_HEAD(&next_scb->hwi_links);
+		INIT_LIST_HEAD(&next_scb->owner_links);
+
+		pdata = asd_alloc_scb_platform_data(asd);
+		if (pdata == NULL) {
+			asd_free_mem(next_scb);
+			break;
+		}
+		next_scb->platform_data = pdata;
+		init_timer(&next_scb->platform_data->timeout);
+		next_scb->hscb_map = hscb_map;
+		next_scb->sg_map = sg_map;
+		next_scb->sg_list = (struct sg_element *)segs;
+		memset(hscb, 0, sizeof(*hscb));
+		next_scb->hscb = hscb;
+		next_scb->hscb_busaddr = asd_htole64(hscb_busaddr);
+		next_scb->sg_list_busaddr = sg_busaddr;
+		next_scb->softc = asd;
+		next_scb->flags = SCB_FLAG_NONE;
+		next_scb->eh_state = SCB_EH_NONE;
+		next_scb->hscb->header.index = asd_htole16(asd->numscbs);
+		asd->scbindex[asd_htole16(asd->numscbs)] = next_scb;
+		
+		/* Add the scb to the free list. */
+		asd_hwi_free_scb(asd, next_scb);
+		hscb++;
+		hscb_busaddr += sizeof(*hscb);
+		segs += asd_sglist_size(asd);
+		sg_busaddr += asd_sglist_size(asd);
+		asd->numscbs++;
+		asd->scbs_left--;
+		asd->sgs_left--;
+	}
+}
+
+/*
+ * Function:
+ *	asd_alloc_ddb
+ *
+ * Description:
+ *      Allocate a DDB site on the controller.
+ *	Returns ASD_INVALID_DDB_INDEX on failure.
+ *	Returns DDB index on success.
+ */
+uint16_t
+asd_alloc_ddb(struct asd_softc *asd)
+{
+	u_int i;
+	u_int bit_index;
+
+	for (i = 0; i < asd->ddb_bitmap_size; i++) {
+		if (asd->free_ddb_bitmap[i] != ~0UL)
+			break;
+	}
+	if (i >= asd->ddb_bitmap_size)
+		return (ASD_INVALID_DDB_INDEX);
+
+	bit_index = ffz(asd->free_ddb_bitmap[i]);
+	asd->free_ddb_bitmap[i] |= 0x1 << bit_index;
+	return ((i * BITS_PER_LONG) + bit_index);
+}
+
+/*
+ * Function:
+ *	asd_free_ddb
+ *
+ * Description:
+ *      Mark the DDB site at "ddb_index" as free.
+ */
+void
+asd_free_ddb(struct asd_softc *asd, uint16_t ddb_index)
+{
+	u_int word_offset;
+	u_int bit_offset;
+
+	word_offset = ddb_index / BITS_PER_LONG;
+	bit_offset = ddb_index & (BITS_PER_LONG - 1);
+	asd->free_ddb_bitmap[word_offset] &= ~(0x1 << bit_offset);
+}
+
+/*
+ * Function:
+ *	asd_hwi_setup_ddb_site()
+ *  
+ * Description:
+ *	Alloc and DDB site and setup the DDB site for the controller.
+ */
+int
+asd_hwi_setup_ddb_site(struct asd_softc *asd, struct asd_target *target)
+{
+	uint16_t	ddb_index;
+
+	/* Allocate a free DDB site. */
+	ddb_index = asd_alloc_ddb(asd);
+	if (ddb_index == ASD_INVALID_DDB_INDEX)
+		return (-1);
+	
+	target->ddb_profile.conn_handle = ddb_index;	
+	
+	asd_hwi_build_ddb_site(asd, target);
+
+	return (0);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_init_sequencers()
+ *
+ * Description:
+ *      Initialize the Central and Link Sequencers. 
+ */
+static int
+asd_hwi_init_sequencers(struct asd_softc *asd)
+{
+	/* Pause the CSEQ. */
+	if (asd_hwi_pause_cseq(asd) != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to pause the CSEQ.\n");
+		return (-1);	
+	}
+	
+	/* Pause all the LSEQs. */
+	if (asd_hwi_pause_lseq(asd, asd->hw_profile.enabled_phys) != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to pause the LSEQs.\n");
+		return (-1);		
+	}
+	/* Download the sequencers. */
+	if (asd_hwi_download_seqs(asd) != 0) {
+		asd_log(ASD_DBG_ERROR, "Failed to setup the SEQs.\n");
+		return (-1);
+	}
+
+	/* 
+	 * Initialiaze the DDB site 0 and 1used internally by the
+	 * sequencer.
+	 */
+	asd_hwi_init_internal_ddb(asd);
+
+	/* Setup and initialize the CSEQ and LSEQ(s). */
+	asd_hwi_setup_seqs(asd);
+	
+	return (0);			
+}
+
+/* 
+ * Function:
+ *	asd_hwi_get_scb()
+ *
+ * Description:
+ *      Get a free SCB Desc from the free list if any.
+ */
+struct scb *
+asd_hwi_get_scb(struct asd_softc *asd, int rsvd_pool)
+{
+	struct scb	*scb;
+
+	ASD_LOCK_ASSERT(asd);
+
+	if (rsvd_pool == 1) {
+		/* Get an SCB from the reserved pool. */
+		if (list_empty(&asd->rsvd_scbs)) {
+			/*
+		 	 * We shouldn't be running out reserved SCBs.
+		 	 */
+			asd_log(ASD_DBG_ERROR, "Running out reserved SCBs.\n");
+			return (NULL);
+		}
+		scb = list_entry(asd->rsvd_scbs.next, struct scb, hwi_links);
+		scb->flags |= SCB_RESERVED;
+	} else {
+		/* Get an SCB from the free pool. */
+		if (list_empty(&asd->free_scbs)) {
+			asd_hwi_alloc_scbs(asd);
+			if (list_empty(&asd->free_scbs)) {
+				asd_log(ASD_DBG_ERROR, 
+					"Failed to get a free SCB.\n");
+				return (NULL);
+			}
+		}
+		scb = list_entry(asd->free_scbs.next, struct scb, hwi_links);
+	}
+	list_del(&scb->hwi_links);
+	scb->post_stack_depth = 0;
+	return (scb);
+}
+
+/*
+ * Function:
+ *	asd_hwi_enable_phy()
+ *
+ * Description:
+ *	Enable the requested phy.
+ */
+int
+asd_hwi_enable_phy(struct asd_softc *asd, struct asd_phy *phy)
+{
+	struct scb		*scb;
+	uint8_t			phy_id;
+	u_long		 	flags;
+
+	phy_id = phy->id;
+
+#if SAS_COMSTOCK_SUPPORT
+	/*
+ 	 * For COMSTOCK:
+	 * 	1. We need to setup OOB signal detection limits.
+	 */
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_BFLTR), 0x40);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_INIT_MIN), 0x06);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_INIT_MAX), 0x13);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_INIT_NEG), 0x13);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_SAS_MIN), 0x13);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_SAS_MAX), 0x36);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_SAS_NEG), 0x36);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_WAKE_MIN), 0x02);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_WAKE_MAX), 0x06);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_WAKE_NEG), 0x06);
+	asd_hwi_swb_write_word(asd, LmSEQ_OOB_REG(phy_id, OOB_IDLE_MAX), 
+			       0x0080);
+	asd_hwi_swb_write_word(asd, LmSEQ_OOB_REG(phy_id, OOB_BURST_MAX), 
+			       0x0080);
+	/*
+ 	 *	2. Put the OOB in slow clock mode. That corrects most of the 
+	 *	   other timer parameters including the signal transmit values
+	 * 	   for 37.5 MHZ.
+	 */
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, OOB_MODE), SLOW_CLK);
+
+#endif /* SAS_COMSTOCK_SUPPORT */
+				
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0x0);
+
+
+#if !SAS_COMSTOCK_SUPPORT
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY),
+			       HOTPLUG_DEFAULT_DELAY);
+
+	/*
+	 * Set the PHY SETTINGS values based on the manufacturing 
+	 * programmed values that we obtained from the NVRAM.
+	 */
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0),
+			       phy->phy_ctl0);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1),
+			       phy->phy_ctl1);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2),
+			       phy->phy_ctl2);
+	asd_hwi_swb_write_byte(asd, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3),
+			       phy->phy_ctl3);
+#endif
+
+	/* Initialize COMINIT_TIMER timeout. */
+	asd_hwi_swb_write_dword(asd, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id),
+				SAS_DEFAULT_COMINIT_TIMEOUT);
+
+	/* Build Identify Frame address. */
+	asd_hwi_build_id_frame(phy);
+
+	/* Fill in bus address for Identify Frame buffer. */
+	asd_hwi_set_hw_addr(asd, LmSEQ_TX_ID_ADDR_FRAME(phy_id),
+			    phy->id_addr_map.busaddr);
+
+	asd_hwi_control_activity_leds(asd, phy->id, ENABLE_PHY);
+
+	asd_lock(asd, &flags);
+
+	scb = asd_hwi_get_scb(asd, 0);
+	if (scb == NULL) {
+		asd_log(ASD_DBG_ERROR, "Failed to get a free SCB.\n");
+		return (-1);
+	}
+
+	/* Store the phy pointer. */
+	scb->io_ctx = (void *) phy;
+	scb->flags |= SCB_INTERNAL;
+
+	/* Build CONTROL PHY SCB. */
+	asd_hwi_build_control_phy(scb, phy, ENABLE_PHY);
+
+	list_add_tail(&scb->owner_links, &phy->pending_scbs);
+
+	asd_hwi_post_scb(asd, scb);
+
+	asd_unlock(asd, &flags);
+
+	return (0);	
+}
+
+void
+asd_hwi_release_sata_spinup_hold(
+struct asd_softc	*asd,
+struct asd_phy		*phy
+)
+{
+	struct scb		*scb;
+	uint8_t			phy_id;
+	u_long		 	flags;
+
+	phy_id = phy->id;
+				
+	asd_lock(asd, &flags);
+
+	scb = asd_hwi_get_scb(asd, 0);
+	if (scb == NULL) {
+		asd_log(ASD_DBG_ERROR, "Failed to get a free SCB.\n");
+		return;
+	}
+
+	/* Store the phy pointer. */
+	scb->io_ctx = (void *) phy;
+	scb->flags |= SCB_INTERNAL;
+
+	/* Build CONTROL PHY SCB. */
+	asd_hwi_build_control_phy(scb, phy, RELEASE_SPINUP_HOLD);
+
+	list_add_tail(&scb->owner_links, &phy->pending_scbs);
+
+	asd_hwi_post_scb(asd, scb);
+
+	asd_unlock(asd, &flags);
+
+	return;
+}
+
+/* 
+ * Function:
+ *	asd_hwi_process_irq()
+ *
+ * Description:
+ *      Process any interrupts pending for our controller.
+ */
+int
+asd_hwi_process_irq(struct asd_softc *asd)
+{
+	struct asd_done_list	*dl_entry;
+	int			irq_processed;
+	uint32_t		intstat;
+
+	ASD_LOCK_ASSERT(asd);
+
+	/*
+	 * Check if any DL entries need to be processed.  If so,
+	 * bypass a costly read of our interrupt status register
+	 * and assume that the done list entries are the cause of
+	 * our interrupt.
+	 */
+	dl_entry = &asd->done_list[asd->dl_next];
+	if ((dl_entry->toggle & ASD_DL_TOGGLE_MASK) == asd->dl_valid)
+		intstat = DLAVAIL;
+	else
+		intstat = asd_read_dword(asd, CHIMINT);
+	if (intstat & DLAVAIL) {
+		asd_write_dword(asd, CHIMINT, DLAVAIL); 
+		/*
+		 * Ensure that the chip sees that we've cleared
+		 * this interrupt before we walk the done_list.
+		 * Otherwise, we may, due to posted bus writes,
+		 * clear the interrupt after we finish the scan,
+		 * and after the sequencer has added new entries
+		 * and asserted the interrupt again.
+		 *
+		 * NOTE: This extra read, and in fact the clearing
+		 *       of the command complete interrupt, will
+		 *       not be needed on systems using MSI.
+		 */
+		asd_flush_device_writes(asd);
+
+		asd_hwi_process_dl(asd);	
+
+		irq_processed = 1;
+	} else
+		irq_processed = 0;
+	
+	return (irq_processed);
+}
+
+/* 
+ * Function:
+ *	asd_hwi_process_dl()
+ *
+ * Description:
+ *      Process posted Done List entries.
+ */
+static void
+asd_hwi_process_dl(struct asd_softc *asd)
+{
+	struct asd_done_list 	*dl_entry;
+	struct scb		*scb;
+
+	/*
+	 * Look for entries in the done list that have completed.
+	 * The valid_tag completion field indicates the validity
+	 * of the entry - the valid value toggles each time through
+	 * the queue.
+	 */
+	if ((asd->flags & ASD_RUNNING_DONE_LIST) != 0)
+		panic("asd_hwi_process_dl recursion");
+
+	asd->flags |= ASD_RUNNING_DONE_LIST;
+
+	for (;;) {
+		dl_entry = &asd->done_list[asd->dl_next];
+
+		if ((dl_entry->toggle & ASD_DL_TOGGLE_MASK) != asd->dl_valid)
+			break;
+
+		scb = asd->scbindex[dl_entry->index];
+
+		if ((scb->flags & SCB_PENDING) != 0) {
+			list_del(&scb->hwi_links);
+			scb->flags &= ~SCB_PENDING;
+		}
+
+		/* Process DL Entry. */
+		switch (dl_entry->opcode) {
+		case TASK_COMP_WO_ERR:
+		case TASK_COMP_W_UNDERRUN:
+		case TASK_COMP_W_OVERRUN:
+		case TASK_F_W_OPEN_REJECT:
+		case TASK_INT_W_BRK_RCVD:
+		case TASK_INT_W_PROT_ERR:
+		case SSP_TASK_COMP_W_RESP:
+		case TASK_INT_W_PHY_DOWN:
+		case LINK_ADMIN_TASK_COMP_W_RESP:
+		case CSMI_TASK_COMP_WO_ERR:
+		case ATA_TASK_COMP_W_RESP:
+		case TASK_INT_W_NAK_RCVD:
+		case RESUME_COMPLETE:
+		case TASK_INT_W_ACKNAK_TO:
+		case TASK_F_W_SMPRSP_TO:
+		case TASK_F_W_SMP_XMTRCV_ERR:
+		case TASK_F_W_NAK_RCVD:
+		case TASK_ABORTED_BY_ITNL_EXP:
+		case ATA_TASK_COMP_W_R_ERR_RCVD:
+		case TMF_F_W_TC_NOT_FOUND:
+		case TASK_ABORTED_ON_REQUEST:
+		case TMF_F_W_TAG_NOT_FOUND:
+		case TMF_F_W_TAG_ALREADY_FREE:
+		case TMF_F_W_TAG_ALREADY_DONE:
+		case TMF_F_W_CONN_HNDL_NOT_FOUND:
+		case TASK_CLEARED:
+		case TASK_UA_W_SYNCS_RCVD:
+			asd_pop_post_stack(asd, scb, dl_entry);
+			break;
+			
+		case CONTROL_PHY_TASK_COMP:
+		{
+			struct control_phy_sb	*cntrl_phy;
+			
+			cntrl_phy = &dl_entry->stat_blk.control_phy;
+	
+			if (cntrl_phy->sb_opcode == PHY_RESET_COMPLETED) {
+				asd_hwi_process_phy_comp(asd, scb, cntrl_phy);
+			} else {
+				asd_log(ASD_DBG_RUNTIME, "Invalid status "
+					"block upcode.\n");
+			}
+
+			/* 
+			 * Post routine needs to be called if the 
+			 * CONTROL PHY is issued as result of error recovery
+			 * process or from CSMI.
+			 */
+			if ((scb->flags & SCB_RECOVERY) != 0)
+				asd_pop_post_stack(asd, scb, dl_entry);
+
+			break;
+		}
+
+		default:
+			/* 
+			 * Making default case for EDB received and
+			 * and non supported DL opcode. 
+			 */
+			if ((dl_entry->opcode >= 0xC1) && 
+			    (dl_entry->opcode <= 0xC7)) 
+				asd_hwi_process_edb(asd, dl_entry);
+			else
+				asd_log(ASD_DBG_RUNTIME, 
+					"Received unsupported "
+					"DL entry (opcode = 0x%x).\n",
+				  	 dl_entry->opcode);
+
+			break;
+		}
+
+		asd->dl_next = (asd->dl_next + 1) & asd->dl_wrap_mask;
+		if (asd->dl_next == 0)
+			asd->dl_valid ^= ASD_DL_TOGGLE_MASK;
+	}
+
+	asd->flags &= ~ASD_RUNNING_DONE_LIST;
+} 
+
+/*
+ * Function:
+ * 	asd_hwi_process_phy_comp()
+ * 
+ * Description:
+ * 	Process phy reset completion.
+ */
+static void
+asd_hwi_process_phy_comp(struct asd_softc *asd, struct scb *scb,
+			 struct control_phy_sb *cntrl_phy)
+{
+	struct asd_phy			*phy;
+	struct asd_control_phy_hscb 	*cntrlphy_scb;
+
+	cntrlphy_scb = &scb->hscb->control_phy;
+	phy = (struct asd_phy *) scb->io_ctx;
+
+	switch (cntrlphy_scb->sub_func) {
+	case DISABLE_PHY:
+	{
+		asd->hw_profile.enabled_phys &= ~(1 << phy->id);
+		phy->state = ASD_PHY_OFFLINE;
+
+		/*
+		 * Check if this phy is attached to a target.
+	       	 */	 
+		if (phy->src_port != NULL) {
+			/*
+			 * DC: Currently, we treat this similar to loss of 
+			 *     signal scenario.
+			 *     Need to examine the behavior once the phy is
+			 *     is disabled !!
+			 *     Prior to disabling the phy that has target
+			 *     connected, we need to abort all outstanding
+			 *     IO to the affected target ports.
+		         */	 
+			phy->attr = (ASD_SSP_INITIATOR | ASD_SMP_INITIATOR | 
+			     	     ASD_STP_INITIATOR);
+			phy->src_port->events |= ASD_LOSS_OF_SIGNAL;
+			asd_wakeup_sem(&asd->platform_data->discovery_sem);
+		}
+		
+		list_del(&scb->owner_links);	
+		asd_hwi_free_scb(asd, scb);
+		break;
+	}
+
+	case ENABLE_PHY:
+		/* Check the OOB status from the link reset sequence. */
+		if (cntrl_phy->oob_status & CURRENT_OOB_DONE) {
+			if ((asd->hw_profile.enabled_phys & 
+				(1 << phy->id)) == 0)
+				asd->hw_profile.enabled_phys |= (1 << phy->id);
+
+			/* There is a device attached. */
+			if (cntrl_phy->oob_status & CURRENT_DEVICE_PRESENT) {
+				phy->attr |= ASD_DEVICE_PRESENT;
+
+				if (cntrl_phy->oob_status & CURRENT_SPINUP_HOLD)
+					phy->attr |= ASD_SATA_SPINUP_HOLD;
+
+				phy->state = ASD_PHY_WAITING_FOR_ID_ADDR;
+			} else {
+				phy->state = ASD_PHY_ONLINE;
+			}
+
+			/* Get the negotiated connection rate. */
+			if (cntrl_phy->oob_mode & PHY_SPEED_30)
+				phy->conn_rate = SAS_30GBPS_RATE;
+			else if (cntrl_phy->oob_mode & PHY_SPEED_15)
+				phy->conn_rate = SAS_15GBPS_RATE;
+			
+			/* Get the transport mode. */
+			if (cntrl_phy->oob_mode & SAS_MODE)
+				phy->attr |= ASD_SAS_MODE;
+			else if (cntrl_phy->oob_mode & SATA_MODE)
+				phy->attr |= ASD_SATA_MODE;
+		} else if (cntrl_phy->oob_status & CURRENT_SPINUP_HOLD) {
+			/*	
+			 * SATA target attached that has not been transmitted
+			 * COMWAKE (spun-up).
+			 */
+			asd_log(ASD_DBG_RUNTIME, "CURRENT SPINUP HOLD.\n");
+		
+			phy->attr |= ASD_SATA_SPINUP_HOLD;
+			phy->state = ASD_PHY_WAITING_FOR_ID_ADDR;
+
+		} else if (cntrl_phy->oob_status & CURRENT_ERR_MASK) {
+			asd_log(ASD_DBG_ERROR, "OOB ERROR.\n");
+			
+			phy->state = ASD_PHY_OFFLINE;
+		} else {
+			/* 
+			 * This should be the case when no device is
+			 * connected.
+			 */
+			phy->state = ASD_PHY_ONLINE;
+		}
+
+#ifdef ASD_TEST
+		asd_hwi_dump_phy(phy);
+#endif
+
+		if ((scb->flags & SCB_RECOVERY) == 0) {
+			list_del(&scb->owner_links);	
+			asd_hwi_free_scb(asd, scb);
+			asd_wakeup_sem(&asd->platform_data->discovery_sem);
+		} else {
+			scb->eh_status = (phy->state == ASD_PHY_OFFLINE) ?
+					  SCB_EH_FAILED : SCB_EH_SUCCEED;
+		}
+		break;
+
+	case RELEASE_SPINUP_HOLD:
+		/* To be implemented */
+		asd_log(ASD_DBG_RUNTIME,
+			"CONTROL PHY : RELEASE SPINUP HOLD.\n");
+		break;
+	
+	case PHY_NO_OP:
+		asd_log(ASD_DBG_RUNTIME, "CONTROL PHY : PHY NO OP.\n");
+
+		if ((scb->flags & SCB_RECOVERY) != 0) {
+			/*
+		 	 * PHY NO OP control completion. The phy no op was 
+			 * issued after HARD RESET completion.
+		 	 */
+			scb->eh_state = SCB_EH_DONE;
+			scb->eh_status = SCB_EH_SUCCEED;
+		}
+		break;
+
+	case EXECUTE_HARD_RESET:
+		asd_log(ASD_DBG_RUNTIME,"CONTROL PHY : EXECUTE HARD RESET.\n");
+
+		if ((scb->flags & SCB_RECOVERY) != 0) {
+			/* 
+	    	 	 * Upon HARD RESET completion, we need to issue 
+			 * PHY NO OP control to enable the hot-plug timer 
+			 * which was disabled prior to issuing HARD RESET.
+		 	 */
+			scb->eh_state = SCB_EH_PHY_NO_OP_REQ;
+			scb->eh_status = SCB_EH_SUCCEED;
+		}
+		break;
+
+	default:
+		asd_log(ASD_DBG_RUNTIME,
+			"CONTROL PHY : INVALID SUBFUNC OPCODE.\n");
+		break;
+	}
+
+	return;
+}
+
+#ifdef ASD_TEST
+static void
+asd_hwi_dump_phy(struct asd_phy  *phy)
+{
+	u_char	i;
+	struct asd_port	*port;
+
+	port = phy->src_port;
+	
+	asd_print("Phy Id = 0x%x.\n", phy->id);
+	asd_print("Phy attr = 0x%x.\n", phy->attr);
+	asd_print("Phy state = 0x%x.\n", phy->state);
+	asd_print("Phy conn_rate = 0x%x.\n", phy->conn_rate);
+	asd_print("Phy src port = %p.\n", phy->src_port);
+	for (i = 0; i < 8; i++)
+		asd_print("Phy %d SAS ADDR[%d]=0x%x.\n", phy->id, i,
+				phy->sas_addr[i]);
+}
+#endif
+
+union edb *
+asd_hwi_indexes_to_edb(struct asd_softc *asd, struct scb **pscb,
+		       u_int escb_index, u_int edb_index)
+{
+	struct scb 		*scb;
+	struct asd_empty_hscb 	*escb;
+	struct empty_buf_elem 	*ebe;
+
+	if (escb_index > asd->hw_profile.max_scbs)
+		return (NULL);
+	scb = asd->scbindex[escb_index];
+	if (scb == NULL)
+		return (NULL);
+	escb = &scb->hscb->empty_scb;
+	ebe = &escb->buf_elem[edb_index];
+	if (ELEM_BUFFER_VALID_FIELD(ebe) != ELEM_BUFFER_VALID)
+		return (NULL);
+	*pscb = scb;
+	return (asd_hwi_get_edb_vaddr(asd, asd_le64toh(ebe->busaddr)));
+}
+
+/*
+ * Function:
+ * 	asd_hwi_process_edb()
+ *
+ * Description:
+ *	Process Empty Data Buffer that was posted by the sequencer.
+ */
+static void
+asd_hwi_process_edb(struct asd_softc *asd, struct asd_done_list *dl_entry)
+{
+	struct edb_rcvd_sb	*edbr;
+	union  edb		*edb;
+	struct scb		*scb;
+	struct asd_phy		*phy;
+	u_char			 phy_id;
+	u_char			 elem_id;
+
+	edbr = &dl_entry->stat_blk.edb_rcvd;
+	elem_id = (dl_entry->opcode & EDB_OPCODE_MASK) - 1;
+	phy_id = edbr->sb_opcode & EDB_OPCODE_MASK;
+	phy = asd->phy_list[phy_id];
+	edbr->sb_opcode &= ~EDB_OPCODE_MASK;	
+	
+	edb = asd_hwi_indexes_to_edb(asd, &scb,
+				     asd_le16toh(dl_entry->index),
+				     elem_id);
+
+	switch (edbr->sb_opcode) {
+	case BYTES_DMAED:
+	{
+		struct bytes_dmaed_subblk	*bytes_dmaed;
+		u_int				 bytes_rcvd;
+
+		bytes_dmaed = &edbr->edb_subblk.bytes_dmaed;
+		bytes_rcvd = asd_le16toh(bytes_dmaed->edb_len) & 
+			     BYTES_DMAED_LEN_MASK;
+
+		if (bytes_rcvd > sizeof(union sas_bytes_dmaed))
+			bytes_rcvd = sizeof(union sas_bytes_dmaed);
+
+		memcpy(&phy->bytes_dmaed_rcvd.id_addr_rcvd, edb, bytes_rcvd);
+		phy->events |= ASD_ID_ADDR_RCVD;
+		phy->state = ASD_PHY_WAITING_FOR_ID_ADDR;
+		asd_wakeup_sem(&asd->platform_data->discovery_sem);
+
+#ifdef ASD_TEST
+		asd_hwi_dump_phy_id_addr(phy);
+#endif
+
+		break;
+	}
+
+	case PRIMITIVE_RCVD:
+	{
+		struct primitive_rcvd_subblk	*prim_rcvd;
+
+		prim_rcvd = &edbr->edb_subblk.prim_rcvd;
+		asd_log(ASD_DBG_RUNTIME,
+			"EDB: PRIMITIVE RCVD. Addr = 0x%x, Cont = 0x%x\n", 
+			 prim_rcvd->reg_addr, prim_rcvd->reg_content);
+		/*
+		 * Only process primitive for phy(s) that already associated
+		 * with port.
+		 */
+		if (phy->src_port != NULL)
+			asd_hwi_process_prim_event(asd, phy,
+						   prim_rcvd->reg_addr,
+						   prim_rcvd->reg_content);
+		break;
+	}
+	
+	case PHY_EVENT:
+	{
+		struct phy_event_subblk *phy_event;
+
+		phy_event = &edbr->edb_subblk.phy_event;
+		asd_log(ASD_DBG_RUNTIME,
+			"EDB: PHY_EVENT. Stat 0x%x, Mode 0x%x, Sigs = 0x%x\n",
+			phy_event->oob_status, phy_event->oob_mode,
+			phy_event->oob_signals);
+
+		phy_event->oob_status &= CURRENT_PHY_MASK;
+		asd_hwi_process_phy_event(asd, phy, 
+					  phy_event->oob_status,
+					  phy_event->oob_mode);
+		break;
+	}
+	
+	case LINK_RESET_ERR:
+	{
+		struct link_reset_err_subblk	*link_rst;
+
+		link_rst = &edbr->edb_subblk.link_reset_err;
+
+		asd_log(ASD_DBG_RUNTIME, "EDB: LINK RESET ERRORS. \n");
+		asd_log(ASD_DBG_RUNTIME, "Timedout waiting %s from Phy %d.\n",
+			((link_rst->error == RCV_FIS_TIMER_EXP) ?
+			 "Initial Device-to-Host Register FIS"  :
+			 "IDENTITY Address Frame"), 
+			phy_id);
+
+		asd_hwi_handle_link_rst_err(asd, phy);
+		break;
+
+	}
+
+	case TIMER_EVENT:
+	{
+		struct timer_event_subblk *timer_event;
+
+		timer_event = &edbr->edb_subblk.timer_event; 
+
+		asd_log(ASD_DBG_RUNTIME, "EDB: TIMER EVENT. Error = 0x%x \n", 
+			timer_event->error);
+		break;
+	}
+	
+	case REQ_TASK_ABORT:
+	{
+		struct req_task_abort_subblk	*req_task_abort;
+			
+		asd_log(ASD_DBG_RUNTIME, "EDB: REQUEST TASK ABORT. \n");
+
+		req_task_abort = &edbr->edb_subblk.req_task_abort; 
+
+		asd_log(ASD_DBG_RUNTIME, "Req TC to Abort = 0x%x, "
+			"Reason = 0x%x.\n", req_task_abort->task_tc_to_abort,
+			req_task_abort->reason);
+
+		asd_hwi_process_req_task(asd, edbr->sb_opcode,
+					 req_task_abort->task_tc_to_abort);
+		break;
+	}
+		
+	case REQ_DEVICE_RESET:
+	{
+		struct req_dev_reset_subblk	*req_dev_reset;
+
+		asd_log(ASD_DBG_RUNTIME, "EDB: REQUEST DEVICE RESET. \n");
+
+		req_dev_reset = &edbr->edb_subblk.req_dev_reset; 
+
+		asd_log(ASD_DBG_RUNTIME, "Req TC to Reset = 0x%x, "
+			"Reason = 0x%x.\n", req_dev_reset->task_tc_to_abort,
+			req_dev_reset->reason);
+
+		asd_hwi_process_req_task(asd, edbr->sb_opcode,
+					 req_dev_reset->task_tc_to_abort);
+		break;
+	}
+
+	default:
+		asd_log(ASD_DBG_RUNTIME, "Invalid EDB opcode.\n");
+		break;
+	}
+		
+	asd_hwi_free_edb(asd, scb, (elem_id));
+}



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

only message in thread, other threads:[~2005-02-17 17:36 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:36 [ANNOUNCE] Adaptec SAS/SATA device driver [07/27] Luben Tuikov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.