Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/pm8001/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 153 kB image not shown  

Quelle  pm8001_hwi.c   Sprache: C

 
/*
 * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
 *
 * Copyright (c) 2008-2009 USI Co., Ltd.
 * 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.
 *
 */

 #include <linux/slab.h>
 #include "pm8001_sas.h"
 #include "pm8001_hwi.h"
 #include "pm8001_chips.h"
 #include "pm8001_ctl.h"
 #include "pm80xx_tracepoints.h"

/**
 * read_main_config_table - read the configure table and save it.
 * @pm8001_ha: our hba card information
 */

static void read_main_config_table(struct pm8001_hba_info *pm8001_ha)
{
 void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.signature =
    pm8001_mr32(address, 0x00);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev =
    pm8001_mr32(address, 0x04);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev =
    pm8001_mr32(address, 0x08);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io =
    pm8001_mr32(address, 0x0C);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl =
    pm8001_mr32(address, 0x10);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.ctrl_cap_flag =
    pm8001_mr32(address, 0x14);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.gst_offset =
    pm8001_mr32(address, 0x18);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_queue_offset =
  pm8001_mr32(address, MAIN_IBQ_OFFSET);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_queue_offset =
  pm8001_mr32(address, MAIN_OBQ_OFFSET);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.hda_mode_flag =
  pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET);

 /* read analog Setting offset from the configuration table */
 pm8001_ha->main_cfg_tbl.pm8001_tbl.anolog_setup_table_offset =
  pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);

 /* read Error Dump Offset and Length */
 pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_offset0 =
  pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_length0 =
  pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_offset1 =
  pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
 pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_dump_length1 =
  pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
}

/**
 * read_general_status_table - read the general status table and save it.
 * @pm8001_ha: our hba card information
 */

static void read_general_status_table(struct pm8001_hba_info *pm8001_ha)
{
 void __iomem *address = pm8001_ha->general_stat_tbl_addr;
 pm8001_ha->gs_tbl.pm8001_tbl.gst_len_mpistate =
    pm8001_mr32(address, 0x00);
 pm8001_ha->gs_tbl.pm8001_tbl.iq_freeze_state0 =
    pm8001_mr32(address, 0x04);
 pm8001_ha->gs_tbl.pm8001_tbl.iq_freeze_state1 =
    pm8001_mr32(address, 0x08);
 pm8001_ha->gs_tbl.pm8001_tbl.msgu_tcnt  =
    pm8001_mr32(address, 0x0C);
 pm8001_ha->gs_tbl.pm8001_tbl.iop_tcnt  =
    pm8001_mr32(address, 0x10);
 pm8001_ha->gs_tbl.pm8001_tbl.rsvd  =
    pm8001_mr32(address, 0x14);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[0] =
    pm8001_mr32(address, 0x18);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[1] =
    pm8001_mr32(address, 0x1C);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[2] =
    pm8001_mr32(address, 0x20);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[3] =
    pm8001_mr32(address, 0x24);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[4] =
    pm8001_mr32(address, 0x28);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[5] =
    pm8001_mr32(address, 0x2C);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[6] =
    pm8001_mr32(address, 0x30);
 pm8001_ha->gs_tbl.pm8001_tbl.phy_state[7] =
    pm8001_mr32(address, 0x34);
 pm8001_ha->gs_tbl.pm8001_tbl.gpio_input_val =
    pm8001_mr32(address, 0x38);
 pm8001_ha->gs_tbl.pm8001_tbl.rsvd1[0]  =
    pm8001_mr32(address, 0x3C);
 pm8001_ha->gs_tbl.pm8001_tbl.rsvd1[1]  =
    pm8001_mr32(address, 0x40);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[0] =
    pm8001_mr32(address, 0x44);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[1] =
    pm8001_mr32(address, 0x48);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[2] =
    pm8001_mr32(address, 0x4C);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[3] =
    pm8001_mr32(address, 0x50);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[4] =
    pm8001_mr32(address, 0x54);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[5] =
    pm8001_mr32(address, 0x58);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[6] =
    pm8001_mr32(address, 0x5C);
 pm8001_ha->gs_tbl.pm8001_tbl.recover_err_info[7] =
    pm8001_mr32(address, 0x60);
}

/**
 * read_inbnd_queue_table - read the inbound queue table and save it.
 * @pm8001_ha: our hba card information
 */

static void read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
{
 int i;
 void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
 for (i = 0; i < PM8001_MAX_INB_NUM; i++) {
  u32 offset = i * 0x20;
  pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
        get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
  pm8001_ha->inbnd_q_tbl[i].pi_offset =
   pm8001_mr32(address, (offset + 0x18));
 }
}

/**
 * read_outbnd_queue_table - read the outbound queue table and save it.
 * @pm8001_ha: our hba card information
 */

static void read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
{
 int i;
 void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
 for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) {
  u32 offset = i * 0x24;
  pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
        get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
  pm8001_ha->outbnd_q_tbl[i].ci_offset =
   pm8001_mr32(address, (offset + 0x18));
 }
}

/**
 * init_default_table_values - init the default table.
 * @pm8001_ha: our hba card information
 */

static void init_default_table_values(struct pm8001_hba_info *pm8001_ha)
{
 int i;
 u32 offsetib, offsetob;
 void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
 void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
 u32 ib_offset = pm8001_ha->ib_offset;
 u32 ob_offset = pm8001_ha->ob_offset;
 u32 ci_offset = pm8001_ha->ci_offset;
 u32 pi_offset = pm8001_ha->pi_offset;

 pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_q_nppd_hppd  = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid0_3 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid4_7 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid0_3 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid4_7 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ITNexus_event_pid0_3 =
          0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ITNexus_event_pid4_7 =
          0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ssp_event_pid0_3 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_ssp_event_pid4_7 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_smp_event_pid0_3 = 0;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_tgt_smp_event_pid4_7 = 0;

 pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_event_log_addr  =
  pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_event_log_addr  =
  pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_size  =
  PM8001_EVENT_LOG_SIZE;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_option  = 0x01;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_iop_event_log_addr =
  pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_iop_event_log_addr =
  pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_size  =
  PM8001_EVENT_LOG_SIZE;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_option  = 0x01;
 pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt  = 0x01;
 for (i = 0; i < pm8001_ha->max_q_num; i++) {
  pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
   PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30);
  pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
   pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_hi;
  pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
  pm8001_ha->memoryMap.region[ib_offset + i].phys_addr_lo;
  pm8001_ha->inbnd_q_tbl[i].base_virt  =
    (u8 *)pm8001_ha->memoryMap.region[ib_offset + i].virt_ptr;
  pm8001_ha->inbnd_q_tbl[i].total_length  =
   pm8001_ha->memoryMap.region[ib_offset + i].total_len;
  pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
   pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_hi;
  pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
   pm8001_ha->memoryMap.region[ci_offset + i].phys_addr_lo;
  pm8001_ha->inbnd_q_tbl[i].ci_virt  =
   pm8001_ha->memoryMap.region[ci_offset + i].virt_ptr;
  pm8001_write_32(pm8001_ha->inbnd_q_tbl[i].ci_virt, 0, 0);
  offsetib = i * 0x20;
  pm8001_ha->inbnd_q_tbl[i].pi_pci_bar  =
   get_pci_bar_index(pm8001_mr32(addressib,
    (offsetib + 0x14)));
  pm8001_ha->inbnd_q_tbl[i].pi_offset  =
   pm8001_mr32(addressib, (offsetib + 0x18));
  pm8001_ha->inbnd_q_tbl[i].producer_idx  = 0;
  pm8001_ha->inbnd_q_tbl[i].consumer_index = 0;
 }
 for (i = 0; i < pm8001_ha->max_q_num; i++) {
  pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
   PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30);
  pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
   pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_hi;
  pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
   pm8001_ha->memoryMap.region[ob_offset + i].phys_addr_lo;
  pm8001_ha->outbnd_q_tbl[i].base_virt  =
    (u8 *)pm8001_ha->memoryMap.region[ob_offset + i].virt_ptr;
  pm8001_ha->outbnd_q_tbl[i].total_length  =
   pm8001_ha->memoryMap.region[ob_offset + i].total_len;
  pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
   pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_hi;
  pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
   pm8001_ha->memoryMap.region[pi_offset + i].phys_addr_lo;
  pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay =
   0 | (10 << 16) | (i << 24);
  pm8001_ha->outbnd_q_tbl[i].pi_virt  =
   pm8001_ha->memoryMap.region[pi_offset + i].virt_ptr;
  pm8001_write_32(pm8001_ha->outbnd_q_tbl[i].pi_virt, 0, 0);
  offsetob = i * 0x24;
  pm8001_ha->outbnd_q_tbl[i].ci_pci_bar  =
   get_pci_bar_index(pm8001_mr32(addressob,
   offsetob + 0x14));
  pm8001_ha->outbnd_q_tbl[i].ci_offset  =
   pm8001_mr32(addressob, (offsetob + 0x18));
  pm8001_ha->outbnd_q_tbl[i].consumer_idx  = 0;
  pm8001_ha->outbnd_q_tbl[i].producer_index = 0;
 }
}

