/* * 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 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);
/** * update_inbnd_queue_table - update the inbound queue table to the HBA. * @pm8001_ha: our hba card information * @number: entry in the queue
*/ staticvoid 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
*/ staticvoid 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; unsignedlong 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));
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.
*/ staticvoid mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
u32 interval)
{
u32 offset;
u32 value;
u32 i; unsignedlong flags;
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
*/ staticint 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
*/ staticint 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;
}
/* 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;
}
/** * pm8001_chip_init - the main init function that initialize whole PM8001 chip. * @pm8001_ha: our hba card information
*/ staticint 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;
}
/* 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 { unsignedlong 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
*/ staticint
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 */ unsignedlong 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);
/* 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));
/** * 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; unsignedlong flags; struct inbound_queue_table *circularQ = &pm8001_ha->inbnd_q_tbl[q_index]; int rv;
u32 htag = le32_to_cpu(*(__le32 *)payload);
/* * 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; unsignedlong 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; unsignedlong flags, flags1; int i, ret = 0;
if (ret == TMF_RESP_FUNC_SUCC)
pm8001_dbg(pm8001_ha, IO, "...Task on lu\n"); elseif (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 */
}
/* 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;
/** * 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.
*/ staticvoid
mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{ struct sas_task *t; struct pm8001_ccb_info *ccb; unsignedlong 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);
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.