162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/message/fusion/mptsas.c
362306a36Sopenharmony_ci *      For use with LSI PCI chip/adapter(s)
462306a36Sopenharmony_ci *      running LSI Fusion MPT (Message Passing Technology) firmware.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (c) 1999-2008 LSI Corporation
762306a36Sopenharmony_ci *  (mailto:DL-MPTFusionLinux@lsi.com)
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci    This program is free software; you can redistribute it and/or modify
1262306a36Sopenharmony_ci    it under the terms of the GNU General Public License as published by
1362306a36Sopenharmony_ci    the Free Software Foundation; version 2 of the License.
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci    This program is distributed in the hope that it will be useful,
1662306a36Sopenharmony_ci    but WITHOUT ANY WARRANTY; without even the implied warranty of
1762306a36Sopenharmony_ci    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1862306a36Sopenharmony_ci    GNU General Public License for more details.
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci    NO WARRANTY
2162306a36Sopenharmony_ci    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2262306a36Sopenharmony_ci    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2362306a36Sopenharmony_ci    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2462306a36Sopenharmony_ci    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2562306a36Sopenharmony_ci    solely responsible for determining the appropriateness of using and
2662306a36Sopenharmony_ci    distributing the Program and assumes all risks associated with its
2762306a36Sopenharmony_ci    exercise of rights under this Agreement, including but not limited to
2862306a36Sopenharmony_ci    the risks and costs of program errors, damage to or loss of data,
2962306a36Sopenharmony_ci    programs or equipment, and unavailability or interruption of operations.
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci    DISCLAIMER OF LIABILITY
3262306a36Sopenharmony_ci    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3362306a36Sopenharmony_ci    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3462306a36Sopenharmony_ci    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3562306a36Sopenharmony_ci    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3662306a36Sopenharmony_ci    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3762306a36Sopenharmony_ci    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3862306a36Sopenharmony_ci    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci    You should have received a copy of the GNU General Public License
4162306a36Sopenharmony_ci    along with this program; if not, write to the Free Software
4262306a36Sopenharmony_ci    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4362306a36Sopenharmony_ci*/
4462306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/module.h>
4762306a36Sopenharmony_ci#include <linux/kernel.h>
4862306a36Sopenharmony_ci#include <linux/slab.h>
4962306a36Sopenharmony_ci#include <linux/init.h>
5062306a36Sopenharmony_ci#include <linux/errno.h>
5162306a36Sopenharmony_ci#include <linux/jiffies.h>
5262306a36Sopenharmony_ci#include <linux/workqueue.h>
5362306a36Sopenharmony_ci#include <linux/delay.h>	/* for mdelay */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include <scsi/scsi.h>
5662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
5762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
5862306a36Sopenharmony_ci#include <scsi/scsi_host.h>
5962306a36Sopenharmony_ci#include <scsi/scsi_transport_sas.h>
6062306a36Sopenharmony_ci#include <scsi/scsi_transport.h>
6162306a36Sopenharmony_ci#include <scsi/scsi_dbg.h>
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#include "mptbase.h"
6462306a36Sopenharmony_ci#include "mptscsih.h"
6562306a36Sopenharmony_ci#include "mptsas.h"
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define my_NAME		"Fusion MPT SAS Host driver"
6962306a36Sopenharmony_ci#define my_VERSION	MPT_LINUX_VERSION_COMMON
7062306a36Sopenharmony_ci#define MYNAM		"mptsas"
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * Reserved channel for integrated raid
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci#define MPTSAS_RAID_CHANNEL	1
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define SAS_CONFIG_PAGE_TIMEOUT		30
7862306a36Sopenharmony_ciMODULE_AUTHOR(MODULEAUTHOR);
7962306a36Sopenharmony_ciMODULE_DESCRIPTION(my_NAME);
8062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
8162306a36Sopenharmony_ciMODULE_VERSION(my_VERSION);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int mpt_pt_clear;
8462306a36Sopenharmony_cimodule_param(mpt_pt_clear, int, 0);
8562306a36Sopenharmony_ciMODULE_PARM_DESC(mpt_pt_clear,
8662306a36Sopenharmony_ci		" Clear persistency table: enable=1  "
8762306a36Sopenharmony_ci		"(default=MPTSCSIH_PT_CLEAR=0)");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* scsi-mid layer global parameter is max_report_luns, which is 511 */
9062306a36Sopenharmony_ci#define MPTSAS_MAX_LUN (16895)
9162306a36Sopenharmony_cistatic int max_lun = MPTSAS_MAX_LUN;
9262306a36Sopenharmony_cimodule_param(max_lun, int, 0);
9362306a36Sopenharmony_ciMODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int mpt_loadtime_max_sectors = 8192;
9662306a36Sopenharmony_cimodule_param(mpt_loadtime_max_sectors, int, 0);
9762306a36Sopenharmony_ciMODULE_PARM_DESC(mpt_loadtime_max_sectors,
9862306a36Sopenharmony_ci		" Maximum sector define for Host Bus Adaptor.Range 64 to 8192 default=8192");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic u8	mptsasDoneCtx = MPT_MAX_PROTOCOL_DRIVERS;
10162306a36Sopenharmony_cistatic u8	mptsasTaskCtx = MPT_MAX_PROTOCOL_DRIVERS;
10262306a36Sopenharmony_cistatic u8	mptsasInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */
10362306a36Sopenharmony_cistatic u8	mptsasMgmtCtx = MPT_MAX_PROTOCOL_DRIVERS;
10462306a36Sopenharmony_cistatic u8	mptsasDeviceResetCtx = MPT_MAX_PROTOCOL_DRIVERS;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void mptsas_firmware_event_work(struct work_struct *work);
10762306a36Sopenharmony_cistatic void mptsas_send_sas_event(struct fw_event_work *fw_event);
10862306a36Sopenharmony_cistatic void mptsas_send_raid_event(struct fw_event_work *fw_event);
10962306a36Sopenharmony_cistatic void mptsas_send_ir2_event(struct fw_event_work *fw_event);
11062306a36Sopenharmony_cistatic void mptsas_parse_device_info(struct sas_identify *identify,
11162306a36Sopenharmony_ci		struct mptsas_devinfo *device_info);
11262306a36Sopenharmony_cistatic inline void mptsas_set_rphy(MPT_ADAPTER *ioc,
11362306a36Sopenharmony_ci		struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy);
11462306a36Sopenharmony_cistatic struct mptsas_phyinfo	*mptsas_find_phyinfo_by_sas_address
11562306a36Sopenharmony_ci		(MPT_ADAPTER *ioc, u64 sas_address);
11662306a36Sopenharmony_cistatic int mptsas_sas_device_pg0(MPT_ADAPTER *ioc,
11762306a36Sopenharmony_ci	struct mptsas_devinfo *device_info, u32 form, u32 form_specific);
11862306a36Sopenharmony_cistatic int mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc,
11962306a36Sopenharmony_ci	struct mptsas_enclosure *enclosure, u32 form, u32 form_specific);
12062306a36Sopenharmony_cistatic int mptsas_add_end_device(MPT_ADAPTER *ioc,
12162306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info);
12262306a36Sopenharmony_cistatic void mptsas_del_end_device(MPT_ADAPTER *ioc,
12362306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info);
12462306a36Sopenharmony_cistatic void mptsas_send_link_status_event(struct fw_event_work *fw_event);
12562306a36Sopenharmony_cistatic struct mptsas_portinfo	*mptsas_find_portinfo_by_sas_address
12662306a36Sopenharmony_ci		(MPT_ADAPTER *ioc, u64 sas_address);
12762306a36Sopenharmony_cistatic void mptsas_expander_delete(MPT_ADAPTER *ioc,
12862306a36Sopenharmony_ci		struct mptsas_portinfo *port_info, u8 force);
12962306a36Sopenharmony_cistatic void mptsas_send_expander_event(struct fw_event_work *fw_event);
13062306a36Sopenharmony_cistatic void mptsas_not_responding_devices(MPT_ADAPTER *ioc);
13162306a36Sopenharmony_cistatic void mptsas_scan_sas_topology(MPT_ADAPTER *ioc);
13262306a36Sopenharmony_cistatic void mptsas_broadcast_primitive_work(struct fw_event_work *fw_event);
13362306a36Sopenharmony_cistatic void mptsas_handle_queue_full_event(struct fw_event_work *fw_event);
13462306a36Sopenharmony_cistatic void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id);
13562306a36Sopenharmony_civoid	mptsas_schedule_target_reset(void *ioc);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void mptsas_print_phy_data(MPT_ADAPTER *ioc,
13862306a36Sopenharmony_ci					MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
14162306a36Sopenharmony_ci	    "---- IO UNIT PAGE 0 ------------\n", ioc->name));
14262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Handle=0x%X\n",
14362306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(phy_data->AttachedDeviceHandle)));
14462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Controller Handle=0x%X\n",
14562306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(phy_data->ControllerDevHandle)));
14662306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Port=0x%X\n",
14762306a36Sopenharmony_ci	    ioc->name, phy_data->Port));
14862306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Port Flags=0x%X\n",
14962306a36Sopenharmony_ci	    ioc->name, phy_data->PortFlags));
15062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Flags=0x%X\n",
15162306a36Sopenharmony_ci	    ioc->name, phy_data->PhyFlags));
15262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Negotiated Link Rate=0x%X\n",
15362306a36Sopenharmony_ci	    ioc->name, phy_data->NegotiatedLinkRate));
15462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
15562306a36Sopenharmony_ci	    "Controller PHY Device Info=0x%X\n", ioc->name,
15662306a36Sopenharmony_ci	    le32_to_cpu(phy_data->ControllerPhyDeviceInfo)));
15762306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DiscoveryStatus=0x%X\n\n",
15862306a36Sopenharmony_ci	    ioc->name, le32_to_cpu(phy_data->DiscoveryStatus)));
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void mptsas_print_phy_pg0(MPT_ADAPTER *ioc, SasPhyPage0_t *pg0)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	__le64 sas_address;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64));
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
16862306a36Sopenharmony_ci	    "---- SAS PHY PAGE 0 ------------\n", ioc->name));
16962306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
17062306a36Sopenharmony_ci	    "Attached Device Handle=0x%X\n", ioc->name,
17162306a36Sopenharmony_ci	    le16_to_cpu(pg0->AttachedDevHandle)));
17262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SAS Address=0x%llX\n",
17362306a36Sopenharmony_ci	    ioc->name, (unsigned long long)le64_to_cpu(sas_address)));
17462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
17562306a36Sopenharmony_ci	    "Attached PHY Identifier=0x%X\n", ioc->name,
17662306a36Sopenharmony_ci	    pg0->AttachedPhyIdentifier));
17762306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Attached Device Info=0x%X\n",
17862306a36Sopenharmony_ci	    ioc->name, le32_to_cpu(pg0->AttachedDeviceInfo)));
17962306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Programmed Link Rate=0x%X\n",
18062306a36Sopenharmony_ci	    ioc->name,  pg0->ProgrammedLinkRate));
18162306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Change Count=0x%X\n",
18262306a36Sopenharmony_ci	    ioc->name, pg0->ChangeCount));
18362306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Info=0x%X\n\n",
18462306a36Sopenharmony_ci	    ioc->name, le32_to_cpu(pg0->PhyInfo)));
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void mptsas_print_phy_pg1(MPT_ADAPTER *ioc, SasPhyPage1_t *pg1)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19062306a36Sopenharmony_ci	    "---- SAS PHY PAGE 1 ------------\n", ioc->name));
19162306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Invalid Dword Count=0x%x\n",
19262306a36Sopenharmony_ci	    ioc->name,  pg1->InvalidDwordCount));
19362306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19462306a36Sopenharmony_ci	    "Running Disparity Error Count=0x%x\n", ioc->name,
19562306a36Sopenharmony_ci	    pg1->RunningDisparityErrorCount));
19662306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19762306a36Sopenharmony_ci	    "Loss Dword Synch Count=0x%x\n", ioc->name,
19862306a36Sopenharmony_ci	    pg1->LossDwordSynchCount));
19962306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
20062306a36Sopenharmony_ci	    "PHY Reset Problem Count=0x%x\n\n", ioc->name,
20162306a36Sopenharmony_ci	    pg1->PhyResetProblemCount));
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void mptsas_print_device_pg0(MPT_ADAPTER *ioc, SasDevicePage0_t *pg0)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	__le64 sas_address;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64));
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
21162306a36Sopenharmony_ci	    "---- SAS DEVICE PAGE 0 ---------\n", ioc->name));
21262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Handle=0x%X\n",
21362306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->DevHandle)));
21462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Parent Handle=0x%X\n",
21562306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->ParentDevHandle)));
21662306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Enclosure Handle=0x%X\n",
21762306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->EnclosureHandle)));
21862306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Slot=0x%X\n",
21962306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->Slot)));
22062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SAS Address=0x%llX\n",
22162306a36Sopenharmony_ci	    ioc->name, (unsigned long long)le64_to_cpu(sas_address)));
22262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Target ID=0x%X\n",
22362306a36Sopenharmony_ci	    ioc->name, pg0->TargetID));
22462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Bus=0x%X\n",
22562306a36Sopenharmony_ci	    ioc->name, pg0->Bus));
22662306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Parent Phy Num=0x%X\n",
22762306a36Sopenharmony_ci	    ioc->name, pg0->PhyNum));
22862306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Access Status=0x%X\n",
22962306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->AccessStatus)));
23062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Device Info=0x%X\n",
23162306a36Sopenharmony_ci	    ioc->name, le32_to_cpu(pg0->DeviceInfo)));
23262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Flags=0x%X\n",
23362306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg0->Flags)));
23462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Physical Port=0x%X\n\n",
23562306a36Sopenharmony_ci	    ioc->name, pg0->PhysicalPort));
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void mptsas_print_expander_pg1(MPT_ADAPTER *ioc, SasExpanderPage1_t *pg1)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
24162306a36Sopenharmony_ci	    "---- SAS EXPANDER PAGE 1 ------------\n", ioc->name));
24262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Physical Port=0x%X\n",
24362306a36Sopenharmony_ci	    ioc->name, pg1->PhysicalPort));
24462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Identifier=0x%X\n",
24562306a36Sopenharmony_ci	    ioc->name, pg1->PhyIdentifier));
24662306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Negotiated Link Rate=0x%X\n",
24762306a36Sopenharmony_ci	    ioc->name, pg1->NegotiatedLinkRate));
24862306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Programmed Link Rate=0x%X\n",
24962306a36Sopenharmony_ci	    ioc->name, pg1->ProgrammedLinkRate));
25062306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Hardware Link Rate=0x%X\n",
25162306a36Sopenharmony_ci	    ioc->name, pg1->HwLinkRate));
25262306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Owner Device Handle=0x%X\n",
25362306a36Sopenharmony_ci	    ioc->name, le16_to_cpu(pg1->OwnerDevHandle)));
25462306a36Sopenharmony_ci	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
25562306a36Sopenharmony_ci	    "Attached Device Handle=0x%X\n\n", ioc->name,
25662306a36Sopenharmony_ci	    le16_to_cpu(pg1->AttachedDevHandle)));
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/* inhibit sas firmware event handling */
26062306a36Sopenharmony_cistatic void
26162306a36Sopenharmony_cimptsas_fw_event_off(MPT_ADAPTER *ioc)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	unsigned long flags;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
26662306a36Sopenharmony_ci	ioc->fw_events_off = 1;
26762306a36Sopenharmony_ci	ioc->sas_discovery_quiesce_io = 0;
26862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/* enable sas firmware event handling */
27362306a36Sopenharmony_cistatic void
27462306a36Sopenharmony_cimptsas_fw_event_on(MPT_ADAPTER *ioc)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	unsigned long flags;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
27962306a36Sopenharmony_ci	ioc->fw_events_off = 0;
28062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/* queue a sas firmware event */
28462306a36Sopenharmony_cistatic void
28562306a36Sopenharmony_cimptsas_add_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
28662306a36Sopenharmony_ci    unsigned long delay)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	unsigned long flags;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
29162306a36Sopenharmony_ci	list_add_tail(&fw_event->list, &ioc->fw_event_list);
29262306a36Sopenharmony_ci	fw_event->users = 1;
29362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work);
29462306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)"
29562306a36Sopenharmony_ci		"on cpuid %d\n", ioc->name, __func__,
29662306a36Sopenharmony_ci		fw_event, smp_processor_id()));
29762306a36Sopenharmony_ci	queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
29862306a36Sopenharmony_ci	    &fw_event->work, delay);
29962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* requeue a sas firmware event */
30362306a36Sopenharmony_cistatic void
30462306a36Sopenharmony_cimptsas_requeue_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
30562306a36Sopenharmony_ci    unsigned long delay)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	unsigned long flags;
30862306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
30962306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task "
31062306a36Sopenharmony_ci	    "(fw_event=0x%p)on cpuid %d\n", ioc->name, __func__,
31162306a36Sopenharmony_ci		fw_event, smp_processor_id()));
31262306a36Sopenharmony_ci	fw_event->retries++;
31362306a36Sopenharmony_ci	queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
31462306a36Sopenharmony_ci	    &fw_event->work, msecs_to_jiffies(delay));
31562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void __mptsas_free_fw_event(MPT_ADAPTER *ioc,
31962306a36Sopenharmony_ci				   struct fw_event_work *fw_event)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: kfree (fw_event=0x%p)\n",
32262306a36Sopenharmony_ci	    ioc->name, __func__, fw_event));
32362306a36Sopenharmony_ci	list_del(&fw_event->list);
32462306a36Sopenharmony_ci	kfree(fw_event);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* free memory associated to a sas firmware event */
32862306a36Sopenharmony_cistatic void
32962306a36Sopenharmony_cimptsas_free_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	unsigned long flags;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
33462306a36Sopenharmony_ci	fw_event->users--;
33562306a36Sopenharmony_ci	if (!fw_event->users)
33662306a36Sopenharmony_ci		__mptsas_free_fw_event(ioc, fw_event);
33762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/* walk the firmware event queue, and either stop or wait for
34162306a36Sopenharmony_ci * outstanding events to complete */
34262306a36Sopenharmony_cistatic void
34362306a36Sopenharmony_cimptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct fw_event_work *fw_event;
34662306a36Sopenharmony_ci	struct mptsas_target_reset_event *target_reset_list, *n;
34762306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
34862306a36Sopenharmony_ci	unsigned long flags;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* flush the target_reset_list */
35162306a36Sopenharmony_ci	if (!list_empty(&hd->target_reset_list)) {
35262306a36Sopenharmony_ci		list_for_each_entry_safe(target_reset_list, n,
35362306a36Sopenharmony_ci		    &hd->target_reset_list, list) {
35462306a36Sopenharmony_ci			dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
35562306a36Sopenharmony_ci			    "%s: removing target reset for id=%d\n",
35662306a36Sopenharmony_ci			    ioc->name, __func__,
35762306a36Sopenharmony_ci			   target_reset_list->sas_event_data.TargetID));
35862306a36Sopenharmony_ci			list_del(&target_reset_list->list);
35962306a36Sopenharmony_ci			kfree(target_reset_list);
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (list_empty(&ioc->fw_event_list) || !ioc->fw_event_q)
36462306a36Sopenharmony_ci		return;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->fw_event_lock, flags);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	while (!list_empty(&ioc->fw_event_list)) {
36962306a36Sopenharmony_ci		bool canceled = false;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		fw_event = list_first_entry(&ioc->fw_event_list,
37262306a36Sopenharmony_ci					    struct fw_event_work, list);
37362306a36Sopenharmony_ci		fw_event->users++;
37462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
37562306a36Sopenharmony_ci		if (cancel_delayed_work_sync(&fw_event->work))
37662306a36Sopenharmony_ci			canceled = true;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		spin_lock_irqsave(&ioc->fw_event_lock, flags);
37962306a36Sopenharmony_ci		if (canceled)
38062306a36Sopenharmony_ci			fw_event->users--;
38162306a36Sopenharmony_ci		fw_event->users--;
38262306a36Sopenharmony_ci		WARN_ON_ONCE(fw_event->users);
38362306a36Sopenharmony_ci		__mptsas_free_fw_event(ioc, fw_event);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
39262306a36Sopenharmony_ci	return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
39862306a36Sopenharmony_ci	return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/*
40262306a36Sopenharmony_ci * mptsas_find_portinfo_by_handle
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * This function should be called with the sas_topology_mutex already held
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic struct mptsas_portinfo *
40762306a36Sopenharmony_cimptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct mptsas_portinfo *port_info, *rc=NULL;
41062306a36Sopenharmony_ci	int i;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	list_for_each_entry(port_info, &ioc->sas_topology, list)
41362306a36Sopenharmony_ci		for (i = 0; i < port_info->num_phys; i++)
41462306a36Sopenharmony_ci			if (port_info->phy_info[i].identify.handle == handle) {
41562306a36Sopenharmony_ci				rc = port_info;
41662306a36Sopenharmony_ci				goto out;
41762306a36Sopenharmony_ci			}
41862306a36Sopenharmony_ci out:
41962306a36Sopenharmony_ci	return rc;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci/**
42362306a36Sopenharmony_ci *	mptsas_find_portinfo_by_sas_address - find and return portinfo for
42462306a36Sopenharmony_ci *		this sas_address
42562306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
42662306a36Sopenharmony_ci *	@sas_address: expander sas address
42762306a36Sopenharmony_ci *
42862306a36Sopenharmony_ci *	This function should be called with the sas_topology_mutex already held.
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci *	Return: %NULL if not found.
43162306a36Sopenharmony_ci **/
43262306a36Sopenharmony_cistatic struct mptsas_portinfo *
43362306a36Sopenharmony_cimptsas_find_portinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct mptsas_portinfo *port_info, *rc = NULL;
43662306a36Sopenharmony_ci	int i;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (sas_address >= ioc->hba_port_sas_addr &&
43962306a36Sopenharmony_ci	    sas_address < (ioc->hba_port_sas_addr +
44062306a36Sopenharmony_ci	    ioc->hba_port_num_phy))
44162306a36Sopenharmony_ci		return ioc->hba_port_info;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
44462306a36Sopenharmony_ci	list_for_each_entry(port_info, &ioc->sas_topology, list)
44562306a36Sopenharmony_ci		for (i = 0; i < port_info->num_phys; i++)
44662306a36Sopenharmony_ci			if (port_info->phy_info[i].identify.sas_address ==
44762306a36Sopenharmony_ci			    sas_address) {
44862306a36Sopenharmony_ci				rc = port_info;
44962306a36Sopenharmony_ci				goto out;
45062306a36Sopenharmony_ci			}
45162306a36Sopenharmony_ci out:
45262306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
45362306a36Sopenharmony_ci	return rc;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/*
45762306a36Sopenharmony_ci * Returns true if there is a scsi end device
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic inline int
46062306a36Sopenharmony_cimptsas_is_end_device(struct mptsas_devinfo * attached)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	if ((attached->sas_address) &&
46362306a36Sopenharmony_ci	    (attached->device_info &
46462306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_END_DEVICE) &&
46562306a36Sopenharmony_ci	    ((attached->device_info &
46662306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SSP_TARGET) |
46762306a36Sopenharmony_ci	    (attached->device_info &
46862306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_STP_TARGET) |
46962306a36Sopenharmony_ci	    (attached->device_info &
47062306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)))
47162306a36Sopenharmony_ci		return 1;
47262306a36Sopenharmony_ci	else
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/* no mutex */
47762306a36Sopenharmony_cistatic void
47862306a36Sopenharmony_cimptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_details)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
48162306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
48262306a36Sopenharmony_ci	u8	i;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (!port_details)
48562306a36Sopenharmony_ci		return;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	port_info = port_details->port_info;
48862306a36Sopenharmony_ci	phy_info = port_info->phy_info;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: num_phys=%02d "
49162306a36Sopenharmony_ci	    "bitmask=0x%016llX\n", ioc->name, __func__, port_details,
49262306a36Sopenharmony_ci	    port_details->num_phys, (unsigned long long)
49362306a36Sopenharmony_ci	    port_details->phy_bitmask));
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++, phy_info++) {
49662306a36Sopenharmony_ci		if(phy_info->port_details != port_details)
49762306a36Sopenharmony_ci			continue;
49862306a36Sopenharmony_ci		memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
49962306a36Sopenharmony_ci		mptsas_set_rphy(ioc, phy_info, NULL);
50062306a36Sopenharmony_ci		phy_info->port_details = NULL;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	kfree(port_details);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic inline struct sas_rphy *
50662306a36Sopenharmony_cimptsas_get_rphy(struct mptsas_phyinfo *phy_info)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	if (phy_info->port_details)
50962306a36Sopenharmony_ci		return phy_info->port_details->rphy;
51062306a36Sopenharmony_ci	else
51162306a36Sopenharmony_ci		return NULL;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic inline void
51562306a36Sopenharmony_cimptsas_set_rphy(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	if (phy_info->port_details) {
51862306a36Sopenharmony_ci		phy_info->port_details->rphy = rphy;
51962306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sas_rphy_add: rphy=%p\n",
52062306a36Sopenharmony_ci		    ioc->name, rphy));
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (rphy) {
52462306a36Sopenharmony_ci		dsaswideprintk(ioc, dev_printk(KERN_DEBUG,
52562306a36Sopenharmony_ci		    &rphy->dev, MYIOC_s_FMT "add:", ioc->name));
52662306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "rphy=%p release=%p\n",
52762306a36Sopenharmony_ci		    ioc->name, rphy, rphy->dev.release));
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic inline struct sas_port *
53262306a36Sopenharmony_cimptsas_get_port(struct mptsas_phyinfo *phy_info)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	if (phy_info->port_details)
53562306a36Sopenharmony_ci		return phy_info->port_details->port;
53662306a36Sopenharmony_ci	else
53762306a36Sopenharmony_ci		return NULL;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic inline void
54162306a36Sopenharmony_cimptsas_set_port(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_port *port)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	if (phy_info->port_details)
54462306a36Sopenharmony_ci		phy_info->port_details->port = port;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (port) {
54762306a36Sopenharmony_ci		dsaswideprintk(ioc, dev_printk(KERN_DEBUG,
54862306a36Sopenharmony_ci		    &port->dev, MYIOC_s_FMT "add:", ioc->name));
54962306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "port=%p release=%p\n",
55062306a36Sopenharmony_ci		    ioc->name, port, port->dev.release));
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic inline struct scsi_target *
55562306a36Sopenharmony_cimptsas_get_starget(struct mptsas_phyinfo *phy_info)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	if (phy_info->port_details)
55862306a36Sopenharmony_ci		return phy_info->port_details->starget;
55962306a36Sopenharmony_ci	else
56062306a36Sopenharmony_ci		return NULL;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic inline void
56462306a36Sopenharmony_cimptsas_set_starget(struct mptsas_phyinfo *phy_info, struct scsi_target *
56562306a36Sopenharmony_cistarget)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	if (phy_info->port_details)
56862306a36Sopenharmony_ci		phy_info->port_details->starget = starget;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/**
57262306a36Sopenharmony_ci *	mptsas_add_device_component - adds a new device component to our lists
57362306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
57462306a36Sopenharmony_ci *	@channel: channel number
57562306a36Sopenharmony_ci *	@id: Logical Target ID for reset (if appropriate)
57662306a36Sopenharmony_ci *	@sas_address: expander sas address
57762306a36Sopenharmony_ci *	@device_info: specific bits (flags) for devices
57862306a36Sopenharmony_ci *	@slot: enclosure slot ID
57962306a36Sopenharmony_ci *	@enclosure_logical_id: enclosure WWN
58062306a36Sopenharmony_ci *
58162306a36Sopenharmony_ci **/
58262306a36Sopenharmony_cistatic void
58362306a36Sopenharmony_cimptsas_add_device_component(MPT_ADAPTER *ioc, u8 channel, u8 id,
58462306a36Sopenharmony_ci	u64 sas_address, u32 device_info, u16 slot, u64 enclosure_logical_id)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct mptsas_device_info	*sas_info, *next;
58762306a36Sopenharmony_ci	struct scsi_device	*sdev;
58862306a36Sopenharmony_ci	struct scsi_target	*starget;
58962306a36Sopenharmony_ci	struct sas_rphy	*rphy;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/*
59262306a36Sopenharmony_ci	 * Delete all matching devices out of the list
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_device_info_mutex);
59562306a36Sopenharmony_ci	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
59662306a36Sopenharmony_ci	    list) {
59762306a36Sopenharmony_ci		if (!sas_info->is_logical_volume &&
59862306a36Sopenharmony_ci		    (sas_info->sas_address == sas_address ||
59962306a36Sopenharmony_ci		    (sas_info->fw.channel == channel &&
60062306a36Sopenharmony_ci		     sas_info->fw.id == id))) {
60162306a36Sopenharmony_ci			list_del(&sas_info->list);
60262306a36Sopenharmony_ci			kfree(sas_info);
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
60762306a36Sopenharmony_ci	if (!sas_info)
60862306a36Sopenharmony_ci		goto out;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	/*
61162306a36Sopenharmony_ci	 * Set Firmware mapping
61262306a36Sopenharmony_ci	 */
61362306a36Sopenharmony_ci	sas_info->fw.id = id;
61462306a36Sopenharmony_ci	sas_info->fw.channel = channel;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	sas_info->sas_address = sas_address;
61762306a36Sopenharmony_ci	sas_info->device_info = device_info;
61862306a36Sopenharmony_ci	sas_info->slot = slot;
61962306a36Sopenharmony_ci	sas_info->enclosure_logical_id = enclosure_logical_id;
62062306a36Sopenharmony_ci	INIT_LIST_HEAD(&sas_info->list);
62162306a36Sopenharmony_ci	list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * Set OS mapping
62562306a36Sopenharmony_ci	 */
62662306a36Sopenharmony_ci	shost_for_each_device(sdev, ioc->sh) {
62762306a36Sopenharmony_ci		starget = scsi_target(sdev);
62862306a36Sopenharmony_ci		rphy = dev_to_rphy(starget->dev.parent);
62962306a36Sopenharmony_ci		if (rphy->identify.sas_address == sas_address) {
63062306a36Sopenharmony_ci			sas_info->os.id = starget->id;
63162306a36Sopenharmony_ci			sas_info->os.channel = starget->channel;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci out:
63662306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_device_info_mutex);
63762306a36Sopenharmony_ci	return;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/**
64162306a36Sopenharmony_ci *	mptsas_add_device_component_by_fw - adds a new device component by FW ID
64262306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
64362306a36Sopenharmony_ci *	@channel: channel number
64462306a36Sopenharmony_ci *	@id: Logical Target ID
64562306a36Sopenharmony_ci *
64662306a36Sopenharmony_ci **/
64762306a36Sopenharmony_cistatic void
64862306a36Sopenharmony_cimptsas_add_device_component_by_fw(MPT_ADAPTER *ioc, u8 channel, u8 id)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct mptsas_devinfo sas_device;
65162306a36Sopenharmony_ci	struct mptsas_enclosure enclosure_info;
65262306a36Sopenharmony_ci	int rc;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	rc = mptsas_sas_device_pg0(ioc, &sas_device,
65562306a36Sopenharmony_ci	    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
65662306a36Sopenharmony_ci	     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
65762306a36Sopenharmony_ci	    (channel << 8) + id);
65862306a36Sopenharmony_ci	if (rc)
65962306a36Sopenharmony_ci		return;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
66262306a36Sopenharmony_ci	mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
66362306a36Sopenharmony_ci	    (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
66462306a36Sopenharmony_ci	     MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
66562306a36Sopenharmony_ci	     sas_device.handle_enclosure);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	mptsas_add_device_component(ioc, sas_device.channel,
66862306a36Sopenharmony_ci	    sas_device.id, sas_device.sas_address, sas_device.device_info,
66962306a36Sopenharmony_ci	    sas_device.slot, enclosure_info.enclosure_logical_id);
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci/**
67362306a36Sopenharmony_ci *	mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding each individual device to list
67462306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
67562306a36Sopenharmony_ci *	@starget: SCSI target for this SCSI device
67662306a36Sopenharmony_ci *
67762306a36Sopenharmony_ci **/
67862306a36Sopenharmony_cistatic void
67962306a36Sopenharmony_cimptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc,
68062306a36Sopenharmony_ci		struct scsi_target *starget)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	CONFIGPARMS			cfg;
68362306a36Sopenharmony_ci	ConfigPageHeader_t		hdr;
68462306a36Sopenharmony_ci	dma_addr_t			dma_handle;
68562306a36Sopenharmony_ci	pRaidVolumePage0_t		buffer = NULL;
68662306a36Sopenharmony_ci	int				i;
68762306a36Sopenharmony_ci	RaidPhysDiskPage0_t 		phys_disk;
68862306a36Sopenharmony_ci	struct mptsas_device_info	*sas_info, *next;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	memset(&cfg, 0 , sizeof(CONFIGPARMS));
69162306a36Sopenharmony_ci	memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
69262306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
69362306a36Sopenharmony_ci	/* assumption that all volumes on channel = 0 */
69462306a36Sopenharmony_ci	cfg.pageAddr = starget->id;
69562306a36Sopenharmony_ci	cfg.cfghdr.hdr = &hdr;
69662306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
69762306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (mpt_config(ioc, &cfg) != 0)
70062306a36Sopenharmony_ci		goto out;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (!hdr.PageLength)
70362306a36Sopenharmony_ci		goto out;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
70662306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!buffer)
70962306a36Sopenharmony_ci		goto out;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
71262306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (mpt_config(ioc, &cfg) != 0)
71562306a36Sopenharmony_ci		goto out;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (!buffer->NumPhysDisks)
71862306a36Sopenharmony_ci		goto out;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/*
72162306a36Sopenharmony_ci	 * Adding entry for hidden components
72262306a36Sopenharmony_ci	 */
72362306a36Sopenharmony_ci	for (i = 0; i < buffer->NumPhysDisks; i++) {
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		if (mpt_raid_phys_disk_pg0(ioc,
72662306a36Sopenharmony_ci		    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
72762306a36Sopenharmony_ci			continue;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		mptsas_add_device_component_by_fw(ioc, phys_disk.PhysDiskBus,
73062306a36Sopenharmony_ci		    phys_disk.PhysDiskID);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		mutex_lock(&ioc->sas_device_info_mutex);
73362306a36Sopenharmony_ci		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
73462306a36Sopenharmony_ci		    list) {
73562306a36Sopenharmony_ci			if (!sas_info->is_logical_volume &&
73662306a36Sopenharmony_ci			    (sas_info->fw.channel == phys_disk.PhysDiskBus &&
73762306a36Sopenharmony_ci			    sas_info->fw.id == phys_disk.PhysDiskID)) {
73862306a36Sopenharmony_ci				sas_info->is_hidden_raid_component = 1;
73962306a36Sopenharmony_ci				sas_info->volume_id = starget->id;
74062306a36Sopenharmony_ci			}
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci		mutex_unlock(&ioc->sas_device_info_mutex);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/*
74762306a36Sopenharmony_ci	 * Delete all matching devices out of the list
74862306a36Sopenharmony_ci	 */
74962306a36Sopenharmony_ci	mutex_lock(&ioc->sas_device_info_mutex);
75062306a36Sopenharmony_ci	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
75162306a36Sopenharmony_ci	    list) {
75262306a36Sopenharmony_ci		if (sas_info->is_logical_volume && sas_info->fw.id ==
75362306a36Sopenharmony_ci		    starget->id) {
75462306a36Sopenharmony_ci			list_del(&sas_info->list);
75562306a36Sopenharmony_ci			kfree(sas_info);
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
76062306a36Sopenharmony_ci	if (sas_info) {
76162306a36Sopenharmony_ci		sas_info->fw.id = starget->id;
76262306a36Sopenharmony_ci		sas_info->os.id = starget->id;
76362306a36Sopenharmony_ci		sas_info->os.channel = starget->channel;
76462306a36Sopenharmony_ci		sas_info->is_logical_volume = 1;
76562306a36Sopenharmony_ci		INIT_LIST_HEAD(&sas_info->list);
76662306a36Sopenharmony_ci		list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_device_info_mutex);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci out:
77162306a36Sopenharmony_ci	if (buffer)
77262306a36Sopenharmony_ci		dma_free_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
77362306a36Sopenharmony_ci				  buffer, dma_handle);
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci/**
77762306a36Sopenharmony_ci *	mptsas_add_device_component_starget - adds a SCSI target device component
77862306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
77962306a36Sopenharmony_ci *	@starget: SCSI target for this SCSI device
78062306a36Sopenharmony_ci *
78162306a36Sopenharmony_ci **/
78262306a36Sopenharmony_cistatic void
78362306a36Sopenharmony_cimptsas_add_device_component_starget(MPT_ADAPTER *ioc,
78462306a36Sopenharmony_ci	struct scsi_target *starget)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct sas_rphy	*rphy;
78762306a36Sopenharmony_ci	struct mptsas_phyinfo	*phy_info = NULL;
78862306a36Sopenharmony_ci	struct mptsas_enclosure	enclosure_info;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	rphy = dev_to_rphy(starget->dev.parent);
79162306a36Sopenharmony_ci	phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
79262306a36Sopenharmony_ci			rphy->identify.sas_address);
79362306a36Sopenharmony_ci	if (!phy_info)
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
79762306a36Sopenharmony_ci	mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
79862306a36Sopenharmony_ci		(MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
79962306a36Sopenharmony_ci		MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
80062306a36Sopenharmony_ci		phy_info->attached.handle_enclosure);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	mptsas_add_device_component(ioc, phy_info->attached.channel,
80362306a36Sopenharmony_ci		phy_info->attached.id, phy_info->attached.sas_address,
80462306a36Sopenharmony_ci		phy_info->attached.device_info,
80562306a36Sopenharmony_ci		phy_info->attached.slot, enclosure_info.enclosure_logical_id);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/**
80962306a36Sopenharmony_ci *	mptsas_del_device_component_by_os - Once a device has been removed, we mark the entry in the list as being cached
81062306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
81162306a36Sopenharmony_ci *	@channel: os mapped id's
81262306a36Sopenharmony_ci *	@id: Logical Target ID
81362306a36Sopenharmony_ci *
81462306a36Sopenharmony_ci **/
81562306a36Sopenharmony_cistatic void
81662306a36Sopenharmony_cimptsas_del_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct mptsas_device_info	*sas_info, *next;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/*
82162306a36Sopenharmony_ci	 * Set is_cached flag
82262306a36Sopenharmony_ci	 */
82362306a36Sopenharmony_ci	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
82462306a36Sopenharmony_ci		list) {
82562306a36Sopenharmony_ci		if (sas_info->os.channel == channel && sas_info->os.id == id)
82662306a36Sopenharmony_ci			sas_info->is_cached = 1;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci/**
83162306a36Sopenharmony_ci *	mptsas_del_device_components - Cleaning the list
83262306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
83362306a36Sopenharmony_ci *
83462306a36Sopenharmony_ci **/
83562306a36Sopenharmony_cistatic void
83662306a36Sopenharmony_cimptsas_del_device_components(MPT_ADAPTER *ioc)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct mptsas_device_info	*sas_info, *next;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	mutex_lock(&ioc->sas_device_info_mutex);
84162306a36Sopenharmony_ci	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
84262306a36Sopenharmony_ci		list) {
84362306a36Sopenharmony_ci		list_del(&sas_info->list);
84462306a36Sopenharmony_ci		kfree(sas_info);
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_device_info_mutex);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/*
85162306a36Sopenharmony_ci * mptsas_setup_wide_ports
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Updates for new and existing narrow/wide port configuration
85462306a36Sopenharmony_ci * in the sas_topology
85562306a36Sopenharmony_ci */
85662306a36Sopenharmony_cistatic void
85762306a36Sopenharmony_cimptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct mptsas_portinfo_details * port_details;
86062306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info, *phy_info_cmp;
86162306a36Sopenharmony_ci	u64	sas_address;
86262306a36Sopenharmony_ci	int	i, j;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	phy_info = port_info->phy_info;
86762306a36Sopenharmony_ci	for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) {
86862306a36Sopenharmony_ci		if (phy_info->attached.handle)
86962306a36Sopenharmony_ci			continue;
87062306a36Sopenharmony_ci		port_details = phy_info->port_details;
87162306a36Sopenharmony_ci		if (!port_details)
87262306a36Sopenharmony_ci			continue;
87362306a36Sopenharmony_ci		if (port_details->num_phys < 2)
87462306a36Sopenharmony_ci			continue;
87562306a36Sopenharmony_ci		/*
87662306a36Sopenharmony_ci		 * Removing a phy from a port, letting the last
87762306a36Sopenharmony_ci		 * phy be removed by firmware events.
87862306a36Sopenharmony_ci		 */
87962306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
88062306a36Sopenharmony_ci		    "%s: [%p]: deleting phy = %d\n",
88162306a36Sopenharmony_ci		    ioc->name, __func__, port_details, i));
88262306a36Sopenharmony_ci		port_details->num_phys--;
88362306a36Sopenharmony_ci		port_details->phy_bitmask &= ~ (1 << phy_info->phy_id);
88462306a36Sopenharmony_ci		memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
88562306a36Sopenharmony_ci		if (phy_info->phy) {
88662306a36Sopenharmony_ci			devtprintk(ioc, dev_printk(KERN_DEBUG,
88762306a36Sopenharmony_ci				&phy_info->phy->dev, MYIOC_s_FMT
88862306a36Sopenharmony_ci				"delete phy %d, phy-obj (0x%p)\n", ioc->name,
88962306a36Sopenharmony_ci				phy_info->phy_id, phy_info->phy));
89062306a36Sopenharmony_ci			sas_port_delete_phy(port_details->port, phy_info->phy);
89162306a36Sopenharmony_ci		}
89262306a36Sopenharmony_ci		phy_info->port_details = NULL;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/*
89662306a36Sopenharmony_ci	 * Populate and refresh the tree
89762306a36Sopenharmony_ci	 */
89862306a36Sopenharmony_ci	phy_info = port_info->phy_info;
89962306a36Sopenharmony_ci	for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) {
90062306a36Sopenharmony_ci		sas_address = phy_info->attached.sas_address;
90162306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "phy_id=%d sas_address=0x%018llX\n",
90262306a36Sopenharmony_ci		    ioc->name, i, (unsigned long long)sas_address));
90362306a36Sopenharmony_ci		if (!sas_address)
90462306a36Sopenharmony_ci			continue;
90562306a36Sopenharmony_ci		port_details = phy_info->port_details;
90662306a36Sopenharmony_ci		/*
90762306a36Sopenharmony_ci		 * Forming a port
90862306a36Sopenharmony_ci		 */
90962306a36Sopenharmony_ci		if (!port_details) {
91062306a36Sopenharmony_ci			port_details = kzalloc(sizeof(struct
91162306a36Sopenharmony_ci				mptsas_portinfo_details), GFP_KERNEL);
91262306a36Sopenharmony_ci			if (!port_details)
91362306a36Sopenharmony_ci				goto out;
91462306a36Sopenharmony_ci			port_details->num_phys = 1;
91562306a36Sopenharmony_ci			port_details->port_info = port_info;
91662306a36Sopenharmony_ci			if (phy_info->phy_id < 64 )
91762306a36Sopenharmony_ci				port_details->phy_bitmask |=
91862306a36Sopenharmony_ci				    (1 << phy_info->phy_id);
91962306a36Sopenharmony_ci			phy_info->sas_port_add_phy=1;
92062306a36Sopenharmony_ci			dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tForming port\n\t\t"
92162306a36Sopenharmony_ci			    "phy_id=%d sas_address=0x%018llX\n",
92262306a36Sopenharmony_ci			    ioc->name, i, (unsigned long long)sas_address));
92362306a36Sopenharmony_ci			phy_info->port_details = port_details;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		if (i == port_info->num_phys - 1)
92762306a36Sopenharmony_ci			continue;
92862306a36Sopenharmony_ci		phy_info_cmp = &port_info->phy_info[i + 1];
92962306a36Sopenharmony_ci		for (j = i + 1 ; j < port_info->num_phys ; j++,
93062306a36Sopenharmony_ci		    phy_info_cmp++) {
93162306a36Sopenharmony_ci			if (!phy_info_cmp->attached.sas_address)
93262306a36Sopenharmony_ci				continue;
93362306a36Sopenharmony_ci			if (sas_address != phy_info_cmp->attached.sas_address)
93462306a36Sopenharmony_ci				continue;
93562306a36Sopenharmony_ci			if (phy_info_cmp->port_details == port_details )
93662306a36Sopenharmony_ci				continue;
93762306a36Sopenharmony_ci			dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
93862306a36Sopenharmony_ci			    "\t\tphy_id=%d sas_address=0x%018llX\n",
93962306a36Sopenharmony_ci			    ioc->name, j, (unsigned long long)
94062306a36Sopenharmony_ci			    phy_info_cmp->attached.sas_address));
94162306a36Sopenharmony_ci			if (phy_info_cmp->port_details) {
94262306a36Sopenharmony_ci				port_details->rphy =
94362306a36Sopenharmony_ci				    mptsas_get_rphy(phy_info_cmp);
94462306a36Sopenharmony_ci				port_details->port =
94562306a36Sopenharmony_ci				    mptsas_get_port(phy_info_cmp);
94662306a36Sopenharmony_ci				port_details->starget =
94762306a36Sopenharmony_ci				    mptsas_get_starget(phy_info_cmp);
94862306a36Sopenharmony_ci				port_details->num_phys =
94962306a36Sopenharmony_ci					phy_info_cmp->port_details->num_phys;
95062306a36Sopenharmony_ci				if (!phy_info_cmp->port_details->num_phys)
95162306a36Sopenharmony_ci					kfree(phy_info_cmp->port_details);
95262306a36Sopenharmony_ci			} else
95362306a36Sopenharmony_ci				phy_info_cmp->sas_port_add_phy=1;
95462306a36Sopenharmony_ci			/*
95562306a36Sopenharmony_ci			 * Adding a phy to a port
95662306a36Sopenharmony_ci			 */
95762306a36Sopenharmony_ci			phy_info_cmp->port_details = port_details;
95862306a36Sopenharmony_ci			if (phy_info_cmp->phy_id < 64 )
95962306a36Sopenharmony_ci				port_details->phy_bitmask |=
96062306a36Sopenharmony_ci				(1 << phy_info_cmp->phy_id);
96162306a36Sopenharmony_ci			port_details->num_phys++;
96262306a36Sopenharmony_ci		}
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci out:
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
96862306a36Sopenharmony_ci		port_details = port_info->phy_info[i].port_details;
96962306a36Sopenharmony_ci		if (!port_details)
97062306a36Sopenharmony_ci			continue;
97162306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
97262306a36Sopenharmony_ci		    "%s: [%p]: phy_id=%02d num_phys=%02d "
97362306a36Sopenharmony_ci		    "bitmask=0x%016llX\n", ioc->name, __func__,
97462306a36Sopenharmony_ci		    port_details, i, port_details->num_phys,
97562306a36Sopenharmony_ci		    (unsigned long long)port_details->phy_bitmask));
97662306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tport = %p rphy=%p\n",
97762306a36Sopenharmony_ci		    ioc->name, port_details->port, port_details->rphy));
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci	dsaswideprintk(ioc, printk("\n"));
98062306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci/**
98462306a36Sopenharmony_ci * mptsas_find_vtarget - find a virtual target device (FC LUN device or
98562306a36Sopenharmony_ci *				SCSI target device)
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
98862306a36Sopenharmony_ci * @channel: channel number
98962306a36Sopenharmony_ci * @id: Logical Target ID
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci **/
99262306a36Sopenharmony_cistatic VirtTarget *
99362306a36Sopenharmony_cimptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct scsi_device 		*sdev;
99662306a36Sopenharmony_ci	VirtDevice			*vdevice;
99762306a36Sopenharmony_ci	VirtTarget 			*vtarget = NULL;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	shost_for_each_device(sdev, ioc->sh) {
100062306a36Sopenharmony_ci		vdevice = sdev->hostdata;
100162306a36Sopenharmony_ci		if ((vdevice == NULL) ||
100262306a36Sopenharmony_ci			(vdevice->vtarget == NULL))
100362306a36Sopenharmony_ci			continue;
100462306a36Sopenharmony_ci		if ((vdevice->vtarget->tflags &
100562306a36Sopenharmony_ci		    MPT_TARGET_FLAGS_RAID_COMPONENT ||
100662306a36Sopenharmony_ci		    vdevice->vtarget->raidVolume))
100762306a36Sopenharmony_ci			continue;
100862306a36Sopenharmony_ci		if (vdevice->vtarget->id == id &&
100962306a36Sopenharmony_ci			vdevice->vtarget->channel == channel)
101062306a36Sopenharmony_ci			vtarget = vdevice->vtarget;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci	return vtarget;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void
101662306a36Sopenharmony_cimptsas_queue_device_delete(MPT_ADAPTER *ioc,
101762306a36Sopenharmony_ci	MpiEventDataSasDeviceStatusChange_t *sas_event_data)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct fw_event_work *fw_event;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	fw_event = kzalloc(sizeof(*fw_event) +
102262306a36Sopenharmony_ci			   sizeof(MpiEventDataSasDeviceStatusChange_t),
102362306a36Sopenharmony_ci			   GFP_ATOMIC);
102462306a36Sopenharmony_ci	if (!fw_event) {
102562306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
102662306a36Sopenharmony_ci		    ioc->name, __func__, __LINE__);
102762306a36Sopenharmony_ci		return;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	memcpy(fw_event->event_data, sas_event_data,
103062306a36Sopenharmony_ci	    sizeof(MpiEventDataSasDeviceStatusChange_t));
103162306a36Sopenharmony_ci	fw_event->event = MPI_EVENT_SAS_DEVICE_STATUS_CHANGE;
103262306a36Sopenharmony_ci	fw_event->ioc = ioc;
103362306a36Sopenharmony_ci	mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistatic void
103762306a36Sopenharmony_cimptsas_queue_rescan(MPT_ADAPTER *ioc)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	struct fw_event_work *fw_event;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	fw_event = kzalloc(sizeof(*fw_event), GFP_ATOMIC);
104262306a36Sopenharmony_ci	if (!fw_event) {
104362306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
104462306a36Sopenharmony_ci		    ioc->name, __func__, __LINE__);
104562306a36Sopenharmony_ci		return;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci	fw_event->event = -1;
104862306a36Sopenharmony_ci	fw_event->ioc = ioc;
104962306a36Sopenharmony_ci	mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci/**
105462306a36Sopenharmony_ci * mptsas_target_reset - Issues TARGET_RESET to end device using
105562306a36Sopenharmony_ci *			 handshaking method
105662306a36Sopenharmony_ci *
105762306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
105862306a36Sopenharmony_ci * @channel: channel number
105962306a36Sopenharmony_ci * @id: Logical Target ID for reset
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * Return: (1) success
106262306a36Sopenharmony_ci *         (0) failure
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci **/
106562306a36Sopenharmony_cistatic int
106662306a36Sopenharmony_cimptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	MPT_FRAME_HDR	*mf;
106962306a36Sopenharmony_ci	SCSITaskMgmt_t	*pScsiTm;
107062306a36Sopenharmony_ci	if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0)
107162306a36Sopenharmony_ci		return 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
107562306a36Sopenharmony_ci	if (mf == NULL) {
107662306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
107762306a36Sopenharmony_ci			"%s, no msg frames @%d!!\n", ioc->name,
107862306a36Sopenharmony_ci			__func__, __LINE__));
107962306a36Sopenharmony_ci		goto out_fail;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
108362306a36Sopenharmony_ci		ioc->name, mf));
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/* Format the Request
108662306a36Sopenharmony_ci	 */
108762306a36Sopenharmony_ci	pScsiTm = (SCSITaskMgmt_t *) mf;
108862306a36Sopenharmony_ci	memset (pScsiTm, 0, sizeof(SCSITaskMgmt_t));
108962306a36Sopenharmony_ci	pScsiTm->TargetID = id;
109062306a36Sopenharmony_ci	pScsiTm->Bus = channel;
109162306a36Sopenharmony_ci	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
109262306a36Sopenharmony_ci	pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
109362306a36Sopenharmony_ci	pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
109862306a36Sopenharmony_ci	   "TaskMgmt type=%d (sas device delete) fw_channel = %d fw_id = %d)\n",
109962306a36Sopenharmony_ci	   ioc->name, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, channel, id));
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return 1;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci out_fail:
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	mpt_clear_taskmgmt_in_progress_flag(ioc);
110862306a36Sopenharmony_ci	return 0;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic void
111262306a36Sopenharmony_cimptsas_block_io_sdev(struct scsi_device *sdev, void *data)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	scsi_device_set_state(sdev, SDEV_BLOCK);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void
111862306a36Sopenharmony_cimptsas_block_io_starget(struct scsi_target *starget)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	if (starget)
112162306a36Sopenharmony_ci		starget_for_each_device(starget, NULL, mptsas_block_io_sdev);
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci/**
112562306a36Sopenharmony_ci * mptsas_target_reset_queue - queue a target reset
112662306a36Sopenharmony_ci *
112762306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
112862306a36Sopenharmony_ci * @sas_event_data: SAS Device Status Change Event data
112962306a36Sopenharmony_ci *
113062306a36Sopenharmony_ci * Receive request for TARGET_RESET after receiving a firmware
113162306a36Sopenharmony_ci * event NOT_RESPONDING_EVENT, then put command in link list
113262306a36Sopenharmony_ci * and queue if task_queue already in use.
113362306a36Sopenharmony_ci *
113462306a36Sopenharmony_ci **/
113562306a36Sopenharmony_cistatic void
113662306a36Sopenharmony_cimptsas_target_reset_queue(MPT_ADAPTER *ioc,
113762306a36Sopenharmony_ci    EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
114062306a36Sopenharmony_ci	VirtTarget *vtarget = NULL;
114162306a36Sopenharmony_ci	struct mptsas_target_reset_event *target_reset_list;
114262306a36Sopenharmony_ci	u8		id, channel;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	id = sas_event_data->TargetID;
114562306a36Sopenharmony_ci	channel = sas_event_data->Bus;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	vtarget = mptsas_find_vtarget(ioc, channel, id);
114862306a36Sopenharmony_ci	if (vtarget) {
114962306a36Sopenharmony_ci		mptsas_block_io_starget(vtarget->starget);
115062306a36Sopenharmony_ci		vtarget->deleted = 1; /* block IO */
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event),
115462306a36Sopenharmony_ci	    GFP_ATOMIC);
115562306a36Sopenharmony_ci	if (!target_reset_list) {
115662306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
115762306a36Sopenharmony_ci			"%s, failed to allocate mem @%d..!!\n",
115862306a36Sopenharmony_ci			ioc->name, __func__, __LINE__));
115962306a36Sopenharmony_ci		return;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	memcpy(&target_reset_list->sas_event_data, sas_event_data,
116362306a36Sopenharmony_ci		sizeof(*sas_event_data));
116462306a36Sopenharmony_ci	list_add_tail(&target_reset_list->list, &hd->target_reset_list);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	target_reset_list->time_count = jiffies;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (mptsas_target_reset(ioc, channel, id)) {
116962306a36Sopenharmony_ci		target_reset_list->target_reset_issued = 1;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci/**
117462306a36Sopenharmony_ci * mptsas_schedule_target_reset- send pending target reset
117562306a36Sopenharmony_ci * @iocp: per adapter object
117662306a36Sopenharmony_ci *
117762306a36Sopenharmony_ci * This function will delete scheduled target reset from the list and
117862306a36Sopenharmony_ci * try to send next target reset. This will be called from completion
117962306a36Sopenharmony_ci * context of any Task management command.
118062306a36Sopenharmony_ci */
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_civoid
118362306a36Sopenharmony_cimptsas_schedule_target_reset(void *iocp)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	MPT_ADAPTER *ioc = (MPT_ADAPTER *)(iocp);
118662306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
118762306a36Sopenharmony_ci	struct list_head *head = &hd->target_reset_list;
118862306a36Sopenharmony_ci	struct mptsas_target_reset_event	*target_reset_list;
118962306a36Sopenharmony_ci	u8		id, channel;
119062306a36Sopenharmony_ci	/*
119162306a36Sopenharmony_ci	 * issue target reset to next device in the queue
119262306a36Sopenharmony_ci	 */
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (list_empty(head))
119562306a36Sopenharmony_ci		return;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	target_reset_list = list_entry(head->next,
119862306a36Sopenharmony_ci		struct mptsas_target_reset_event, list);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	id = target_reset_list->sas_event_data.TargetID;
120162306a36Sopenharmony_ci	channel = target_reset_list->sas_event_data.Bus;
120262306a36Sopenharmony_ci	target_reset_list->time_count = jiffies;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (mptsas_target_reset(ioc, channel, id))
120562306a36Sopenharmony_ci		target_reset_list->target_reset_issued = 1;
120662306a36Sopenharmony_ci	return;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci/**
121162306a36Sopenharmony_ci *	mptsas_taskmgmt_complete - complete SAS task management function
121262306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
121362306a36Sopenharmony_ci *	@mf: MPT message frame
121462306a36Sopenharmony_ci *	@mr: SCSI Task Management Reply structure ptr (may be %NULL)
121562306a36Sopenharmony_ci *
121662306a36Sopenharmony_ci *	Completion for TARGET_RESET after NOT_RESPONDING_EVENT, enable work
121762306a36Sopenharmony_ci *	queue to finish off removing device from upper layers, then send next
121862306a36Sopenharmony_ci *	TARGET_RESET in the queue.
121962306a36Sopenharmony_ci **/
122062306a36Sopenharmony_cistatic int
122162306a36Sopenharmony_cimptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
122462306a36Sopenharmony_ci        struct list_head *head = &hd->target_reset_list;
122562306a36Sopenharmony_ci	u8		id, channel;
122662306a36Sopenharmony_ci	struct mptsas_target_reset_event	*target_reset_list;
122762306a36Sopenharmony_ci	SCSITaskMgmtReply_t *pScsiTmReply;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed: "
123062306a36Sopenharmony_ci	    "(mf = %p, mr = %p)\n", ioc->name, mf, mr));
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	pScsiTmReply = (SCSITaskMgmtReply_t *)mr;
123362306a36Sopenharmony_ci	if (!pScsiTmReply)
123462306a36Sopenharmony_ci		return 0;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
123762306a36Sopenharmony_ci	    "\tTaskMgmt completed: fw_channel = %d, fw_id = %d,\n"
123862306a36Sopenharmony_ci	    "\ttask_type = 0x%02X, iocstatus = 0x%04X "
123962306a36Sopenharmony_ci	    "loginfo = 0x%08X,\n\tresponse_code = 0x%02X, "
124062306a36Sopenharmony_ci	    "term_cmnds = %d\n", ioc->name,
124162306a36Sopenharmony_ci	    pScsiTmReply->Bus, pScsiTmReply->TargetID,
124262306a36Sopenharmony_ci	    pScsiTmReply->TaskType,
124362306a36Sopenharmony_ci	    le16_to_cpu(pScsiTmReply->IOCStatus),
124462306a36Sopenharmony_ci	    le32_to_cpu(pScsiTmReply->IOCLogInfo),
124562306a36Sopenharmony_ci	    pScsiTmReply->ResponseCode,
124662306a36Sopenharmony_ci	    le32_to_cpu(pScsiTmReply->TerminationCount)));
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (pScsiTmReply->ResponseCode)
124962306a36Sopenharmony_ci		mptscsih_taskmgmt_response_code(ioc,
125062306a36Sopenharmony_ci		pScsiTmReply->ResponseCode);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	if (pScsiTmReply->TaskType ==
125362306a36Sopenharmony_ci	    MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK || pScsiTmReply->TaskType ==
125462306a36Sopenharmony_ci	     MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET) {
125562306a36Sopenharmony_ci		ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
125662306a36Sopenharmony_ci		ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
125762306a36Sopenharmony_ci		memcpy(ioc->taskmgmt_cmds.reply, mr,
125862306a36Sopenharmony_ci		    min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
125962306a36Sopenharmony_ci		if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
126062306a36Sopenharmony_ci			ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
126162306a36Sopenharmony_ci			complete(&ioc->taskmgmt_cmds.done);
126262306a36Sopenharmony_ci			return 1;
126362306a36Sopenharmony_ci		}
126462306a36Sopenharmony_ci		return 0;
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	mpt_clear_taskmgmt_in_progress_flag(ioc);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (list_empty(head))
127062306a36Sopenharmony_ci		return 1;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	target_reset_list = list_entry(head->next,
127362306a36Sopenharmony_ci	    struct mptsas_target_reset_event, list);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
127662306a36Sopenharmony_ci	    "TaskMgmt: completed (%d seconds)\n",
127762306a36Sopenharmony_ci	    ioc->name, jiffies_to_msecs(jiffies -
127862306a36Sopenharmony_ci	    target_reset_list->time_count)/1000));
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	id = pScsiTmReply->TargetID;
128162306a36Sopenharmony_ci	channel = pScsiTmReply->Bus;
128262306a36Sopenharmony_ci	target_reset_list->time_count = jiffies;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/*
128562306a36Sopenharmony_ci	 * retry target reset
128662306a36Sopenharmony_ci	 */
128762306a36Sopenharmony_ci	if (!target_reset_list->target_reset_issued) {
128862306a36Sopenharmony_ci		if (mptsas_target_reset(ioc, channel, id))
128962306a36Sopenharmony_ci			target_reset_list->target_reset_issued = 1;
129062306a36Sopenharmony_ci		return 1;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/*
129462306a36Sopenharmony_ci	 * enable work queue to remove device from upper layers
129562306a36Sopenharmony_ci	 */
129662306a36Sopenharmony_ci	list_del(&target_reset_list->list);
129762306a36Sopenharmony_ci	if (!ioc->fw_events_off)
129862306a36Sopenharmony_ci		mptsas_queue_device_delete(ioc,
129962306a36Sopenharmony_ci			&target_reset_list->sas_event_data);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	ioc->schedule_target_reset(ioc);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	return 1;
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci/**
130862306a36Sopenharmony_ci * mptsas_ioc_reset - issue an IOC reset for this reset phase
130962306a36Sopenharmony_ci *
131062306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
131162306a36Sopenharmony_ci * @reset_phase: id of phase of reset
131262306a36Sopenharmony_ci *
131362306a36Sopenharmony_ci **/
131462306a36Sopenharmony_cistatic int
131562306a36Sopenharmony_cimptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd;
131862306a36Sopenharmony_ci	int rc;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	rc = mptscsih_ioc_reset(ioc, reset_phase);
132162306a36Sopenharmony_ci	if ((ioc->bus_type != SAS) || (!rc))
132262306a36Sopenharmony_ci		return rc;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	hd = shost_priv(ioc->sh);
132562306a36Sopenharmony_ci	if (!hd->ioc)
132662306a36Sopenharmony_ci		goto out;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	switch (reset_phase) {
132962306a36Sopenharmony_ci	case MPT_IOC_SETUP_RESET:
133062306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
133162306a36Sopenharmony_ci		    "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
133262306a36Sopenharmony_ci		mptsas_fw_event_off(ioc);
133362306a36Sopenharmony_ci		break;
133462306a36Sopenharmony_ci	case MPT_IOC_PRE_RESET:
133562306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
133662306a36Sopenharmony_ci		    "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
133762306a36Sopenharmony_ci		break;
133862306a36Sopenharmony_ci	case MPT_IOC_POST_RESET:
133962306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
134062306a36Sopenharmony_ci		    "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
134162306a36Sopenharmony_ci		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
134262306a36Sopenharmony_ci			ioc->sas_mgmt.status |= MPT_MGMT_STATUS_DID_IOCRESET;
134362306a36Sopenharmony_ci			complete(&ioc->sas_mgmt.done);
134462306a36Sopenharmony_ci		}
134562306a36Sopenharmony_ci		mptsas_cleanup_fw_event_q(ioc);
134662306a36Sopenharmony_ci		mptsas_queue_rescan(ioc);
134762306a36Sopenharmony_ci		break;
134862306a36Sopenharmony_ci	default:
134962306a36Sopenharmony_ci		break;
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci out:
135362306a36Sopenharmony_ci	return rc;
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci/**
135862306a36Sopenharmony_ci * enum device_state - TUR device state
135962306a36Sopenharmony_ci * @DEVICE_RETRY: need to retry the TUR
136062306a36Sopenharmony_ci * @DEVICE_ERROR: TUR return error, don't add device
136162306a36Sopenharmony_ci * @DEVICE_READY: device can be added
136262306a36Sopenharmony_ci *
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cienum device_state{
136562306a36Sopenharmony_ci	DEVICE_RETRY,
136662306a36Sopenharmony_ci	DEVICE_ERROR,
136762306a36Sopenharmony_ci	DEVICE_READY,
136862306a36Sopenharmony_ci};
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic int
137162306a36Sopenharmony_cimptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
137262306a36Sopenharmony_ci		u32 form, u32 form_specific)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
137562306a36Sopenharmony_ci	CONFIGPARMS cfg;
137662306a36Sopenharmony_ci	SasEnclosurePage0_t *buffer;
137762306a36Sopenharmony_ci	dma_addr_t dma_handle;
137862306a36Sopenharmony_ci	int error;
137962306a36Sopenharmony_ci	__le64 le_identifier;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	memset(&hdr, 0, sizeof(hdr));
138262306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASENCLOSURE0_PAGEVERSION;
138362306a36Sopenharmony_ci	hdr.PageNumber = 0;
138462306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
138562306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_ENCLOSURE;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
138862306a36Sopenharmony_ci	cfg.physAddr = -1;
138962306a36Sopenharmony_ci	cfg.pageAddr = form + form_specific;
139062306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
139162306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
139262306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
139562306a36Sopenharmony_ci	if (error)
139662306a36Sopenharmony_ci		goto out;
139762306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
139862306a36Sopenharmony_ci		error = -ENXIO;
139962306a36Sopenharmony_ci		goto out;
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
140362306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
140462306a36Sopenharmony_ci	if (!buffer) {
140562306a36Sopenharmony_ci		error = -ENOMEM;
140662306a36Sopenharmony_ci		goto out;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
141062306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
141362306a36Sopenharmony_ci	if (error)
141462306a36Sopenharmony_ci		goto out_free_consistent;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	/* save config data */
141762306a36Sopenharmony_ci	memcpy(&le_identifier, &buffer->EnclosureLogicalID, sizeof(__le64));
141862306a36Sopenharmony_ci	enclosure->enclosure_logical_id = le64_to_cpu(le_identifier);
141962306a36Sopenharmony_ci	enclosure->enclosure_handle = le16_to_cpu(buffer->EnclosureHandle);
142062306a36Sopenharmony_ci	enclosure->flags = le16_to_cpu(buffer->Flags);
142162306a36Sopenharmony_ci	enclosure->num_slot = le16_to_cpu(buffer->NumSlots);
142262306a36Sopenharmony_ci	enclosure->start_slot = le16_to_cpu(buffer->StartSlot);
142362306a36Sopenharmony_ci	enclosure->start_id = buffer->StartTargetID;
142462306a36Sopenharmony_ci	enclosure->start_channel = buffer->StartBus;
142562306a36Sopenharmony_ci	enclosure->sep_id = buffer->SEPTargetID;
142662306a36Sopenharmony_ci	enclosure->sep_channel = buffer->SEPBus;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci out_free_consistent:
142962306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
143062306a36Sopenharmony_ci			  dma_handle);
143162306a36Sopenharmony_ci out:
143262306a36Sopenharmony_ci	return error;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci/**
143662306a36Sopenharmony_ci *	mptsas_add_end_device - report a new end device to sas transport layer
143762306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
143862306a36Sopenharmony_ci *	@phy_info: describes attached device
143962306a36Sopenharmony_ci *
144062306a36Sopenharmony_ci *	return (0) success (1) failure
144162306a36Sopenharmony_ci *
144262306a36Sopenharmony_ci **/
144362306a36Sopenharmony_cistatic int
144462306a36Sopenharmony_cimptsas_add_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct sas_rphy *rphy;
144762306a36Sopenharmony_ci	struct sas_port *port;
144862306a36Sopenharmony_ci	struct sas_identify identify;
144962306a36Sopenharmony_ci	char *ds = NULL;
145062306a36Sopenharmony_ci	u8 fw_id;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!phy_info) {
145362306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
145462306a36Sopenharmony_ci			"%s: exit at line=%d\n", ioc->name,
145562306a36Sopenharmony_ci			 __func__, __LINE__));
145662306a36Sopenharmony_ci		return 1;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	fw_id = phy_info->attached.id;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (mptsas_get_rphy(phy_info)) {
146262306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
146362306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
146462306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
146562306a36Sopenharmony_ci		return 2;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	port = mptsas_get_port(phy_info);
146962306a36Sopenharmony_ci	if (!port) {
147062306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
147162306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
147262306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
147362306a36Sopenharmony_ci		return 3;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	if (phy_info->attached.device_info &
147762306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SSP_TARGET)
147862306a36Sopenharmony_ci		ds = "ssp";
147962306a36Sopenharmony_ci	if (phy_info->attached.device_info &
148062306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_STP_TARGET)
148162306a36Sopenharmony_ci		ds = "stp";
148262306a36Sopenharmony_ci	if (phy_info->attached.device_info &
148362306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)
148462306a36Sopenharmony_ci		ds = "sata";
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	printk(MYIOC_s_INFO_FMT "attaching %s device: fw_channel %d, fw_id %d,"
148762306a36Sopenharmony_ci	    " phy %d, sas_addr 0x%llx\n", ioc->name, ds,
148862306a36Sopenharmony_ci	    phy_info->attached.channel, phy_info->attached.id,
148962306a36Sopenharmony_ci	    phy_info->attached.phy_id, (unsigned long long)
149062306a36Sopenharmony_ci	    phy_info->attached.sas_address);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	mptsas_parse_device_info(&identify, &phy_info->attached);
149362306a36Sopenharmony_ci	rphy = sas_end_device_alloc(port);
149462306a36Sopenharmony_ci	if (!rphy) {
149562306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
149662306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
149762306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
149862306a36Sopenharmony_ci		return 5; /* non-fatal: an rphy can be added later */
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	rphy->identify = identify;
150262306a36Sopenharmony_ci	if (sas_rphy_add(rphy)) {
150362306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
150462306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
150562306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
150662306a36Sopenharmony_ci		sas_rphy_free(rphy);
150762306a36Sopenharmony_ci		return 6;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci	mptsas_set_rphy(ioc, phy_info, rphy);
151062306a36Sopenharmony_ci	return 0;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci/**
151462306a36Sopenharmony_ci *	mptsas_del_end_device - report a deleted end device to sas transport layer
151562306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
151662306a36Sopenharmony_ci *	@phy_info: describes attached device
151762306a36Sopenharmony_ci *
151862306a36Sopenharmony_ci **/
151962306a36Sopenharmony_cistatic void
152062306a36Sopenharmony_cimptsas_del_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct sas_rphy *rphy;
152362306a36Sopenharmony_ci	struct sas_port *port;
152462306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
152562306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info_parent;
152662306a36Sopenharmony_ci	int i;
152762306a36Sopenharmony_ci	char *ds = NULL;
152862306a36Sopenharmony_ci	u8 fw_id;
152962306a36Sopenharmony_ci	u64 sas_address;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	if (!phy_info)
153262306a36Sopenharmony_ci		return;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	fw_id = phy_info->attached.id;
153562306a36Sopenharmony_ci	sas_address = phy_info->attached.sas_address;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (!phy_info->port_details) {
153862306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
153962306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
154062306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
154162306a36Sopenharmony_ci		return;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	rphy = mptsas_get_rphy(phy_info);
154462306a36Sopenharmony_ci	if (!rphy) {
154562306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
154662306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
154762306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
154862306a36Sopenharmony_ci		return;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_INITIATOR
155262306a36Sopenharmony_ci		|| phy_info->attached.device_info
155362306a36Sopenharmony_ci			& MPI_SAS_DEVICE_INFO_SMP_INITIATOR
155462306a36Sopenharmony_ci		|| phy_info->attached.device_info
155562306a36Sopenharmony_ci			& MPI_SAS_DEVICE_INFO_STP_INITIATOR)
155662306a36Sopenharmony_ci		ds = "initiator";
155762306a36Sopenharmony_ci	if (phy_info->attached.device_info &
155862306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SSP_TARGET)
155962306a36Sopenharmony_ci		ds = "ssp";
156062306a36Sopenharmony_ci	if (phy_info->attached.device_info &
156162306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_STP_TARGET)
156262306a36Sopenharmony_ci		ds = "stp";
156362306a36Sopenharmony_ci	if (phy_info->attached.device_info &
156462306a36Sopenharmony_ci	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)
156562306a36Sopenharmony_ci		ds = "sata";
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	dev_printk(KERN_DEBUG, &rphy->dev, MYIOC_s_FMT
156862306a36Sopenharmony_ci	    "removing %s device: fw_channel %d, fw_id %d, phy %d,"
156962306a36Sopenharmony_ci	    "sas_addr 0x%llx\n", ioc->name, ds, phy_info->attached.channel,
157062306a36Sopenharmony_ci	    phy_info->attached.id, phy_info->attached.phy_id,
157162306a36Sopenharmony_ci	    (unsigned long long) sas_address);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	port = mptsas_get_port(phy_info);
157462306a36Sopenharmony_ci	if (!port) {
157562306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
157662306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
157762306a36Sopenharmony_ci			 __func__, fw_id, __LINE__));
157862306a36Sopenharmony_ci		return;
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci	port_info = phy_info->portinfo;
158162306a36Sopenharmony_ci	phy_info_parent = port_info->phy_info;
158262306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++, phy_info_parent++) {
158362306a36Sopenharmony_ci		if (!phy_info_parent->phy)
158462306a36Sopenharmony_ci			continue;
158562306a36Sopenharmony_ci		if (phy_info_parent->attached.sas_address !=
158662306a36Sopenharmony_ci		    sas_address)
158762306a36Sopenharmony_ci			continue;
158862306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &phy_info_parent->phy->dev,
158962306a36Sopenharmony_ci		    MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n",
159062306a36Sopenharmony_ci		    ioc->name, phy_info_parent->phy_id,
159162306a36Sopenharmony_ci		    phy_info_parent->phy);
159262306a36Sopenharmony_ci		sas_port_delete_phy(port, phy_info_parent->phy);
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
159662306a36Sopenharmony_ci	    "delete port %d, sas_addr (0x%llx)\n", ioc->name,
159762306a36Sopenharmony_ci	     port->port_identifier, (unsigned long long)sas_address);
159862306a36Sopenharmony_ci	sas_port_delete(port);
159962306a36Sopenharmony_ci	mptsas_set_port(ioc, phy_info, NULL);
160062306a36Sopenharmony_ci	mptsas_port_delete(ioc, phy_info->port_details);
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic struct mptsas_phyinfo *
160462306a36Sopenharmony_cimptsas_refreshing_device_handles(MPT_ADAPTER *ioc,
160562306a36Sopenharmony_ci	struct mptsas_devinfo *sas_device)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
160862306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
160962306a36Sopenharmony_ci	int i;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
161262306a36Sopenharmony_ci	    sas_device->sas_address);
161362306a36Sopenharmony_ci	if (!phy_info)
161462306a36Sopenharmony_ci		goto out;
161562306a36Sopenharmony_ci	port_info = phy_info->portinfo;
161662306a36Sopenharmony_ci	if (!port_info)
161762306a36Sopenharmony_ci		goto out;
161862306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
161962306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
162062306a36Sopenharmony_ci		if (port_info->phy_info[i].attached.sas_address !=
162162306a36Sopenharmony_ci			sas_device->sas_address)
162262306a36Sopenharmony_ci			continue;
162362306a36Sopenharmony_ci		port_info->phy_info[i].attached.channel = sas_device->channel;
162462306a36Sopenharmony_ci		port_info->phy_info[i].attached.id = sas_device->id;
162562306a36Sopenharmony_ci		port_info->phy_info[i].attached.sas_address =
162662306a36Sopenharmony_ci		    sas_device->sas_address;
162762306a36Sopenharmony_ci		port_info->phy_info[i].attached.handle = sas_device->handle;
162862306a36Sopenharmony_ci		port_info->phy_info[i].attached.handle_parent =
162962306a36Sopenharmony_ci		    sas_device->handle_parent;
163062306a36Sopenharmony_ci		port_info->phy_info[i].attached.handle_enclosure =
163162306a36Sopenharmony_ci		    sas_device->handle_enclosure;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
163462306a36Sopenharmony_ci out:
163562306a36Sopenharmony_ci	return phy_info;
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci/**
163962306a36Sopenharmony_ci * mptsas_firmware_event_work - work thread for processing fw events
164062306a36Sopenharmony_ci * @work: work queue payload containing info describing the event
164162306a36Sopenharmony_ci * Context: user
164262306a36Sopenharmony_ci *
164362306a36Sopenharmony_ci */
164462306a36Sopenharmony_cistatic void
164562306a36Sopenharmony_cimptsas_firmware_event_work(struct work_struct *work)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	struct fw_event_work *fw_event =
164862306a36Sopenharmony_ci		container_of(work, struct fw_event_work, work.work);
164962306a36Sopenharmony_ci	MPT_ADAPTER *ioc = fw_event->ioc;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	/* special rescan topology handling */
165262306a36Sopenharmony_ci	if (fw_event->event == -1) {
165362306a36Sopenharmony_ci		if (ioc->in_rescan) {
165462306a36Sopenharmony_ci			devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
165562306a36Sopenharmony_ci				"%s: rescan ignored as it is in progress\n",
165662306a36Sopenharmony_ci				ioc->name, __func__));
165762306a36Sopenharmony_ci			return;
165862306a36Sopenharmony_ci		}
165962306a36Sopenharmony_ci		devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: rescan after "
166062306a36Sopenharmony_ci		    "reset\n", ioc->name, __func__));
166162306a36Sopenharmony_ci		ioc->in_rescan = 1;
166262306a36Sopenharmony_ci		mptsas_not_responding_devices(ioc);
166362306a36Sopenharmony_ci		mptsas_scan_sas_topology(ioc);
166462306a36Sopenharmony_ci		ioc->in_rescan = 0;
166562306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
166662306a36Sopenharmony_ci		mptsas_fw_event_on(ioc);
166762306a36Sopenharmony_ci		return;
166862306a36Sopenharmony_ci	}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	/* events handling turned off during host reset */
167162306a36Sopenharmony_ci	if (ioc->fw_events_off) {
167262306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
167362306a36Sopenharmony_ci		return;
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: fw_event=(0x%p), "
167762306a36Sopenharmony_ci	    "event = (0x%02x)\n", ioc->name, __func__, fw_event,
167862306a36Sopenharmony_ci	    (fw_event->event & 0xFF)));
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	switch (fw_event->event) {
168162306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
168262306a36Sopenharmony_ci		mptsas_send_sas_event(fw_event);
168362306a36Sopenharmony_ci		break;
168462306a36Sopenharmony_ci	case MPI_EVENT_INTEGRATED_RAID:
168562306a36Sopenharmony_ci		mptsas_send_raid_event(fw_event);
168662306a36Sopenharmony_ci		break;
168762306a36Sopenharmony_ci	case MPI_EVENT_IR2:
168862306a36Sopenharmony_ci		mptsas_send_ir2_event(fw_event);
168962306a36Sopenharmony_ci		break;
169062306a36Sopenharmony_ci	case MPI_EVENT_PERSISTENT_TABLE_FULL:
169162306a36Sopenharmony_ci		mptbase_sas_persist_operation(ioc,
169262306a36Sopenharmony_ci		    MPI_SAS_OP_CLEAR_NOT_PRESENT);
169362306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
169462306a36Sopenharmony_ci		break;
169562306a36Sopenharmony_ci	case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
169662306a36Sopenharmony_ci		mptsas_broadcast_primitive_work(fw_event);
169762306a36Sopenharmony_ci		break;
169862306a36Sopenharmony_ci	case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
169962306a36Sopenharmony_ci		mptsas_send_expander_event(fw_event);
170062306a36Sopenharmony_ci		break;
170162306a36Sopenharmony_ci	case MPI_EVENT_SAS_PHY_LINK_STATUS:
170262306a36Sopenharmony_ci		mptsas_send_link_status_event(fw_event);
170362306a36Sopenharmony_ci		break;
170462306a36Sopenharmony_ci	case MPI_EVENT_QUEUE_FULL:
170562306a36Sopenharmony_ci		mptsas_handle_queue_full_event(fw_event);
170662306a36Sopenharmony_ci		break;
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ci}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_cistatic int
171362306a36Sopenharmony_cimptsas_slave_configure(struct scsi_device *sdev)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct Scsi_Host	*host = sdev->host;
171662306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd = shost_priv(host);
171762306a36Sopenharmony_ci	MPT_ADAPTER	*ioc = hd->ioc;
171862306a36Sopenharmony_ci	VirtDevice	*vdevice = sdev->hostdata;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	if (vdevice->vtarget->deleted) {
172162306a36Sopenharmony_ci		sdev_printk(KERN_INFO, sdev, "clearing deleted flag\n");
172262306a36Sopenharmony_ci		vdevice->vtarget->deleted = 0;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	/*
172662306a36Sopenharmony_ci	 * RAID volumes placed beyond the last expected port.
172762306a36Sopenharmony_ci	 * Ignore sending sas mode pages in that case..
172862306a36Sopenharmony_ci	 */
172962306a36Sopenharmony_ci	if (sdev->channel == MPTSAS_RAID_CHANNEL) {
173062306a36Sopenharmony_ci		mptsas_add_device_component_starget_ir(ioc, scsi_target(sdev));
173162306a36Sopenharmony_ci		goto out;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	sas_read_port_mode_page(sdev);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	mptsas_add_device_component_starget(ioc, scsi_target(sdev));
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci out:
173962306a36Sopenharmony_ci	return mptscsih_slave_configure(sdev);
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic int
174362306a36Sopenharmony_cimptsas_target_alloc(struct scsi_target *starget)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci	struct Scsi_Host *host = dev_to_shost(&starget->dev);
174662306a36Sopenharmony_ci	MPT_SCSI_HOST		*hd = shost_priv(host);
174762306a36Sopenharmony_ci	VirtTarget		*vtarget;
174862306a36Sopenharmony_ci	u8			id, channel;
174962306a36Sopenharmony_ci	struct sas_rphy		*rphy;
175062306a36Sopenharmony_ci	struct mptsas_portinfo	*p;
175162306a36Sopenharmony_ci	int 			 i;
175262306a36Sopenharmony_ci	MPT_ADAPTER		*ioc = hd->ioc;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
175562306a36Sopenharmony_ci	if (!vtarget)
175662306a36Sopenharmony_ci		return -ENOMEM;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	vtarget->starget = starget;
175962306a36Sopenharmony_ci	vtarget->ioc_id = ioc->id;
176062306a36Sopenharmony_ci	vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
176162306a36Sopenharmony_ci	id = starget->id;
176262306a36Sopenharmony_ci	channel = 0;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	/*
176562306a36Sopenharmony_ci	 * RAID volumes placed beyond the last expected port.
176662306a36Sopenharmony_ci	 */
176762306a36Sopenharmony_ci	if (starget->channel == MPTSAS_RAID_CHANNEL) {
176862306a36Sopenharmony_ci		if (!ioc->raid_data.pIocPg2) {
176962306a36Sopenharmony_ci			kfree(vtarget);
177062306a36Sopenharmony_ci			return -ENXIO;
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci		for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
177362306a36Sopenharmony_ci			if (id == ioc->raid_data.pIocPg2->
177462306a36Sopenharmony_ci					RaidVolume[i].VolumeID) {
177562306a36Sopenharmony_ci				channel = ioc->raid_data.pIocPg2->
177662306a36Sopenharmony_ci					RaidVolume[i].VolumeBus;
177762306a36Sopenharmony_ci			}
177862306a36Sopenharmony_ci		}
177962306a36Sopenharmony_ci		vtarget->raidVolume = 1;
178062306a36Sopenharmony_ci		goto out;
178162306a36Sopenharmony_ci	}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	rphy = dev_to_rphy(starget->dev.parent);
178462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
178562306a36Sopenharmony_ci	list_for_each_entry(p, &ioc->sas_topology, list) {
178662306a36Sopenharmony_ci		for (i = 0; i < p->num_phys; i++) {
178762306a36Sopenharmony_ci			if (p->phy_info[i].attached.sas_address !=
178862306a36Sopenharmony_ci					rphy->identify.sas_address)
178962306a36Sopenharmony_ci				continue;
179062306a36Sopenharmony_ci			id = p->phy_info[i].attached.id;
179162306a36Sopenharmony_ci			channel = p->phy_info[i].attached.channel;
179262306a36Sopenharmony_ci			mptsas_set_starget(&p->phy_info[i], starget);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci			/*
179562306a36Sopenharmony_ci			 * Exposing hidden raid components
179662306a36Sopenharmony_ci			 */
179762306a36Sopenharmony_ci			if (mptscsih_is_phys_disk(ioc, channel, id)) {
179862306a36Sopenharmony_ci				id = mptscsih_raid_id_to_num(ioc,
179962306a36Sopenharmony_ci						channel, id);
180062306a36Sopenharmony_ci				vtarget->tflags |=
180162306a36Sopenharmony_ci				    MPT_TARGET_FLAGS_RAID_COMPONENT;
180262306a36Sopenharmony_ci				p->phy_info[i].attached.phys_disk_num = id;
180362306a36Sopenharmony_ci			}
180462306a36Sopenharmony_ci			mutex_unlock(&ioc->sas_topology_mutex);
180562306a36Sopenharmony_ci			goto out;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	kfree(vtarget);
181162306a36Sopenharmony_ci	return -ENXIO;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci out:
181462306a36Sopenharmony_ci	vtarget->id = id;
181562306a36Sopenharmony_ci	vtarget->channel = channel;
181662306a36Sopenharmony_ci	starget->hostdata = vtarget;
181762306a36Sopenharmony_ci	return 0;
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_cistatic void
182162306a36Sopenharmony_cimptsas_target_destroy(struct scsi_target *starget)
182262306a36Sopenharmony_ci{
182362306a36Sopenharmony_ci	struct Scsi_Host *host = dev_to_shost(&starget->dev);
182462306a36Sopenharmony_ci	MPT_SCSI_HOST		*hd = shost_priv(host);
182562306a36Sopenharmony_ci	struct sas_rphy		*rphy;
182662306a36Sopenharmony_ci	struct mptsas_portinfo	*p;
182762306a36Sopenharmony_ci	int 			 i;
182862306a36Sopenharmony_ci	MPT_ADAPTER	*ioc = hd->ioc;
182962306a36Sopenharmony_ci	VirtTarget	*vtarget;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (!starget->hostdata)
183262306a36Sopenharmony_ci		return;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	vtarget = starget->hostdata;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	mptsas_del_device_component_by_os(ioc, starget->channel,
183762306a36Sopenharmony_ci	    starget->id);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	if (starget->channel == MPTSAS_RAID_CHANNEL)
184162306a36Sopenharmony_ci		goto out;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	rphy = dev_to_rphy(starget->dev.parent);
184462306a36Sopenharmony_ci	list_for_each_entry(p, &ioc->sas_topology, list) {
184562306a36Sopenharmony_ci		for (i = 0; i < p->num_phys; i++) {
184662306a36Sopenharmony_ci			if (p->phy_info[i].attached.sas_address !=
184762306a36Sopenharmony_ci					rphy->identify.sas_address)
184862306a36Sopenharmony_ci				continue;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci			starget_printk(KERN_INFO, starget, MYIOC_s_FMT
185162306a36Sopenharmony_ci			"delete device: fw_channel %d, fw_id %d, phy %d, "
185262306a36Sopenharmony_ci			"sas_addr 0x%llx\n", ioc->name,
185362306a36Sopenharmony_ci			p->phy_info[i].attached.channel,
185462306a36Sopenharmony_ci			p->phy_info[i].attached.id,
185562306a36Sopenharmony_ci			p->phy_info[i].attached.phy_id, (unsigned long long)
185662306a36Sopenharmony_ci			p->phy_info[i].attached.sas_address);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci			mptsas_set_starget(&p->phy_info[i], NULL);
185962306a36Sopenharmony_ci		}
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci out:
186362306a36Sopenharmony_ci	vtarget->starget = NULL;
186462306a36Sopenharmony_ci	kfree(starget->hostdata);
186562306a36Sopenharmony_ci	starget->hostdata = NULL;
186662306a36Sopenharmony_ci}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_cistatic int
187062306a36Sopenharmony_cimptsas_slave_alloc(struct scsi_device *sdev)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	struct Scsi_Host	*host = sdev->host;
187362306a36Sopenharmony_ci	MPT_SCSI_HOST		*hd = shost_priv(host);
187462306a36Sopenharmony_ci	struct sas_rphy		*rphy;
187562306a36Sopenharmony_ci	struct mptsas_portinfo	*p;
187662306a36Sopenharmony_ci	VirtDevice		*vdevice;
187762306a36Sopenharmony_ci	struct scsi_target 	*starget;
187862306a36Sopenharmony_ci	int 			i;
187962306a36Sopenharmony_ci	MPT_ADAPTER *ioc = hd->ioc;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
188262306a36Sopenharmony_ci	if (!vdevice) {
188362306a36Sopenharmony_ci		printk(MYIOC_s_ERR_FMT "slave_alloc kzalloc(%zd) FAILED!\n",
188462306a36Sopenharmony_ci				ioc->name, sizeof(VirtDevice));
188562306a36Sopenharmony_ci		return -ENOMEM;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci	starget = scsi_target(sdev);
188862306a36Sopenharmony_ci	vdevice->vtarget = starget->hostdata;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	if (sdev->channel == MPTSAS_RAID_CHANNEL)
189162306a36Sopenharmony_ci		goto out;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	rphy = dev_to_rphy(sdev->sdev_target->dev.parent);
189462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
189562306a36Sopenharmony_ci	list_for_each_entry(p, &ioc->sas_topology, list) {
189662306a36Sopenharmony_ci		for (i = 0; i < p->num_phys; i++) {
189762306a36Sopenharmony_ci			if (p->phy_info[i].attached.sas_address !=
189862306a36Sopenharmony_ci					rphy->identify.sas_address)
189962306a36Sopenharmony_ci				continue;
190062306a36Sopenharmony_ci			vdevice->lun = sdev->lun;
190162306a36Sopenharmony_ci			/*
190262306a36Sopenharmony_ci			 * Exposing hidden raid components
190362306a36Sopenharmony_ci			 */
190462306a36Sopenharmony_ci			if (mptscsih_is_phys_disk(ioc,
190562306a36Sopenharmony_ci			    p->phy_info[i].attached.channel,
190662306a36Sopenharmony_ci			    p->phy_info[i].attached.id))
190762306a36Sopenharmony_ci				sdev->no_uld_attach = 1;
190862306a36Sopenharmony_ci			mutex_unlock(&ioc->sas_topology_mutex);
190962306a36Sopenharmony_ci			goto out;
191062306a36Sopenharmony_ci		}
191162306a36Sopenharmony_ci	}
191262306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	kfree(vdevice);
191562306a36Sopenharmony_ci	return -ENXIO;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci out:
191862306a36Sopenharmony_ci	vdevice->vtarget->num_luns++;
191962306a36Sopenharmony_ci	sdev->hostdata = vdevice;
192062306a36Sopenharmony_ci	return 0;
192162306a36Sopenharmony_ci}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_cistatic int
192462306a36Sopenharmony_cimptsas_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *SCpnt)
192562306a36Sopenharmony_ci{
192662306a36Sopenharmony_ci	MPT_SCSI_HOST	*hd;
192762306a36Sopenharmony_ci	MPT_ADAPTER	*ioc;
192862306a36Sopenharmony_ci	VirtDevice	*vdevice = SCpnt->device->hostdata;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (!vdevice || !vdevice->vtarget || vdevice->vtarget->deleted) {
193162306a36Sopenharmony_ci		SCpnt->result = DID_NO_CONNECT << 16;
193262306a36Sopenharmony_ci		scsi_done(SCpnt);
193362306a36Sopenharmony_ci		return 0;
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	hd = shost_priv(shost);
193762306a36Sopenharmony_ci	ioc = hd->ioc;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	if (ioc->sas_discovery_quiesce_io)
194062306a36Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	if (ioc->debug_level & MPT_DEBUG_SCSI)
194362306a36Sopenharmony_ci		scsi_print_command(SCpnt);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	return mptscsih_qcmd(SCpnt);
194662306a36Sopenharmony_ci}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci/**
194962306a36Sopenharmony_ci *	mptsas_eh_timed_out - resets the scsi_cmnd timeout
195062306a36Sopenharmony_ci *		if the device under question is currently in the
195162306a36Sopenharmony_ci *		device removal delay.
195262306a36Sopenharmony_ci *	@sc: scsi command that the midlayer is about to time out
195362306a36Sopenharmony_ci *
195462306a36Sopenharmony_ci **/
195562306a36Sopenharmony_cistatic enum scsi_timeout_action mptsas_eh_timed_out(struct scsi_cmnd *sc)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	MPT_SCSI_HOST *hd;
195862306a36Sopenharmony_ci	MPT_ADAPTER   *ioc;
195962306a36Sopenharmony_ci	VirtDevice    *vdevice;
196062306a36Sopenharmony_ci	enum scsi_timeout_action rc = SCSI_EH_NOT_HANDLED;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	hd = shost_priv(sc->device->host);
196362306a36Sopenharmony_ci	if (hd == NULL) {
196462306a36Sopenharmony_ci		printk(KERN_ERR MYNAM ": %s: Can't locate host! (sc=%p)\n",
196562306a36Sopenharmony_ci		    __func__, sc);
196662306a36Sopenharmony_ci		goto done;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	ioc = hd->ioc;
197062306a36Sopenharmony_ci	if (ioc->bus_type != SAS) {
197162306a36Sopenharmony_ci		printk(KERN_ERR MYNAM ": %s: Wrong bus type (sc=%p)\n",
197262306a36Sopenharmony_ci		    __func__, sc);
197362306a36Sopenharmony_ci		goto done;
197462306a36Sopenharmony_ci	}
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	/* In case if IOC is in reset from internal context.
197762306a36Sopenharmony_ci	*  Do not execute EEH for the same IOC. SML should to reset timer.
197862306a36Sopenharmony_ci	*/
197962306a36Sopenharmony_ci	if (ioc->ioc_reset_in_progress) {
198062306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT ": %s: ioc is in reset,"
198162306a36Sopenharmony_ci		    "SML need to reset the timer (sc=%p)\n",
198262306a36Sopenharmony_ci		    ioc->name, __func__, sc));
198362306a36Sopenharmony_ci		rc = SCSI_EH_RESET_TIMER;
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci	vdevice = sc->device->hostdata;
198662306a36Sopenharmony_ci	if (vdevice && vdevice->vtarget && (vdevice->vtarget->inDMD
198762306a36Sopenharmony_ci		|| vdevice->vtarget->deleted)) {
198862306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT ": %s: target removed "
198962306a36Sopenharmony_ci		    "or in device removal delay (sc=%p)\n",
199062306a36Sopenharmony_ci		    ioc->name, __func__, sc));
199162306a36Sopenharmony_ci		rc = SCSI_EH_RESET_TIMER;
199262306a36Sopenharmony_ci		goto done;
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cidone:
199662306a36Sopenharmony_ci	return rc;
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_cistatic const struct scsi_host_template mptsas_driver_template = {
200162306a36Sopenharmony_ci	.module				= THIS_MODULE,
200262306a36Sopenharmony_ci	.proc_name			= "mptsas",
200362306a36Sopenharmony_ci	.show_info			= mptscsih_show_info,
200462306a36Sopenharmony_ci	.name				= "MPT SAS Host",
200562306a36Sopenharmony_ci	.info				= mptscsih_info,
200662306a36Sopenharmony_ci	.queuecommand			= mptsas_qcmd,
200762306a36Sopenharmony_ci	.target_alloc			= mptsas_target_alloc,
200862306a36Sopenharmony_ci	.slave_alloc			= mptsas_slave_alloc,
200962306a36Sopenharmony_ci	.slave_configure		= mptsas_slave_configure,
201062306a36Sopenharmony_ci	.target_destroy			= mptsas_target_destroy,
201162306a36Sopenharmony_ci	.slave_destroy			= mptscsih_slave_destroy,
201262306a36Sopenharmony_ci	.change_queue_depth 		= mptscsih_change_queue_depth,
201362306a36Sopenharmony_ci	.eh_timed_out			= mptsas_eh_timed_out,
201462306a36Sopenharmony_ci	.eh_abort_handler		= mptscsih_abort,
201562306a36Sopenharmony_ci	.eh_device_reset_handler	= mptscsih_dev_reset,
201662306a36Sopenharmony_ci	.eh_host_reset_handler		= mptscsih_host_reset,
201762306a36Sopenharmony_ci	.bios_param			= mptscsih_bios_param,
201862306a36Sopenharmony_ci	.can_queue			= MPT_SAS_CAN_QUEUE,
201962306a36Sopenharmony_ci	.this_id			= -1,
202062306a36Sopenharmony_ci	.sg_tablesize			= MPT_SCSI_SG_DEPTH,
202162306a36Sopenharmony_ci	.max_sectors			= 8192,
202262306a36Sopenharmony_ci	.cmd_per_lun			= 7,
202362306a36Sopenharmony_ci	.shost_groups			= mptscsih_host_attr_groups,
202462306a36Sopenharmony_ci	.no_write_same			= 1,
202562306a36Sopenharmony_ci};
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_cistatic int mptsas_get_linkerrors(struct sas_phy *phy)
202862306a36Sopenharmony_ci{
202962306a36Sopenharmony_ci	MPT_ADAPTER *ioc = phy_to_ioc(phy);
203062306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
203162306a36Sopenharmony_ci	CONFIGPARMS cfg;
203262306a36Sopenharmony_ci	SasPhyPage1_t *buffer;
203362306a36Sopenharmony_ci	dma_addr_t dma_handle;
203462306a36Sopenharmony_ci	int error;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	/* FIXME: only have link errors on local phys */
203762306a36Sopenharmony_ci	if (!scsi_is_sas_phy_local(phy))
203862306a36Sopenharmony_ci		return -EINVAL;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASPHY1_PAGEVERSION;
204162306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
204262306a36Sopenharmony_ci	hdr.PageNumber = 1 /* page number 1*/;
204362306a36Sopenharmony_ci	hdr.Reserved1 = 0;
204462306a36Sopenharmony_ci	hdr.Reserved2 = 0;
204562306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
204662306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
204962306a36Sopenharmony_ci	cfg.physAddr = -1;
205062306a36Sopenharmony_ci	cfg.pageAddr = phy->identify.phy_identifier;
205162306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
205262306a36Sopenharmony_ci	cfg.dir = 0;    /* read */
205362306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
205662306a36Sopenharmony_ci	if (error)
205762306a36Sopenharmony_ci		return error;
205862306a36Sopenharmony_ci	if (!hdr.ExtPageLength)
205962306a36Sopenharmony_ci		return -ENXIO;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
206262306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
206362306a36Sopenharmony_ci	if (!buffer)
206462306a36Sopenharmony_ci		return -ENOMEM;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
206762306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
207062306a36Sopenharmony_ci	if (error)
207162306a36Sopenharmony_ci		goto out_free_consistent;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	mptsas_print_phy_pg1(ioc, buffer);
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	phy->invalid_dword_count = le32_to_cpu(buffer->InvalidDwordCount);
207662306a36Sopenharmony_ci	phy->running_disparity_error_count =
207762306a36Sopenharmony_ci		le32_to_cpu(buffer->RunningDisparityErrorCount);
207862306a36Sopenharmony_ci	phy->loss_of_dword_sync_count =
207962306a36Sopenharmony_ci		le32_to_cpu(buffer->LossDwordSynchCount);
208062306a36Sopenharmony_ci	phy->phy_reset_problem_count =
208162306a36Sopenharmony_ci		le32_to_cpu(buffer->PhyResetProblemCount);
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci out_free_consistent:
208462306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
208562306a36Sopenharmony_ci			  dma_handle);
208662306a36Sopenharmony_ci	return error;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
209062306a36Sopenharmony_ci		MPT_FRAME_HDR *reply)
209162306a36Sopenharmony_ci{
209262306a36Sopenharmony_ci	ioc->sas_mgmt.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
209362306a36Sopenharmony_ci	if (reply != NULL) {
209462306a36Sopenharmony_ci		ioc->sas_mgmt.status |= MPT_MGMT_STATUS_RF_VALID;
209562306a36Sopenharmony_ci		memcpy(ioc->sas_mgmt.reply, reply,
209662306a36Sopenharmony_ci		    min(ioc->reply_sz, 4 * reply->u.reply.MsgLength));
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
210062306a36Sopenharmony_ci		ioc->sas_mgmt.status &= ~MPT_MGMT_STATUS_PENDING;
210162306a36Sopenharmony_ci		complete(&ioc->sas_mgmt.done);
210262306a36Sopenharmony_ci		return 1;
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci	return 0;
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	MPT_ADAPTER *ioc = phy_to_ioc(phy);
211062306a36Sopenharmony_ci	SasIoUnitControlRequest_t *req;
211162306a36Sopenharmony_ci	SasIoUnitControlReply_t *reply;
211262306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
211362306a36Sopenharmony_ci	MPIHeader_t *hdr;
211462306a36Sopenharmony_ci	unsigned long timeleft;
211562306a36Sopenharmony_ci	int error = -ERESTARTSYS;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	/* FIXME: fusion doesn't allow non-local phy reset */
211862306a36Sopenharmony_ci	if (!scsi_is_sas_phy_local(phy))
211962306a36Sopenharmony_ci		return -EINVAL;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	/* not implemented for expanders */
212262306a36Sopenharmony_ci	if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP)
212362306a36Sopenharmony_ci		return -ENXIO;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	if (mutex_lock_interruptible(&ioc->sas_mgmt.mutex))
212662306a36Sopenharmony_ci		goto out;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
212962306a36Sopenharmony_ci	if (!mf) {
213062306a36Sopenharmony_ci		error = -ENOMEM;
213162306a36Sopenharmony_ci		goto out_unlock;
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	hdr = (MPIHeader_t *) mf;
213562306a36Sopenharmony_ci	req = (SasIoUnitControlRequest_t *)mf;
213662306a36Sopenharmony_ci	memset(req, 0, sizeof(SasIoUnitControlRequest_t));
213762306a36Sopenharmony_ci	req->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL;
213862306a36Sopenharmony_ci	req->MsgContext = hdr->MsgContext;
213962306a36Sopenharmony_ci	req->Operation = hard_reset ?
214062306a36Sopenharmony_ci		MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET;
214162306a36Sopenharmony_ci	req->PhyNum = phy->identify.phy_identifier;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
214462306a36Sopenharmony_ci	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done,
214762306a36Sopenharmony_ci			10 * HZ);
214862306a36Sopenharmony_ci	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
214962306a36Sopenharmony_ci		error = -ETIME;
215062306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
215162306a36Sopenharmony_ci		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
215262306a36Sopenharmony_ci			goto out_unlock;
215362306a36Sopenharmony_ci		if (!timeleft)
215462306a36Sopenharmony_ci			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
215562306a36Sopenharmony_ci		goto out_unlock;
215662306a36Sopenharmony_ci	}
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	/* a reply frame is expected */
215962306a36Sopenharmony_ci	if ((ioc->sas_mgmt.status &
216062306a36Sopenharmony_ci	    MPT_MGMT_STATUS_RF_VALID) == 0) {
216162306a36Sopenharmony_ci		error = -ENXIO;
216262306a36Sopenharmony_ci		goto out_unlock;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	/* process the completed Reply Message Frame */
216662306a36Sopenharmony_ci	reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply;
216762306a36Sopenharmony_ci	if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) {
216862306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
216962306a36Sopenharmony_ci		    ioc->name, __func__, reply->IOCStatus, reply->IOCLogInfo);
217062306a36Sopenharmony_ci		error = -ENXIO;
217162306a36Sopenharmony_ci		goto out_unlock;
217262306a36Sopenharmony_ci	}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	error = 0;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci out_unlock:
217762306a36Sopenharmony_ci	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
217862306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_mgmt.mutex);
217962306a36Sopenharmony_ci out:
218062306a36Sopenharmony_ci	return error;
218162306a36Sopenharmony_ci}
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_cistatic int
218462306a36Sopenharmony_cimptsas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
218562306a36Sopenharmony_ci{
218662306a36Sopenharmony_ci	MPT_ADAPTER *ioc = rphy_to_ioc(rphy);
218762306a36Sopenharmony_ci	int i, error;
218862306a36Sopenharmony_ci	struct mptsas_portinfo *p;
218962306a36Sopenharmony_ci	struct mptsas_enclosure enclosure_info;
219062306a36Sopenharmony_ci	u64 enclosure_handle;
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
219362306a36Sopenharmony_ci	list_for_each_entry(p, &ioc->sas_topology, list) {
219462306a36Sopenharmony_ci		for (i = 0; i < p->num_phys; i++) {
219562306a36Sopenharmony_ci			if (p->phy_info[i].attached.sas_address ==
219662306a36Sopenharmony_ci			    rphy->identify.sas_address) {
219762306a36Sopenharmony_ci				enclosure_handle = p->phy_info[i].
219862306a36Sopenharmony_ci					attached.handle_enclosure;
219962306a36Sopenharmony_ci				goto found_info;
220062306a36Sopenharmony_ci			}
220162306a36Sopenharmony_ci		}
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
220462306a36Sopenharmony_ci	return -ENXIO;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci found_info:
220762306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
220862306a36Sopenharmony_ci	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
220962306a36Sopenharmony_ci	error = mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
221062306a36Sopenharmony_ci			(MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
221162306a36Sopenharmony_ci			 MPI_SAS_ENCLOS_PGAD_FORM_SHIFT), enclosure_handle);
221262306a36Sopenharmony_ci	if (!error)
221362306a36Sopenharmony_ci		*identifier = enclosure_info.enclosure_logical_id;
221462306a36Sopenharmony_ci	return error;
221562306a36Sopenharmony_ci}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_cistatic int
221862306a36Sopenharmony_cimptsas_get_bay_identifier(struct sas_rphy *rphy)
221962306a36Sopenharmony_ci{
222062306a36Sopenharmony_ci	MPT_ADAPTER *ioc = rphy_to_ioc(rphy);
222162306a36Sopenharmony_ci	struct mptsas_portinfo *p;
222262306a36Sopenharmony_ci	int i, rc;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
222562306a36Sopenharmony_ci	list_for_each_entry(p, &ioc->sas_topology, list) {
222662306a36Sopenharmony_ci		for (i = 0; i < p->num_phys; i++) {
222762306a36Sopenharmony_ci			if (p->phy_info[i].attached.sas_address ==
222862306a36Sopenharmony_ci			    rphy->identify.sas_address) {
222962306a36Sopenharmony_ci				rc = p->phy_info[i].attached.slot;
223062306a36Sopenharmony_ci				goto out;
223162306a36Sopenharmony_ci			}
223262306a36Sopenharmony_ci		}
223362306a36Sopenharmony_ci	}
223462306a36Sopenharmony_ci	rc = -ENXIO;
223562306a36Sopenharmony_ci out:
223662306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
223762306a36Sopenharmony_ci	return rc;
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic void mptsas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
224162306a36Sopenharmony_ci		struct sas_rphy *rphy)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	MPT_ADAPTER *ioc = ((MPT_SCSI_HOST *) shost->hostdata)->ioc;
224462306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
224562306a36Sopenharmony_ci	SmpPassthroughRequest_t *smpreq;
224662306a36Sopenharmony_ci	int flagsLength;
224762306a36Sopenharmony_ci	unsigned long timeleft;
224862306a36Sopenharmony_ci	char *psge;
224962306a36Sopenharmony_ci	u64 sas_address = 0;
225062306a36Sopenharmony_ci	unsigned int reslen = 0;
225162306a36Sopenharmony_ci	int ret = -EINVAL;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	/* do we need to support multiple segments? */
225462306a36Sopenharmony_ci	if (job->request_payload.sg_cnt > 1 ||
225562306a36Sopenharmony_ci	    job->reply_payload.sg_cnt > 1) {
225662306a36Sopenharmony_ci		printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u, rsp %u\n",
225762306a36Sopenharmony_ci		    ioc->name, __func__, job->request_payload.payload_len,
225862306a36Sopenharmony_ci		    job->reply_payload.payload_len);
225962306a36Sopenharmony_ci		goto out;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
226362306a36Sopenharmony_ci	if (ret)
226462306a36Sopenharmony_ci		goto out;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
226762306a36Sopenharmony_ci	if (!mf) {
226862306a36Sopenharmony_ci		ret = -ENOMEM;
226962306a36Sopenharmony_ci		goto out_unlock;
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	smpreq = (SmpPassthroughRequest_t *)mf;
227362306a36Sopenharmony_ci	memset(smpreq, 0, sizeof(*smpreq));
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	smpreq->RequestDataLength =
227662306a36Sopenharmony_ci		cpu_to_le16(job->request_payload.payload_len - 4);
227762306a36Sopenharmony_ci	smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	if (rphy)
228062306a36Sopenharmony_ci		sas_address = rphy->identify.sas_address;
228162306a36Sopenharmony_ci	else {
228262306a36Sopenharmony_ci		struct mptsas_portinfo *port_info;
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci		mutex_lock(&ioc->sas_topology_mutex);
228562306a36Sopenharmony_ci		port_info = ioc->hba_port_info;
228662306a36Sopenharmony_ci		if (port_info && port_info->phy_info)
228762306a36Sopenharmony_ci			sas_address =
228862306a36Sopenharmony_ci				port_info->phy_info[0].phy->identify.sas_address;
228962306a36Sopenharmony_ci		mutex_unlock(&ioc->sas_topology_mutex);
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	*((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	psge = (char *)
229562306a36Sopenharmony_ci		(((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4));
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	/* request */
229862306a36Sopenharmony_ci	flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
229962306a36Sopenharmony_ci		       MPI_SGE_FLAGS_END_OF_BUFFER |
230062306a36Sopenharmony_ci		       MPI_SGE_FLAGS_DIRECTION)
230162306a36Sopenharmony_ci		       << MPI_SGE_FLAGS_SHIFT;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	if (!dma_map_sg(&ioc->pcidev->dev, job->request_payload.sg_list,
230462306a36Sopenharmony_ci			1, DMA_BIDIRECTIONAL))
230562306a36Sopenharmony_ci		goto put_mf;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	flagsLength |= (sg_dma_len(job->request_payload.sg_list) - 4);
230862306a36Sopenharmony_ci	ioc->add_sge(psge, flagsLength,
230962306a36Sopenharmony_ci			sg_dma_address(job->request_payload.sg_list));
231062306a36Sopenharmony_ci	psge += ioc->SGE_size;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	/* response */
231362306a36Sopenharmony_ci	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
231462306a36Sopenharmony_ci		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
231562306a36Sopenharmony_ci		MPI_SGE_FLAGS_IOC_TO_HOST |
231662306a36Sopenharmony_ci		MPI_SGE_FLAGS_END_OF_BUFFER;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	if (!dma_map_sg(&ioc->pcidev->dev, job->reply_payload.sg_list,
232162306a36Sopenharmony_ci			1, DMA_BIDIRECTIONAL))
232262306a36Sopenharmony_ci		goto unmap_out;
232362306a36Sopenharmony_ci	flagsLength |= sg_dma_len(job->reply_payload.sg_list) + 4;
232462306a36Sopenharmony_ci	ioc->add_sge(psge, flagsLength,
232562306a36Sopenharmony_ci			sg_dma_address(job->reply_payload.sg_list));
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
232862306a36Sopenharmony_ci	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
233162306a36Sopenharmony_ci	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
233262306a36Sopenharmony_ci		ret = -ETIME;
233362306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
233462306a36Sopenharmony_ci		mf = NULL;
233562306a36Sopenharmony_ci		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
233662306a36Sopenharmony_ci			goto unmap_in;
233762306a36Sopenharmony_ci		if (!timeleft)
233862306a36Sopenharmony_ci			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
233962306a36Sopenharmony_ci		goto unmap_in;
234062306a36Sopenharmony_ci	}
234162306a36Sopenharmony_ci	mf = NULL;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
234462306a36Sopenharmony_ci		SmpPassthroughReply_t *smprep;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci		smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
234762306a36Sopenharmony_ci		memcpy(job->reply, smprep, sizeof(*smprep));
234862306a36Sopenharmony_ci		job->reply_len = sizeof(*smprep);
234962306a36Sopenharmony_ci		reslen = smprep->ResponseDataLength;
235062306a36Sopenharmony_ci	} else {
235162306a36Sopenharmony_ci		printk(MYIOC_s_ERR_FMT
235262306a36Sopenharmony_ci		    "%s: smp passthru reply failed to be returned\n",
235362306a36Sopenharmony_ci		    ioc->name, __func__);
235462306a36Sopenharmony_ci		ret = -ENXIO;
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ciunmap_in:
235862306a36Sopenharmony_ci	dma_unmap_sg(&ioc->pcidev->dev, job->reply_payload.sg_list, 1,
235962306a36Sopenharmony_ci			DMA_BIDIRECTIONAL);
236062306a36Sopenharmony_ciunmap_out:
236162306a36Sopenharmony_ci	dma_unmap_sg(&ioc->pcidev->dev, job->request_payload.sg_list, 1,
236262306a36Sopenharmony_ci			DMA_BIDIRECTIONAL);
236362306a36Sopenharmony_ciput_mf:
236462306a36Sopenharmony_ci	if (mf)
236562306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
236662306a36Sopenharmony_ciout_unlock:
236762306a36Sopenharmony_ci	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
236862306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_mgmt.mutex);
236962306a36Sopenharmony_ciout:
237062306a36Sopenharmony_ci	bsg_job_done(job, ret, reslen);
237162306a36Sopenharmony_ci}
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_cistatic struct sas_function_template mptsas_transport_functions = {
237462306a36Sopenharmony_ci	.get_linkerrors		= mptsas_get_linkerrors,
237562306a36Sopenharmony_ci	.get_enclosure_identifier = mptsas_get_enclosure_identifier,
237662306a36Sopenharmony_ci	.get_bay_identifier	= mptsas_get_bay_identifier,
237762306a36Sopenharmony_ci	.phy_reset		= mptsas_phy_reset,
237862306a36Sopenharmony_ci	.smp_handler		= mptsas_smp_handler,
237962306a36Sopenharmony_ci};
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_cistatic struct scsi_transport_template *mptsas_transport_template;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_cistatic int
238462306a36Sopenharmony_cimptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
238762306a36Sopenharmony_ci	CONFIGPARMS cfg;
238862306a36Sopenharmony_ci	SasIOUnitPage0_t *buffer;
238962306a36Sopenharmony_ci	dma_addr_t dma_handle;
239062306a36Sopenharmony_ci	int error, i;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION;
239362306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
239462306a36Sopenharmony_ci	hdr.PageNumber = 0;
239562306a36Sopenharmony_ci	hdr.Reserved1 = 0;
239662306a36Sopenharmony_ci	hdr.Reserved2 = 0;
239762306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
239862306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
240162306a36Sopenharmony_ci	cfg.physAddr = -1;
240262306a36Sopenharmony_ci	cfg.pageAddr = 0;
240362306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
240462306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
240562306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
240862306a36Sopenharmony_ci	if (error)
240962306a36Sopenharmony_ci		goto out;
241062306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
241162306a36Sopenharmony_ci		error = -ENXIO;
241262306a36Sopenharmony_ci		goto out;
241362306a36Sopenharmony_ci	}
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
241662306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
241762306a36Sopenharmony_ci	if (!buffer) {
241862306a36Sopenharmony_ci		error = -ENOMEM;
241962306a36Sopenharmony_ci		goto out;
242062306a36Sopenharmony_ci	}
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
242362306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
242662306a36Sopenharmony_ci	if (error)
242762306a36Sopenharmony_ci		goto out_free_consistent;
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	port_info->num_phys = buffer->NumPhys;
243062306a36Sopenharmony_ci	port_info->phy_info = kcalloc(port_info->num_phys,
243162306a36Sopenharmony_ci		sizeof(struct mptsas_phyinfo), GFP_KERNEL);
243262306a36Sopenharmony_ci	if (!port_info->phy_info) {
243362306a36Sopenharmony_ci		error = -ENOMEM;
243462306a36Sopenharmony_ci		goto out_free_consistent;
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	ioc->nvdata_version_persistent =
243862306a36Sopenharmony_ci	    le16_to_cpu(buffer->NvdataVersionPersistent);
243962306a36Sopenharmony_ci	ioc->nvdata_version_default =
244062306a36Sopenharmony_ci	    le16_to_cpu(buffer->NvdataVersionDefault);
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
244362306a36Sopenharmony_ci		mptsas_print_phy_data(ioc, &buffer->PhyData[i]);
244462306a36Sopenharmony_ci		port_info->phy_info[i].phy_id = i;
244562306a36Sopenharmony_ci		port_info->phy_info[i].port_id =
244662306a36Sopenharmony_ci		    buffer->PhyData[i].Port;
244762306a36Sopenharmony_ci		port_info->phy_info[i].negotiated_link_rate =
244862306a36Sopenharmony_ci		    buffer->PhyData[i].NegotiatedLinkRate;
244962306a36Sopenharmony_ci		port_info->phy_info[i].portinfo = port_info;
245062306a36Sopenharmony_ci		port_info->phy_info[i].handle =
245162306a36Sopenharmony_ci		    le16_to_cpu(buffer->PhyData[i].ControllerDevHandle);
245262306a36Sopenharmony_ci	}
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci out_free_consistent:
245562306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
245662306a36Sopenharmony_ci			  dma_handle);
245762306a36Sopenharmony_ci out:
245862306a36Sopenharmony_ci	return error;
245962306a36Sopenharmony_ci}
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_cistatic int
246262306a36Sopenharmony_cimptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc)
246362306a36Sopenharmony_ci{
246462306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
246562306a36Sopenharmony_ci	CONFIGPARMS cfg;
246662306a36Sopenharmony_ci	SasIOUnitPage1_t *buffer;
246762306a36Sopenharmony_ci	dma_addr_t dma_handle;
246862306a36Sopenharmony_ci	int error;
246962306a36Sopenharmony_ci	u8 device_missing_delay;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t));
247262306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(CONFIGPARMS));
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
247562306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
247662306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
247762306a36Sopenharmony_ci	cfg.cfghdr.ehdr->PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
247862306a36Sopenharmony_ci	cfg.cfghdr.ehdr->ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
247962306a36Sopenharmony_ci	cfg.cfghdr.ehdr->PageVersion = MPI_SASIOUNITPAGE1_PAGEVERSION;
248062306a36Sopenharmony_ci	cfg.cfghdr.ehdr->PageNumber = 1;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
248362306a36Sopenharmony_ci	if (error)
248462306a36Sopenharmony_ci		goto out;
248562306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
248662306a36Sopenharmony_ci		error = -ENXIO;
248762306a36Sopenharmony_ci		goto out;
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
249162306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
249262306a36Sopenharmony_ci	if (!buffer) {
249362306a36Sopenharmony_ci		error = -ENOMEM;
249462306a36Sopenharmony_ci		goto out;
249562306a36Sopenharmony_ci	}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
249862306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
250162306a36Sopenharmony_ci	if (error)
250262306a36Sopenharmony_ci		goto out_free_consistent;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	ioc->io_missing_delay  =
250562306a36Sopenharmony_ci	    le16_to_cpu(buffer->IODeviceMissingDelay);
250662306a36Sopenharmony_ci	device_missing_delay = buffer->ReportDeviceMissingDelay;
250762306a36Sopenharmony_ci	ioc->device_missing_delay = (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_UNIT_16) ?
250862306a36Sopenharmony_ci	    (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16 :
250962306a36Sopenharmony_ci	    device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci out_free_consistent:
251262306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
251362306a36Sopenharmony_ci			  dma_handle);
251462306a36Sopenharmony_ci out:
251562306a36Sopenharmony_ci	return error;
251662306a36Sopenharmony_ci}
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_cistatic int
251962306a36Sopenharmony_cimptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
252062306a36Sopenharmony_ci		u32 form, u32 form_specific)
252162306a36Sopenharmony_ci{
252262306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
252362306a36Sopenharmony_ci	CONFIGPARMS cfg;
252462306a36Sopenharmony_ci	SasPhyPage0_t *buffer;
252562306a36Sopenharmony_ci	dma_addr_t dma_handle;
252662306a36Sopenharmony_ci	int error;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASPHY0_PAGEVERSION;
252962306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
253062306a36Sopenharmony_ci	hdr.PageNumber = 0;
253162306a36Sopenharmony_ci	hdr.Reserved1 = 0;
253262306a36Sopenharmony_ci	hdr.Reserved2 = 0;
253362306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
253462306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
253762306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
253862306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	/* Get Phy Pg 0 for each Phy. */
254162306a36Sopenharmony_ci	cfg.physAddr = -1;
254262306a36Sopenharmony_ci	cfg.pageAddr = form + form_specific;
254362306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
254662306a36Sopenharmony_ci	if (error)
254762306a36Sopenharmony_ci		goto out;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
255062306a36Sopenharmony_ci		error = -ENXIO;
255162306a36Sopenharmony_ci		goto out;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
255562306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
255662306a36Sopenharmony_ci	if (!buffer) {
255762306a36Sopenharmony_ci		error = -ENOMEM;
255862306a36Sopenharmony_ci		goto out;
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
256262306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
256562306a36Sopenharmony_ci	if (error)
256662306a36Sopenharmony_ci		goto out_free_consistent;
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	mptsas_print_phy_pg0(ioc, buffer);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	phy_info->hw_link_rate = buffer->HwLinkRate;
257162306a36Sopenharmony_ci	phy_info->programmed_link_rate = buffer->ProgrammedLinkRate;
257262306a36Sopenharmony_ci	phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle);
257362306a36Sopenharmony_ci	phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci out_free_consistent:
257662306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
257762306a36Sopenharmony_ci			  dma_handle);
257862306a36Sopenharmony_ci out:
257962306a36Sopenharmony_ci	return error;
258062306a36Sopenharmony_ci}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_cistatic int
258362306a36Sopenharmony_cimptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
258462306a36Sopenharmony_ci		u32 form, u32 form_specific)
258562306a36Sopenharmony_ci{
258662306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
258762306a36Sopenharmony_ci	CONFIGPARMS cfg;
258862306a36Sopenharmony_ci	SasDevicePage0_t *buffer;
258962306a36Sopenharmony_ci	dma_addr_t dma_handle;
259062306a36Sopenharmony_ci	__le64 sas_address;
259162306a36Sopenharmony_ci	int error=0;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION;
259462306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
259562306a36Sopenharmony_ci	hdr.PageNumber = 0;
259662306a36Sopenharmony_ci	hdr.Reserved1 = 0;
259762306a36Sopenharmony_ci	hdr.Reserved2 = 0;
259862306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
259962306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
260262306a36Sopenharmony_ci	cfg.pageAddr = form + form_specific;
260362306a36Sopenharmony_ci	cfg.physAddr = -1;
260462306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
260562306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
260662306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	memset(device_info, 0, sizeof(struct mptsas_devinfo));
260962306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
261062306a36Sopenharmony_ci	if (error)
261162306a36Sopenharmony_ci		goto out;
261262306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
261362306a36Sopenharmony_ci		error = -ENXIO;
261462306a36Sopenharmony_ci		goto out;
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
261862306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
261962306a36Sopenharmony_ci	if (!buffer) {
262062306a36Sopenharmony_ci		error = -ENOMEM;
262162306a36Sopenharmony_ci		goto out;
262262306a36Sopenharmony_ci	}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
262562306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
263062306a36Sopenharmony_ci		error = -ENODEV;
263162306a36Sopenharmony_ci		goto out_free_consistent;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	if (error)
263562306a36Sopenharmony_ci		goto out_free_consistent;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	mptsas_print_device_pg0(ioc, buffer);
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	memset(device_info, 0, sizeof(struct mptsas_devinfo));
264062306a36Sopenharmony_ci	device_info->handle = le16_to_cpu(buffer->DevHandle);
264162306a36Sopenharmony_ci	device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle);
264262306a36Sopenharmony_ci	device_info->handle_enclosure =
264362306a36Sopenharmony_ci	    le16_to_cpu(buffer->EnclosureHandle);
264462306a36Sopenharmony_ci	device_info->slot = le16_to_cpu(buffer->Slot);
264562306a36Sopenharmony_ci	device_info->phy_id = buffer->PhyNum;
264662306a36Sopenharmony_ci	device_info->port_id = buffer->PhysicalPort;
264762306a36Sopenharmony_ci	device_info->id = buffer->TargetID;
264862306a36Sopenharmony_ci	device_info->phys_disk_num = ~0;
264962306a36Sopenharmony_ci	device_info->channel = buffer->Bus;
265062306a36Sopenharmony_ci	memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
265162306a36Sopenharmony_ci	device_info->sas_address = le64_to_cpu(sas_address);
265262306a36Sopenharmony_ci	device_info->device_info =
265362306a36Sopenharmony_ci	    le32_to_cpu(buffer->DeviceInfo);
265462306a36Sopenharmony_ci	device_info->flags = le16_to_cpu(buffer->Flags);
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci out_free_consistent:
265762306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
265862306a36Sopenharmony_ci			  dma_handle);
265962306a36Sopenharmony_ci out:
266062306a36Sopenharmony_ci	return error;
266162306a36Sopenharmony_ci}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_cistatic int
266462306a36Sopenharmony_cimptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
266562306a36Sopenharmony_ci		u32 form, u32 form_specific)
266662306a36Sopenharmony_ci{
266762306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
266862306a36Sopenharmony_ci	CONFIGPARMS cfg;
266962306a36Sopenharmony_ci	SasExpanderPage0_t *buffer;
267062306a36Sopenharmony_ci	dma_addr_t dma_handle;
267162306a36Sopenharmony_ci	int i, error;
267262306a36Sopenharmony_ci	__le64 sas_address;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	memset(port_info, 0, sizeof(struct mptsas_portinfo));
267562306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
267662306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
267762306a36Sopenharmony_ci	hdr.PageNumber = 0;
267862306a36Sopenharmony_ci	hdr.Reserved1 = 0;
267962306a36Sopenharmony_ci	hdr.Reserved2 = 0;
268062306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
268162306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
268462306a36Sopenharmony_ci	cfg.physAddr = -1;
268562306a36Sopenharmony_ci	cfg.pageAddr = form + form_specific;
268662306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
268762306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
268862306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	memset(port_info, 0, sizeof(struct mptsas_portinfo));
269162306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
269262306a36Sopenharmony_ci	if (error)
269362306a36Sopenharmony_ci		goto out;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
269662306a36Sopenharmony_ci		error = -ENXIO;
269762306a36Sopenharmony_ci		goto out;
269862306a36Sopenharmony_ci	}
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
270162306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
270262306a36Sopenharmony_ci	if (!buffer) {
270362306a36Sopenharmony_ci		error = -ENOMEM;
270462306a36Sopenharmony_ci		goto out;
270562306a36Sopenharmony_ci	}
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
270862306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
271162306a36Sopenharmony_ci	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
271262306a36Sopenharmony_ci		error = -ENODEV;
271362306a36Sopenharmony_ci		goto out_free_consistent;
271462306a36Sopenharmony_ci	}
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	if (error)
271762306a36Sopenharmony_ci		goto out_free_consistent;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	/* save config data */
272062306a36Sopenharmony_ci	port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1;
272162306a36Sopenharmony_ci	port_info->phy_info = kcalloc(port_info->num_phys,
272262306a36Sopenharmony_ci		sizeof(struct mptsas_phyinfo), GFP_KERNEL);
272362306a36Sopenharmony_ci	if (!port_info->phy_info) {
272462306a36Sopenharmony_ci		error = -ENOMEM;
272562306a36Sopenharmony_ci		goto out_free_consistent;
272662306a36Sopenharmony_ci	}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
272962306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
273062306a36Sopenharmony_ci		port_info->phy_info[i].portinfo = port_info;
273162306a36Sopenharmony_ci		port_info->phy_info[i].handle =
273262306a36Sopenharmony_ci		    le16_to_cpu(buffer->DevHandle);
273362306a36Sopenharmony_ci		port_info->phy_info[i].identify.sas_address =
273462306a36Sopenharmony_ci		    le64_to_cpu(sas_address);
273562306a36Sopenharmony_ci		port_info->phy_info[i].identify.handle_parent =
273662306a36Sopenharmony_ci		    le16_to_cpu(buffer->ParentDevHandle);
273762306a36Sopenharmony_ci	}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci out_free_consistent:
274062306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
274162306a36Sopenharmony_ci			  dma_handle);
274262306a36Sopenharmony_ci out:
274362306a36Sopenharmony_ci	return error;
274462306a36Sopenharmony_ci}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_cistatic int
274762306a36Sopenharmony_cimptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
274862306a36Sopenharmony_ci		u32 form, u32 form_specific)
274962306a36Sopenharmony_ci{
275062306a36Sopenharmony_ci	ConfigExtendedPageHeader_t hdr;
275162306a36Sopenharmony_ci	CONFIGPARMS cfg;
275262306a36Sopenharmony_ci	SasExpanderPage1_t *buffer;
275362306a36Sopenharmony_ci	dma_addr_t dma_handle;
275462306a36Sopenharmony_ci	int error=0;
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	hdr.PageVersion = MPI_SASEXPANDER1_PAGEVERSION;
275762306a36Sopenharmony_ci	hdr.ExtPageLength = 0;
275862306a36Sopenharmony_ci	hdr.PageNumber = 1;
275962306a36Sopenharmony_ci	hdr.Reserved1 = 0;
276062306a36Sopenharmony_ci	hdr.Reserved2 = 0;
276162306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
276262306a36Sopenharmony_ci	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	cfg.cfghdr.ehdr = &hdr;
276562306a36Sopenharmony_ci	cfg.physAddr = -1;
276662306a36Sopenharmony_ci	cfg.pageAddr = form + form_specific;
276762306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
276862306a36Sopenharmony_ci	cfg.dir = 0;	/* read */
276962306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
277262306a36Sopenharmony_ci	if (error)
277362306a36Sopenharmony_ci		goto out;
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	if (!hdr.ExtPageLength) {
277662306a36Sopenharmony_ci		error = -ENXIO;
277762306a36Sopenharmony_ci		goto out;
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
278162306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
278262306a36Sopenharmony_ci	if (!buffer) {
278362306a36Sopenharmony_ci		error = -ENOMEM;
278462306a36Sopenharmony_ci		goto out;
278562306a36Sopenharmony_ci	}
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
278862306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	error = mpt_config(ioc, &cfg);
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
279362306a36Sopenharmony_ci		error = -ENODEV;
279462306a36Sopenharmony_ci		goto out_free_consistent;
279562306a36Sopenharmony_ci	}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	if (error)
279862306a36Sopenharmony_ci		goto out_free_consistent;
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	mptsas_print_expander_pg1(ioc, buffer);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	/* save config data */
280462306a36Sopenharmony_ci	phy_info->phy_id = buffer->PhyIdentifier;
280562306a36Sopenharmony_ci	phy_info->port_id = buffer->PhysicalPort;
280662306a36Sopenharmony_ci	phy_info->negotiated_link_rate = buffer->NegotiatedLinkRate;
280762306a36Sopenharmony_ci	phy_info->programmed_link_rate = buffer->ProgrammedLinkRate;
280862306a36Sopenharmony_ci	phy_info->hw_link_rate = buffer->HwLinkRate;
280962306a36Sopenharmony_ci	phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle);
281062306a36Sopenharmony_ci	phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle);
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci out_free_consistent:
281362306a36Sopenharmony_ci	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
281462306a36Sopenharmony_ci			  dma_handle);
281562306a36Sopenharmony_ci out:
281662306a36Sopenharmony_ci	return error;
281762306a36Sopenharmony_ci}
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_cistruct rep_manu_request{
282062306a36Sopenharmony_ci	u8 smp_frame_type;
282162306a36Sopenharmony_ci	u8 function;
282262306a36Sopenharmony_ci	u8 reserved;
282362306a36Sopenharmony_ci	u8 request_length;
282462306a36Sopenharmony_ci};
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_cistruct rep_manu_reply{
282762306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
282862306a36Sopenharmony_ci	u8 function; /* 0x01 */
282962306a36Sopenharmony_ci	u8 function_result;
283062306a36Sopenharmony_ci	u8 response_length;
283162306a36Sopenharmony_ci	u16 expander_change_count;
283262306a36Sopenharmony_ci	u8 reserved0[2];
283362306a36Sopenharmony_ci	u8 sas_format:1;
283462306a36Sopenharmony_ci	u8 reserved1:7;
283562306a36Sopenharmony_ci	u8 reserved2[3];
283662306a36Sopenharmony_ci	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
283762306a36Sopenharmony_ci	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
283862306a36Sopenharmony_ci	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
283962306a36Sopenharmony_ci	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
284062306a36Sopenharmony_ci	u16 component_id;
284162306a36Sopenharmony_ci	u8 component_revision_id;
284262306a36Sopenharmony_ci	u8 reserved3;
284362306a36Sopenharmony_ci	u8 vendor_specific[8];
284462306a36Sopenharmony_ci};
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci/**
284762306a36Sopenharmony_ci  * mptsas_exp_repmanufacture_info - sets expander manufacturer info
284862306a36Sopenharmony_ci  * @ioc: per adapter object
284962306a36Sopenharmony_ci  * @sas_address: expander sas address
285062306a36Sopenharmony_ci  * @edev: the sas_expander_device object
285162306a36Sopenharmony_ci  *
285262306a36Sopenharmony_ci  * For an edge expander or a fanout expander:
285362306a36Sopenharmony_ci  * fills in the sas_expander_device object when SMP port is created.
285462306a36Sopenharmony_ci  *
285562306a36Sopenharmony_ci  * Return: 0 for success, non-zero for failure.
285662306a36Sopenharmony_ci  */
285762306a36Sopenharmony_cistatic int
285862306a36Sopenharmony_cimptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc,
285962306a36Sopenharmony_ci	u64 sas_address, struct sas_expander_device *edev)
286062306a36Sopenharmony_ci{
286162306a36Sopenharmony_ci	MPT_FRAME_HDR *mf;
286262306a36Sopenharmony_ci	SmpPassthroughRequest_t *smpreq;
286362306a36Sopenharmony_ci	SmpPassthroughReply_t *smprep;
286462306a36Sopenharmony_ci	struct rep_manu_reply *manufacture_reply;
286562306a36Sopenharmony_ci	struct rep_manu_request *manufacture_request;
286662306a36Sopenharmony_ci	int ret;
286762306a36Sopenharmony_ci	int flagsLength;
286862306a36Sopenharmony_ci	unsigned long timeleft;
286962306a36Sopenharmony_ci	char *psge;
287062306a36Sopenharmony_ci	unsigned long flags;
287162306a36Sopenharmony_ci	void *data_out = NULL;
287262306a36Sopenharmony_ci	dma_addr_t data_out_dma = 0;
287362306a36Sopenharmony_ci	u32 sz;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
287662306a36Sopenharmony_ci	if (ioc->ioc_reset_in_progress) {
287762306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
287862306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "%s: host reset in progress!\n",
287962306a36Sopenharmony_ci			__func__, ioc->name);
288062306a36Sopenharmony_ci		return -EFAULT;
288162306a36Sopenharmony_ci	}
288262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
288562306a36Sopenharmony_ci	if (ret)
288662306a36Sopenharmony_ci		goto out;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
288962306a36Sopenharmony_ci	if (!mf) {
289062306a36Sopenharmony_ci		ret = -ENOMEM;
289162306a36Sopenharmony_ci		goto out_unlock;
289262306a36Sopenharmony_ci	}
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	smpreq = (SmpPassthroughRequest_t *)mf;
289562306a36Sopenharmony_ci	memset(smpreq, 0, sizeof(*smpreq));
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply);
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pcidev->dev, sz, &data_out_dma,
290062306a36Sopenharmony_ci				      GFP_KERNEL);
290162306a36Sopenharmony_ci	if (!data_out) {
290262306a36Sopenharmony_ci		printk(KERN_ERR "Memory allocation failure at %s:%d/%s()!\n",
290362306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
290462306a36Sopenharmony_ci		ret = -ENOMEM;
290562306a36Sopenharmony_ci		goto put_mf;
290662306a36Sopenharmony_ci	}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	manufacture_request = data_out;
290962306a36Sopenharmony_ci	manufacture_request->smp_frame_type = 0x40;
291062306a36Sopenharmony_ci	manufacture_request->function = 1;
291162306a36Sopenharmony_ci	manufacture_request->reserved = 0;
291262306a36Sopenharmony_ci	manufacture_request->request_length = 0;
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
291562306a36Sopenharmony_ci	smpreq->PhysicalPort = 0xFF;
291662306a36Sopenharmony_ci	*((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address);
291762306a36Sopenharmony_ci	smpreq->RequestDataLength = sizeof(struct rep_manu_request);
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	psge = (char *)
292062306a36Sopenharmony_ci		(((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4));
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
292362306a36Sopenharmony_ci		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
292462306a36Sopenharmony_ci		MPI_SGE_FLAGS_HOST_TO_IOC |
292562306a36Sopenharmony_ci		MPI_SGE_FLAGS_END_OF_BUFFER;
292662306a36Sopenharmony_ci	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
292762306a36Sopenharmony_ci	flagsLength |= sizeof(struct rep_manu_request);
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	ioc->add_sge(psge, flagsLength, data_out_dma);
293062306a36Sopenharmony_ci	psge += ioc->SGE_size;
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
293362306a36Sopenharmony_ci		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
293462306a36Sopenharmony_ci		MPI_SGE_FLAGS_IOC_TO_HOST |
293562306a36Sopenharmony_ci		MPI_SGE_FLAGS_END_OF_BUFFER;
293662306a36Sopenharmony_ci	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
293762306a36Sopenharmony_ci	flagsLength |= sizeof(struct rep_manu_reply);
293862306a36Sopenharmony_ci	ioc->add_sge(psge, flagsLength, data_out_dma +
293962306a36Sopenharmony_ci	sizeof(struct rep_manu_request));
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
294262306a36Sopenharmony_ci	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
294562306a36Sopenharmony_ci	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
294662306a36Sopenharmony_ci		ret = -ETIME;
294762306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
294862306a36Sopenharmony_ci		mf = NULL;
294962306a36Sopenharmony_ci		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
295062306a36Sopenharmony_ci			goto out_free;
295162306a36Sopenharmony_ci		if (!timeleft)
295262306a36Sopenharmony_ci			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
295362306a36Sopenharmony_ci		goto out_free;
295462306a36Sopenharmony_ci	}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	mf = NULL;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
295962306a36Sopenharmony_ci		u8 *tmp;
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci		smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
296262306a36Sopenharmony_ci		if (le16_to_cpu(smprep->ResponseDataLength) !=
296362306a36Sopenharmony_ci		    sizeof(struct rep_manu_reply))
296462306a36Sopenharmony_ci			goto out_free;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci		manufacture_reply = data_out + sizeof(struct rep_manu_request);
296762306a36Sopenharmony_ci		strncpy(edev->vendor_id, manufacture_reply->vendor_id,
296862306a36Sopenharmony_ci			SAS_EXPANDER_VENDOR_ID_LEN);
296962306a36Sopenharmony_ci		strncpy(edev->product_id, manufacture_reply->product_id,
297062306a36Sopenharmony_ci			SAS_EXPANDER_PRODUCT_ID_LEN);
297162306a36Sopenharmony_ci		strncpy(edev->product_rev, manufacture_reply->product_rev,
297262306a36Sopenharmony_ci			SAS_EXPANDER_PRODUCT_REV_LEN);
297362306a36Sopenharmony_ci		edev->level = manufacture_reply->sas_format;
297462306a36Sopenharmony_ci		if (manufacture_reply->sas_format) {
297562306a36Sopenharmony_ci			strncpy(edev->component_vendor_id,
297662306a36Sopenharmony_ci				manufacture_reply->component_vendor_id,
297762306a36Sopenharmony_ci				SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
297862306a36Sopenharmony_ci			tmp = (u8 *)&manufacture_reply->component_id;
297962306a36Sopenharmony_ci			edev->component_id = tmp[0] << 8 | tmp[1];
298062306a36Sopenharmony_ci			edev->component_revision_id =
298162306a36Sopenharmony_ci				manufacture_reply->component_revision_id;
298262306a36Sopenharmony_ci		}
298362306a36Sopenharmony_ci	} else {
298462306a36Sopenharmony_ci		printk(MYIOC_s_ERR_FMT
298562306a36Sopenharmony_ci			"%s: smp passthru reply failed to be returned\n",
298662306a36Sopenharmony_ci			ioc->name, __func__);
298762306a36Sopenharmony_ci		ret = -ENXIO;
298862306a36Sopenharmony_ci	}
298962306a36Sopenharmony_ciout_free:
299062306a36Sopenharmony_ci	if (data_out_dma)
299162306a36Sopenharmony_ci		dma_free_coherent(&ioc->pcidev->dev, sz, data_out,
299262306a36Sopenharmony_ci				  data_out_dma);
299362306a36Sopenharmony_ciput_mf:
299462306a36Sopenharmony_ci	if (mf)
299562306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
299662306a36Sopenharmony_ciout_unlock:
299762306a36Sopenharmony_ci	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
299862306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_mgmt.mutex);
299962306a36Sopenharmony_ciout:
300062306a36Sopenharmony_ci	return ret;
300162306a36Sopenharmony_ci}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_cistatic void
300462306a36Sopenharmony_cimptsas_parse_device_info(struct sas_identify *identify,
300562306a36Sopenharmony_ci		struct mptsas_devinfo *device_info)
300662306a36Sopenharmony_ci{
300762306a36Sopenharmony_ci	u16 protocols;
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	identify->sas_address = device_info->sas_address;
301062306a36Sopenharmony_ci	identify->phy_identifier = device_info->phy_id;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	/*
301362306a36Sopenharmony_ci	 * Fill in Phy Initiator Port Protocol.
301462306a36Sopenharmony_ci	 * Bits 6:3, more than one bit can be set, fall through cases.
301562306a36Sopenharmony_ci	 */
301662306a36Sopenharmony_ci	protocols = device_info->device_info & 0x78;
301762306a36Sopenharmony_ci	identify->initiator_port_protocols = 0;
301862306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SSP_INITIATOR)
301962306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
302062306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_STP_INITIATOR)
302162306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
302262306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SMP_INITIATOR)
302362306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
302462306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SATA_HOST)
302562306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	/*
302862306a36Sopenharmony_ci	 * Fill in Phy Target Port Protocol.
302962306a36Sopenharmony_ci	 * Bits 10:7, more than one bit can be set, fall through cases.
303062306a36Sopenharmony_ci	 */
303162306a36Sopenharmony_ci	protocols = device_info->device_info & 0x780;
303262306a36Sopenharmony_ci	identify->target_port_protocols = 0;
303362306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SSP_TARGET)
303462306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
303562306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_STP_TARGET)
303662306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_STP;
303762306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SMP_TARGET)
303862306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
303962306a36Sopenharmony_ci	if (protocols & MPI_SAS_DEVICE_INFO_SATA_DEVICE)
304062306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SATA;
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci	/*
304362306a36Sopenharmony_ci	 * Fill in Attached device type.
304462306a36Sopenharmony_ci	 */
304562306a36Sopenharmony_ci	switch (device_info->device_info &
304662306a36Sopenharmony_ci			MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
304762306a36Sopenharmony_ci	case MPI_SAS_DEVICE_INFO_NO_DEVICE:
304862306a36Sopenharmony_ci		identify->device_type = SAS_PHY_UNUSED;
304962306a36Sopenharmony_ci		break;
305062306a36Sopenharmony_ci	case MPI_SAS_DEVICE_INFO_END_DEVICE:
305162306a36Sopenharmony_ci		identify->device_type = SAS_END_DEVICE;
305262306a36Sopenharmony_ci		break;
305362306a36Sopenharmony_ci	case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER:
305462306a36Sopenharmony_ci		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
305562306a36Sopenharmony_ci		break;
305662306a36Sopenharmony_ci	case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER:
305762306a36Sopenharmony_ci		identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
305862306a36Sopenharmony_ci		break;
305962306a36Sopenharmony_ci	}
306062306a36Sopenharmony_ci}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_cistatic int mptsas_probe_one_phy(struct device *dev,
306362306a36Sopenharmony_ci		struct mptsas_phyinfo *phy_info, int index, int local)
306462306a36Sopenharmony_ci{
306562306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
306662306a36Sopenharmony_ci	struct sas_phy *phy;
306762306a36Sopenharmony_ci	struct sas_port *port;
306862306a36Sopenharmony_ci	int error = 0;
306962306a36Sopenharmony_ci	VirtTarget *vtarget;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	if (!dev) {
307262306a36Sopenharmony_ci		error = -ENODEV;
307362306a36Sopenharmony_ci		goto out;
307462306a36Sopenharmony_ci	}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	if (!phy_info->phy) {
307762306a36Sopenharmony_ci		phy = sas_phy_alloc(dev, index);
307862306a36Sopenharmony_ci		if (!phy) {
307962306a36Sopenharmony_ci			error = -ENOMEM;
308062306a36Sopenharmony_ci			goto out;
308162306a36Sopenharmony_ci		}
308262306a36Sopenharmony_ci	} else
308362306a36Sopenharmony_ci		phy = phy_info->phy;
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	mptsas_parse_device_info(&phy->identify, &phy_info->identify);
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	/*
308862306a36Sopenharmony_ci	 * Set Negotiated link rate.
308962306a36Sopenharmony_ci	 */
309062306a36Sopenharmony_ci	switch (phy_info->negotiated_link_rate) {
309162306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED:
309262306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_PHY_DISABLED;
309362306a36Sopenharmony_ci		break;
309462306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION:
309562306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_LINK_RATE_FAILED;
309662306a36Sopenharmony_ci		break;
309762306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_1_5:
309862306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
309962306a36Sopenharmony_ci		break;
310062306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_3_0:
310162306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
310262306a36Sopenharmony_ci		break;
310362306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_6_0:
310462306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
310562306a36Sopenharmony_ci		break;
310662306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE:
310762306a36Sopenharmony_ci	case MPI_SAS_IOUNIT0_RATE_UNKNOWN:
310862306a36Sopenharmony_ci	default:
310962306a36Sopenharmony_ci		phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
311062306a36Sopenharmony_ci		break;
311162306a36Sopenharmony_ci	}
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_ci	/*
311462306a36Sopenharmony_ci	 * Set Max hardware link rate.
311562306a36Sopenharmony_ci	 */
311662306a36Sopenharmony_ci	switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
311762306a36Sopenharmony_ci	case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5:
311862306a36Sopenharmony_ci		phy->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
311962306a36Sopenharmony_ci		break;
312062306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
312162306a36Sopenharmony_ci		phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
312262306a36Sopenharmony_ci		break;
312362306a36Sopenharmony_ci	default:
312462306a36Sopenharmony_ci		break;
312562306a36Sopenharmony_ci	}
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	/*
312862306a36Sopenharmony_ci	 * Set Max programmed link rate.
312962306a36Sopenharmony_ci	 */
313062306a36Sopenharmony_ci	switch (phy_info->programmed_link_rate &
313162306a36Sopenharmony_ci			MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
313262306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5:
313362306a36Sopenharmony_ci		phy->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS;
313462306a36Sopenharmony_ci		break;
313562306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
313662306a36Sopenharmony_ci		phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
313762306a36Sopenharmony_ci		break;
313862306a36Sopenharmony_ci	default:
313962306a36Sopenharmony_ci		break;
314062306a36Sopenharmony_ci	}
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	/*
314362306a36Sopenharmony_ci	 * Set Min hardware link rate.
314462306a36Sopenharmony_ci	 */
314562306a36Sopenharmony_ci	switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) {
314662306a36Sopenharmony_ci	case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5:
314762306a36Sopenharmony_ci		phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
314862306a36Sopenharmony_ci		break;
314962306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
315062306a36Sopenharmony_ci		phy->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
315162306a36Sopenharmony_ci		break;
315262306a36Sopenharmony_ci	default:
315362306a36Sopenharmony_ci		break;
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	/*
315762306a36Sopenharmony_ci	 * Set Min programmed link rate.
315862306a36Sopenharmony_ci	 */
315962306a36Sopenharmony_ci	switch (phy_info->programmed_link_rate &
316062306a36Sopenharmony_ci			MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) {
316162306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5:
316262306a36Sopenharmony_ci		phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
316362306a36Sopenharmony_ci		break;
316462306a36Sopenharmony_ci	case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
316562306a36Sopenharmony_ci		phy->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS;
316662306a36Sopenharmony_ci		break;
316762306a36Sopenharmony_ci	default:
316862306a36Sopenharmony_ci		break;
316962306a36Sopenharmony_ci	}
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	if (!phy_info->phy) {
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci		error = sas_phy_add(phy);
317462306a36Sopenharmony_ci		if (error) {
317562306a36Sopenharmony_ci			sas_phy_free(phy);
317662306a36Sopenharmony_ci			goto out;
317762306a36Sopenharmony_ci		}
317862306a36Sopenharmony_ci		phy_info->phy = phy;
317962306a36Sopenharmony_ci	}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	if (!phy_info->attached.handle ||
318262306a36Sopenharmony_ci			!phy_info->port_details)
318362306a36Sopenharmony_ci		goto out;
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	port = mptsas_get_port(phy_info);
318662306a36Sopenharmony_ci	ioc = phy_to_ioc(phy_info->phy);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	if (phy_info->sas_port_add_phy) {
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci		if (!port) {
319162306a36Sopenharmony_ci			port = sas_port_alloc_num(dev);
319262306a36Sopenharmony_ci			if (!port) {
319362306a36Sopenharmony_ci				error = -ENOMEM;
319462306a36Sopenharmony_ci				goto out;
319562306a36Sopenharmony_ci			}
319662306a36Sopenharmony_ci			error = sas_port_add(port);
319762306a36Sopenharmony_ci			if (error) {
319862306a36Sopenharmony_ci				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
319962306a36Sopenharmony_ci					"%s: exit at line=%d\n", ioc->name,
320062306a36Sopenharmony_ci					__func__, __LINE__));
320162306a36Sopenharmony_ci				goto out;
320262306a36Sopenharmony_ci			}
320362306a36Sopenharmony_ci			mptsas_set_port(ioc, phy_info, port);
320462306a36Sopenharmony_ci			devtprintk(ioc, dev_printk(KERN_DEBUG, &port->dev,
320562306a36Sopenharmony_ci			    MYIOC_s_FMT "add port %d, sas_addr (0x%llx)\n",
320662306a36Sopenharmony_ci			    ioc->name, port->port_identifier,
320762306a36Sopenharmony_ci			    (unsigned long long)phy_info->
320862306a36Sopenharmony_ci			    attached.sas_address));
320962306a36Sopenharmony_ci		}
321062306a36Sopenharmony_ci		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
321162306a36Sopenharmony_ci			"sas_port_add_phy: phy_id=%d\n",
321262306a36Sopenharmony_ci			ioc->name, phy_info->phy_id));
321362306a36Sopenharmony_ci		sas_port_add_phy(port, phy_info->phy);
321462306a36Sopenharmony_ci		phy_info->sas_port_add_phy = 0;
321562306a36Sopenharmony_ci		devtprintk(ioc, dev_printk(KERN_DEBUG, &phy_info->phy->dev,
321662306a36Sopenharmony_ci		    MYIOC_s_FMT "add phy %d, phy-obj (0x%p)\n", ioc->name,
321762306a36Sopenharmony_ci		     phy_info->phy_id, phy_info->phy));
321862306a36Sopenharmony_ci	}
321962306a36Sopenharmony_ci	if (!mptsas_get_rphy(phy_info) && port && !port->rphy) {
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci		struct sas_rphy *rphy;
322262306a36Sopenharmony_ci		struct device *parent;
322362306a36Sopenharmony_ci		struct sas_identify identify;
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci		parent = dev->parent->parent;
322662306a36Sopenharmony_ci		/*
322762306a36Sopenharmony_ci		 * Let the hotplug_work thread handle processing
322862306a36Sopenharmony_ci		 * the adding/removing of devices that occur
322962306a36Sopenharmony_ci		 * after start of day.
323062306a36Sopenharmony_ci		 */
323162306a36Sopenharmony_ci		if (mptsas_is_end_device(&phy_info->attached) &&
323262306a36Sopenharmony_ci		    phy_info->attached.handle_parent) {
323362306a36Sopenharmony_ci			goto out;
323462306a36Sopenharmony_ci		}
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci		mptsas_parse_device_info(&identify, &phy_info->attached);
323762306a36Sopenharmony_ci		if (scsi_is_host_device(parent)) {
323862306a36Sopenharmony_ci			struct mptsas_portinfo *port_info;
323962306a36Sopenharmony_ci			int i;
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci			port_info = ioc->hba_port_info;
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci			for (i = 0; i < port_info->num_phys; i++)
324462306a36Sopenharmony_ci				if (port_info->phy_info[i].identify.sas_address ==
324562306a36Sopenharmony_ci				    identify.sas_address) {
324662306a36Sopenharmony_ci					sas_port_mark_backlink(port);
324762306a36Sopenharmony_ci					goto out;
324862306a36Sopenharmony_ci				}
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci		} else if (scsi_is_sas_rphy(parent)) {
325162306a36Sopenharmony_ci			struct sas_rphy *parent_rphy = dev_to_rphy(parent);
325262306a36Sopenharmony_ci			if (identify.sas_address ==
325362306a36Sopenharmony_ci			    parent_rphy->identify.sas_address) {
325462306a36Sopenharmony_ci				sas_port_mark_backlink(port);
325562306a36Sopenharmony_ci				goto out;
325662306a36Sopenharmony_ci			}
325762306a36Sopenharmony_ci		}
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci		switch (identify.device_type) {
326062306a36Sopenharmony_ci		case SAS_END_DEVICE:
326162306a36Sopenharmony_ci			rphy = sas_end_device_alloc(port);
326262306a36Sopenharmony_ci			break;
326362306a36Sopenharmony_ci		case SAS_EDGE_EXPANDER_DEVICE:
326462306a36Sopenharmony_ci		case SAS_FANOUT_EXPANDER_DEVICE:
326562306a36Sopenharmony_ci			rphy = sas_expander_alloc(port, identify.device_type);
326662306a36Sopenharmony_ci			break;
326762306a36Sopenharmony_ci		default:
326862306a36Sopenharmony_ci			rphy = NULL;
326962306a36Sopenharmony_ci			break;
327062306a36Sopenharmony_ci		}
327162306a36Sopenharmony_ci		if (!rphy) {
327262306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
327362306a36Sopenharmony_ci				"%s: exit at line=%d\n", ioc->name,
327462306a36Sopenharmony_ci				__func__, __LINE__));
327562306a36Sopenharmony_ci			goto out;
327662306a36Sopenharmony_ci		}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci		rphy->identify = identify;
327962306a36Sopenharmony_ci		error = sas_rphy_add(rphy);
328062306a36Sopenharmony_ci		if (error) {
328162306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
328262306a36Sopenharmony_ci				"%s: exit at line=%d\n", ioc->name,
328362306a36Sopenharmony_ci				__func__, __LINE__));
328462306a36Sopenharmony_ci			sas_rphy_free(rphy);
328562306a36Sopenharmony_ci			goto out;
328662306a36Sopenharmony_ci		}
328762306a36Sopenharmony_ci		mptsas_set_rphy(ioc, phy_info, rphy);
328862306a36Sopenharmony_ci		if (identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
328962306a36Sopenharmony_ci			identify.device_type == SAS_FANOUT_EXPANDER_DEVICE)
329062306a36Sopenharmony_ci				mptsas_exp_repmanufacture_info(ioc,
329162306a36Sopenharmony_ci					identify.sas_address,
329262306a36Sopenharmony_ci					rphy_to_expander_device(rphy));
329362306a36Sopenharmony_ci	}
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci	/* If the device exists, verify it wasn't previously flagged
329662306a36Sopenharmony_ci	as a missing device.  If so, clear it */
329762306a36Sopenharmony_ci	vtarget = mptsas_find_vtarget(ioc,
329862306a36Sopenharmony_ci	    phy_info->attached.channel,
329962306a36Sopenharmony_ci	    phy_info->attached.id);
330062306a36Sopenharmony_ci	if (vtarget && vtarget->inDMD) {
330162306a36Sopenharmony_ci		printk(KERN_INFO "Device returned, unsetting inDMD\n");
330262306a36Sopenharmony_ci		vtarget->inDMD = 0;
330362306a36Sopenharmony_ci	}
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci out:
330662306a36Sopenharmony_ci	return error;
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_cistatic int
331062306a36Sopenharmony_cimptsas_probe_hba_phys(MPT_ADAPTER *ioc)
331162306a36Sopenharmony_ci{
331262306a36Sopenharmony_ci	struct mptsas_portinfo *port_info, *hba;
331362306a36Sopenharmony_ci	int error = -ENOMEM, i;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	hba = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
331662306a36Sopenharmony_ci	if (! hba)
331762306a36Sopenharmony_ci		goto out;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	error = mptsas_sas_io_unit_pg0(ioc, hba);
332062306a36Sopenharmony_ci	if (error)
332162306a36Sopenharmony_ci		goto out_free_port_info;
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	mptsas_sas_io_unit_pg1(ioc);
332462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
332562306a36Sopenharmony_ci	port_info = ioc->hba_port_info;
332662306a36Sopenharmony_ci	if (!port_info) {
332762306a36Sopenharmony_ci		ioc->hba_port_info = port_info = hba;
332862306a36Sopenharmony_ci		ioc->hba_port_num_phy = port_info->num_phys;
332962306a36Sopenharmony_ci		list_add_tail(&port_info->list, &ioc->sas_topology);
333062306a36Sopenharmony_ci	} else {
333162306a36Sopenharmony_ci		for (i = 0; i < hba->num_phys; i++) {
333262306a36Sopenharmony_ci			port_info->phy_info[i].negotiated_link_rate =
333362306a36Sopenharmony_ci				hba->phy_info[i].negotiated_link_rate;
333462306a36Sopenharmony_ci			port_info->phy_info[i].handle =
333562306a36Sopenharmony_ci				hba->phy_info[i].handle;
333662306a36Sopenharmony_ci			port_info->phy_info[i].port_id =
333762306a36Sopenharmony_ci				hba->phy_info[i].port_id;
333862306a36Sopenharmony_ci		}
333962306a36Sopenharmony_ci		kfree(hba->phy_info);
334062306a36Sopenharmony_ci		kfree(hba);
334162306a36Sopenharmony_ci		hba = NULL;
334262306a36Sopenharmony_ci	}
334362306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
334462306a36Sopenharmony_ci#if defined(CPQ_CIM)
334562306a36Sopenharmony_ci	ioc->num_ports = port_info->num_phys;
334662306a36Sopenharmony_ci#endif
334762306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
334862306a36Sopenharmony_ci		mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
334962306a36Sopenharmony_ci			(MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
335062306a36Sopenharmony_ci			 MPI_SAS_PHY_PGAD_FORM_SHIFT), i);
335162306a36Sopenharmony_ci		port_info->phy_info[i].identify.handle =
335262306a36Sopenharmony_ci		    port_info->phy_info[i].handle;
335362306a36Sopenharmony_ci		mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify,
335462306a36Sopenharmony_ci			(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
335562306a36Sopenharmony_ci			 MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
335662306a36Sopenharmony_ci			 port_info->phy_info[i].identify.handle);
335762306a36Sopenharmony_ci		if (!ioc->hba_port_sas_addr)
335862306a36Sopenharmony_ci			ioc->hba_port_sas_addr =
335962306a36Sopenharmony_ci			    port_info->phy_info[i].identify.sas_address;
336062306a36Sopenharmony_ci		port_info->phy_info[i].identify.phy_id =
336162306a36Sopenharmony_ci		    port_info->phy_info[i].phy_id = i;
336262306a36Sopenharmony_ci		if (port_info->phy_info[i].attached.handle)
336362306a36Sopenharmony_ci			mptsas_sas_device_pg0(ioc,
336462306a36Sopenharmony_ci				&port_info->phy_info[i].attached,
336562306a36Sopenharmony_ci				(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
336662306a36Sopenharmony_ci				 MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
336762306a36Sopenharmony_ci				port_info->phy_info[i].attached.handle);
336862306a36Sopenharmony_ci	}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	mptsas_setup_wide_ports(ioc, port_info);
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
337362306a36Sopenharmony_ci		mptsas_probe_one_phy(&ioc->sh->shost_gendev,
337462306a36Sopenharmony_ci		    &port_info->phy_info[i], ioc->sas_index, 1);
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	return 0;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci out_free_port_info:
337962306a36Sopenharmony_ci	kfree(hba);
338062306a36Sopenharmony_ci out:
338162306a36Sopenharmony_ci	return error;
338262306a36Sopenharmony_ci}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_cistatic void
338562306a36Sopenharmony_cimptsas_expander_refresh(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
338662306a36Sopenharmony_ci{
338762306a36Sopenharmony_ci	struct mptsas_portinfo *parent;
338862306a36Sopenharmony_ci	struct device *parent_dev;
338962306a36Sopenharmony_ci	struct sas_rphy	*rphy;
339062306a36Sopenharmony_ci	int		i;
339162306a36Sopenharmony_ci	u64		sas_address; /* expander sas address */
339262306a36Sopenharmony_ci	u32		handle;
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	handle = port_info->phy_info[0].handle;
339562306a36Sopenharmony_ci	sas_address = port_info->phy_info[0].identify.sas_address;
339662306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
339762306a36Sopenharmony_ci		mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i],
339862306a36Sopenharmony_ci		    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
339962306a36Sopenharmony_ci		    MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + handle);
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci		mptsas_sas_device_pg0(ioc,
340262306a36Sopenharmony_ci		    &port_info->phy_info[i].identify,
340362306a36Sopenharmony_ci		    (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
340462306a36Sopenharmony_ci		    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
340562306a36Sopenharmony_ci		    port_info->phy_info[i].identify.handle);
340662306a36Sopenharmony_ci		port_info->phy_info[i].identify.phy_id =
340762306a36Sopenharmony_ci		    port_info->phy_info[i].phy_id;
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci		if (port_info->phy_info[i].attached.handle) {
341062306a36Sopenharmony_ci			mptsas_sas_device_pg0(ioc,
341162306a36Sopenharmony_ci			    &port_info->phy_info[i].attached,
341262306a36Sopenharmony_ci			    (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
341362306a36Sopenharmony_ci			     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
341462306a36Sopenharmony_ci			    port_info->phy_info[i].attached.handle);
341562306a36Sopenharmony_ci			port_info->phy_info[i].attached.phy_id =
341662306a36Sopenharmony_ci			    port_info->phy_info[i].phy_id;
341762306a36Sopenharmony_ci		}
341862306a36Sopenharmony_ci	}
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
342162306a36Sopenharmony_ci	parent = mptsas_find_portinfo_by_handle(ioc,
342262306a36Sopenharmony_ci	    port_info->phy_info[0].identify.handle_parent);
342362306a36Sopenharmony_ci	if (!parent) {
342462306a36Sopenharmony_ci		mutex_unlock(&ioc->sas_topology_mutex);
342562306a36Sopenharmony_ci		return;
342662306a36Sopenharmony_ci	}
342762306a36Sopenharmony_ci	for (i = 0, parent_dev = NULL; i < parent->num_phys && !parent_dev;
342862306a36Sopenharmony_ci	    i++) {
342962306a36Sopenharmony_ci		if (parent->phy_info[i].attached.sas_address == sas_address) {
343062306a36Sopenharmony_ci			rphy = mptsas_get_rphy(&parent->phy_info[i]);
343162306a36Sopenharmony_ci			parent_dev = &rphy->dev;
343262306a36Sopenharmony_ci		}
343362306a36Sopenharmony_ci	}
343462306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci	mptsas_setup_wide_ports(ioc, port_info);
343762306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
343862306a36Sopenharmony_ci		mptsas_probe_one_phy(parent_dev, &port_info->phy_info[i],
343962306a36Sopenharmony_ci		    ioc->sas_index, 0);
344062306a36Sopenharmony_ci}
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_cistatic void
344362306a36Sopenharmony_cimptsas_expander_event_add(MPT_ADAPTER *ioc,
344462306a36Sopenharmony_ci    MpiEventDataSasExpanderStatusChange_t *expander_data)
344562306a36Sopenharmony_ci{
344662306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
344762306a36Sopenharmony_ci	int i;
344862306a36Sopenharmony_ci	__le64 sas_address;
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
345162306a36Sopenharmony_ci	BUG_ON(!port_info);
345262306a36Sopenharmony_ci	port_info->num_phys = (expander_data->NumPhys) ?
345362306a36Sopenharmony_ci	    expander_data->NumPhys : 1;
345462306a36Sopenharmony_ci	port_info->phy_info = kcalloc(port_info->num_phys,
345562306a36Sopenharmony_ci	    sizeof(struct mptsas_phyinfo), GFP_KERNEL);
345662306a36Sopenharmony_ci	BUG_ON(!port_info->phy_info);
345762306a36Sopenharmony_ci	memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
345862306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++) {
345962306a36Sopenharmony_ci		port_info->phy_info[i].portinfo = port_info;
346062306a36Sopenharmony_ci		port_info->phy_info[i].handle =
346162306a36Sopenharmony_ci		    le16_to_cpu(expander_data->DevHandle);
346262306a36Sopenharmony_ci		port_info->phy_info[i].identify.sas_address =
346362306a36Sopenharmony_ci		    le64_to_cpu(sas_address);
346462306a36Sopenharmony_ci		port_info->phy_info[i].identify.handle_parent =
346562306a36Sopenharmony_ci		    le16_to_cpu(expander_data->ParentDevHandle);
346662306a36Sopenharmony_ci	}
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
346962306a36Sopenharmony_ci	list_add_tail(&port_info->list, &ioc->sas_topology);
347062306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
347362306a36Sopenharmony_ci	    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
347462306a36Sopenharmony_ci	    (unsigned long long)sas_address);
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	mptsas_expander_refresh(ioc, port_info);
347762306a36Sopenharmony_ci}
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci/**
348062306a36Sopenharmony_ci * mptsas_delete_expander_siblings - remove siblings attached to expander
348162306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
348262306a36Sopenharmony_ci * @parent: the parent port_info object
348362306a36Sopenharmony_ci * @expander: the expander port_info object
348462306a36Sopenharmony_ci **/
348562306a36Sopenharmony_cistatic void
348662306a36Sopenharmony_cimptsas_delete_expander_siblings(MPT_ADAPTER *ioc, struct mptsas_portinfo
348762306a36Sopenharmony_ci    *parent, struct mptsas_portinfo *expander)
348862306a36Sopenharmony_ci{
348962306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
349062306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
349162306a36Sopenharmony_ci	struct sas_rphy *rphy;
349262306a36Sopenharmony_ci	int i;
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci	phy_info = expander->phy_info;
349562306a36Sopenharmony_ci	for (i = 0; i < expander->num_phys; i++, phy_info++) {
349662306a36Sopenharmony_ci		rphy = mptsas_get_rphy(phy_info);
349762306a36Sopenharmony_ci		if (!rphy)
349862306a36Sopenharmony_ci			continue;
349962306a36Sopenharmony_ci		if (rphy->identify.device_type == SAS_END_DEVICE)
350062306a36Sopenharmony_ci			mptsas_del_end_device(ioc, phy_info);
350162306a36Sopenharmony_ci	}
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	phy_info = expander->phy_info;
350462306a36Sopenharmony_ci	for (i = 0; i < expander->num_phys; i++, phy_info++) {
350562306a36Sopenharmony_ci		rphy = mptsas_get_rphy(phy_info);
350662306a36Sopenharmony_ci		if (!rphy)
350762306a36Sopenharmony_ci			continue;
350862306a36Sopenharmony_ci		if (rphy->identify.device_type ==
350962306a36Sopenharmony_ci		    MPI_SAS_DEVICE_INFO_EDGE_EXPANDER ||
351062306a36Sopenharmony_ci		    rphy->identify.device_type ==
351162306a36Sopenharmony_ci		    MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
351262306a36Sopenharmony_ci			port_info = mptsas_find_portinfo_by_sas_address(ioc,
351362306a36Sopenharmony_ci			    rphy->identify.sas_address);
351462306a36Sopenharmony_ci			if (!port_info)
351562306a36Sopenharmony_ci				continue;
351662306a36Sopenharmony_ci			if (port_info == parent) /* backlink rphy */
351762306a36Sopenharmony_ci				continue;
351862306a36Sopenharmony_ci			/*
351962306a36Sopenharmony_ci			Delete this expander even if the expdevpage is exists
352062306a36Sopenharmony_ci			because the parent expander is already deleted
352162306a36Sopenharmony_ci			*/
352262306a36Sopenharmony_ci			mptsas_expander_delete(ioc, port_info, 1);
352362306a36Sopenharmony_ci		}
352462306a36Sopenharmony_ci	}
352562306a36Sopenharmony_ci}
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci/**
352962306a36Sopenharmony_ci *	mptsas_expander_delete - remove this expander
353062306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
353162306a36Sopenharmony_ci *	@port_info: expander port_info struct
353262306a36Sopenharmony_ci *	@force: Flag to forcefully delete the expander
353362306a36Sopenharmony_ci *
353462306a36Sopenharmony_ci **/
353562306a36Sopenharmony_ci
353662306a36Sopenharmony_cistatic void mptsas_expander_delete(MPT_ADAPTER *ioc,
353762306a36Sopenharmony_ci		struct mptsas_portinfo *port_info, u8 force)
353862306a36Sopenharmony_ci{
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	struct mptsas_portinfo *parent;
354162306a36Sopenharmony_ci	int		i;
354262306a36Sopenharmony_ci	u64		expander_sas_address;
354362306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
354462306a36Sopenharmony_ci	struct mptsas_portinfo buffer;
354562306a36Sopenharmony_ci	struct mptsas_portinfo_details *port_details;
354662306a36Sopenharmony_ci	struct sas_port *port;
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci	if (!port_info)
354962306a36Sopenharmony_ci		return;
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	/* see if expander is still there before deleting */
355262306a36Sopenharmony_ci	mptsas_sas_expander_pg0(ioc, &buffer,
355362306a36Sopenharmony_ci	    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
355462306a36Sopenharmony_ci	    MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
355562306a36Sopenharmony_ci	    port_info->phy_info[0].identify.handle);
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	if (buffer.num_phys) {
355862306a36Sopenharmony_ci		kfree(buffer.phy_info);
355962306a36Sopenharmony_ci		if (!force)
356062306a36Sopenharmony_ci			return;
356162306a36Sopenharmony_ci	}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	/*
356562306a36Sopenharmony_ci	 * Obtain the port_info instance to the parent port
356662306a36Sopenharmony_ci	 */
356762306a36Sopenharmony_ci	port_details = NULL;
356862306a36Sopenharmony_ci	expander_sas_address =
356962306a36Sopenharmony_ci	    port_info->phy_info[0].identify.sas_address;
357062306a36Sopenharmony_ci	parent = mptsas_find_portinfo_by_handle(ioc,
357162306a36Sopenharmony_ci	    port_info->phy_info[0].identify.handle_parent);
357262306a36Sopenharmony_ci	mptsas_delete_expander_siblings(ioc, parent, port_info);
357362306a36Sopenharmony_ci	if (!parent)
357462306a36Sopenharmony_ci		goto out;
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci	/*
357762306a36Sopenharmony_ci	 * Delete rphys in the parent that point
357862306a36Sopenharmony_ci	 * to this expander.
357962306a36Sopenharmony_ci	 */
358062306a36Sopenharmony_ci	phy_info = parent->phy_info;
358162306a36Sopenharmony_ci	port = NULL;
358262306a36Sopenharmony_ci	for (i = 0; i < parent->num_phys; i++, phy_info++) {
358362306a36Sopenharmony_ci		if (!phy_info->phy)
358462306a36Sopenharmony_ci			continue;
358562306a36Sopenharmony_ci		if (phy_info->attached.sas_address !=
358662306a36Sopenharmony_ci		    expander_sas_address)
358762306a36Sopenharmony_ci			continue;
358862306a36Sopenharmony_ci		if (!port) {
358962306a36Sopenharmony_ci			port = mptsas_get_port(phy_info);
359062306a36Sopenharmony_ci			port_details = phy_info->port_details;
359162306a36Sopenharmony_ci		}
359262306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &phy_info->phy->dev,
359362306a36Sopenharmony_ci		    MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n", ioc->name,
359462306a36Sopenharmony_ci		    phy_info->phy_id, phy_info->phy);
359562306a36Sopenharmony_ci		sas_port_delete_phy(port, phy_info->phy);
359662306a36Sopenharmony_ci	}
359762306a36Sopenharmony_ci	if (port) {
359862306a36Sopenharmony_ci		dev_printk(KERN_DEBUG, &port->dev,
359962306a36Sopenharmony_ci		    MYIOC_s_FMT "delete port %d, sas_addr (0x%llx)\n",
360062306a36Sopenharmony_ci		    ioc->name, port->port_identifier,
360162306a36Sopenharmony_ci		    (unsigned long long)expander_sas_address);
360262306a36Sopenharmony_ci		sas_port_delete(port);
360362306a36Sopenharmony_ci		mptsas_port_delete(ioc, port_details);
360462306a36Sopenharmony_ci	}
360562306a36Sopenharmony_ci out:
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	printk(MYIOC_s_INFO_FMT "delete expander: num_phys %d, "
360862306a36Sopenharmony_ci	    "sas_addr (0x%llx)\n",  ioc->name, port_info->num_phys,
360962306a36Sopenharmony_ci	    (unsigned long long)expander_sas_address);
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	/*
361262306a36Sopenharmony_ci	 * free link
361362306a36Sopenharmony_ci	 */
361462306a36Sopenharmony_ci	list_del(&port_info->list);
361562306a36Sopenharmony_ci	kfree(port_info->phy_info);
361662306a36Sopenharmony_ci	kfree(port_info);
361762306a36Sopenharmony_ci}
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci/**
362162306a36Sopenharmony_ci * mptsas_send_expander_event - expanders events
362262306a36Sopenharmony_ci * @fw_event: event data
362362306a36Sopenharmony_ci *
362462306a36Sopenharmony_ci *
362562306a36Sopenharmony_ci * This function handles adding, removing, and refreshing
362662306a36Sopenharmony_ci * device handles within the expander objects.
362762306a36Sopenharmony_ci */
362862306a36Sopenharmony_cistatic void
362962306a36Sopenharmony_cimptsas_send_expander_event(struct fw_event_work *fw_event)
363062306a36Sopenharmony_ci{
363162306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
363262306a36Sopenharmony_ci	MpiEventDataSasExpanderStatusChange_t *expander_data;
363362306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
363462306a36Sopenharmony_ci	__le64 sas_address;
363562306a36Sopenharmony_ci	int i;
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	ioc = fw_event->ioc;
363862306a36Sopenharmony_ci	expander_data = (MpiEventDataSasExpanderStatusChange_t *)
363962306a36Sopenharmony_ci	    fw_event->event_data;
364062306a36Sopenharmony_ci	memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
364162306a36Sopenharmony_ci	sas_address = le64_to_cpu(sas_address);
364262306a36Sopenharmony_ci	port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
364562306a36Sopenharmony_ci		if (port_info) {
364662306a36Sopenharmony_ci			for (i = 0; i < port_info->num_phys; i++) {
364762306a36Sopenharmony_ci				port_info->phy_info[i].portinfo = port_info;
364862306a36Sopenharmony_ci				port_info->phy_info[i].handle =
364962306a36Sopenharmony_ci				    le16_to_cpu(expander_data->DevHandle);
365062306a36Sopenharmony_ci				port_info->phy_info[i].identify.sas_address =
365162306a36Sopenharmony_ci				    le64_to_cpu(sas_address);
365262306a36Sopenharmony_ci				port_info->phy_info[i].identify.handle_parent =
365362306a36Sopenharmony_ci				    le16_to_cpu(expander_data->ParentDevHandle);
365462306a36Sopenharmony_ci			}
365562306a36Sopenharmony_ci			mptsas_expander_refresh(ioc, port_info);
365662306a36Sopenharmony_ci		} else if (!port_info && expander_data->NumPhys)
365762306a36Sopenharmony_ci			mptsas_expander_event_add(ioc, expander_data);
365862306a36Sopenharmony_ci	} else if (expander_data->ReasonCode ==
365962306a36Sopenharmony_ci	    MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING)
366062306a36Sopenharmony_ci		mptsas_expander_delete(ioc, port_info, 0);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci	mptsas_free_fw_event(ioc, fw_event);
366362306a36Sopenharmony_ci}
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci/**
366762306a36Sopenharmony_ci * mptsas_expander_add - adds a newly discovered expander
366862306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
366962306a36Sopenharmony_ci * @handle: device handle
367062306a36Sopenharmony_ci *
367162306a36Sopenharmony_ci */
367262306a36Sopenharmony_cistatic struct mptsas_portinfo *
367362306a36Sopenharmony_cimptsas_expander_add(MPT_ADAPTER *ioc, u16 handle)
367462306a36Sopenharmony_ci{
367562306a36Sopenharmony_ci	struct mptsas_portinfo buffer, *port_info;
367662306a36Sopenharmony_ci	int i;
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_ci	if ((mptsas_sas_expander_pg0(ioc, &buffer,
367962306a36Sopenharmony_ci	    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
368062306a36Sopenharmony_ci	    MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)))
368162306a36Sopenharmony_ci		return NULL;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
368462306a36Sopenharmony_ci	if (!port_info) {
368562306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
368662306a36Sopenharmony_ci		"%s: exit at line=%d\n", ioc->name,
368762306a36Sopenharmony_ci		__func__, __LINE__));
368862306a36Sopenharmony_ci		return NULL;
368962306a36Sopenharmony_ci	}
369062306a36Sopenharmony_ci	port_info->num_phys = buffer.num_phys;
369162306a36Sopenharmony_ci	port_info->phy_info = buffer.phy_info;
369262306a36Sopenharmony_ci	for (i = 0; i < port_info->num_phys; i++)
369362306a36Sopenharmony_ci		port_info->phy_info[i].portinfo = port_info;
369462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
369562306a36Sopenharmony_ci	list_add_tail(&port_info->list, &ioc->sas_topology);
369662306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
369762306a36Sopenharmony_ci	printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
369862306a36Sopenharmony_ci	    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
369962306a36Sopenharmony_ci	    (unsigned long long)buffer.phy_info[0].identify.sas_address);
370062306a36Sopenharmony_ci	mptsas_expander_refresh(ioc, port_info);
370162306a36Sopenharmony_ci	return port_info;
370262306a36Sopenharmony_ci}
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_cistatic void
370562306a36Sopenharmony_cimptsas_send_link_status_event(struct fw_event_work *fw_event)
370662306a36Sopenharmony_ci{
370762306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
370862306a36Sopenharmony_ci	MpiEventDataSasPhyLinkStatus_t *link_data;
370962306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
371062306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info = NULL;
371162306a36Sopenharmony_ci	__le64 sas_address;
371262306a36Sopenharmony_ci	u8 phy_num;
371362306a36Sopenharmony_ci	u8 link_rate;
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	ioc = fw_event->ioc;
371662306a36Sopenharmony_ci	link_data = (MpiEventDataSasPhyLinkStatus_t *)fw_event->event_data;
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	memcpy(&sas_address, &link_data->SASAddress, sizeof(__le64));
371962306a36Sopenharmony_ci	sas_address = le64_to_cpu(sas_address);
372062306a36Sopenharmony_ci	link_rate = link_data->LinkRates >> 4;
372162306a36Sopenharmony_ci	phy_num = link_data->PhyNum;
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
372462306a36Sopenharmony_ci	if (port_info) {
372562306a36Sopenharmony_ci		phy_info = &port_info->phy_info[phy_num];
372662306a36Sopenharmony_ci		if (phy_info)
372762306a36Sopenharmony_ci			phy_info->negotiated_link_rate = link_rate;
372862306a36Sopenharmony_ci	}
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
373162306a36Sopenharmony_ci	    link_rate == MPI_SAS_IOUNIT0_RATE_3_0 ||
373262306a36Sopenharmony_ci	    link_rate == MPI_SAS_IOUNIT0_RATE_6_0) {
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci		if (!port_info) {
373562306a36Sopenharmony_ci			if (ioc->old_sas_discovery_protocal) {
373662306a36Sopenharmony_ci				port_info = mptsas_expander_add(ioc,
373762306a36Sopenharmony_ci					le16_to_cpu(link_data->DevHandle));
373862306a36Sopenharmony_ci				if (port_info)
373962306a36Sopenharmony_ci					goto out;
374062306a36Sopenharmony_ci			}
374162306a36Sopenharmony_ci			goto out;
374262306a36Sopenharmony_ci		}
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci		if (port_info == ioc->hba_port_info)
374562306a36Sopenharmony_ci			mptsas_probe_hba_phys(ioc);
374662306a36Sopenharmony_ci		else
374762306a36Sopenharmony_ci			mptsas_expander_refresh(ioc, port_info);
374862306a36Sopenharmony_ci	} else if (phy_info && phy_info->phy) {
374962306a36Sopenharmony_ci		if (link_rate ==  MPI_SAS_IOUNIT0_RATE_PHY_DISABLED)
375062306a36Sopenharmony_ci			phy_info->phy->negotiated_linkrate =
375162306a36Sopenharmony_ci			    SAS_PHY_DISABLED;
375262306a36Sopenharmony_ci		else if (link_rate ==
375362306a36Sopenharmony_ci		    MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION)
375462306a36Sopenharmony_ci			phy_info->phy->negotiated_linkrate =
375562306a36Sopenharmony_ci			    SAS_LINK_RATE_FAILED;
375662306a36Sopenharmony_ci		else {
375762306a36Sopenharmony_ci			phy_info->phy->negotiated_linkrate =
375862306a36Sopenharmony_ci			    SAS_LINK_RATE_UNKNOWN;
375962306a36Sopenharmony_ci			if (ioc->device_missing_delay &&
376062306a36Sopenharmony_ci			    mptsas_is_end_device(&phy_info->attached)) {
376162306a36Sopenharmony_ci				struct scsi_device		*sdev;
376262306a36Sopenharmony_ci				VirtDevice			*vdevice;
376362306a36Sopenharmony_ci				u8	channel, id;
376462306a36Sopenharmony_ci				id = phy_info->attached.id;
376562306a36Sopenharmony_ci				channel = phy_info->attached.channel;
376662306a36Sopenharmony_ci				devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
376762306a36Sopenharmony_ci				"Link down for fw_id %d:fw_channel %d\n",
376862306a36Sopenharmony_ci				    ioc->name, phy_info->attached.id,
376962306a36Sopenharmony_ci				    phy_info->attached.channel));
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_ci				shost_for_each_device(sdev, ioc->sh) {
377262306a36Sopenharmony_ci					vdevice = sdev->hostdata;
377362306a36Sopenharmony_ci					if ((vdevice == NULL) ||
377462306a36Sopenharmony_ci						(vdevice->vtarget == NULL))
377562306a36Sopenharmony_ci						continue;
377662306a36Sopenharmony_ci					if ((vdevice->vtarget->tflags &
377762306a36Sopenharmony_ci					    MPT_TARGET_FLAGS_RAID_COMPONENT ||
377862306a36Sopenharmony_ci					    vdevice->vtarget->raidVolume))
377962306a36Sopenharmony_ci						continue;
378062306a36Sopenharmony_ci					if (vdevice->vtarget->id == id &&
378162306a36Sopenharmony_ci						vdevice->vtarget->channel ==
378262306a36Sopenharmony_ci						channel)
378362306a36Sopenharmony_ci						devtprintk(ioc,
378462306a36Sopenharmony_ci						printk(MYIOC_s_DEBUG_FMT
378562306a36Sopenharmony_ci						"SDEV OUTSTANDING CMDS"
378662306a36Sopenharmony_ci						"%d\n", ioc->name,
378762306a36Sopenharmony_ci						scsi_device_busy(sdev)));
378862306a36Sopenharmony_ci				}
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci			}
379162306a36Sopenharmony_ci		}
379262306a36Sopenharmony_ci	}
379362306a36Sopenharmony_ci out:
379462306a36Sopenharmony_ci	mptsas_free_fw_event(ioc, fw_event);
379562306a36Sopenharmony_ci}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_cistatic void
379862306a36Sopenharmony_cimptsas_not_responding_devices(MPT_ADAPTER *ioc)
379962306a36Sopenharmony_ci{
380062306a36Sopenharmony_ci	struct mptsas_portinfo buffer, *port_info;
380162306a36Sopenharmony_ci	struct mptsas_device_info	*sas_info;
380262306a36Sopenharmony_ci	struct mptsas_devinfo sas_device;
380362306a36Sopenharmony_ci	u32	handle;
380462306a36Sopenharmony_ci	VirtTarget *vtarget = NULL;
380562306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
380662306a36Sopenharmony_ci	u8 found_expander;
380762306a36Sopenharmony_ci	int retval, retry_count;
380862306a36Sopenharmony_ci	unsigned long flags;
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	mpt_findImVolumes(ioc);
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
381362306a36Sopenharmony_ci	if (ioc->ioc_reset_in_progress) {
381462306a36Sopenharmony_ci		dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
381562306a36Sopenharmony_ci		   "%s: exiting due to a parallel reset \n", ioc->name,
381662306a36Sopenharmony_ci		    __func__));
381762306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
381862306a36Sopenharmony_ci		return;
381962306a36Sopenharmony_ci	}
382062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci	/* devices, logical volumes */
382362306a36Sopenharmony_ci	mutex_lock(&ioc->sas_device_info_mutex);
382462306a36Sopenharmony_ci redo_device_scan:
382562306a36Sopenharmony_ci	list_for_each_entry(sas_info, &ioc->sas_device_info_list, list) {
382662306a36Sopenharmony_ci		if (sas_info->is_cached)
382762306a36Sopenharmony_ci			continue;
382862306a36Sopenharmony_ci		if (!sas_info->is_logical_volume) {
382962306a36Sopenharmony_ci			sas_device.handle = 0;
383062306a36Sopenharmony_ci			retry_count = 0;
383162306a36Sopenharmony_ciretry_page:
383262306a36Sopenharmony_ci			retval = mptsas_sas_device_pg0(ioc, &sas_device,
383362306a36Sopenharmony_ci				(MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID
383462306a36Sopenharmony_ci				<< MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
383562306a36Sopenharmony_ci				(sas_info->fw.channel << 8) +
383662306a36Sopenharmony_ci				sas_info->fw.id);
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci			if (sas_device.handle)
383962306a36Sopenharmony_ci				continue;
384062306a36Sopenharmony_ci			if (retval == -EBUSY) {
384162306a36Sopenharmony_ci				spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
384262306a36Sopenharmony_ci				if (ioc->ioc_reset_in_progress) {
384362306a36Sopenharmony_ci					dfailprintk(ioc,
384462306a36Sopenharmony_ci					printk(MYIOC_s_DEBUG_FMT
384562306a36Sopenharmony_ci					"%s: exiting due to reset\n",
384662306a36Sopenharmony_ci					ioc->name, __func__));
384762306a36Sopenharmony_ci					spin_unlock_irqrestore
384862306a36Sopenharmony_ci					(&ioc->taskmgmt_lock, flags);
384962306a36Sopenharmony_ci					mutex_unlock(&ioc->
385062306a36Sopenharmony_ci					sas_device_info_mutex);
385162306a36Sopenharmony_ci					return;
385262306a36Sopenharmony_ci				}
385362306a36Sopenharmony_ci				spin_unlock_irqrestore(&ioc->taskmgmt_lock,
385462306a36Sopenharmony_ci				flags);
385562306a36Sopenharmony_ci			}
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci			if (retval && (retval != -ENODEV)) {
385862306a36Sopenharmony_ci				if (retry_count < 10) {
385962306a36Sopenharmony_ci					retry_count++;
386062306a36Sopenharmony_ci					goto retry_page;
386162306a36Sopenharmony_ci				} else {
386262306a36Sopenharmony_ci					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
386362306a36Sopenharmony_ci					"%s: Config page retry exceeded retry "
386462306a36Sopenharmony_ci					"count deleting device 0x%llx\n",
386562306a36Sopenharmony_ci					ioc->name, __func__,
386662306a36Sopenharmony_ci					sas_info->sas_address));
386762306a36Sopenharmony_ci				}
386862306a36Sopenharmony_ci			}
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci			/* delete device */
387162306a36Sopenharmony_ci			vtarget = mptsas_find_vtarget(ioc,
387262306a36Sopenharmony_ci				sas_info->fw.channel, sas_info->fw.id);
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci			if (vtarget)
387562306a36Sopenharmony_ci				vtarget->deleted = 1;
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci			phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
387862306a36Sopenharmony_ci					sas_info->sas_address);
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci			mptsas_del_end_device(ioc, phy_info);
388162306a36Sopenharmony_ci			goto redo_device_scan;
388262306a36Sopenharmony_ci		} else
388362306a36Sopenharmony_ci			mptsas_volume_delete(ioc, sas_info->fw.id);
388462306a36Sopenharmony_ci	}
388562306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_device_info_mutex);
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	/* expanders */
388862306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
388962306a36Sopenharmony_ci redo_expander_scan:
389062306a36Sopenharmony_ci	list_for_each_entry(port_info, &ioc->sas_topology, list) {
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci		if (!(port_info->phy_info[0].identify.device_info &
389362306a36Sopenharmony_ci		    MPI_SAS_DEVICE_INFO_SMP_TARGET))
389462306a36Sopenharmony_ci			continue;
389562306a36Sopenharmony_ci		found_expander = 0;
389662306a36Sopenharmony_ci		handle = 0xFFFF;
389762306a36Sopenharmony_ci		while (!mptsas_sas_expander_pg0(ioc, &buffer,
389862306a36Sopenharmony_ci		    (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
389962306a36Sopenharmony_ci		     MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle) &&
390062306a36Sopenharmony_ci		    !found_expander) {
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci			handle = buffer.phy_info[0].handle;
390362306a36Sopenharmony_ci			if (buffer.phy_info[0].identify.sas_address ==
390462306a36Sopenharmony_ci			    port_info->phy_info[0].identify.sas_address) {
390562306a36Sopenharmony_ci				found_expander = 1;
390662306a36Sopenharmony_ci			}
390762306a36Sopenharmony_ci			kfree(buffer.phy_info);
390862306a36Sopenharmony_ci		}
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_ci		if (!found_expander) {
391162306a36Sopenharmony_ci			mptsas_expander_delete(ioc, port_info, 0);
391262306a36Sopenharmony_ci			goto redo_expander_scan;
391362306a36Sopenharmony_ci		}
391462306a36Sopenharmony_ci	}
391562306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
391662306a36Sopenharmony_ci}
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci/**
391962306a36Sopenharmony_ci *	mptsas_probe_expanders - adding expanders
392062306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
392162306a36Sopenharmony_ci *
392262306a36Sopenharmony_ci **/
392362306a36Sopenharmony_cistatic void
392462306a36Sopenharmony_cimptsas_probe_expanders(MPT_ADAPTER *ioc)
392562306a36Sopenharmony_ci{
392662306a36Sopenharmony_ci	struct mptsas_portinfo buffer, *port_info;
392762306a36Sopenharmony_ci	u32 			handle;
392862306a36Sopenharmony_ci	int i;
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci	handle = 0xFFFF;
393162306a36Sopenharmony_ci	while (!mptsas_sas_expander_pg0(ioc, &buffer,
393262306a36Sopenharmony_ci	    (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
393362306a36Sopenharmony_ci	     MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)) {
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci		handle = buffer.phy_info[0].handle;
393662306a36Sopenharmony_ci		port_info = mptsas_find_portinfo_by_sas_address(ioc,
393762306a36Sopenharmony_ci		    buffer.phy_info[0].identify.sas_address);
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_ci		if (port_info) {
394062306a36Sopenharmony_ci			/* refreshing handles */
394162306a36Sopenharmony_ci			for (i = 0; i < buffer.num_phys; i++) {
394262306a36Sopenharmony_ci				port_info->phy_info[i].handle = handle;
394362306a36Sopenharmony_ci				port_info->phy_info[i].identify.handle_parent =
394462306a36Sopenharmony_ci				    buffer.phy_info[0].identify.handle_parent;
394562306a36Sopenharmony_ci			}
394662306a36Sopenharmony_ci			mptsas_expander_refresh(ioc, port_info);
394762306a36Sopenharmony_ci			kfree(buffer.phy_info);
394862306a36Sopenharmony_ci			continue;
394962306a36Sopenharmony_ci		}
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci		port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
395262306a36Sopenharmony_ci		if (!port_info) {
395362306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
395462306a36Sopenharmony_ci			"%s: exit at line=%d\n", ioc->name,
395562306a36Sopenharmony_ci			__func__, __LINE__));
395662306a36Sopenharmony_ci			return;
395762306a36Sopenharmony_ci		}
395862306a36Sopenharmony_ci		port_info->num_phys = buffer.num_phys;
395962306a36Sopenharmony_ci		port_info->phy_info = buffer.phy_info;
396062306a36Sopenharmony_ci		for (i = 0; i < port_info->num_phys; i++)
396162306a36Sopenharmony_ci			port_info->phy_info[i].portinfo = port_info;
396262306a36Sopenharmony_ci		mutex_lock(&ioc->sas_topology_mutex);
396362306a36Sopenharmony_ci		list_add_tail(&port_info->list, &ioc->sas_topology);
396462306a36Sopenharmony_ci		mutex_unlock(&ioc->sas_topology_mutex);
396562306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
396662306a36Sopenharmony_ci		    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
396762306a36Sopenharmony_ci	    (unsigned long long)buffer.phy_info[0].identify.sas_address);
396862306a36Sopenharmony_ci		mptsas_expander_refresh(ioc, port_info);
396962306a36Sopenharmony_ci	}
397062306a36Sopenharmony_ci}
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_cistatic void
397362306a36Sopenharmony_cimptsas_probe_devices(MPT_ADAPTER *ioc)
397462306a36Sopenharmony_ci{
397562306a36Sopenharmony_ci	u16 handle;
397662306a36Sopenharmony_ci	struct mptsas_devinfo sas_device;
397762306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
397862306a36Sopenharmony_ci
397962306a36Sopenharmony_ci	handle = 0xFFFF;
398062306a36Sopenharmony_ci	while (!(mptsas_sas_device_pg0(ioc, &sas_device,
398162306a36Sopenharmony_ci	    MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_ci		handle = sas_device.handle;
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci		if ((sas_device.device_info &
398662306a36Sopenharmony_ci		     (MPI_SAS_DEVICE_INFO_SSP_TARGET |
398762306a36Sopenharmony_ci		      MPI_SAS_DEVICE_INFO_STP_TARGET |
398862306a36Sopenharmony_ci		      MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0)
398962306a36Sopenharmony_ci			continue;
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci		/* If there is no FW B_T mapping for this device then continue
399262306a36Sopenharmony_ci		 * */
399362306a36Sopenharmony_ci		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
399462306a36Sopenharmony_ci			|| !(sas_device.flags &
399562306a36Sopenharmony_ci			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
399662306a36Sopenharmony_ci			continue;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci		phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
399962306a36Sopenharmony_ci		if (!phy_info)
400062306a36Sopenharmony_ci			continue;
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci		if (mptsas_get_rphy(phy_info))
400362306a36Sopenharmony_ci			continue;
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci		mptsas_add_end_device(ioc, phy_info);
400662306a36Sopenharmony_ci	}
400762306a36Sopenharmony_ci}
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_ci/**
401062306a36Sopenharmony_ci *	mptsas_scan_sas_topology - scans new SAS topology
401162306a36Sopenharmony_ci *	  (part of probe or rescan)
401262306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
401362306a36Sopenharmony_ci *
401462306a36Sopenharmony_ci **/
401562306a36Sopenharmony_cistatic void
401662306a36Sopenharmony_cimptsas_scan_sas_topology(MPT_ADAPTER *ioc)
401762306a36Sopenharmony_ci{
401862306a36Sopenharmony_ci	struct scsi_device *sdev;
401962306a36Sopenharmony_ci	int i;
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci	mptsas_probe_hba_phys(ioc);
402262306a36Sopenharmony_ci	mptsas_probe_expanders(ioc);
402362306a36Sopenharmony_ci	mptsas_probe_devices(ioc);
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_ci	/*
402662306a36Sopenharmony_ci	  Reporting RAID volumes.
402762306a36Sopenharmony_ci	*/
402862306a36Sopenharmony_ci	if (!ioc->ir_firmware || !ioc->raid_data.pIocPg2 ||
402962306a36Sopenharmony_ci	    !ioc->raid_data.pIocPg2->NumActiveVolumes)
403062306a36Sopenharmony_ci		return;
403162306a36Sopenharmony_ci	for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
403262306a36Sopenharmony_ci		sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
403362306a36Sopenharmony_ci		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
403462306a36Sopenharmony_ci		if (sdev) {
403562306a36Sopenharmony_ci			scsi_device_put(sdev);
403662306a36Sopenharmony_ci			continue;
403762306a36Sopenharmony_ci		}
403862306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
403962306a36Sopenharmony_ci		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
404062306a36Sopenharmony_ci		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID);
404162306a36Sopenharmony_ci		scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
404262306a36Sopenharmony_ci		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
404362306a36Sopenharmony_ci	}
404462306a36Sopenharmony_ci}
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_cistatic void
404862306a36Sopenharmony_cimptsas_handle_queue_full_event(struct fw_event_work *fw_event)
404962306a36Sopenharmony_ci{
405062306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
405162306a36Sopenharmony_ci	EventDataQueueFull_t *qfull_data;
405262306a36Sopenharmony_ci	struct mptsas_device_info *sas_info;
405362306a36Sopenharmony_ci	struct scsi_device	*sdev;
405462306a36Sopenharmony_ci	int depth;
405562306a36Sopenharmony_ci	int id = -1;
405662306a36Sopenharmony_ci	int channel = -1;
405762306a36Sopenharmony_ci	int fw_id, fw_channel;
405862306a36Sopenharmony_ci	u16 current_depth;
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci	ioc = fw_event->ioc;
406262306a36Sopenharmony_ci	qfull_data = (EventDataQueueFull_t *)fw_event->event_data;
406362306a36Sopenharmony_ci	fw_id = qfull_data->TargetID;
406462306a36Sopenharmony_ci	fw_channel = qfull_data->Bus;
406562306a36Sopenharmony_ci	current_depth = le16_to_cpu(qfull_data->CurrentDepth);
406662306a36Sopenharmony_ci
406762306a36Sopenharmony_ci	/* if hidden raid component, look for the volume id */
406862306a36Sopenharmony_ci	mutex_lock(&ioc->sas_device_info_mutex);
406962306a36Sopenharmony_ci	if (mptscsih_is_phys_disk(ioc, fw_channel, fw_id)) {
407062306a36Sopenharmony_ci		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
407162306a36Sopenharmony_ci		    list) {
407262306a36Sopenharmony_ci			if (sas_info->is_cached ||
407362306a36Sopenharmony_ci			    sas_info->is_logical_volume)
407462306a36Sopenharmony_ci				continue;
407562306a36Sopenharmony_ci			if (sas_info->is_hidden_raid_component &&
407662306a36Sopenharmony_ci			    (sas_info->fw.channel == fw_channel &&
407762306a36Sopenharmony_ci			    sas_info->fw.id == fw_id)) {
407862306a36Sopenharmony_ci				id = sas_info->volume_id;
407962306a36Sopenharmony_ci				channel = MPTSAS_RAID_CHANNEL;
408062306a36Sopenharmony_ci				goto out;
408162306a36Sopenharmony_ci			}
408262306a36Sopenharmony_ci		}
408362306a36Sopenharmony_ci	} else {
408462306a36Sopenharmony_ci		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
408562306a36Sopenharmony_ci		    list) {
408662306a36Sopenharmony_ci			if (sas_info->is_cached ||
408762306a36Sopenharmony_ci			    sas_info->is_hidden_raid_component ||
408862306a36Sopenharmony_ci			    sas_info->is_logical_volume)
408962306a36Sopenharmony_ci				continue;
409062306a36Sopenharmony_ci			if (sas_info->fw.channel == fw_channel &&
409162306a36Sopenharmony_ci			    sas_info->fw.id == fw_id) {
409262306a36Sopenharmony_ci				id = sas_info->os.id;
409362306a36Sopenharmony_ci				channel = sas_info->os.channel;
409462306a36Sopenharmony_ci				goto out;
409562306a36Sopenharmony_ci			}
409662306a36Sopenharmony_ci		}
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci	}
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_ci out:
410162306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_device_info_mutex);
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	if (id != -1) {
410462306a36Sopenharmony_ci		shost_for_each_device(sdev, ioc->sh) {
410562306a36Sopenharmony_ci			if (sdev->id == id && sdev->channel == channel) {
410662306a36Sopenharmony_ci				if (current_depth > sdev->queue_depth) {
410762306a36Sopenharmony_ci					sdev_printk(KERN_INFO, sdev,
410862306a36Sopenharmony_ci					    "strange observation, the queue "
410962306a36Sopenharmony_ci					    "depth is (%d) meanwhile fw queue "
411062306a36Sopenharmony_ci					    "depth (%d)\n", sdev->queue_depth,
411162306a36Sopenharmony_ci					    current_depth);
411262306a36Sopenharmony_ci					continue;
411362306a36Sopenharmony_ci				}
411462306a36Sopenharmony_ci				depth = scsi_track_queue_full(sdev,
411562306a36Sopenharmony_ci					sdev->queue_depth - 1);
411662306a36Sopenharmony_ci				if (depth > 0)
411762306a36Sopenharmony_ci					sdev_printk(KERN_INFO, sdev,
411862306a36Sopenharmony_ci					"Queue depth reduced to (%d)\n",
411962306a36Sopenharmony_ci					   depth);
412062306a36Sopenharmony_ci				else if (depth < 0)
412162306a36Sopenharmony_ci					sdev_printk(KERN_INFO, sdev,
412262306a36Sopenharmony_ci					"Tagged Command Queueing is being "
412362306a36Sopenharmony_ci					"disabled\n");
412462306a36Sopenharmony_ci				else if (depth == 0)
412562306a36Sopenharmony_ci					sdev_printk(KERN_DEBUG, sdev,
412662306a36Sopenharmony_ci					"Queue depth not changed yet\n");
412762306a36Sopenharmony_ci			}
412862306a36Sopenharmony_ci		}
412962306a36Sopenharmony_ci	}
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci	mptsas_free_fw_event(ioc, fw_event);
413262306a36Sopenharmony_ci}
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci
413562306a36Sopenharmony_cistatic struct mptsas_phyinfo *
413662306a36Sopenharmony_cimptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
413762306a36Sopenharmony_ci{
413862306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
413962306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info = NULL;
414062306a36Sopenharmony_ci	int i;
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
414362306a36Sopenharmony_ci	list_for_each_entry(port_info, &ioc->sas_topology, list) {
414462306a36Sopenharmony_ci		for (i = 0; i < port_info->num_phys; i++) {
414562306a36Sopenharmony_ci			if (!mptsas_is_end_device(
414662306a36Sopenharmony_ci				&port_info->phy_info[i].attached))
414762306a36Sopenharmony_ci				continue;
414862306a36Sopenharmony_ci			if (port_info->phy_info[i].attached.sas_address
414962306a36Sopenharmony_ci			    != sas_address)
415062306a36Sopenharmony_ci				continue;
415162306a36Sopenharmony_ci			phy_info = &port_info->phy_info[i];
415262306a36Sopenharmony_ci			break;
415362306a36Sopenharmony_ci		}
415462306a36Sopenharmony_ci	}
415562306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
415662306a36Sopenharmony_ci	return phy_info;
415762306a36Sopenharmony_ci}
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci/**
416062306a36Sopenharmony_ci *	mptsas_find_phyinfo_by_phys_disk_num - find phyinfo for the
416162306a36Sopenharmony_ci *	  specified @phys_disk_num
416262306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
416362306a36Sopenharmony_ci *	@phys_disk_num: (hot plug) physical disk number (for RAID support)
416462306a36Sopenharmony_ci *	@channel: channel number
416562306a36Sopenharmony_ci *	@id: Logical Target ID
416662306a36Sopenharmony_ci *
416762306a36Sopenharmony_ci **/
416862306a36Sopenharmony_cistatic struct mptsas_phyinfo *
416962306a36Sopenharmony_cimptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 phys_disk_num,
417062306a36Sopenharmony_ci	u8 channel, u8 id)
417162306a36Sopenharmony_ci{
417262306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info = NULL;
417362306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
417462306a36Sopenharmony_ci	RaidPhysDiskPage1_t *phys_disk = NULL;
417562306a36Sopenharmony_ci	int num_paths;
417662306a36Sopenharmony_ci	u64 sas_address = 0;
417762306a36Sopenharmony_ci	int i;
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci	phy_info = NULL;
418062306a36Sopenharmony_ci	if (!ioc->raid_data.pIocPg3)
418162306a36Sopenharmony_ci		return NULL;
418262306a36Sopenharmony_ci	/* dual port support */
418362306a36Sopenharmony_ci	num_paths = mpt_raid_phys_disk_get_num_paths(ioc, phys_disk_num);
418462306a36Sopenharmony_ci	if (!num_paths)
418562306a36Sopenharmony_ci		goto out;
418662306a36Sopenharmony_ci	phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
418762306a36Sopenharmony_ci	   (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
418862306a36Sopenharmony_ci	if (!phys_disk)
418962306a36Sopenharmony_ci		goto out;
419062306a36Sopenharmony_ci	mpt_raid_phys_disk_pg1(ioc, phys_disk_num, phys_disk);
419162306a36Sopenharmony_ci	for (i = 0; i < num_paths; i++) {
419262306a36Sopenharmony_ci		if ((phys_disk->Path[i].Flags & 1) != 0)
419362306a36Sopenharmony_ci			/* entry no longer valid */
419462306a36Sopenharmony_ci			continue;
419562306a36Sopenharmony_ci		if ((id == phys_disk->Path[i].PhysDiskID) &&
419662306a36Sopenharmony_ci		    (channel == phys_disk->Path[i].PhysDiskBus)) {
419762306a36Sopenharmony_ci			memcpy(&sas_address, &phys_disk->Path[i].WWID,
419862306a36Sopenharmony_ci				sizeof(u64));
419962306a36Sopenharmony_ci			phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
420062306a36Sopenharmony_ci					sas_address);
420162306a36Sopenharmony_ci			goto out;
420262306a36Sopenharmony_ci		}
420362306a36Sopenharmony_ci	}
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci out:
420662306a36Sopenharmony_ci	kfree(phys_disk);
420762306a36Sopenharmony_ci	if (phy_info)
420862306a36Sopenharmony_ci		return phy_info;
420962306a36Sopenharmony_ci
421062306a36Sopenharmony_ci	/*
421162306a36Sopenharmony_ci	 * Extra code to handle RAID0 case, where the sas_address is not updated
421262306a36Sopenharmony_ci	 * in phys_disk_page_1 when hotswapped
421362306a36Sopenharmony_ci	 */
421462306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
421562306a36Sopenharmony_ci	list_for_each_entry(port_info, &ioc->sas_topology, list) {
421662306a36Sopenharmony_ci		for (i = 0; i < port_info->num_phys && !phy_info; i++) {
421762306a36Sopenharmony_ci			if (!mptsas_is_end_device(
421862306a36Sopenharmony_ci				&port_info->phy_info[i].attached))
421962306a36Sopenharmony_ci				continue;
422062306a36Sopenharmony_ci			if (port_info->phy_info[i].attached.phys_disk_num == ~0)
422162306a36Sopenharmony_ci				continue;
422262306a36Sopenharmony_ci			if ((port_info->phy_info[i].attached.phys_disk_num ==
422362306a36Sopenharmony_ci			    phys_disk_num) &&
422462306a36Sopenharmony_ci			    (port_info->phy_info[i].attached.id == id) &&
422562306a36Sopenharmony_ci			    (port_info->phy_info[i].attached.channel ==
422662306a36Sopenharmony_ci			     channel))
422762306a36Sopenharmony_ci				phy_info = &port_info->phy_info[i];
422862306a36Sopenharmony_ci		}
422962306a36Sopenharmony_ci	}
423062306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
423162306a36Sopenharmony_ci	return phy_info;
423262306a36Sopenharmony_ci}
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_cistatic void
423562306a36Sopenharmony_cimptsas_reprobe_lun(struct scsi_device *sdev, void *data)
423662306a36Sopenharmony_ci{
423762306a36Sopenharmony_ci	int rc;
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	sdev->no_uld_attach = data ? 1 : 0;
424062306a36Sopenharmony_ci	rc = scsi_device_reprobe(sdev);
424162306a36Sopenharmony_ci}
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_cistatic void
424462306a36Sopenharmony_cimptsas_reprobe_target(struct scsi_target *starget, int uld_attach)
424562306a36Sopenharmony_ci{
424662306a36Sopenharmony_ci	starget_for_each_device(starget, uld_attach ? (void *)1 : NULL,
424762306a36Sopenharmony_ci			mptsas_reprobe_lun);
424862306a36Sopenharmony_ci}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_cistatic void
425162306a36Sopenharmony_cimptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
425262306a36Sopenharmony_ci{
425362306a36Sopenharmony_ci	CONFIGPARMS			cfg;
425462306a36Sopenharmony_ci	ConfigPageHeader_t		hdr;
425562306a36Sopenharmony_ci	dma_addr_t			dma_handle;
425662306a36Sopenharmony_ci	pRaidVolumePage0_t		buffer = NULL;
425762306a36Sopenharmony_ci	RaidPhysDiskPage0_t 		phys_disk;
425862306a36Sopenharmony_ci	int				i;
425962306a36Sopenharmony_ci	struct mptsas_phyinfo	*phy_info;
426062306a36Sopenharmony_ci	struct mptsas_devinfo		sas_device;
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	memset(&cfg, 0 , sizeof(CONFIGPARMS));
426362306a36Sopenharmony_ci	memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
426462306a36Sopenharmony_ci	hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
426562306a36Sopenharmony_ci	cfg.pageAddr = (channel << 8) + id;
426662306a36Sopenharmony_ci	cfg.cfghdr.hdr = &hdr;
426762306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
426862306a36Sopenharmony_ci	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	if (mpt_config(ioc, &cfg) != 0)
427162306a36Sopenharmony_ci		goto out;
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_ci	if (!hdr.PageLength)
427462306a36Sopenharmony_ci		goto out;
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
427762306a36Sopenharmony_ci				    &dma_handle, GFP_KERNEL);
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci	if (!buffer)
428062306a36Sopenharmony_ci		goto out;
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	cfg.physAddr = dma_handle;
428362306a36Sopenharmony_ci	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	if (mpt_config(ioc, &cfg) != 0)
428662306a36Sopenharmony_ci		goto out;
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_ci	if (!(buffer->VolumeStatus.Flags &
428962306a36Sopenharmony_ci	    MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE))
429062306a36Sopenharmony_ci		goto out;
429162306a36Sopenharmony_ci
429262306a36Sopenharmony_ci	if (!buffer->NumPhysDisks)
429362306a36Sopenharmony_ci		goto out;
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	for (i = 0; i < buffer->NumPhysDisks; i++) {
429662306a36Sopenharmony_ci
429762306a36Sopenharmony_ci		if (mpt_raid_phys_disk_pg0(ioc,
429862306a36Sopenharmony_ci		    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
429962306a36Sopenharmony_ci			continue;
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci		if (mptsas_sas_device_pg0(ioc, &sas_device,
430262306a36Sopenharmony_ci		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
430362306a36Sopenharmony_ci		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
430462306a36Sopenharmony_ci			(phys_disk.PhysDiskBus << 8) +
430562306a36Sopenharmony_ci			phys_disk.PhysDiskID))
430662306a36Sopenharmony_ci			continue;
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci		/* If there is no FW B_T mapping for this device then continue
430962306a36Sopenharmony_ci		 * */
431062306a36Sopenharmony_ci		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
431162306a36Sopenharmony_ci			|| !(sas_device.flags &
431262306a36Sopenharmony_ci			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
431362306a36Sopenharmony_ci			continue;
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
431762306a36Sopenharmony_ci		    sas_device.sas_address);
431862306a36Sopenharmony_ci		mptsas_add_end_device(ioc, phy_info);
431962306a36Sopenharmony_ci	}
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci out:
432262306a36Sopenharmony_ci	if (buffer)
432362306a36Sopenharmony_ci		dma_free_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
432462306a36Sopenharmony_ci				  buffer, dma_handle);
432562306a36Sopenharmony_ci}
432662306a36Sopenharmony_ci/*
432762306a36Sopenharmony_ci * Work queue thread to handle SAS hotplug events
432862306a36Sopenharmony_ci */
432962306a36Sopenharmony_cistatic void
433062306a36Sopenharmony_cimptsas_hotplug_work(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
433162306a36Sopenharmony_ci    struct mptsas_hotplug_event *hot_plug_info)
433262306a36Sopenharmony_ci{
433362306a36Sopenharmony_ci	struct mptsas_phyinfo *phy_info;
433462306a36Sopenharmony_ci	struct scsi_target * starget;
433562306a36Sopenharmony_ci	struct mptsas_devinfo sas_device;
433662306a36Sopenharmony_ci	VirtTarget *vtarget;
433762306a36Sopenharmony_ci	int i;
433862306a36Sopenharmony_ci	struct mptsas_portinfo *port_info;
433962306a36Sopenharmony_ci
434062306a36Sopenharmony_ci	switch (hot_plug_info->event_type) {
434162306a36Sopenharmony_ci
434262306a36Sopenharmony_ci	case MPTSAS_ADD_PHYSDISK:
434362306a36Sopenharmony_ci
434462306a36Sopenharmony_ci		if (!ioc->raid_data.pIocPg2)
434562306a36Sopenharmony_ci			break;
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci		for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
434862306a36Sopenharmony_ci			if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID ==
434962306a36Sopenharmony_ci			    hot_plug_info->id) {
435062306a36Sopenharmony_ci				printk(MYIOC_s_WARN_FMT "firmware bug: unable "
435162306a36Sopenharmony_ci				    "to add hidden disk - target_id matches "
435262306a36Sopenharmony_ci				    "volume_id\n", ioc->name);
435362306a36Sopenharmony_ci				mptsas_free_fw_event(ioc, fw_event);
435462306a36Sopenharmony_ci				return;
435562306a36Sopenharmony_ci			}
435662306a36Sopenharmony_ci		}
435762306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
435862306a36Sopenharmony_ci		fallthrough;
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	case MPTSAS_ADD_DEVICE:
436162306a36Sopenharmony_ci		memset(&sas_device, 0, sizeof(struct mptsas_devinfo));
436262306a36Sopenharmony_ci		mptsas_sas_device_pg0(ioc, &sas_device,
436362306a36Sopenharmony_ci		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
436462306a36Sopenharmony_ci		    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
436562306a36Sopenharmony_ci		    (hot_plug_info->channel << 8) +
436662306a36Sopenharmony_ci		    hot_plug_info->id);
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci		/* If there is no FW B_T mapping for this device then break
436962306a36Sopenharmony_ci		 * */
437062306a36Sopenharmony_ci		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
437162306a36Sopenharmony_ci			|| !(sas_device.flags &
437262306a36Sopenharmony_ci			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
437362306a36Sopenharmony_ci			break;
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci		if (!sas_device.handle)
437662306a36Sopenharmony_ci			return;
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci		phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
437962306a36Sopenharmony_ci		/* Device hot plug */
438062306a36Sopenharmony_ci		if (!phy_info) {
438162306a36Sopenharmony_ci			devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
438262306a36Sopenharmony_ci				"%s %d HOT PLUG: "
438362306a36Sopenharmony_ci				"parent handle of device %x\n", ioc->name,
438462306a36Sopenharmony_ci				__func__, __LINE__, sas_device.handle_parent));
438562306a36Sopenharmony_ci			port_info = mptsas_find_portinfo_by_handle(ioc,
438662306a36Sopenharmony_ci				sas_device.handle_parent);
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci			if (port_info == ioc->hba_port_info)
438962306a36Sopenharmony_ci				mptsas_probe_hba_phys(ioc);
439062306a36Sopenharmony_ci			else if (port_info)
439162306a36Sopenharmony_ci				mptsas_expander_refresh(ioc, port_info);
439262306a36Sopenharmony_ci			else {
439362306a36Sopenharmony_ci				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
439462306a36Sopenharmony_ci					"%s %d port info is NULL\n",
439562306a36Sopenharmony_ci					ioc->name, __func__, __LINE__));
439662306a36Sopenharmony_ci				break;
439762306a36Sopenharmony_ci			}
439862306a36Sopenharmony_ci			phy_info = mptsas_refreshing_device_handles
439962306a36Sopenharmony_ci				(ioc, &sas_device);
440062306a36Sopenharmony_ci		}
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci		if (!phy_info) {
440362306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
440462306a36Sopenharmony_ci				"%s %d phy info is NULL\n",
440562306a36Sopenharmony_ci				ioc->name, __func__, __LINE__));
440662306a36Sopenharmony_ci			break;
440762306a36Sopenharmony_ci		}
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci		if (mptsas_get_rphy(phy_info))
441062306a36Sopenharmony_ci			break;
441162306a36Sopenharmony_ci
441262306a36Sopenharmony_ci		mptsas_add_end_device(ioc, phy_info);
441362306a36Sopenharmony_ci		break;
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci	case MPTSAS_DEL_DEVICE:
441662306a36Sopenharmony_ci		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
441762306a36Sopenharmony_ci		    hot_plug_info->sas_address);
441862306a36Sopenharmony_ci		mptsas_del_end_device(ioc, phy_info);
441962306a36Sopenharmony_ci		break;
442062306a36Sopenharmony_ci
442162306a36Sopenharmony_ci	case MPTSAS_DEL_PHYSDISK:
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
442462306a36Sopenharmony_ci
442562306a36Sopenharmony_ci		phy_info = mptsas_find_phyinfo_by_phys_disk_num(
442662306a36Sopenharmony_ci				ioc, hot_plug_info->phys_disk_num,
442762306a36Sopenharmony_ci				hot_plug_info->channel,
442862306a36Sopenharmony_ci				hot_plug_info->id);
442962306a36Sopenharmony_ci		mptsas_del_end_device(ioc, phy_info);
443062306a36Sopenharmony_ci		break;
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci	case MPTSAS_ADD_PHYSDISK_REPROBE:
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci		if (mptsas_sas_device_pg0(ioc, &sas_device,
443562306a36Sopenharmony_ci		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
443662306a36Sopenharmony_ci		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
443762306a36Sopenharmony_ci		    (hot_plug_info->channel << 8) + hot_plug_info->id)) {
443862306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
443962306a36Sopenharmony_ci			"%s: fw_id=%d exit at line=%d\n", ioc->name,
444062306a36Sopenharmony_ci				 __func__, hot_plug_info->id, __LINE__));
444162306a36Sopenharmony_ci			break;
444262306a36Sopenharmony_ci		}
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci		/* If there is no FW B_T mapping for this device then break
444562306a36Sopenharmony_ci		 * */
444662306a36Sopenharmony_ci		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
444762306a36Sopenharmony_ci			|| !(sas_device.flags &
444862306a36Sopenharmony_ci			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
444962306a36Sopenharmony_ci			break;
445062306a36Sopenharmony_ci
445162306a36Sopenharmony_ci		phy_info = mptsas_find_phyinfo_by_sas_address(
445262306a36Sopenharmony_ci		    ioc, sas_device.sas_address);
445362306a36Sopenharmony_ci
445462306a36Sopenharmony_ci		if (!phy_info) {
445562306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
445662306a36Sopenharmony_ci				"%s: fw_id=%d exit at line=%d\n", ioc->name,
445762306a36Sopenharmony_ci				 __func__, hot_plug_info->id, __LINE__));
445862306a36Sopenharmony_ci			break;
445962306a36Sopenharmony_ci		}
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_ci		starget = mptsas_get_starget(phy_info);
446262306a36Sopenharmony_ci		if (!starget) {
446362306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
446462306a36Sopenharmony_ci				"%s: fw_id=%d exit at line=%d\n", ioc->name,
446562306a36Sopenharmony_ci				 __func__, hot_plug_info->id, __LINE__));
446662306a36Sopenharmony_ci			break;
446762306a36Sopenharmony_ci		}
446862306a36Sopenharmony_ci
446962306a36Sopenharmony_ci		vtarget = starget->hostdata;
447062306a36Sopenharmony_ci		if (!vtarget) {
447162306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
447262306a36Sopenharmony_ci				"%s: fw_id=%d exit at line=%d\n", ioc->name,
447362306a36Sopenharmony_ci				 __func__, hot_plug_info->id, __LINE__));
447462306a36Sopenharmony_ci			break;
447562306a36Sopenharmony_ci		}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci		starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Hidding: "
448062306a36Sopenharmony_ci		    "fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
448162306a36Sopenharmony_ci		    ioc->name, hot_plug_info->channel, hot_plug_info->id,
448262306a36Sopenharmony_ci		    hot_plug_info->phys_disk_num, (unsigned long long)
448362306a36Sopenharmony_ci		    sas_device.sas_address);
448462306a36Sopenharmony_ci
448562306a36Sopenharmony_ci		vtarget->id = hot_plug_info->phys_disk_num;
448662306a36Sopenharmony_ci		vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
448762306a36Sopenharmony_ci		phy_info->attached.phys_disk_num = hot_plug_info->phys_disk_num;
448862306a36Sopenharmony_ci		mptsas_reprobe_target(starget, 1);
448962306a36Sopenharmony_ci		break;
449062306a36Sopenharmony_ci
449162306a36Sopenharmony_ci	case MPTSAS_DEL_PHYSDISK_REPROBE:
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci		if (mptsas_sas_device_pg0(ioc, &sas_device,
449462306a36Sopenharmony_ci		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
449562306a36Sopenharmony_ci		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
449662306a36Sopenharmony_ci			(hot_plug_info->channel << 8) + hot_plug_info->id)) {
449762306a36Sopenharmony_ci				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
449862306a36Sopenharmony_ci				    "%s: fw_id=%d exit at line=%d\n",
449962306a36Sopenharmony_ci				    ioc->name, __func__,
450062306a36Sopenharmony_ci				    hot_plug_info->id, __LINE__));
450162306a36Sopenharmony_ci			break;
450262306a36Sopenharmony_ci		}
450362306a36Sopenharmony_ci
450462306a36Sopenharmony_ci		/* If there is no FW B_T mapping for this device then break
450562306a36Sopenharmony_ci		 * */
450662306a36Sopenharmony_ci		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
450762306a36Sopenharmony_ci			|| !(sas_device.flags &
450862306a36Sopenharmony_ci			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
450962306a36Sopenharmony_ci			break;
451062306a36Sopenharmony_ci
451162306a36Sopenharmony_ci		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
451262306a36Sopenharmony_ci				sas_device.sas_address);
451362306a36Sopenharmony_ci		if (!phy_info) {
451462306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
451562306a36Sopenharmony_ci			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
451662306a36Sopenharmony_ci			 __func__, hot_plug_info->id, __LINE__));
451762306a36Sopenharmony_ci			break;
451862306a36Sopenharmony_ci		}
451962306a36Sopenharmony_ci
452062306a36Sopenharmony_ci		starget = mptsas_get_starget(phy_info);
452162306a36Sopenharmony_ci		if (!starget) {
452262306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
452362306a36Sopenharmony_ci			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
452462306a36Sopenharmony_ci			 __func__, hot_plug_info->id, __LINE__));
452562306a36Sopenharmony_ci			break;
452662306a36Sopenharmony_ci		}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci		vtarget = starget->hostdata;
452962306a36Sopenharmony_ci		if (!vtarget) {
453062306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
453162306a36Sopenharmony_ci			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
453262306a36Sopenharmony_ci			 __func__, hot_plug_info->id, __LINE__));
453362306a36Sopenharmony_ci			break;
453462306a36Sopenharmony_ci		}
453562306a36Sopenharmony_ci
453662306a36Sopenharmony_ci		if (!(vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)) {
453762306a36Sopenharmony_ci			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
453862306a36Sopenharmony_ci			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
453962306a36Sopenharmony_ci			 __func__, hot_plug_info->id, __LINE__));
454062306a36Sopenharmony_ci			break;
454162306a36Sopenharmony_ci		}
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci		starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Exposing:"
454662306a36Sopenharmony_ci		    " fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
454762306a36Sopenharmony_ci		    ioc->name, hot_plug_info->channel, hot_plug_info->id,
454862306a36Sopenharmony_ci		    hot_plug_info->phys_disk_num, (unsigned long long)
454962306a36Sopenharmony_ci		    sas_device.sas_address);
455062306a36Sopenharmony_ci
455162306a36Sopenharmony_ci		vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
455262306a36Sopenharmony_ci		vtarget->id = hot_plug_info->id;
455362306a36Sopenharmony_ci		phy_info->attached.phys_disk_num = ~0;
455462306a36Sopenharmony_ci		mptsas_reprobe_target(starget, 0);
455562306a36Sopenharmony_ci		mptsas_add_device_component_by_fw(ioc,
455662306a36Sopenharmony_ci		    hot_plug_info->channel, hot_plug_info->id);
455762306a36Sopenharmony_ci		break;
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_ci	case MPTSAS_ADD_RAID:
456062306a36Sopenharmony_ci
456162306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
456262306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
456362306a36Sopenharmony_ci		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
456462306a36Sopenharmony_ci		    hot_plug_info->id);
456562306a36Sopenharmony_ci		scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
456662306a36Sopenharmony_ci		    hot_plug_info->id, 0);
456762306a36Sopenharmony_ci		break;
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci	case MPTSAS_DEL_RAID:
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
457262306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
457362306a36Sopenharmony_ci		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
457462306a36Sopenharmony_ci		    hot_plug_info->id);
457562306a36Sopenharmony_ci		scsi_remove_device(hot_plug_info->sdev);
457662306a36Sopenharmony_ci		scsi_device_put(hot_plug_info->sdev);
457762306a36Sopenharmony_ci		break;
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci	case MPTSAS_ADD_INACTIVE_VOLUME:
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci		mpt_findImVolumes(ioc);
458262306a36Sopenharmony_ci		mptsas_adding_inactive_raid_components(ioc,
458362306a36Sopenharmony_ci		    hot_plug_info->channel, hot_plug_info->id);
458462306a36Sopenharmony_ci		break;
458562306a36Sopenharmony_ci
458662306a36Sopenharmony_ci	default:
458762306a36Sopenharmony_ci		break;
458862306a36Sopenharmony_ci	}
458962306a36Sopenharmony_ci
459062306a36Sopenharmony_ci	mptsas_free_fw_event(ioc, fw_event);
459162306a36Sopenharmony_ci}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_cistatic void
459462306a36Sopenharmony_cimptsas_send_sas_event(struct fw_event_work *fw_event)
459562306a36Sopenharmony_ci{
459662306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
459762306a36Sopenharmony_ci	struct mptsas_hotplug_event hot_plug_info;
459862306a36Sopenharmony_ci	EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
459962306a36Sopenharmony_ci	u32 device_info;
460062306a36Sopenharmony_ci	u64 sas_address;
460162306a36Sopenharmony_ci
460262306a36Sopenharmony_ci	ioc = fw_event->ioc;
460362306a36Sopenharmony_ci	sas_event_data = (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)
460462306a36Sopenharmony_ci	    fw_event->event_data;
460562306a36Sopenharmony_ci	device_info = le32_to_cpu(sas_event_data->DeviceInfo);
460662306a36Sopenharmony_ci
460762306a36Sopenharmony_ci	if ((device_info &
460862306a36Sopenharmony_ci		(MPI_SAS_DEVICE_INFO_SSP_TARGET |
460962306a36Sopenharmony_ci		MPI_SAS_DEVICE_INFO_STP_TARGET |
461062306a36Sopenharmony_ci		MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0) {
461162306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
461262306a36Sopenharmony_ci		return;
461362306a36Sopenharmony_ci	}
461462306a36Sopenharmony_ci
461562306a36Sopenharmony_ci	if (sas_event_data->ReasonCode ==
461662306a36Sopenharmony_ci		MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED) {
461762306a36Sopenharmony_ci		mptbase_sas_persist_operation(ioc,
461862306a36Sopenharmony_ci		MPI_SAS_OP_CLEAR_NOT_PRESENT);
461962306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
462062306a36Sopenharmony_ci		return;
462162306a36Sopenharmony_ci	}
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci	switch (sas_event_data->ReasonCode) {
462462306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING:
462562306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
462662306a36Sopenharmony_ci		memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
462762306a36Sopenharmony_ci		hot_plug_info.handle = le16_to_cpu(sas_event_data->DevHandle);
462862306a36Sopenharmony_ci		hot_plug_info.channel = sas_event_data->Bus;
462962306a36Sopenharmony_ci		hot_plug_info.id = sas_event_data->TargetID;
463062306a36Sopenharmony_ci		hot_plug_info.phy_id = sas_event_data->PhyNum;
463162306a36Sopenharmony_ci		memcpy(&sas_address, &sas_event_data->SASAddress,
463262306a36Sopenharmony_ci		    sizeof(u64));
463362306a36Sopenharmony_ci		hot_plug_info.sas_address = le64_to_cpu(sas_address);
463462306a36Sopenharmony_ci		hot_plug_info.device_info = device_info;
463562306a36Sopenharmony_ci		if (sas_event_data->ReasonCode &
463662306a36Sopenharmony_ci		    MPI_EVENT_SAS_DEV_STAT_RC_ADDED)
463762306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_ADD_DEVICE;
463862306a36Sopenharmony_ci		else
463962306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_DEL_DEVICE;
464062306a36Sopenharmony_ci		mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
464162306a36Sopenharmony_ci		break;
464262306a36Sopenharmony_ci
464362306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED:
464462306a36Sopenharmony_ci		mptbase_sas_persist_operation(ioc,
464562306a36Sopenharmony_ci		    MPI_SAS_OP_CLEAR_NOT_PRESENT);
464662306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
464762306a36Sopenharmony_ci		break;
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
465062306a36Sopenharmony_ci	/* TODO */
465162306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
465262306a36Sopenharmony_ci	/* TODO */
465362306a36Sopenharmony_ci	default:
465462306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
465562306a36Sopenharmony_ci		break;
465662306a36Sopenharmony_ci	}
465762306a36Sopenharmony_ci}
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_cistatic void
466062306a36Sopenharmony_cimptsas_send_raid_event(struct fw_event_work *fw_event)
466162306a36Sopenharmony_ci{
466262306a36Sopenharmony_ci	MPT_ADAPTER *ioc;
466362306a36Sopenharmony_ci	EVENT_DATA_RAID *raid_event_data;
466462306a36Sopenharmony_ci	struct mptsas_hotplug_event hot_plug_info;
466562306a36Sopenharmony_ci	int status;
466662306a36Sopenharmony_ci	int state;
466762306a36Sopenharmony_ci	struct scsi_device *sdev = NULL;
466862306a36Sopenharmony_ci	VirtDevice *vdevice = NULL;
466962306a36Sopenharmony_ci	RaidPhysDiskPage0_t phys_disk;
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	ioc = fw_event->ioc;
467262306a36Sopenharmony_ci	raid_event_data = (EVENT_DATA_RAID *)fw_event->event_data;
467362306a36Sopenharmony_ci	status = le32_to_cpu(raid_event_data->SettingsStatus);
467462306a36Sopenharmony_ci	state = (status >> 8) & 0xff;
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
467762306a36Sopenharmony_ci	hot_plug_info.id = raid_event_data->VolumeID;
467862306a36Sopenharmony_ci	hot_plug_info.channel = raid_event_data->VolumeBus;
467962306a36Sopenharmony_ci	hot_plug_info.phys_disk_num = raid_event_data->PhysDiskNum;
468062306a36Sopenharmony_ci
468162306a36Sopenharmony_ci	if (raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_DELETED ||
468262306a36Sopenharmony_ci	    raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_CREATED ||
468362306a36Sopenharmony_ci	    raid_event_data->ReasonCode ==
468462306a36Sopenharmony_ci	    MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED) {
468562306a36Sopenharmony_ci		sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
468662306a36Sopenharmony_ci		    hot_plug_info.id, 0);
468762306a36Sopenharmony_ci		hot_plug_info.sdev = sdev;
468862306a36Sopenharmony_ci		if (sdev)
468962306a36Sopenharmony_ci			vdevice = sdev->hostdata;
469062306a36Sopenharmony_ci	}
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
469362306a36Sopenharmony_ci	    "ReasonCode=%02x\n", ioc->name, __func__,
469462306a36Sopenharmony_ci	    raid_event_data->ReasonCode));
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	switch (raid_event_data->ReasonCode) {
469762306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
469862306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK_REPROBE;
469962306a36Sopenharmony_ci		break;
470062306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_PHYSDISK_CREATED:
470162306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK_REPROBE;
470262306a36Sopenharmony_ci		break;
470362306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED:
470462306a36Sopenharmony_ci		switch (state) {
470562306a36Sopenharmony_ci		case MPI_PD_STATE_ONLINE:
470662306a36Sopenharmony_ci		case MPI_PD_STATE_NOT_COMPATIBLE:
470762306a36Sopenharmony_ci			mpt_raid_phys_disk_pg0(ioc,
470862306a36Sopenharmony_ci			    raid_event_data->PhysDiskNum, &phys_disk);
470962306a36Sopenharmony_ci			hot_plug_info.id = phys_disk.PhysDiskID;
471062306a36Sopenharmony_ci			hot_plug_info.channel = phys_disk.PhysDiskBus;
471162306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
471262306a36Sopenharmony_ci			break;
471362306a36Sopenharmony_ci		case MPI_PD_STATE_FAILED:
471462306a36Sopenharmony_ci		case MPI_PD_STATE_MISSING:
471562306a36Sopenharmony_ci		case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST:
471662306a36Sopenharmony_ci		case MPI_PD_STATE_FAILED_AT_HOST_REQUEST:
471762306a36Sopenharmony_ci		case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON:
471862306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
471962306a36Sopenharmony_ci			break;
472062306a36Sopenharmony_ci		default:
472162306a36Sopenharmony_ci			break;
472262306a36Sopenharmony_ci		}
472362306a36Sopenharmony_ci		break;
472462306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_VOLUME_DELETED:
472562306a36Sopenharmony_ci		if (!sdev)
472662306a36Sopenharmony_ci			break;
472762306a36Sopenharmony_ci		vdevice->vtarget->deleted = 1; /* block IO */
472862306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_DEL_RAID;
472962306a36Sopenharmony_ci		break;
473062306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_VOLUME_CREATED:
473162306a36Sopenharmony_ci		if (sdev) {
473262306a36Sopenharmony_ci			scsi_device_put(sdev);
473362306a36Sopenharmony_ci			break;
473462306a36Sopenharmony_ci		}
473562306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_ADD_RAID;
473662306a36Sopenharmony_ci		break;
473762306a36Sopenharmony_ci	case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED:
473862306a36Sopenharmony_ci		if (!(status & MPI_RAIDVOL0_STATUS_FLAG_ENABLED)) {
473962306a36Sopenharmony_ci			if (!sdev)
474062306a36Sopenharmony_ci				break;
474162306a36Sopenharmony_ci			vdevice->vtarget->deleted = 1; /* block IO */
474262306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_DEL_RAID;
474362306a36Sopenharmony_ci			break;
474462306a36Sopenharmony_ci		}
474562306a36Sopenharmony_ci		switch (state) {
474662306a36Sopenharmony_ci		case MPI_RAIDVOL0_STATUS_STATE_FAILED:
474762306a36Sopenharmony_ci		case MPI_RAIDVOL0_STATUS_STATE_MISSING:
474862306a36Sopenharmony_ci			if (!sdev)
474962306a36Sopenharmony_ci				break;
475062306a36Sopenharmony_ci			vdevice->vtarget->deleted = 1; /* block IO */
475162306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_DEL_RAID;
475262306a36Sopenharmony_ci			break;
475362306a36Sopenharmony_ci		case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL:
475462306a36Sopenharmony_ci		case MPI_RAIDVOL0_STATUS_STATE_DEGRADED:
475562306a36Sopenharmony_ci			if (sdev) {
475662306a36Sopenharmony_ci				scsi_device_put(sdev);
475762306a36Sopenharmony_ci				break;
475862306a36Sopenharmony_ci			}
475962306a36Sopenharmony_ci			hot_plug_info.event_type = MPTSAS_ADD_RAID;
476062306a36Sopenharmony_ci			break;
476162306a36Sopenharmony_ci		default:
476262306a36Sopenharmony_ci			break;
476362306a36Sopenharmony_ci		}
476462306a36Sopenharmony_ci		break;
476562306a36Sopenharmony_ci	default:
476662306a36Sopenharmony_ci		break;
476762306a36Sopenharmony_ci	}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	if (hot_plug_info.event_type != MPTSAS_IGNORE_EVENT)
477062306a36Sopenharmony_ci		mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
477162306a36Sopenharmony_ci	else
477262306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
477362306a36Sopenharmony_ci}
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci/**
477662306a36Sopenharmony_ci *	mptsas_issue_tm - send mptsas internal tm request
477762306a36Sopenharmony_ci *	@ioc: Pointer to MPT_ADAPTER structure
477862306a36Sopenharmony_ci *	@type: Task Management type
477962306a36Sopenharmony_ci *	@channel: channel number for task management
478062306a36Sopenharmony_ci *	@id: Logical Target ID for reset (if appropriate)
478162306a36Sopenharmony_ci *	@lun: Logical unit for reset (if appropriate)
478262306a36Sopenharmony_ci *	@task_context: Context for the task to be aborted
478362306a36Sopenharmony_ci *	@timeout: timeout for task management control
478462306a36Sopenharmony_ci *	@issue_reset: set to 1 on return if reset is needed, else 0
478562306a36Sopenharmony_ci *
478662306a36Sopenharmony_ci *	Return: 0 on success or -1 on failure.
478762306a36Sopenharmony_ci *
478862306a36Sopenharmony_ci */
478962306a36Sopenharmony_cistatic int
479062306a36Sopenharmony_cimptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun,
479162306a36Sopenharmony_ci	int task_context, ulong timeout, u8 *issue_reset)
479262306a36Sopenharmony_ci{
479362306a36Sopenharmony_ci	MPT_FRAME_HDR	*mf;
479462306a36Sopenharmony_ci	SCSITaskMgmt_t	*pScsiTm;
479562306a36Sopenharmony_ci	int		 retval;
479662306a36Sopenharmony_ci	unsigned long	 timeleft;
479762306a36Sopenharmony_ci
479862306a36Sopenharmony_ci	*issue_reset = 0;
479962306a36Sopenharmony_ci	mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
480062306a36Sopenharmony_ci	if (mf == NULL) {
480162306a36Sopenharmony_ci		retval = -1; /* return failure */
480262306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt request: no "
480362306a36Sopenharmony_ci		    "msg frames!!\n", ioc->name));
480462306a36Sopenharmony_ci		goto out;
480562306a36Sopenharmony_ci	}
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request: mr = %p, "
480862306a36Sopenharmony_ci	    "task_type = 0x%02X,\n\t timeout = %ld, fw_channel = %d, "
480962306a36Sopenharmony_ci	    "fw_id = %d, lun = %lld,\n\t task_context = 0x%x\n", ioc->name, mf,
481062306a36Sopenharmony_ci	     type, timeout, channel, id, (unsigned long long)lun,
481162306a36Sopenharmony_ci	     task_context));
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci	pScsiTm = (SCSITaskMgmt_t *) mf;
481462306a36Sopenharmony_ci	memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
481562306a36Sopenharmony_ci	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
481662306a36Sopenharmony_ci	pScsiTm->TaskType = type;
481762306a36Sopenharmony_ci	pScsiTm->MsgFlags = 0;
481862306a36Sopenharmony_ci	pScsiTm->TargetID = id;
481962306a36Sopenharmony_ci	pScsiTm->Bus = channel;
482062306a36Sopenharmony_ci	pScsiTm->ChainOffset = 0;
482162306a36Sopenharmony_ci	pScsiTm->Reserved = 0;
482262306a36Sopenharmony_ci	pScsiTm->Reserved1 = 0;
482362306a36Sopenharmony_ci	pScsiTm->TaskMsgContext = task_context;
482462306a36Sopenharmony_ci	int_to_scsilun(lun, (struct scsi_lun *)pScsiTm->LUN);
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
482762306a36Sopenharmony_ci	CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
482862306a36Sopenharmony_ci	retval = 0;
482962306a36Sopenharmony_ci	mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	/* Now wait for the command to complete */
483262306a36Sopenharmony_ci	timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
483362306a36Sopenharmony_ci	    timeout*HZ);
483462306a36Sopenharmony_ci	if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
483562306a36Sopenharmony_ci		retval = -1; /* return failure */
483662306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
483762306a36Sopenharmony_ci		    "TaskMgmt request: TIMED OUT!(mr=%p)\n", ioc->name, mf));
483862306a36Sopenharmony_ci		mpt_free_msg_frame(ioc, mf);
483962306a36Sopenharmony_ci		if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
484062306a36Sopenharmony_ci			goto out;
484162306a36Sopenharmony_ci		*issue_reset = 1;
484262306a36Sopenharmony_ci		goto out;
484362306a36Sopenharmony_ci	}
484462306a36Sopenharmony_ci
484562306a36Sopenharmony_ci	if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
484662306a36Sopenharmony_ci		retval = -1; /* return failure */
484762306a36Sopenharmony_ci		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
484862306a36Sopenharmony_ci		    "TaskMgmt request: failed with no reply\n", ioc->name));
484962306a36Sopenharmony_ci		goto out;
485062306a36Sopenharmony_ci	}
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_ci out:
485362306a36Sopenharmony_ci	CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
485462306a36Sopenharmony_ci	return retval;
485562306a36Sopenharmony_ci}
485662306a36Sopenharmony_ci
485762306a36Sopenharmony_ci/**
485862306a36Sopenharmony_ci *	mptsas_broadcast_primitive_work - Handle broadcast primitives
485962306a36Sopenharmony_ci *	@fw_event: work queue payload containing info describing the event
486062306a36Sopenharmony_ci *
486162306a36Sopenharmony_ci *	This will be handled in workqueue context.
486262306a36Sopenharmony_ci */
486362306a36Sopenharmony_cistatic void
486462306a36Sopenharmony_cimptsas_broadcast_primitive_work(struct fw_event_work *fw_event)
486562306a36Sopenharmony_ci{
486662306a36Sopenharmony_ci	MPT_ADAPTER *ioc = fw_event->ioc;
486762306a36Sopenharmony_ci	MPT_FRAME_HDR	*mf;
486862306a36Sopenharmony_ci	VirtDevice	*vdevice;
486962306a36Sopenharmony_ci	int			ii;
487062306a36Sopenharmony_ci	struct scsi_cmnd	*sc;
487162306a36Sopenharmony_ci	SCSITaskMgmtReply_t	*pScsiTmReply;
487262306a36Sopenharmony_ci	u8			issue_reset;
487362306a36Sopenharmony_ci	int			task_context;
487462306a36Sopenharmony_ci	u8			channel, id;
487562306a36Sopenharmony_ci	int			 lun;
487662306a36Sopenharmony_ci	u32			 termination_count;
487762306a36Sopenharmony_ci	u32			 query_count;
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
488062306a36Sopenharmony_ci	    "%s - enter\n", ioc->name, __func__));
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci	mutex_lock(&ioc->taskmgmt_cmds.mutex);
488362306a36Sopenharmony_ci	if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
488462306a36Sopenharmony_ci		mutex_unlock(&ioc->taskmgmt_cmds.mutex);
488562306a36Sopenharmony_ci		mptsas_requeue_fw_event(ioc, fw_event, 1000);
488662306a36Sopenharmony_ci		return;
488762306a36Sopenharmony_ci	}
488862306a36Sopenharmony_ci
488962306a36Sopenharmony_ci	issue_reset = 0;
489062306a36Sopenharmony_ci	termination_count = 0;
489162306a36Sopenharmony_ci	query_count = 0;
489262306a36Sopenharmony_ci	mpt_findImVolumes(ioc);
489362306a36Sopenharmony_ci	pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_ci	for (ii = 0; ii < ioc->req_depth; ii++) {
489662306a36Sopenharmony_ci		if (ioc->fw_events_off)
489762306a36Sopenharmony_ci			goto out;
489862306a36Sopenharmony_ci		sc = mptscsih_get_scsi_lookup(ioc, ii);
489962306a36Sopenharmony_ci		if (!sc)
490062306a36Sopenharmony_ci			continue;
490162306a36Sopenharmony_ci		mf = MPT_INDEX_2_MFPTR(ioc, ii);
490262306a36Sopenharmony_ci		if (!mf)
490362306a36Sopenharmony_ci			continue;
490462306a36Sopenharmony_ci		task_context = mf->u.frame.hwhdr.msgctxu.MsgContext;
490562306a36Sopenharmony_ci		vdevice = sc->device->hostdata;
490662306a36Sopenharmony_ci		if (!vdevice || !vdevice->vtarget)
490762306a36Sopenharmony_ci			continue;
490862306a36Sopenharmony_ci		if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
490962306a36Sopenharmony_ci			continue; /* skip hidden raid components */
491062306a36Sopenharmony_ci		if (vdevice->vtarget->raidVolume)
491162306a36Sopenharmony_ci			continue; /* skip hidden raid components */
491262306a36Sopenharmony_ci		channel = vdevice->vtarget->channel;
491362306a36Sopenharmony_ci		id = vdevice->vtarget->id;
491462306a36Sopenharmony_ci		lun = vdevice->lun;
491562306a36Sopenharmony_ci		if (mptsas_issue_tm(ioc, MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK,
491662306a36Sopenharmony_ci		    channel, id, (u64)lun, task_context, 30, &issue_reset))
491762306a36Sopenharmony_ci			goto out;
491862306a36Sopenharmony_ci		query_count++;
491962306a36Sopenharmony_ci		termination_count +=
492062306a36Sopenharmony_ci		    le32_to_cpu(pScsiTmReply->TerminationCount);
492162306a36Sopenharmony_ci		if ((pScsiTmReply->IOCStatus == MPI_IOCSTATUS_SUCCESS) &&
492262306a36Sopenharmony_ci		    (pScsiTmReply->ResponseCode ==
492362306a36Sopenharmony_ci		    MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
492462306a36Sopenharmony_ci		    pScsiTmReply->ResponseCode ==
492562306a36Sopenharmony_ci		    MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))
492662306a36Sopenharmony_ci			continue;
492762306a36Sopenharmony_ci		if (mptsas_issue_tm(ioc,
492862306a36Sopenharmony_ci		    MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET,
492962306a36Sopenharmony_ci		    channel, id, (u64)lun, 0, 30, &issue_reset))
493062306a36Sopenharmony_ci			goto out;
493162306a36Sopenharmony_ci		termination_count +=
493262306a36Sopenharmony_ci		    le32_to_cpu(pScsiTmReply->TerminationCount);
493362306a36Sopenharmony_ci	}
493462306a36Sopenharmony_ci
493562306a36Sopenharmony_ci out:
493662306a36Sopenharmony_ci	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
493762306a36Sopenharmony_ci	    "%s - exit, query_count = %d termination_count = %d\n",
493862306a36Sopenharmony_ci	    ioc->name, __func__, query_count, termination_count));
493962306a36Sopenharmony_ci
494062306a36Sopenharmony_ci	ioc->broadcast_aen_busy = 0;
494162306a36Sopenharmony_ci	mpt_clear_taskmgmt_in_progress_flag(ioc);
494262306a36Sopenharmony_ci	mutex_unlock(&ioc->taskmgmt_cmds.mutex);
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	if (issue_reset) {
494562306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT
494662306a36Sopenharmony_ci		       "Issuing Reset from %s!! doorbell=0x%08x\n",
494762306a36Sopenharmony_ci		       ioc->name, __func__, mpt_GetIocState(ioc, 0));
494862306a36Sopenharmony_ci		mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
494962306a36Sopenharmony_ci	}
495062306a36Sopenharmony_ci	mptsas_free_fw_event(ioc, fw_event);
495162306a36Sopenharmony_ci}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci/*
495462306a36Sopenharmony_ci * mptsas_send_ir2_event - handle exposing hidden disk when
495562306a36Sopenharmony_ci * an inactive raid volume is added
495662306a36Sopenharmony_ci *
495762306a36Sopenharmony_ci * @ioc: Pointer to MPT_ADAPTER structure
495862306a36Sopenharmony_ci * @ir2_data
495962306a36Sopenharmony_ci *
496062306a36Sopenharmony_ci */
496162306a36Sopenharmony_cistatic void
496262306a36Sopenharmony_cimptsas_send_ir2_event(struct fw_event_work *fw_event)
496362306a36Sopenharmony_ci{
496462306a36Sopenharmony_ci	MPT_ADAPTER	*ioc;
496562306a36Sopenharmony_ci	struct mptsas_hotplug_event hot_plug_info;
496662306a36Sopenharmony_ci	MPI_EVENT_DATA_IR2	*ir2_data;
496762306a36Sopenharmony_ci	u8 reasonCode;
496862306a36Sopenharmony_ci	RaidPhysDiskPage0_t phys_disk;
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	ioc = fw_event->ioc;
497162306a36Sopenharmony_ci	ir2_data = (MPI_EVENT_DATA_IR2 *)fw_event->event_data;
497262306a36Sopenharmony_ci	reasonCode = ir2_data->ReasonCode;
497362306a36Sopenharmony_ci
497462306a36Sopenharmony_ci	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
497562306a36Sopenharmony_ci	    "ReasonCode=%02x\n", ioc->name, __func__, reasonCode));
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci	memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
497862306a36Sopenharmony_ci	hot_plug_info.id = ir2_data->TargetID;
497962306a36Sopenharmony_ci	hot_plug_info.channel = ir2_data->Bus;
498062306a36Sopenharmony_ci	switch (reasonCode) {
498162306a36Sopenharmony_ci	case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
498262306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_ADD_INACTIVE_VOLUME;
498362306a36Sopenharmony_ci		break;
498462306a36Sopenharmony_ci	case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
498562306a36Sopenharmony_ci		hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
498662306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
498762306a36Sopenharmony_ci		break;
498862306a36Sopenharmony_ci	case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
498962306a36Sopenharmony_ci		hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
499062306a36Sopenharmony_ci		mpt_raid_phys_disk_pg0(ioc,
499162306a36Sopenharmony_ci		    ir2_data->PhysDiskNum, &phys_disk);
499262306a36Sopenharmony_ci		hot_plug_info.id = phys_disk.PhysDiskID;
499362306a36Sopenharmony_ci		hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
499462306a36Sopenharmony_ci		break;
499562306a36Sopenharmony_ci	default:
499662306a36Sopenharmony_ci		mptsas_free_fw_event(ioc, fw_event);
499762306a36Sopenharmony_ci		return;
499862306a36Sopenharmony_ci	}
499962306a36Sopenharmony_ci	mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
500062306a36Sopenharmony_ci}
500162306a36Sopenharmony_ci
500262306a36Sopenharmony_cistatic int
500362306a36Sopenharmony_cimptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
500462306a36Sopenharmony_ci{
500562306a36Sopenharmony_ci	u32 event = le32_to_cpu(reply->Event);
500662306a36Sopenharmony_ci	int event_data_sz;
500762306a36Sopenharmony_ci	struct fw_event_work *fw_event;
500862306a36Sopenharmony_ci	unsigned long delay;
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_ci	if (ioc->bus_type != SAS)
501162306a36Sopenharmony_ci		return 0;
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci	/* events turned off due to host reset or driver unloading */
501462306a36Sopenharmony_ci	if (ioc->fw_events_off)
501562306a36Sopenharmony_ci		return 0;
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	delay = msecs_to_jiffies(1);
501862306a36Sopenharmony_ci	switch (event) {
501962306a36Sopenharmony_ci	case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
502062306a36Sopenharmony_ci	{
502162306a36Sopenharmony_ci		EVENT_DATA_SAS_BROADCAST_PRIMITIVE *broadcast_event_data =
502262306a36Sopenharmony_ci		    (EVENT_DATA_SAS_BROADCAST_PRIMITIVE *)reply->Data;
502362306a36Sopenharmony_ci		if (broadcast_event_data->Primitive !=
502462306a36Sopenharmony_ci		    MPI_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
502562306a36Sopenharmony_ci			return 0;
502662306a36Sopenharmony_ci		if (ioc->broadcast_aen_busy)
502762306a36Sopenharmony_ci			return 0;
502862306a36Sopenharmony_ci		ioc->broadcast_aen_busy = 1;
502962306a36Sopenharmony_ci		break;
503062306a36Sopenharmony_ci	}
503162306a36Sopenharmony_ci	case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
503262306a36Sopenharmony_ci	{
503362306a36Sopenharmony_ci		EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data =
503462306a36Sopenharmony_ci		    (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data;
503562306a36Sopenharmony_ci		u16	ioc_stat;
503662306a36Sopenharmony_ci		ioc_stat = le16_to_cpu(reply->IOCStatus);
503762306a36Sopenharmony_ci
503862306a36Sopenharmony_ci		if (sas_event_data->ReasonCode ==
503962306a36Sopenharmony_ci		    MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING) {
504062306a36Sopenharmony_ci			mptsas_target_reset_queue(ioc, sas_event_data);
504162306a36Sopenharmony_ci			return 0;
504262306a36Sopenharmony_ci		}
504362306a36Sopenharmony_ci		if (sas_event_data->ReasonCode ==
504462306a36Sopenharmony_ci			MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
504562306a36Sopenharmony_ci			ioc->device_missing_delay &&
504662306a36Sopenharmony_ci			(ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)) {
504762306a36Sopenharmony_ci			VirtTarget *vtarget = NULL;
504862306a36Sopenharmony_ci			u8		id, channel;
504962306a36Sopenharmony_ci
505062306a36Sopenharmony_ci			id = sas_event_data->TargetID;
505162306a36Sopenharmony_ci			channel = sas_event_data->Bus;
505262306a36Sopenharmony_ci
505362306a36Sopenharmony_ci			vtarget = mptsas_find_vtarget(ioc, channel, id);
505462306a36Sopenharmony_ci			if (vtarget) {
505562306a36Sopenharmony_ci				devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
505662306a36Sopenharmony_ci				    "LogInfo (0x%x) available for "
505762306a36Sopenharmony_ci				   "INTERNAL_DEVICE_RESET"
505862306a36Sopenharmony_ci				   "fw_id %d fw_channel %d\n", ioc->name,
505962306a36Sopenharmony_ci				   le32_to_cpu(reply->IOCLogInfo),
506062306a36Sopenharmony_ci				   id, channel));
506162306a36Sopenharmony_ci				if (vtarget->raidVolume) {
506262306a36Sopenharmony_ci					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
506362306a36Sopenharmony_ci					"Skipping Raid Volume for inDMD\n",
506462306a36Sopenharmony_ci					ioc->name));
506562306a36Sopenharmony_ci				} else {
506662306a36Sopenharmony_ci					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
506762306a36Sopenharmony_ci					"Setting device flag inDMD\n",
506862306a36Sopenharmony_ci					ioc->name));
506962306a36Sopenharmony_ci					vtarget->inDMD = 1;
507062306a36Sopenharmony_ci				}
507162306a36Sopenharmony_ci
507262306a36Sopenharmony_ci			}
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci		}
507562306a36Sopenharmony_ci
507662306a36Sopenharmony_ci		break;
507762306a36Sopenharmony_ci	}
507862306a36Sopenharmony_ci	case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
507962306a36Sopenharmony_ci	{
508062306a36Sopenharmony_ci		MpiEventDataSasExpanderStatusChange_t *expander_data =
508162306a36Sopenharmony_ci		    (MpiEventDataSasExpanderStatusChange_t *)reply->Data;
508262306a36Sopenharmony_ci
508362306a36Sopenharmony_ci		if (ioc->old_sas_discovery_protocal)
508462306a36Sopenharmony_ci			return 0;
508562306a36Sopenharmony_ci
508662306a36Sopenharmony_ci		if (expander_data->ReasonCode ==
508762306a36Sopenharmony_ci		    MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING &&
508862306a36Sopenharmony_ci		    ioc->device_missing_delay)
508962306a36Sopenharmony_ci			delay = HZ * ioc->device_missing_delay;
509062306a36Sopenharmony_ci		break;
509162306a36Sopenharmony_ci	}
509262306a36Sopenharmony_ci	case MPI_EVENT_SAS_DISCOVERY:
509362306a36Sopenharmony_ci	{
509462306a36Sopenharmony_ci		u32 discovery_status;
509562306a36Sopenharmony_ci		EventDataSasDiscovery_t *discovery_data =
509662306a36Sopenharmony_ci		    (EventDataSasDiscovery_t *)reply->Data;
509762306a36Sopenharmony_ci
509862306a36Sopenharmony_ci		discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
509962306a36Sopenharmony_ci		ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
510062306a36Sopenharmony_ci		if (ioc->old_sas_discovery_protocal && !discovery_status)
510162306a36Sopenharmony_ci			mptsas_queue_rescan(ioc);
510262306a36Sopenharmony_ci		return 0;
510362306a36Sopenharmony_ci	}
510462306a36Sopenharmony_ci	case MPI_EVENT_INTEGRATED_RAID:
510562306a36Sopenharmony_ci	case MPI_EVENT_PERSISTENT_TABLE_FULL:
510662306a36Sopenharmony_ci	case MPI_EVENT_IR2:
510762306a36Sopenharmony_ci	case MPI_EVENT_SAS_PHY_LINK_STATUS:
510862306a36Sopenharmony_ci	case MPI_EVENT_QUEUE_FULL:
510962306a36Sopenharmony_ci		break;
511062306a36Sopenharmony_ci	default:
511162306a36Sopenharmony_ci		return 0;
511262306a36Sopenharmony_ci	}
511362306a36Sopenharmony_ci
511462306a36Sopenharmony_ci	event_data_sz = ((reply->MsgLength * 4) -
511562306a36Sopenharmony_ci	    offsetof(EventNotificationReply_t, Data));
511662306a36Sopenharmony_ci	fw_event = kzalloc(sizeof(*fw_event) + event_data_sz, GFP_ATOMIC);
511762306a36Sopenharmony_ci	if (!fw_event) {
511862306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n", ioc->name,
511962306a36Sopenharmony_ci		 __func__, __LINE__);
512062306a36Sopenharmony_ci		return 0;
512162306a36Sopenharmony_ci	}
512262306a36Sopenharmony_ci	memcpy(fw_event->event_data, reply->Data, event_data_sz);
512362306a36Sopenharmony_ci	fw_event->event = event;
512462306a36Sopenharmony_ci	fw_event->ioc = ioc;
512562306a36Sopenharmony_ci	mptsas_add_fw_event(ioc, fw_event, delay);
512662306a36Sopenharmony_ci	return 0;
512762306a36Sopenharmony_ci}
512862306a36Sopenharmony_ci
512962306a36Sopenharmony_ci/* Delete a volume when no longer listed in ioc pg2
513062306a36Sopenharmony_ci */
513162306a36Sopenharmony_cistatic void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id)
513262306a36Sopenharmony_ci{
513362306a36Sopenharmony_ci	struct scsi_device *sdev;
513462306a36Sopenharmony_ci	int i;
513562306a36Sopenharmony_ci
513662306a36Sopenharmony_ci	sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, id, 0);
513762306a36Sopenharmony_ci	if (!sdev)
513862306a36Sopenharmony_ci		return;
513962306a36Sopenharmony_ci	if (!ioc->raid_data.pIocPg2)
514062306a36Sopenharmony_ci		goto out;
514162306a36Sopenharmony_ci	if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
514262306a36Sopenharmony_ci		goto out;
514362306a36Sopenharmony_ci	for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
514462306a36Sopenharmony_ci		if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID == id)
514562306a36Sopenharmony_ci			goto release_sdev;
514662306a36Sopenharmony_ci out:
514762306a36Sopenharmony_ci	printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
514862306a36Sopenharmony_ci	    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL, id);
514962306a36Sopenharmony_ci	scsi_remove_device(sdev);
515062306a36Sopenharmony_ci release_sdev:
515162306a36Sopenharmony_ci	scsi_device_put(sdev);
515262306a36Sopenharmony_ci}
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_cistatic int
515562306a36Sopenharmony_cimptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
515662306a36Sopenharmony_ci{
515762306a36Sopenharmony_ci	struct Scsi_Host	*sh;
515862306a36Sopenharmony_ci	MPT_SCSI_HOST		*hd;
515962306a36Sopenharmony_ci	MPT_ADAPTER 		*ioc;
516062306a36Sopenharmony_ci	unsigned long		 flags;
516162306a36Sopenharmony_ci	int			 ii;
516262306a36Sopenharmony_ci	int			 numSGE = 0;
516362306a36Sopenharmony_ci	int			 scale;
516462306a36Sopenharmony_ci	int			 ioc_cap;
516562306a36Sopenharmony_ci	int			error=0;
516662306a36Sopenharmony_ci	int			r;
516762306a36Sopenharmony_ci
516862306a36Sopenharmony_ci	r = mpt_attach(pdev,id);
516962306a36Sopenharmony_ci	if (r)
517062306a36Sopenharmony_ci		return r;
517162306a36Sopenharmony_ci
517262306a36Sopenharmony_ci	ioc = pci_get_drvdata(pdev);
517362306a36Sopenharmony_ci	mptsas_fw_event_off(ioc);
517462306a36Sopenharmony_ci	ioc->DoneCtx = mptsasDoneCtx;
517562306a36Sopenharmony_ci	ioc->TaskCtx = mptsasTaskCtx;
517662306a36Sopenharmony_ci	ioc->InternalCtx = mptsasInternalCtx;
517762306a36Sopenharmony_ci	ioc->schedule_target_reset = &mptsas_schedule_target_reset;
517862306a36Sopenharmony_ci	ioc->schedule_dead_ioc_flush_running_cmds =
517962306a36Sopenharmony_ci				&mptscsih_flush_running_cmds;
518062306a36Sopenharmony_ci	/*  Added sanity check on readiness of the MPT adapter.
518162306a36Sopenharmony_ci	 */
518262306a36Sopenharmony_ci	if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
518362306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT
518462306a36Sopenharmony_ci		  "Skipping because it's not operational!\n",
518562306a36Sopenharmony_ci		  ioc->name);
518662306a36Sopenharmony_ci		error = -ENODEV;
518762306a36Sopenharmony_ci		goto out_mptsas_probe;
518862306a36Sopenharmony_ci	}
518962306a36Sopenharmony_ci
519062306a36Sopenharmony_ci	if (!ioc->active) {
519162306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
519262306a36Sopenharmony_ci		  ioc->name);
519362306a36Sopenharmony_ci		error = -ENODEV;
519462306a36Sopenharmony_ci		goto out_mptsas_probe;
519562306a36Sopenharmony_ci	}
519662306a36Sopenharmony_ci
519762306a36Sopenharmony_ci	/*  Sanity check - ensure at least 1 port is INITIATOR capable
519862306a36Sopenharmony_ci	 */
519962306a36Sopenharmony_ci	ioc_cap = 0;
520062306a36Sopenharmony_ci	for (ii = 0; ii < ioc->facts.NumberOfPorts; ii++) {
520162306a36Sopenharmony_ci		if (ioc->pfacts[ii].ProtocolFlags &
520262306a36Sopenharmony_ci				MPI_PORTFACTS_PROTOCOL_INITIATOR)
520362306a36Sopenharmony_ci			ioc_cap++;
520462306a36Sopenharmony_ci	}
520562306a36Sopenharmony_ci
520662306a36Sopenharmony_ci	if (!ioc_cap) {
520762306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT
520862306a36Sopenharmony_ci			"Skipping ioc=%p because SCSI Initiator mode "
520962306a36Sopenharmony_ci			"is NOT enabled!\n", ioc->name, ioc);
521062306a36Sopenharmony_ci		return 0;
521162306a36Sopenharmony_ci	}
521262306a36Sopenharmony_ci
521362306a36Sopenharmony_ci	sh = scsi_host_alloc(&mptsas_driver_template, sizeof(MPT_SCSI_HOST));
521462306a36Sopenharmony_ci	if (!sh) {
521562306a36Sopenharmony_ci		printk(MYIOC_s_WARN_FMT
521662306a36Sopenharmony_ci			"Unable to register controller with SCSI subsystem\n",
521762306a36Sopenharmony_ci			ioc->name);
521862306a36Sopenharmony_ci		error = -1;
521962306a36Sopenharmony_ci		goto out_mptsas_probe;
522062306a36Sopenharmony_ci        }
522162306a36Sopenharmony_ci
522262306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->FreeQlock, flags);
522362306a36Sopenharmony_ci
522462306a36Sopenharmony_ci	/* Attach the SCSI Host to the IOC structure
522562306a36Sopenharmony_ci	 */
522662306a36Sopenharmony_ci	ioc->sh = sh;
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci	sh->io_port = 0;
522962306a36Sopenharmony_ci	sh->n_io_port = 0;
523062306a36Sopenharmony_ci	sh->irq = 0;
523162306a36Sopenharmony_ci
523262306a36Sopenharmony_ci	/* set 16 byte cdb's */
523362306a36Sopenharmony_ci	sh->max_cmd_len = 16;
523462306a36Sopenharmony_ci	sh->can_queue = min_t(int, ioc->req_depth - 10, sh->can_queue);
523562306a36Sopenharmony_ci	sh->max_id = -1;
523662306a36Sopenharmony_ci	sh->max_lun = max_lun;
523762306a36Sopenharmony_ci	sh->transportt = mptsas_transport_template;
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci	/* Required entry.
524062306a36Sopenharmony_ci	 */
524162306a36Sopenharmony_ci	sh->unique_id = ioc->id;
524262306a36Sopenharmony_ci
524362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ioc->sas_topology);
524462306a36Sopenharmony_ci	mutex_init(&ioc->sas_topology_mutex);
524562306a36Sopenharmony_ci	mutex_init(&ioc->sas_discovery_mutex);
524662306a36Sopenharmony_ci	mutex_init(&ioc->sas_mgmt.mutex);
524762306a36Sopenharmony_ci	init_completion(&ioc->sas_mgmt.done);
524862306a36Sopenharmony_ci
524962306a36Sopenharmony_ci	/* Verify that we won't exceed the maximum
525062306a36Sopenharmony_ci	 * number of chain buffers
525162306a36Sopenharmony_ci	 * We can optimize:  ZZ = req_sz/sizeof(SGE)
525262306a36Sopenharmony_ci	 * For 32bit SGE's:
525362306a36Sopenharmony_ci	 *  numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
525462306a36Sopenharmony_ci	 *               + (req_sz - 64)/sizeof(SGE)
525562306a36Sopenharmony_ci	 * A slightly different algorithm is required for
525662306a36Sopenharmony_ci	 * 64bit SGEs.
525762306a36Sopenharmony_ci	 */
525862306a36Sopenharmony_ci	scale = ioc->req_sz/ioc->SGE_size;
525962306a36Sopenharmony_ci	if (ioc->sg_addr_size == sizeof(u64)) {
526062306a36Sopenharmony_ci		numSGE = (scale - 1) *
526162306a36Sopenharmony_ci		  (ioc->facts.MaxChainDepth-1) + scale +
526262306a36Sopenharmony_ci		  (ioc->req_sz - 60) / ioc->SGE_size;
526362306a36Sopenharmony_ci	} else {
526462306a36Sopenharmony_ci		numSGE = 1 + (scale - 1) *
526562306a36Sopenharmony_ci		  (ioc->facts.MaxChainDepth-1) + scale +
526662306a36Sopenharmony_ci		  (ioc->req_sz - 64) / ioc->SGE_size;
526762306a36Sopenharmony_ci	}
526862306a36Sopenharmony_ci
526962306a36Sopenharmony_ci	if (numSGE < sh->sg_tablesize) {
527062306a36Sopenharmony_ci		/* Reset this value */
527162306a36Sopenharmony_ci		dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
527262306a36Sopenharmony_ci		  "Resetting sg_tablesize to %d from %d\n",
527362306a36Sopenharmony_ci		  ioc->name, numSGE, sh->sg_tablesize));
527462306a36Sopenharmony_ci		sh->sg_tablesize = numSGE;
527562306a36Sopenharmony_ci	}
527662306a36Sopenharmony_ci
527762306a36Sopenharmony_ci	if (mpt_loadtime_max_sectors) {
527862306a36Sopenharmony_ci		if (mpt_loadtime_max_sectors < 64 ||
527962306a36Sopenharmony_ci			mpt_loadtime_max_sectors > 8192) {
528062306a36Sopenharmony_ci			printk(MYIOC_s_INFO_FMT "Invalid value passed for"
528162306a36Sopenharmony_ci				"mpt_loadtime_max_sectors %d."
528262306a36Sopenharmony_ci				"Range from 64 to 8192\n", ioc->name,
528362306a36Sopenharmony_ci				mpt_loadtime_max_sectors);
528462306a36Sopenharmony_ci		}
528562306a36Sopenharmony_ci		mpt_loadtime_max_sectors &=  0xFFFFFFFE;
528662306a36Sopenharmony_ci		dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
528762306a36Sopenharmony_ci			"Resetting max sector to %d from %d\n",
528862306a36Sopenharmony_ci		  ioc->name, mpt_loadtime_max_sectors, sh->max_sectors));
528962306a36Sopenharmony_ci		sh->max_sectors = mpt_loadtime_max_sectors;
529062306a36Sopenharmony_ci	}
529162306a36Sopenharmony_ci
529262306a36Sopenharmony_ci	hd = shost_priv(sh);
529362306a36Sopenharmony_ci	hd->ioc = ioc;
529462306a36Sopenharmony_ci
529562306a36Sopenharmony_ci	/* SCSI needs scsi_cmnd lookup table!
529662306a36Sopenharmony_ci	 * (with size equal to req_depth*PtrSz!)
529762306a36Sopenharmony_ci	 */
529862306a36Sopenharmony_ci	ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
529962306a36Sopenharmony_ci	if (!ioc->ScsiLookup) {
530062306a36Sopenharmony_ci		error = -ENOMEM;
530162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->FreeQlock, flags);
530262306a36Sopenharmony_ci		goto out_mptsas_probe;
530362306a36Sopenharmony_ci	}
530462306a36Sopenharmony_ci	spin_lock_init(&ioc->scsi_lookup_lock);
530562306a36Sopenharmony_ci
530662306a36Sopenharmony_ci	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
530762306a36Sopenharmony_ci		 ioc->name, ioc->ScsiLookup));
530862306a36Sopenharmony_ci
530962306a36Sopenharmony_ci	ioc->sas_data.ptClear = mpt_pt_clear;
531062306a36Sopenharmony_ci
531162306a36Sopenharmony_ci	hd->last_queue_full = 0;
531262306a36Sopenharmony_ci	INIT_LIST_HEAD(&hd->target_reset_list);
531362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ioc->sas_device_info_list);
531462306a36Sopenharmony_ci	mutex_init(&ioc->sas_device_info_mutex);
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->FreeQlock, flags);
531762306a36Sopenharmony_ci
531862306a36Sopenharmony_ci	if (ioc->sas_data.ptClear==1) {
531962306a36Sopenharmony_ci		mptbase_sas_persist_operation(
532062306a36Sopenharmony_ci		    ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT);
532162306a36Sopenharmony_ci	}
532262306a36Sopenharmony_ci
532362306a36Sopenharmony_ci	error = scsi_add_host(sh, &ioc->pcidev->dev);
532462306a36Sopenharmony_ci	if (error) {
532562306a36Sopenharmony_ci		dprintk(ioc, printk(MYIOC_s_ERR_FMT
532662306a36Sopenharmony_ci		  "scsi_add_host failed\n", ioc->name));
532762306a36Sopenharmony_ci		goto out_mptsas_probe;
532862306a36Sopenharmony_ci	}
532962306a36Sopenharmony_ci
533062306a36Sopenharmony_ci	/* older firmware doesn't support expander events */
533162306a36Sopenharmony_ci	if ((ioc->facts.HeaderVersion >> 8) < 0xE)
533262306a36Sopenharmony_ci		ioc->old_sas_discovery_protocal = 1;
533362306a36Sopenharmony_ci	mptsas_scan_sas_topology(ioc);
533462306a36Sopenharmony_ci	mptsas_fw_event_on(ioc);
533562306a36Sopenharmony_ci	return 0;
533662306a36Sopenharmony_ci
533762306a36Sopenharmony_ci out_mptsas_probe:
533862306a36Sopenharmony_ci
533962306a36Sopenharmony_ci	mptscsih_remove(pdev);
534062306a36Sopenharmony_ci	return error;
534162306a36Sopenharmony_ci}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_cistatic void
534462306a36Sopenharmony_cimptsas_shutdown(struct pci_dev *pdev)
534562306a36Sopenharmony_ci{
534662306a36Sopenharmony_ci	MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
534762306a36Sopenharmony_ci
534862306a36Sopenharmony_ci	mptsas_fw_event_off(ioc);
534962306a36Sopenharmony_ci	mptsas_cleanup_fw_event_q(ioc);
535062306a36Sopenharmony_ci}
535162306a36Sopenharmony_ci
535262306a36Sopenharmony_cistatic void mptsas_remove(struct pci_dev *pdev)
535362306a36Sopenharmony_ci{
535462306a36Sopenharmony_ci	MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
535562306a36Sopenharmony_ci	struct mptsas_portinfo *p, *n;
535662306a36Sopenharmony_ci	int i;
535762306a36Sopenharmony_ci
535862306a36Sopenharmony_ci	if (!ioc->sh) {
535962306a36Sopenharmony_ci		printk(MYIOC_s_INFO_FMT "IOC is in Target mode\n", ioc->name);
536062306a36Sopenharmony_ci		mpt_detach(pdev);
536162306a36Sopenharmony_ci		return;
536262306a36Sopenharmony_ci	}
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci	mptsas_shutdown(pdev);
536562306a36Sopenharmony_ci
536662306a36Sopenharmony_ci	mptsas_del_device_components(ioc);
536762306a36Sopenharmony_ci
536862306a36Sopenharmony_ci	ioc->sas_discovery_ignore_events = 1;
536962306a36Sopenharmony_ci	sas_remove_host(ioc->sh);
537062306a36Sopenharmony_ci
537162306a36Sopenharmony_ci	mutex_lock(&ioc->sas_topology_mutex);
537262306a36Sopenharmony_ci	list_for_each_entry_safe(p, n, &ioc->sas_topology, list) {
537362306a36Sopenharmony_ci		list_del(&p->list);
537462306a36Sopenharmony_ci		for (i = 0 ; i < p->num_phys ; i++)
537562306a36Sopenharmony_ci			mptsas_port_delete(ioc, p->phy_info[i].port_details);
537662306a36Sopenharmony_ci
537762306a36Sopenharmony_ci		kfree(p->phy_info);
537862306a36Sopenharmony_ci		kfree(p);
537962306a36Sopenharmony_ci	}
538062306a36Sopenharmony_ci	mutex_unlock(&ioc->sas_topology_mutex);
538162306a36Sopenharmony_ci	ioc->hba_port_info = NULL;
538262306a36Sopenharmony_ci	mptscsih_remove(pdev);
538362306a36Sopenharmony_ci}
538462306a36Sopenharmony_ci
538562306a36Sopenharmony_cistatic struct pci_device_id mptsas_pci_table[] = {
538662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064,
538762306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
538862306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068,
538962306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
539062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064E,
539162306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
539262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068E,
539362306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
539462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1078,
539562306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
539662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068_820XELP,
539762306a36Sopenharmony_ci		PCI_ANY_ID, PCI_ANY_ID },
539862306a36Sopenharmony_ci	{0}	/* Terminating entry */
539962306a36Sopenharmony_ci};
540062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mptsas_pci_table);
540162306a36Sopenharmony_ci
540262306a36Sopenharmony_ci
540362306a36Sopenharmony_cistatic struct pci_driver mptsas_driver = {
540462306a36Sopenharmony_ci	.name		= "mptsas",
540562306a36Sopenharmony_ci	.id_table	= mptsas_pci_table,
540662306a36Sopenharmony_ci	.probe		= mptsas_probe,
540762306a36Sopenharmony_ci	.remove		= mptsas_remove,
540862306a36Sopenharmony_ci	.shutdown	= mptsas_shutdown,
540962306a36Sopenharmony_ci#ifdef CONFIG_PM
541062306a36Sopenharmony_ci	.suspend	= mptscsih_suspend,
541162306a36Sopenharmony_ci	.resume		= mptscsih_resume,
541262306a36Sopenharmony_ci#endif
541362306a36Sopenharmony_ci};
541462306a36Sopenharmony_ci
541562306a36Sopenharmony_cistatic int __init
541662306a36Sopenharmony_cimptsas_init(void)
541762306a36Sopenharmony_ci{
541862306a36Sopenharmony_ci	int error;
541962306a36Sopenharmony_ci
542062306a36Sopenharmony_ci	show_mptmod_ver(my_NAME, my_VERSION);
542162306a36Sopenharmony_ci
542262306a36Sopenharmony_ci	mptsas_transport_template =
542362306a36Sopenharmony_ci	    sas_attach_transport(&mptsas_transport_functions);
542462306a36Sopenharmony_ci	if (!mptsas_transport_template)
542562306a36Sopenharmony_ci		return -ENODEV;
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci	mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER,
542862306a36Sopenharmony_ci	    "mptscsih_io_done");
542962306a36Sopenharmony_ci	mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER,
543062306a36Sopenharmony_ci	    "mptscsih_taskmgmt_complete");
543162306a36Sopenharmony_ci	mptsasInternalCtx =
543262306a36Sopenharmony_ci		mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER,
543362306a36Sopenharmony_ci		    "mptscsih_scandv_complete");
543462306a36Sopenharmony_ci	mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER,
543562306a36Sopenharmony_ci	    "mptsas_mgmt_done");
543662306a36Sopenharmony_ci	mptsasDeviceResetCtx =
543762306a36Sopenharmony_ci		mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER,
543862306a36Sopenharmony_ci		    "mptsas_taskmgmt_complete");
543962306a36Sopenharmony_ci
544062306a36Sopenharmony_ci	mpt_event_register(mptsasDoneCtx, mptsas_event_process);
544162306a36Sopenharmony_ci	mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset);
544262306a36Sopenharmony_ci
544362306a36Sopenharmony_ci	error = pci_register_driver(&mptsas_driver);
544462306a36Sopenharmony_ci	if (error)
544562306a36Sopenharmony_ci		sas_release_transport(mptsas_transport_template);
544662306a36Sopenharmony_ci
544762306a36Sopenharmony_ci	return error;
544862306a36Sopenharmony_ci}
544962306a36Sopenharmony_ci
545062306a36Sopenharmony_cistatic void __exit
545162306a36Sopenharmony_cimptsas_exit(void)
545262306a36Sopenharmony_ci{
545362306a36Sopenharmony_ci	pci_unregister_driver(&mptsas_driver);
545462306a36Sopenharmony_ci	sas_release_transport(mptsas_transport_template);
545562306a36Sopenharmony_ci
545662306a36Sopenharmony_ci	mpt_reset_deregister(mptsasDoneCtx);
545762306a36Sopenharmony_ci	mpt_event_deregister(mptsasDoneCtx);
545862306a36Sopenharmony_ci
545962306a36Sopenharmony_ci	mpt_deregister(mptsasMgmtCtx);
546062306a36Sopenharmony_ci	mpt_deregister(mptsasInternalCtx);
546162306a36Sopenharmony_ci	mpt_deregister(mptsasTaskCtx);
546262306a36Sopenharmony_ci	mpt_deregister(mptsasDoneCtx);
546362306a36Sopenharmony_ci	mpt_deregister(mptsasDeviceResetCtx);
546462306a36Sopenharmony_ci}
546562306a36Sopenharmony_ci
546662306a36Sopenharmony_cimodule_init(mptsas_init);
546762306a36Sopenharmony_cimodule_exit(mptsas_exit);
5468