/**
 * update_main_config_table - update the main default table to the HBA.
 * @pm8001_ha: our hba card information
 */

static void update_main_config_table(struct pm8001_hba_info *pm8001_ha)
{
 void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
 pm8001_mw32(address, 0x24,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.inbound_q_nppd_hppd);
 pm8001_mw32(address, 0x28,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid0_3);
 pm8001_mw32(address, 0x2C,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_hw_event_pid4_7);
 pm8001_mw32(address, 0x30,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid0_3);
 pm8001_mw32(address, 0x34,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.outbound_ncq_event_pid4_7);
 pm8001_mw32(address, 0x38,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_ITNexus_event_pid0_3);
 pm8001_mw32(address, 0x3C,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_ITNexus_event_pid4_7);
 pm8001_mw32(address, 0x40,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_ssp_event_pid0_3);
 pm8001_mw32(address, 0x44,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_ssp_event_pid4_7);
 pm8001_mw32(address, 0x48,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_smp_event_pid0_3);
 pm8001_mw32(address, 0x4C,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.
     outbound_tgt_smp_event_pid4_7);
 pm8001_mw32(address, 0x50,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_event_log_addr);
 pm8001_mw32(address, 0x54,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_event_log_addr);
 pm8001_mw32(address, 0x58,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_size);
 pm8001_mw32(address, 0x5C,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.event_log_option);
 pm8001_mw32(address, 0x60,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.upper_iop_event_log_addr);
 pm8001_mw32(address, 0x64,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.lower_iop_event_log_addr);
 pm8001_mw32(address, 0x68,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_size);
 pm8001_mw32(address, 0x6C,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.iop_event_log_option);
 pm8001_mw32(address, 0x70,
  pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt);
}

/**
 * update_inbnd_queue_table - update the inbound queue table to the HBA.
 * @pm8001_ha: our hba card information
 * @number: entry in the queue
 */

static void update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
         int number)
{
 void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
 u16 offset = number * 0x20;
 pm8001_mw32(address, offset + 0x00,
  pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
 pm8001_mw32(address, offset + 0x04,
  pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
 pm8001_mw32(address, offset + 0x08,
  pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
 pm8001_mw32(address, offset + 0x0C,
  pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
 pm8001_mw32(address, offset + 0x10,
  pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
}

/**
 * update_outbnd_queue_table - update the outbound queue table to the HBA.
 * @pm8001_ha: our hba card information
 * @number: entry in the queue
 */

static void update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha,
          int number)
{
 void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
 u16 offset = number * 0x24;
 pm8001_mw32(address, offset + 0x00,
  pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
 pm8001_mw32(address, offset + 0x04,
  pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
 pm8001_mw32(address, offset + 0x08,
  pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
 pm8001_mw32(address, offset + 0x0C,
  pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
 pm8001_mw32(address, offset + 0x10,
  pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
 pm8001_mw32(address, offset + 0x1C,
  pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
}

/**
 * pm8001_bar4_shift - function is called to shift BAR base address
 * @pm8001_ha : our hba card information
 * @shiftValue : shifting value in memory bar.
 */

int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
{
 u32 regVal;
 unsigned long start;

 /* program the inbound AXI translation Lower Address */
 pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);

 /* confirm the setting is written */
 start = jiffies + HZ; /* 1 sec */
 do {
  regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
 } while ((regVal != shiftValue) && time_before(jiffies, start));

 if (regVal != shiftValue) {
  pm8001_dbg(pm8001_ha, INIT,
      "TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW = 0x%x\n",
      regVal);
  return -1;
 }
 return 0;
}

/**
 * mpi_set_phys_g3_with_ssc
 * @pm8001_ha: our hba card information
 * @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc.
 */

static void mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha,
         u32 SSCbit)
{
 u32 offset, i;
 unsigned long flags;

#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
#define SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET 0x1074
#define SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET 0x1074
#define PHY_G3_WITHOUT_SSC_BIT_SHIFT 12
#define PHY_G3_WITH_SSC_BIT_SHIFT 13
#define SNW3_PHY_CAPABILITIES_PARITY 31

   /*
    * Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
    * Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
    */

 spin_lock_irqsave(&pm8001_ha->lock, flags);
 if (-1 == pm8001_bar4_shift(pm8001_ha,
    SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  return;
 }

 for (i = 0; i < 4; i++) {
  offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
  pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
 }
 /* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
 if (-1 == pm8001_bar4_shift(pm8001_ha,
    SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  return;
 }
 for (i = 4; i < 8; i++) {
  offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
  pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
 }
 /*************************************************************
Change the SSC upspreading value to 0x0 so that upspreading is disabled.
Device MABC SMOD0 Controls
Address: (via MEMBASE-III):
Using shifted destination address 0x0_0000: with Offset 0xD8

31:28 R/W Reserved Do not change
27:24 R/W SAS_SMOD_SPRDUP 0000
23:20 R/W SAS_SMOD_SPRDDN 0000
19:0  R/W  Reserved Do not change
Upon power-up this register will read as 0x8990c016,
and I would like you to change the SAS_SMOD_SPRDUP bits to 0b0000
so that the written value will be 0x8090c016.
This will ensure only down-spreading SSC is enabled on the SPC.
*************************************************************/

 pm8001_cr32(pm8001_ha, 2, 0xd8);
 pm8001_cw32(pm8001_ha, 2, 0xd8, 0x8000C016);

 /*set the shifted destination address to 0x0 to avoid error operation */
 pm8001_bar4_shift(pm8001_ha, 0x0);
 spin_unlock_irqrestore(&pm8001_ha->lock, flags);
 return;
}

/**
 * mpi_set_open_retry_interval_reg
 * @pm8001_ha: our hba card information
 * @interval: interval time for each OPEN_REJECT (RETRY). The units are in 1us.
 */

static void mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
         u32 interval)
{
 u32 offset;
 u32 value;
 u32 i;
 unsigned long flags;

#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
#define OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET 0x30B4
#define OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET 0x30B4
#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF

 value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
 spin_lock_irqsave(&pm8001_ha->lock, flags);
 /* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
 if (-1 == pm8001_bar4_shift(pm8001_ha,
        OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  return;
 }
 for (i = 0; i < 4; i++) {
  offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
  pm8001_cw32(pm8001_ha, 2, offset, value);
 }

 if (-1 == pm8001_bar4_shift(pm8001_ha,
        OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  return;
 }
 for (i = 4; i < 8; i++) {
  offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
  pm8001_cw32(pm8001_ha, 2, offset, value);
 }
 /*set the shifted destination address to 0x0 to avoid error operation */
 pm8001_bar4_shift(pm8001_ha, 0x0);
 spin_unlock_irqrestore(&pm8001_ha->lock, flags);
 return;
}

/**
 * mpi_init_check - check firmware initialization status.
 * @pm8001_ha: our hba card information
 */

static int mpi_init_check(struct pm8001_hba_info *pm8001_ha)
{
 u32 max_wait_count;
 u32 value;
 u32 gst_len_mpistate;
 /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the
table is updated */

 pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE);
 /* wait until Inbound DoorBell Clear Register toggled */
 max_wait_count = 1 * 1000 * 1000;/* 1 sec */
 do {
  udelay(1);
  value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
  value &= SPC_MSGU_CFG_TABLE_UPDATE;
 } while ((value != 0) && (--max_wait_count));

 if (!max_wait_count)
  return -1;
 /* check the MPI-State for initialization */
 gst_len_mpistate =
  pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
  GST_GSTLEN_MPIS_OFFSET);
 if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK))
  return -1;
 /* check MPI Initialization error */
 gst_len_mpistate = gst_len_mpistate >> 16;
 if (0x0000 != gst_len_mpistate)
  return -1;
 return 0;
}

/**
 * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
 * @pm8001_ha: our hba card information
 */

static int check_fw_ready(struct pm8001_hba_info *pm8001_ha)
{
 u32 value, value1;
 u32 max_wait_count;
 /* check error state */
 value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
 value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
 /* check AAP error */
 if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) {
  /* error state */
  value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
  return -1;
 }

 /* check IOP error */
 if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) {
  /* error state */
  value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
  return -1;
 }

 /* bit 4-31 of scratch pad1 should be zeros if it is not
in error state*/

 if (value & SCRATCH_PAD1_STATE_MASK) {
  /* error case */
  pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
  return -1;
 }

 /* bit 2, 4-31 of scratch pad2 should be zeros if it is not
in error state */

 if (value1 & SCRATCH_PAD2_STATE_MASK) {
  /* error case */
  return -1;
 }

 max_wait_count = 1 * 1000 * 1000;/* 1 sec timeout */

 /* wait until scratch pad 1 and 2 registers in ready state  */
 do {
  udelay(1);
  value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
   & SCRATCH_PAD1_RDY;
  value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
   & SCRATCH_PAD2_RDY;
  if ((--max_wait_count) == 0)
   return -1;
 } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY));
 return 0;
}

