* [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.