static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
{
 void __iomem *base_addr;
 u32 value;
 u32 offset;
 u32 pcibar;
 u32 pcilogic;

 value = pm8001_cr32(pm8001_ha, 0, 0x44);
 offset = value & 0x03FFFFFF;
 pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 Offset: %x\n", offset);
 pcilogic = (value & 0xFC000000) >> 26;
 pcibar = get_pci_bar_index(pcilogic);
 pm8001_dbg(pm8001_ha, INIT, "Scratchpad 0 PCI BAR: %d\n", pcibar);
 pm8001_ha->main_cfg_tbl_addr = base_addr =
  pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
 pm8001_ha->general_stat_tbl_addr =
  base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x18);
 pm8001_ha->inbnd_q_tbl_addr =
  base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C);
 pm8001_ha->outbnd_q_tbl_addr =
  base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20);
}

/**
 * pm8001_chip_init - the main init function that initialize whole PM8001 chip.
 * @pm8001_ha: our hba card information
 */

static int pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
{
 u32 i = 0;
 u16 deviceid;
 pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
 /* 8081 controllers need BAR shift to access MPI space
* as this is shared with BIOS data */

 if (deviceid == 0x8081 || deviceid == 0x0042) {
  if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
   pm8001_dbg(pm8001_ha, FAIL,
       "Shift Bar4 to 0x%x failed\n",
       GSM_SM_BASE);
   return -1;
  }
 }
 /* check the firmware status */
 if (-1 == check_fw_ready(pm8001_ha)) {
  pm8001_dbg(pm8001_ha, FAIL, "Firmware is not ready!\n");
  return -EBUSY;
 }

 /* Initialize pci space address eg: mpi offset */
 init_pci_device_addresses(pm8001_ha);
 init_default_table_values(pm8001_ha);
 read_main_config_table(pm8001_ha);
 read_general_status_table(pm8001_ha);
 read_inbnd_queue_table(pm8001_ha);
 read_outbnd_queue_table(pm8001_ha);
 /* update main config table ,inbound table and outbound table */
 update_main_config_table(pm8001_ha);
 for (i = 0; i < pm8001_ha->max_q_num; i++)
  update_inbnd_queue_table(pm8001_ha, i);
 for (i = 0; i < pm8001_ha->max_q_num; i++)
  update_outbnd_queue_table(pm8001_ha, i);
 /* 8081 controller donot require these operations */
 if (deviceid != 0x8081 && deviceid != 0x0042) {
  mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
  /* 7->130ms, 34->500ms, 119->1.5s */
  mpi_set_open_retry_interval_reg(pm8001_ha, 119);
 }
 /* notify firmware update finished and check initialization status */
 if (0 == mpi_init_check(pm8001_ha)) {
  pm8001_dbg(pm8001_ha, INIT, "MPI initialize successful!\n");
 } else
  return -EBUSY;
 /*This register is a 16-bit timer with a resolution of 1us. This is the
timer used for interrupt delay/coalescing in the PCIe Application Layer.
Zero is not a valid value. A value of 1 in the register will cause the
interrupts to be normal. A value greater than 1 will cause coalescing
delays.*/

 pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1);
 pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0x0);
 return 0;
}

static void pm8001_chip_post_init(struct pm8001_hba_info *pm8001_ha)
{
}

static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
{
 u32 max_wait_count;
 u32 value;
 u32 gst_len_mpistate;
 u16 deviceid;
 pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid);
 if (deviceid == 0x8081 || deviceid == 0x0042) {
  if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_SM_BASE)) {
   pm8001_dbg(pm8001_ha, FAIL,
       "Shift Bar4 to 0x%x failed\n",
       GSM_SM_BASE);
   return -1;
  }
 }
 init_pci_device_addresses(pm8001_ha);
 /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
table is stop */

 pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET);

 /* wait until Inbound DoorBell Clear Register toggled */
 max_wait_count = 1 * 1000 * 1000;/* 1 sec */
 do {
  udelay(1);
  value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
  value &= SPC_MSGU_CFG_TABLE_RESET;
 } while ((value != 0) && (--max_wait_count));

 if (!max_wait_count) {
  pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:IBDB value/=0x%x\n",
      value);
  return -1;
 }

 /* check the MPI-State for termination in progress */
 /* wait until Inbound DoorBell Clear Register toggled */
 max_wait_count = 1 * 1000 * 1000;  /* 1 sec */
 do {
  udelay(1);
  gst_len_mpistate =
   pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
   GST_GSTLEN_MPIS_OFFSET);
  if (GST_MPI_STATE_UNINIT ==
   (gst_len_mpistate & GST_MPI_STATE_MASK))
   break;
 } while (--max_wait_count);
 if (!max_wait_count) {
  pm8001_dbg(pm8001_ha, FAIL, " TIME OUT MPI State = 0x%x\n",
      gst_len_mpistate & GST_MPI_STATE_MASK);
  return -1;
 }
 return 0;
}

/**
 * soft_reset_ready_check - Function to check FW is ready for soft reset.
 * @pm8001_ha: our hba card information
 */

static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
{
 u32 regVal, regVal1, regVal2;
 if (mpi_uninit_check(pm8001_ha) != 0) {
  pm8001_dbg(pm8001_ha, FAIL, "MPI state is not ready\n");
  return -1;
 }
 /* read the scratch pad 2 register bit 2 */
 regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
  & SCRATCH_PAD2_FWRDY_RST;
 if (regVal == SCRATCH_PAD2_FWRDY_RST) {
  pm8001_dbg(pm8001_ha, INIT, "Firmware is ready for reset.\n");
 } else {
  unsigned long flags;
  /* Trigger NMI twice via RB6 */
  spin_lock_irqsave(&pm8001_ha->lock, flags);
  if (-1 == pm8001_bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   pm8001_dbg(pm8001_ha, FAIL,
       "Shift Bar4 to 0x%x failed\n",
       RB6_ACCESS_REG);
   return -1;
  }
  pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET,
   RB6_MAGIC_NUMBER_RST);
  pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST);
  /* wait for 100 ms */
  mdelay(100);
  regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) &
   SCRATCH_PAD2_FWRDY_RST;
  if (regVal != SCRATCH_PAD2_FWRDY_RST) {
   regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
   regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
   pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT:MSGU_SCRATCH_PAD1=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
       regVal1, regVal2);
   pm8001_dbg(pm8001_ha, FAIL,
       "SCRATCH_PAD0 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0));
   pm8001_dbg(pm8001_ha, FAIL,
       "SCRATCH_PAD3 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3));
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   return -1;
  }
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
 }
 return 0;
}

/**
 * pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all
 * the FW register status to the originated status.
 * @pm8001_ha: our hba card information
 */

static int
pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha)
{
 u32 regVal, toggleVal;
 u32 max_wait_count;
 u32 regVal1, regVal2, regVal3;
 u32 signature = 0x252acbcd; /* for host scratch pad0 */
 unsigned long flags;

 /* step1: Check FW is ready for soft reset */
 if (soft_reset_ready_check(pm8001_ha) != 0) {
  pm8001_dbg(pm8001_ha, FAIL, "FW is not ready\n");
  return -1;
 }

 /* step 2: clear NMI status register on AAP1 and IOP, write the same
value to clear */

 /* map 0x60000 to BAR4(0x20), BAR2(win) */
 spin_lock_irqsave(&pm8001_ha->lock, flags);
 if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
      MBIC_AAP1_ADDR_BASE);
  return -1;
 }
 regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP);
 pm8001_dbg(pm8001_ha, INIT, "MBIC - NMI Enable VPE0 (IOP)= 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
 /* map 0x70000 to BAR4(0x20), BAR2(win) */
 if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
      MBIC_IOP_ADDR_BASE);
  return -1;
 }
 regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1);
 pm8001_dbg(pm8001_ha, INIT, "MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0);

 regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE);
 pm8001_dbg(pm8001_ha, INIT, "PCIE -Event Interrupt Enable = 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0);

 regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT);
 pm8001_dbg(pm8001_ha, INIT, "PCIE - Event Interrupt = 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal);

 regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE);
 pm8001_dbg(pm8001_ha, INIT, "PCIE -Error Interrupt Enable = 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0);

 regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT);
 pm8001_dbg(pm8001_ha, INIT, "PCIE - Error Interrupt = 0x%x\n", regVal);
 pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal);

 /* read the scratch pad 1 register bit 2 */
 regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
  & SCRATCH_PAD1_RST;
 toggleVal = regVal ^ SCRATCH_PAD1_RST;

 /* set signature in host scratch pad0 register to tell SPC that the
host performs the soft reset */

 pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature);

 /* read required registers for confirmming */
 /* map 0x0700000 to BAR4(0x20), BAR2(win) */
 if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
      GSM_ADDR_BASE);
  return -1;
 }
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x0(0x00007b88)-GSM Configuration and Reset = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));

 /* step 3: host read GSM Configuration and Reset register */
 regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
 /* Put those bits to low */
 /* GSM XCBI offset = 0x70 0000
0x00 Bit 13 COM_SLV_SW_RSTB 1
0x00 Bit 12 QSSP_SW_RSTB 1
0x00 Bit 11 RAAE_SW_RSTB 1
0x00 Bit 9 RB_1_SW_RSTB 1
0x00 Bit 8 SM_SW_RSTB 1
*/

 regVal &= ~(0x00003b00);
 /* host write GSM Configuration and Reset register */
 pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM Configuration and Reset is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));

 /* step 4: */
 /* disable GSM - Read Address Parity Check */
 regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700038 - Read Address Parity Check Enable = 0x%x\n",
     regVal1);
 pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700038 - Read Address Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK));

 /* disable GSM - Write Address Parity Check */
 regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700040 - Write Address Parity Check Enable = 0x%x\n",
     regVal2);
 pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700040 - Write Address Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK));

 /* disable GSM - Write Data Parity Check */
 regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
 pm8001_dbg(pm8001_ha, INIT, "GSM 0x300048 - Write Data Parity Check Enable = 0x%x\n",
     regVal3);
 pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x300048 - Write Data Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK));

 /* step 5: delay 10 usec */
 udelay(10);
 /* step 5-b: set GPIO-0 output control to tristate anyway */
 if (-1 == pm8001_bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, INIT, "Shift Bar4 to 0x%x failed\n",
      GPIO_ADDR_BASE);
  return -1;
 }
 regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET);
 pm8001_dbg(pm8001_ha, INIT, "GPIO Output Control Register: = 0x%x\n",
     regVal);
 /* set GPIO-0 output control to tri-state */
 regVal &= 0xFFFFFFFC;
 pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal);

 /* Step 6: Reset the IOP and AAP1 */
 /* map 0x00000 to BAR4(0x20), BAR2(win) */
 if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "SPC Shift Bar4 to 0x%x failed\n",
      SPC_TOP_LEVEL_ADDR_BASE);
  return -1;
 }
 regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
 pm8001_dbg(pm8001_ha, INIT, "Top Register before resetting IOP/AAP1:= 0x%x\n",
     regVal);
 regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
 pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);

 /* step 7: Reset the BDMA/OSSP */
 regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
 pm8001_dbg(pm8001_ha, INIT, "Top Register before resetting BDMA/OSSP: = 0x%x\n",
     regVal);
 regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
 pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);

 /* step 8: delay 10 usec */
 udelay(10);

 /* step 9: bring the BDMA and OSSP out of reset */
 regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
 pm8001_dbg(pm8001_ha, INIT,
     "Top Register before bringing up BDMA/OSSP:= 0x%x\n",
     regVal);
 regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
 pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);

 /* step 10: delay 10 usec */
 udelay(10);

 /* step 11: reads and sets the GSM Configuration and Reset Register */
 /* map 0x0700000 to BAR4(0x20), BAR2(win) */
 if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "SPC Shift Bar4 to 0x%x failed\n",
      GSM_ADDR_BASE);
  return -1;
 }
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x0 (0x00007b88)-GSM Configuration and Reset = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));
 regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
 /* Put those bits to high */
 /* GSM XCBI offset = 0x70 0000
0x00 Bit 13 COM_SLV_SW_RSTB 1
0x00 Bit 12 QSSP_SW_RSTB 1
0x00 Bit 11 RAAE_SW_RSTB 1
0x00 Bit 9   RB_1_SW_RSTB 1
0x00 Bit 8   SM_SW_RSTB 1
*/

 regVal |= (GSM_CONFIG_RESET_VALUE);
 pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
 pm8001_dbg(pm8001_ha, INIT, "GSM (0x00004088 ==> 0x00007b88) - GSM Configuration and Reset is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET));

 /* step 12: Restore GSM - Read Address Parity Check */
 regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
 /* just for debugging */
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700038 - Read Address Parity Check Enable = 0x%x\n",
     regVal);
 pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1);
 pm8001_dbg(pm8001_ha, INIT, "GSM 0x700038 - Read Address Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK));
 /* Restore GSM - Write Address Parity Check */
 regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
 pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700040 - Write Address Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK));
 /* Restore GSM - Write Data Parity Check */
 regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
 pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3);
 pm8001_dbg(pm8001_ha, INIT,
     "GSM 0x700048 - Write Data Parity Check Enable is set to = 0x%x\n",
     pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK));

 /* step 13: bring the IOP and AAP1 out of reset */
 /* map 0x00000 to BAR4(0x20), BAR2(win) */
 if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
  spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  pm8001_dbg(pm8001_ha, FAIL, "Shift Bar4 to 0x%x failed\n",
      SPC_TOP_LEVEL_ADDR_BASE);
  return -1;
 }
 regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
 regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
 pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);

 /* step 14: delay 10 usec - Normal Mode */
 udelay(10);
 /* check Soft Reset Normal mode or Soft Reset HDA mode */
 if (signature == SPC_SOFT_RESET_SIGNATURE) {
  /* step 15 (Normal Mode): wait until scratch pad1 register
bit 2 toggled */

  max_wait_count = 2 * 1000 * 1000;/* 2 sec */
  do {
   udelay(1);
   regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) &
    SCRATCH_PAD1_RST;
  } while ((regVal != toggleVal) && (--max_wait_count));

  if (!max_wait_count) {
   regVal = pm8001_cr32(pm8001_ha, 0,
    MSGU_SCRATCH_PAD_1);
   pm8001_dbg(pm8001_ha, FAIL, "TIMEOUT : ToggleVal 0x%x,MSGU_SCRATCH_PAD1 = 0x%x\n",
       toggleVal, regVal);
   pm8001_dbg(pm8001_ha, FAIL,
       "SCRATCH_PAD0 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0,
            MSGU_SCRATCH_PAD_0));
   pm8001_dbg(pm8001_ha, FAIL,
       "SCRATCH_PAD2 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0,
            MSGU_SCRATCH_PAD_2));
   pm8001_dbg(pm8001_ha, FAIL,
       "SCRATCH_PAD3 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0,
            MSGU_SCRATCH_PAD_3));
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   return -1;
  }

  /* step 16 (Normal) - Clear ODMR and ODCR */
  pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
  pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);

  /* step 17 (Normal Mode): wait for the FW and IOP to get
ready - 1 sec timeout */

  /* Wait for the SPC Configuration Table to be ready */
  if (check_fw_ready(pm8001_ha) == -1) {
   regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
   /* return error if MPI Configuration Table not ready */
   pm8001_dbg(pm8001_ha, INIT,
       "FW not ready SCRATCH_PAD1 = 0x%x\n",
       regVal);
   regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
   /* return error if MPI Configuration Table not ready */
   pm8001_dbg(pm8001_ha, INIT,
       "FW not ready SCRATCH_PAD2 = 0x%x\n",
       regVal);
   pm8001_dbg(pm8001_ha, INIT,
       "SCRATCH_PAD0 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0,
            MSGU_SCRATCH_PAD_0));
   pm8001_dbg(pm8001_ha, INIT,
       "SCRATCH_PAD3 value = 0x%x\n",
       pm8001_cr32(pm8001_ha, 0,
            MSGU_SCRATCH_PAD_3));
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   return -1;
  }
 }
 pm8001_bar4_shift(pm8001_ha, 0);
 spin_unlock_irqrestore(&pm8001_ha->lock, flags);

 pm8001_dbg(pm8001_ha, INIT, "SPC soft reset Complete\n");
 return 0;
}

static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
{
 u32 i;
 u32 regVal;
 pm8001_dbg(pm8001_ha, INIT, "chip reset start\n");

 /* do SPC chip reset. */
 regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
 regVal &= ~(SPC_REG_RESET_DEVICE);
 pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);

 /* delay 10 usec */
 udelay(10);

 /* bring chip reset out of reset */
 regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
 regVal |= SPC_REG_RESET_DEVICE;
 pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);

 /* delay 10 usec */
 udelay(10);

 /* wait for 20 msec until the firmware gets reloaded */
 i = 20;
 do {
  mdelay(1);
 } while ((--i) != 0);

 pm8001_dbg(pm8001_ha, INIT, "chip reset finished\n");
}

/**
 * pm8001_chip_iounmap - which mapped when initialized.
 * @pm8001_ha: our hba card information
 */

void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
{
 s8 bar, logical = 0;
 for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
  /*
** logical BARs for SPC:
** bar 0 and 1 - logical BAR0
** bar 2 and 3 - logical BAR1
** bar4 - logical BAR2
** bar5 - logical BAR3
** Skip the appropriate assignments:
*/

  if ((bar == 1) || (bar == 3))
   continue;
  if (pm8001_ha->io_mem[logical].memvirtaddr) {
   iounmap(pm8001_ha->io_mem[logical].memvirtaddr);
   logical++;
  }
 }
}

/**
 * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
 * @pm8001_ha: our hba card information
 * @vec: unused
 */

static void
pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
 if (pm8001_ha->use_msix) {
  pm8001_cw32(pm8001_ha, 0, MSIX_TABLE_BASE,
       MSIX_INTERRUPT_ENABLE);
  pm8001_cw32(pm8001_ha, 0,  MSGU_ODCR, 1);
 } else {
  pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
  pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
 }
}

/**
 * pm8001_chip_interrupt_disable - disable PM8001 chip interrupt
 * @pm8001_ha: our hba card information
 * @vec: unused
 */

static void
pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha, u8 vec)
{
 if (pm8001_ha->use_msix)
  pm8001_cw32(pm8001_ha, 0, MSIX_TABLE_BASE,
       MSIX_INTERRUPT_DISABLE);
 else
  pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
}

/**
 * pm8001_mpi_msg_free_get - get the free message buffer for transfer
 * inbound queue.
 * @circularQ: the inbound queue  we want to transfer to HBA.
 * @messageSize: the message size of this transfer, normally it is 64 bytes
 * @messagePtr: the pointer to message.
 */

int pm8001_mpi_msg_free_get(struct inbound_queue_table *circularQ,
       u16 messageSize, void **messagePtr)
{
 u32 offset, consumer_index;
 struct mpi_msg_hdr *msgHeader;
 u8 bcCount = 1; /* only support single buffer */

 /* Checks is the requested message size can be allocated in this queue*/
 if (messageSize > IOMB_SIZE_SPCV) {
  *messagePtr = NULL;
  return -1;
 }

 /* Stores the new consumer index */
 consumer_index = pm8001_read_32(circularQ->ci_virt);
 circularQ->consumer_index = cpu_to_le32(consumer_index);
 if (((circularQ->producer_idx + bcCount) % PM8001_MPI_QUEUE) ==
  le32_to_cpu(circularQ->consumer_index)) {
  *messagePtr = NULL;
  return -1;
 }
 /* get memory IOMB buffer address */
 offset = circularQ->producer_idx * messageSize;
 /* increment to next bcCount element */
 circularQ->producer_idx = (circularQ->producer_idx + bcCount)
    % PM8001_MPI_QUEUE;
 /* Adds that distance to the base of the region virtual address plus
the message header size*/

 msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset);
 *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr);
 return 0;
}

/**
 * pm8001_mpi_build_cmd- build the message queue for transfer, update the PI to
 * FW to tell the fw to get this message from IOMB.
 * @pm8001_ha: our hba card information
 * @q_index: the index in the inbound queue we want to transfer to HBA.
 * @opCode: the operation code represents commands which LLDD and fw recognized.
 * @payload: the command payload of each operation command.
 * @nb: size in bytes of the command payload
 * @responseQueue: queue to interrupt on w/ command response (if any)
 */

int pm8001_mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
    u32 q_index, u32 opCode, void *payload, size_t nb,
    u32 responseQueue)
{
 u32 Header = 0, hpriority = 0, bc = 1, category = 0x02;
 void *pMessage;
 unsigned long flags;
 struct inbound_queue_table *circularQ = &pm8001_ha->inbnd_q_tbl[q_index];
 int rv;
 u32 htag = le32_to_cpu(*(__le32 *)payload);

 trace_pm80xx_mpi_build_cmd(pm8001_ha->id, opCode, htag, q_index,
  circularQ->producer_idx, le32_to_cpu(circularQ->consumer_index));

 if (WARN_ON(q_index >= pm8001_ha->max_q_num))
  return -EINVAL;

 spin_lock_irqsave(&circularQ->iq_lock, flags);
 rv = pm8001_mpi_msg_free_get(circularQ, pm8001_ha->iomb_size,
   &pMessage);
 if (rv < 0) {
  pm8001_dbg(pm8001_ha, IO, "No free mpi buffer\n");
  rv = -ENOMEM;
  goto done;
 }

 if (nb > (pm8001_ha->iomb_size - sizeof(struct mpi_msg_hdr)))
  nb = pm8001_ha->iomb_size - sizeof(struct mpi_msg_hdr);
 memcpy(pMessage, payload, nb);
 if (nb + sizeof(struct mpi_msg_hdr) < pm8001_ha->iomb_size)
  memset(pMessage + nb, 0, pm8001_ha->iomb_size -
    (nb + sizeof(struct mpi_msg_hdr)));

 /*Build the header*/
 Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
  | ((responseQueue & 0x3F) << 16)
  | ((category & 0xF) << 12) | (opCode & 0xFFF));

 pm8001_write_32((pMessage - 4), 0, cpu_to_le32(Header));
 /*Update the PI to the firmware*/
 pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar,
  circularQ->pi_offset, circularQ->producer_idx);
 pm8001_dbg(pm8001_ha, DEVIO,
     "INB Q %x OPCODE:%x , UPDATED PI=%d CI=%d\n",
     responseQueue, opCode, circularQ->producer_idx,
     circularQ->consumer_index);
done:
 spin_unlock_irqrestore(&circularQ->iq_lock, flags);
 return rv;
}

u32 pm8001_mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
       struct outbound_queue_table *circularQ, u8 bc)
{
 u32 producer_index;
 struct mpi_msg_hdr *msgHeader;
 struct mpi_msg_hdr *pOutBoundMsgHeader;

 msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr));
 pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt +
    circularQ->consumer_idx * pm8001_ha->iomb_size);
 if (pOutBoundMsgHeader != msgHeader) {
  pm8001_dbg(pm8001_ha, FAIL,
      "consumer_idx = %d msgHeader = %p\n",
      circularQ->consumer_idx, msgHeader);

  /* Update the producer index from SPC */
  producer_index = pm8001_read_32(circularQ->pi_virt);
  circularQ->producer_index = cpu_to_le32(producer_index);
  pm8001_dbg(pm8001_ha, FAIL,
      "consumer_idx = %d producer_index = %dmsgHeader = %p\n",
      circularQ->consumer_idx,
      circularQ->producer_index, msgHeader);
  return 0;
 }
 /* free the circular queue buffer elements associated with the message*/
 circularQ->consumer_idx = (circularQ->consumer_idx + bc)
    % PM8001_MPI_QUEUE;
 /* update the CI of outbound queue */
 pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset,
  circularQ->consumer_idx);
 /* Update the producer index from SPC*/
 producer_index = pm8001_read_32(circularQ->pi_virt);
 circularQ->producer_index = cpu_to_le32(producer_index);
 pm8001_dbg(pm8001_ha, IO, " CI=%d PI=%d\n",
     circularQ->consumer_idx, circularQ->producer_index);
 return 0;
}

/**
 * pm8001_mpi_msg_consume- get the MPI message from outbound queue
 * message table.
 * @pm8001_ha: our hba card information
 * @circularQ: the outbound queue  table.
 * @messagePtr1: the message contents of this outbound message.
 * @pBC: the message size.
 */

u32 pm8001_mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
      struct outbound_queue_table *circularQ,
      void **messagePtr1, u8 *pBC)
{
 struct mpi_msg_hdr *msgHeader;
 __le32 msgHeader_tmp;
 u32 header_tmp;
 do {
  /* If there are not-yet-delivered messages ... */
  if (le32_to_cpu(circularQ->producer_index)
   != circularQ->consumer_idx) {
   /*Get the pointer to the circular queue buffer element*/
   msgHeader = (struct mpi_msg_hdr *)
    (circularQ->base_virt +
    circularQ->consumer_idx * pm8001_ha->iomb_size);
   /* read header */
   header_tmp = pm8001_read_32(msgHeader);
   msgHeader_tmp = cpu_to_le32(header_tmp);
   pm8001_dbg(pm8001_ha, DEVIO,
       "outbound opcode msgheader:%x ci=%d pi=%d\n",
       msgHeader_tmp, circularQ->consumer_idx,
       circularQ->producer_index);
   if (0 != (le32_to_cpu(msgHeader_tmp) & 0x80000000)) {
    if (OPC_OUB_SKIP_ENTRY !=
     (le32_to_cpu(msgHeader_tmp) & 0xfff)) {
     *messagePtr1 =
      ((u8 *)msgHeader) +
      sizeof(struct mpi_msg_hdr);
     *pBC = (u8)((le32_to_cpu(msgHeader_tmp)
      >> 24) & 0x1f);
     pm8001_dbg(pm8001_ha, IO,
         ": CI=%d PI=%d msgHeader=%x\n",
         circularQ->consumer_idx,
         circularQ->producer_index,
         msgHeader_tmp);
     return MPI_IO_STATUS_SUCCESS;
    } else {
     circularQ->consumer_idx =
      (circularQ->consumer_idx +
      ((le32_to_cpu(msgHeader_tmp)
       >> 24) & 0x1f))
       % PM8001_MPI_QUEUE;
     msgHeader_tmp = 0;
     pm8001_write_32(msgHeader, 0, 0);
     /* update the CI of outbound queue */
     pm8001_cw32(pm8001_ha,
      circularQ->ci_pci_bar,
      circularQ->ci_offset,
      circularQ->consumer_idx);
    }
   } else {
    circularQ->consumer_idx =
     (circularQ->consumer_idx +
     ((le32_to_cpu(msgHeader_tmp) >> 24) &
     0x1f)) % PM8001_MPI_QUEUE;
    msgHeader_tmp = 0;
    pm8001_write_32(msgHeader, 0, 0);
    /* update the CI of outbound queue */
    pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar,
     circularQ->ci_offset,
     circularQ->consumer_idx);
    return MPI_IO_STATUS_FAIL;
   }
  } else {
   u32 producer_index;
   void *pi_virt = circularQ->pi_virt;
   /* spurious interrupt during setup if
 * kexec-ing and driver doing a doorbell access
 * with the pre-kexec oq interrupt setup
 */

   if (!pi_virt)
    break;
   /* Update the producer index from SPC */
   producer_index = pm8001_read_32(pi_virt);
   circularQ->producer_index = cpu_to_le32(producer_index);
  }
 } while (le32_to_cpu(circularQ->producer_index) !=
  circularQ->consumer_idx);
 /* while we don't have any more not-yet-delivered message */
 /* report empty */
 return MPI_IO_STATUS_BUSY;
}

void pm8001_work_fn(struct work_struct *work)
{
 struct pm8001_work *pw = container_of(work, struct pm8001_work, work);
 struct pm8001_device *pm8001_dev;
 struct domain_device *dev;

 /*
 * So far, all users of this stash an associated structure here.
 * If we get here, and this pointer is null, then the action
 * was cancelled. This nullification happens when the device
 * goes away.
 */

 if (pw->handler != IO_FATAL_ERROR) {
  pm8001_dev = pw->data; /* Most stash device structure */
  if ((pm8001_dev == NULL)
   || ((pw->handler != IO_XFER_ERROR_BREAK)
    && (pm8001_dev->dev_type == SAS_PHY_UNUSED))) {
   kfree(pw);
   return;
  }
 }

 switch (pw->handler) {
 case IO_XFER_ERROR_BREAK:
 { /* This one stashes the sas_task instead */
  struct sas_task *t = (struct sas_task *)pm8001_dev;
  struct pm8001_ccb_info *ccb;
  struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
  unsigned long flags, flags1;
  struct task_status_struct *ts;
  int i;

  if (pm8001_query_task(t) == TMF_RESP_FUNC_SUCC)
   break/* Task still on lu */
  spin_lock_irqsave(&pm8001_ha->lock, flags);

  spin_lock_irqsave(&t->task_state_lock, flags1);
  if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
   spin_unlock_irqrestore(&t->task_state_lock, flags1);
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   break/* Task got completed by another */
  }
  spin_unlock_irqrestore(&t->task_state_lock, flags1);

  /* Search for a possible ccb that matches the task */
  for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
   ccb = &pm8001_ha->ccb_info[i];
   if ((ccb->ccb_tag != PM8001_INVALID_TAG) &&
       (ccb->task == t))
    break;
  }
  if (!ccb) {
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   break/* Task got freed by another */
  }
  ts = &t->task_status;
  ts->resp = SAS_TASK_COMPLETE;
  /* Force the midlayer to retry */
  ts->stat = SAS_QUEUE_FULL;
  pm8001_dev = ccb->device;
  if (pm8001_dev)
   atomic_dec(&pm8001_dev->running_req);
  spin_lock_irqsave(&t->task_state_lock, flags1);
  t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
  t->task_state_flags |= SAS_TASK_STATE_DONE;
  if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
   spin_unlock_irqrestore(&t->task_state_lock, flags1);
   pm8001_dbg(pm8001_ha, FAIL, "task 0x%p done with event 0x%x resp 0x%x stat 0x%x but aborted by upper layer!\n",
       t, pw->handler, ts->resp, ts->stat);
   pm8001_ccb_task_free(pm8001_ha, ccb);
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
  } else {
   spin_unlock_irqrestore(&t->task_state_lock, flags1);
   pm8001_ccb_task_free(pm8001_ha, ccb);
   mb();/* in order to force CPU ordering */
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   t->task_done(t);
  }
 } break;
 case IO_XFER_OPEN_RETRY_TIMEOUT:
 { /* This one stashes the sas_task instead */
  struct sas_task *t = (struct sas_task *)pm8001_dev;
  struct pm8001_ccb_info *ccb;
  struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
  unsigned long flags, flags1;
  int i, ret = 0;

  pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");

  ret = pm8001_query_task(t);

  if (ret == TMF_RESP_FUNC_SUCC)
   pm8001_dbg(pm8001_ha, IO, "...Task on lu\n");
  else if (ret == TMF_RESP_FUNC_COMPLETE)
   pm8001_dbg(pm8001_ha, IO, "...Task NOT on lu\n");
  else
   pm8001_dbg(pm8001_ha, DEVIO, "...query task failed!!!\n");

  spin_lock_irqsave(&pm8001_ha->lock, flags);

  spin_lock_irqsave(&t->task_state_lock, flags1);

  if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
   spin_unlock_irqrestore(&t->task_state_lock, flags1);
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
    (void)pm8001_abort_task(t);
   break/* Task got completed by another */
  }

  spin_unlock_irqrestore(&t->task_state_lock, flags1);

  /* Search for a possible ccb that matches the task */
  for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
   ccb = &pm8001_ha->ccb_info[i];
   if ((ccb->ccb_tag != PM8001_INVALID_TAG) &&
       (ccb->task == t))
    break;
  }
  if (!ccb) {
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
    (void)pm8001_abort_task(t);
   break/* Task got freed by another */
  }

  pm8001_dev = ccb->device;
  dev = pm8001_dev->sas_device;

  switch (ret) {
  case TMF_RESP_FUNC_SUCC: /* task on lu */
   ccb->open_retry = 1; /* Snub completion */
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   ret = pm8001_abort_task(t);
   ccb->open_retry = 0;
   switch (ret) {
   case TMF_RESP_FUNC_SUCC:
   case TMF_RESP_FUNC_COMPLETE:
    break;
   default/* device misbehavior */
    ret = TMF_RESP_FUNC_FAILED;
    pm8001_dbg(pm8001_ha, IO, "...Reset phy\n");
    pm8001_I_T_nexus_reset(dev);
    break;
   }
   break;

  case TMF_RESP_FUNC_COMPLETE: /* task not on lu */
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   /* Do we need to abort the task locally? */
   break;

  default/* device misbehavior */
   spin_unlock_irqrestore(&pm8001_ha->lock, flags);
   ret = TMF_RESP_FUNC_FAILED;
   pm8001_dbg(pm8001_ha, IO, "...Reset phy\n");
   pm8001_I_T_nexus_reset(dev);
  }

  if (ret == TMF_RESP_FUNC_FAILED)
   t = NULL;
  pm8001_open_reject_retry(pm8001_ha, t, pm8001_dev);
  pm8001_dbg(pm8001_ha, IO, "...Complete\n");
 } break;
 case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
  dev = pm8001_dev->sas_device;
  pm8001_I_T_nexus_event_handler(dev);
  break;
 case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
  dev = pm8001_dev->sas_device;
  pm8001_I_T_nexus_reset(dev);
  break;
 case IO_DS_IN_ERROR:
  dev = pm8001_dev->sas_device;
  pm8001_I_T_nexus_reset(dev);
  break;
 case IO_DS_NON_OPERATIONAL:
  dev = pm8001_dev->sas_device;
  pm8001_I_T_nexus_reset(dev);
  break;
 case IO_FATAL_ERROR:
 {
  struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
  struct pm8001_ccb_info *ccb;
  struct task_status_struct *ts;
  struct sas_task *task;
  int i;
  u32 device_id;

  for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
   ccb = &pm8001_ha->ccb_info[i];
   task = ccb->task;
   ts = &task->task_status;

   if (task != NULL) {
    dev = task->dev;
    if (!dev) {
     pm8001_dbg(pm8001_ha, FAIL,
      "dev is NULL\n");
     continue;
    }
    /*complete sas task and update to top layer */
    pm8001_ccb_task_free(pm8001_ha, ccb);
    ts->resp = SAS_TASK_COMPLETE;
    task->task_done(task);
   } else if (ccb->ccb_tag != PM8001_INVALID_TAG) {
    /* complete the internal commands/non-sas task */
    pm8001_dev = ccb->device;
    if (pm8001_dev->dcompletion) {
     complete(pm8001_dev->dcompletion);
     pm8001_dev->dcompletion = NULL;
    }
    complete(pm8001_ha->nvmd_completion);
    pm8001_ccb_free(pm8001_ha, ccb);
   }
  }
  /* Deregister all the device ids  */
  for (i = 0; i < PM8001_MAX_DEVICES; i++) {
   pm8001_dev = &pm8001_ha->devices[i];
   device_id = pm8001_dev->device_id;
   if (device_id) {
    PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
    pm8001_free_dev(pm8001_dev);
   }
  }
 }
 break;
 case IO_XFER_ERROR_ABORTED_NCQ_MODE:
 {
  dev = pm8001_dev->sas_device;
  sas_ata_device_link_abort(dev, false);
 }
 break;
 }
 kfree(pw);
}

int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
          int handler)
{
 struct pm8001_work *pw;
 int ret = 0;

 pw = kmalloc(sizeof(struct pm8001_work), GFP_ATOMIC);
 if (pw) {
  pw->pm8001_ha = pm8001_ha;
  pw->data = data;
  pw->handler = handler;
  INIT_WORK(&pw->work, pm8001_work_fn);
  queue_work(pm8001_wq, &pw->work);
 } else
  ret = -ENOMEM;

 return ret;
}

/**
 * mpi_ssp_completion- process the event that FW response to the SSP request.
 * @pm8001_ha: our hba card information
 * @piomb: the message contents of this outbound message.
 *
 * When FW has completed a ssp request for example a IO request, after it has
 * filled the SG data with the data, it will trigger this event representing
 * that he has finished the job; please check the corresponding buffer.
 * So we will tell the caller who maybe waiting the result to tell upper layer
 * that the task has been finished.
 */

static void
mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
 struct sas_task *t;
 struct pm8001_ccb_info *ccb;
 unsigned long flags;
 u32 status;
 u32 param;
 u32 tag;
 struct ssp_completion_resp *psspPayload;
 struct task_status_struct *ts;
 struct ssp_response_iu *iu;
 struct pm8001_device *pm8001_dev;
 psspPayload = (struct ssp_completion_resp *)(piomb + 4);
 status = le32_to_cpu(psspPayload->status);
 tag = le32_to_cpu(psspPayload->tag);
 ccb = &pm8001_ha->ccb_info[tag];
 if ((status == IO_ABORTED) && ccb->open_retry) {
  /* Being completed by another */
  ccb->open_retry = 0;
  return;
 }
 pm8001_dev = ccb->device;
 param = le32_to_cpu(psspPayload->param);

 t = ccb->task;

 if (status && status != IO_UNDERFLOW)
  pm8001_dbg(pm8001_ha, FAIL, "sas IO status 0x%x\n", status);
 if (unlikely(!t || !t->lldd_task || !t->dev))
  return;
 ts = &t->task_status;
 /* Print sas address of IO failed device */
 if ((status != IO_SUCCESS) && (status != IO_OVERFLOW) &&
  (status != IO_UNDERFLOW))
  pm8001_dbg(pm8001_ha, FAIL, "SAS Address of IO Failure Drive:%016llx\n",
      SAS_ADDR(t->dev->sas_addr));

 if (status)
  pm8001_dbg(pm8001_ha, IOERR,
      "status:0x%x, tag:0x%x, task:0x%p\n",
      status, tag, t);

 switch (status) {
 case IO_SUCCESS:
  pm8001_dbg(pm8001_ha, IO, "IO_SUCCESS,param = %d\n",
      param);
  if (param == 0) {
   ts->resp = SAS_TASK_COMPLETE;
   ts->stat = SAS_SAM_STAT_GOOD;
  } else {
   ts->resp = SAS_TASK_COMPLETE;
   ts->stat = SAS_PROTO_RESPONSE;
   ts->residual = param;
   iu = &psspPayload->ssp_resp_iu;
   sas_ssp_task_response(pm8001_ha->dev, t, iu);
  }
  if (pm8001_dev)
   atomic_dec(&pm8001_dev->running_req);
  break;
 case IO_ABORTED:
  pm8001_dbg(pm8001_ha, IO, "IO_ABORTED IOMB Tag\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_ABORTED_TASK;
  break;
 case IO_UNDERFLOW:
  /* SSP Completion with error */
  pm8001_dbg(pm8001_ha, IO, "IO_UNDERFLOW,param = %d\n",
      param);
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_DATA_UNDERRUN;
  ts->residual = param;
  if (pm8001_dev)
   atomic_dec(&pm8001_dev->running_req);
  break;
 case IO_NO_DEVICE:
  pm8001_dbg(pm8001_ha, IO, "IO_NO_DEVICE\n");
  ts->resp = SAS_TASK_UNDELIVERED;
  ts->stat = SAS_PHY_DOWN;
  break;
 case IO_XFER_ERROR_BREAK:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_BREAK\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  /* Force the midlayer to retry */
  ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
  break;
 case IO_XFER_ERROR_PHY_NOT_READY:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_PHY_NOT_READY\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
  break;
 case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
  pm8001_dbg(pm8001_ha, IO,
      "IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_EPROTO;
  break;
 case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
  pm8001_dbg(pm8001_ha, IO,
      "IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_UNKNOWN;
  break;
 case IO_OPEN_CNX_ERROR_BREAK:
  pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_BREAK\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
  break;
 case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
  pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_UNKNOWN;
  if (!t->uldd_task)
   pm8001_handle_event(pm8001_ha,
    pm8001_dev,
    IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
  break;
 case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
  pm8001_dbg(pm8001_ha, IO,
      "IO_OPEN_CNX_ERROR_BAD_DESTINATION\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_BAD_DEST;
  break;
 case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
  pm8001_dbg(pm8001_ha, IO, "IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_CONN_RATE;
  break;
 case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
  pm8001_dbg(pm8001_ha, IO,
      "IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n");
  ts->resp = SAS_TASK_UNDELIVERED;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
  break;
 case IO_XFER_ERROR_NAK_RECEIVED:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_NAK_RECEIVED\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
  break;
 case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_ACK_NAK_TIMEOUT\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_NAK_R_ERR;
  break;
 case IO_XFER_ERROR_DMA:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_DMA\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  break;
 case IO_XFER_OPEN_RETRY_TIMEOUT:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_OPEN_RETRY_TIMEOUT\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
  break;
 case IO_XFER_ERROR_OFFSET_MISMATCH:
  pm8001_dbg(pm8001_ha, IO, "IO_XFER_ERROR_OFFSET_MISMATCH\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  break;
 case IO_PORT_IN_RESET:
  pm8001_dbg(pm8001_ha, IO, "IO_PORT_IN_RESET\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  break;
 case IO_DS_NON_OPERATIONAL:
  pm8001_dbg(pm8001_ha, IO, "IO_DS_NON_OPERATIONAL\n");
  ts->resp = SAS_TASK_COMPLETE;
  ts->stat = SAS_OPEN_REJECT;
  if (!t->uldd_task)
   pm8001_handle_event(pm8001_ha,
    pm8001_dev,
    IO_DS_NON_OPERATIONAL);
  break;
 case IO_DS_IN_RECOVERY:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=86 G=88

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.