xref: /openbmc/linux/drivers/message/fusion/mptsas.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
10c33b27dSChristoph Hellwig /*
20c33b27dSChristoph Hellwig  *  linux/drivers/message/fusion/mptsas.c
3f36789e2SPrakash, Sathya  *      For use with LSI PCI chip/adapter(s)
4f36789e2SPrakash, Sathya  *      running LSI Fusion MPT (Message Passing Technology) firmware.
50c33b27dSChristoph Hellwig  *
6cddc0ab7SPrakash, Sathya  *  Copyright (c) 1999-2008 LSI Corporation
716d20101SEric Moore  *  (mailto:DL-MPTFusionLinux@lsi.com)
80c33b27dSChristoph Hellwig  */
90c33b27dSChristoph Hellwig /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
100c33b27dSChristoph Hellwig /*
110c33b27dSChristoph Hellwig     This program is free software; you can redistribute it and/or modify
120c33b27dSChristoph Hellwig     it under the terms of the GNU General Public License as published by
130c33b27dSChristoph Hellwig     the Free Software Foundation; version 2 of the License.
140c33b27dSChristoph Hellwig 
150c33b27dSChristoph Hellwig     This program is distributed in the hope that it will be useful,
160c33b27dSChristoph Hellwig     but WITHOUT ANY WARRANTY; without even the implied warranty of
170c33b27dSChristoph Hellwig     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
180c33b27dSChristoph Hellwig     GNU General Public License for more details.
190c33b27dSChristoph Hellwig 
200c33b27dSChristoph Hellwig     NO WARRANTY
210c33b27dSChristoph Hellwig     THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
220c33b27dSChristoph Hellwig     CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
230c33b27dSChristoph Hellwig     LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
240c33b27dSChristoph Hellwig     MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
250c33b27dSChristoph Hellwig     solely responsible for determining the appropriateness of using and
260c33b27dSChristoph Hellwig     distributing the Program and assumes all risks associated with its
270c33b27dSChristoph Hellwig     exercise of rights under this Agreement, including but not limited to
280c33b27dSChristoph Hellwig     the risks and costs of program errors, damage to or loss of data,
290c33b27dSChristoph Hellwig     programs or equipment, and unavailability or interruption of operations.
300c33b27dSChristoph Hellwig 
310c33b27dSChristoph Hellwig     DISCLAIMER OF LIABILITY
320c33b27dSChristoph Hellwig     NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
330c33b27dSChristoph Hellwig     DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
340c33b27dSChristoph Hellwig     DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
350c33b27dSChristoph Hellwig     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
360c33b27dSChristoph Hellwig     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
370c33b27dSChristoph Hellwig     USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
380c33b27dSChristoph Hellwig     HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
390c33b27dSChristoph Hellwig 
400c33b27dSChristoph Hellwig     You should have received a copy of the GNU General Public License
410c33b27dSChristoph Hellwig     along with this program; if not, write to the Free Software
420c33b27dSChristoph Hellwig     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
430c33b27dSChristoph Hellwig */
440c33b27dSChristoph Hellwig /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
450c33b27dSChristoph Hellwig 
460c33b27dSChristoph Hellwig #include <linux/module.h>
470c33b27dSChristoph Hellwig #include <linux/kernel.h>
485a0e3ad6STejun Heo #include <linux/slab.h>
490c33b27dSChristoph Hellwig #include <linux/init.h>
500c33b27dSChristoph Hellwig #include <linux/errno.h>
51cd354f1aSTim Schmielau #include <linux/jiffies.h>
520c33b27dSChristoph Hellwig #include <linux/workqueue.h>
53547f9a21SEric Moore #include <linux/delay.h>	/* for mdelay */
540c33b27dSChristoph Hellwig 
55547f9a21SEric Moore #include <scsi/scsi.h>
560c33b27dSChristoph Hellwig #include <scsi/scsi_cmnd.h>
570c33b27dSChristoph Hellwig #include <scsi/scsi_device.h>
580c33b27dSChristoph Hellwig #include <scsi/scsi_host.h>
590c33b27dSChristoph Hellwig #include <scsi/scsi_transport_sas.h>
60c9de7dc4SKashyap, Desai #include <scsi/scsi_transport.h>
61547f9a21SEric Moore #include <scsi/scsi_dbg.h>
620c33b27dSChristoph Hellwig 
630c33b27dSChristoph Hellwig #include "mptbase.h"
640c33b27dSChristoph Hellwig #include "mptscsih.h"
6556af97aeSPrakash, Sathya #include "mptsas.h"
660c33b27dSChristoph Hellwig 
670c33b27dSChristoph Hellwig 
680c33b27dSChristoph Hellwig #define my_NAME		"Fusion MPT SAS Host driver"
690c33b27dSChristoph Hellwig #define my_VERSION	MPT_LINUX_VERSION_COMMON
700c33b27dSChristoph Hellwig #define MYNAM		"mptsas"
710c33b27dSChristoph Hellwig 
72e8bf3941SJames Bottomley /*
73e8bf3941SJames Bottomley  * Reserved channel for integrated raid
74e8bf3941SJames Bottomley  */
75e8bf3941SJames Bottomley #define MPTSAS_RAID_CHANNEL	1
76e8bf3941SJames Bottomley 
774b97650bSKashyap, Desai #define SAS_CONFIG_PAGE_TIMEOUT		30
780c33b27dSChristoph Hellwig MODULE_AUTHOR(MODULEAUTHOR);
790c33b27dSChristoph Hellwig MODULE_DESCRIPTION(my_NAME);
800c33b27dSChristoph Hellwig MODULE_LICENSE("GPL");
819f4203b3SEric Moore MODULE_VERSION(my_VERSION);
820c33b27dSChristoph Hellwig 
830c33b27dSChristoph Hellwig static int mpt_pt_clear;
840c33b27dSChristoph Hellwig module_param(mpt_pt_clear, int, 0);
850c33b27dSChristoph Hellwig MODULE_PARM_DESC(mpt_pt_clear,
860c33b27dSChristoph Hellwig 		" Clear persistency table: enable=1  "
870c33b27dSChristoph Hellwig 		"(default=MPTSCSIH_PT_CLEAR=0)");
880c33b27dSChristoph Hellwig 
89cdcda465SRandy Dunlap /* scsi-mid layer global parameter is max_report_luns, which is 511 */
90793955f5SEric Moore #define MPTSAS_MAX_LUN (16895)
91793955f5SEric Moore static int max_lun = MPTSAS_MAX_LUN;
92793955f5SEric Moore module_param(max_lun, int, 0);
93793955f5SEric Moore MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
94793955f5SEric Moore 
953850b14eSkashyap.desai@lsi.com static int mpt_loadtime_max_sectors = 8192;
963850b14eSkashyap.desai@lsi.com module_param(mpt_loadtime_max_sectors, int, 0);
973850b14eSkashyap.desai@lsi.com MODULE_PARM_DESC(mpt_loadtime_max_sectors,
983850b14eSkashyap.desai@lsi.com 		" Maximum sector define for Host Bus Adaptor.Range 64 to 8192 default=8192");
993850b14eSkashyap.desai@lsi.com 
100f606f571SPrakash, Sathya static u8	mptsasDoneCtx = MPT_MAX_PROTOCOL_DRIVERS;
101f606f571SPrakash, Sathya static u8	mptsasTaskCtx = MPT_MAX_PROTOCOL_DRIVERS;
102f606f571SPrakash, Sathya static u8	mptsasInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for internal commands */
103f606f571SPrakash, Sathya static u8	mptsasMgmtCtx = MPT_MAX_PROTOCOL_DRIVERS;
104e7deff33SKashyap, Desai static u8	mptsasDeviceResetCtx = MPT_MAX_PROTOCOL_DRIVERS;
1050c33b27dSChristoph Hellwig 
1063eb0822cSKashyap, Desai static void mptsas_firmware_event_work(struct work_struct *work);
1073eb0822cSKashyap, Desai static void mptsas_send_sas_event(struct fw_event_work *fw_event);
1083eb0822cSKashyap, Desai static void mptsas_send_raid_event(struct fw_event_work *fw_event);
1093eb0822cSKashyap, Desai static void mptsas_send_ir2_event(struct fw_event_work *fw_event);
1103eb0822cSKashyap, Desai static void mptsas_parse_device_info(struct sas_identify *identify,
1113eb0822cSKashyap, Desai 		struct mptsas_devinfo *device_info);
1123eb0822cSKashyap, Desai static inline void mptsas_set_rphy(MPT_ADAPTER *ioc,
1133eb0822cSKashyap, Desai 		struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy);
1143eb0822cSKashyap, Desai static struct mptsas_phyinfo	*mptsas_find_phyinfo_by_sas_address
1153eb0822cSKashyap, Desai 		(MPT_ADAPTER *ioc, u64 sas_address);
1163eb0822cSKashyap, Desai static int mptsas_sas_device_pg0(MPT_ADAPTER *ioc,
1173eb0822cSKashyap, Desai 	struct mptsas_devinfo *device_info, u32 form, u32 form_specific);
1183eb0822cSKashyap, Desai static int mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc,
1193eb0822cSKashyap, Desai 	struct mptsas_enclosure *enclosure, u32 form, u32 form_specific);
1203eb0822cSKashyap, Desai static int mptsas_add_end_device(MPT_ADAPTER *ioc,
1213eb0822cSKashyap, Desai 	struct mptsas_phyinfo *phy_info);
1223eb0822cSKashyap, Desai static void mptsas_del_end_device(MPT_ADAPTER *ioc,
1233eb0822cSKashyap, Desai 	struct mptsas_phyinfo *phy_info);
124f9c34022SKashyap, Desai static void mptsas_send_link_status_event(struct fw_event_work *fw_event);
125f9c34022SKashyap, Desai static struct mptsas_portinfo	*mptsas_find_portinfo_by_sas_address
126f9c34022SKashyap, Desai 		(MPT_ADAPTER *ioc, u64 sas_address);
127f9c34022SKashyap, Desai static void mptsas_expander_delete(MPT_ADAPTER *ioc,
128f9c34022SKashyap, Desai 		struct mptsas_portinfo *port_info, u8 force);
129f9c34022SKashyap, Desai static void mptsas_send_expander_event(struct fw_event_work *fw_event);
130eedf92b9SKashyap, Desai static void mptsas_not_responding_devices(MPT_ADAPTER *ioc);
131eedf92b9SKashyap, Desai static void mptsas_scan_sas_topology(MPT_ADAPTER *ioc);
13294e989deSColin Ian King static void mptsas_broadcast_primitive_work(struct fw_event_work *fw_event);
13357e98513SKashyap, Desai static void mptsas_handle_queue_full_event(struct fw_event_work *fw_event);
134a7938b0bSKashyap, Desai static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id);
135b68bf096SKashyap, Desai void	mptsas_schedule_target_reset(void *ioc);
1360c33b27dSChristoph Hellwig 
mptsas_print_phy_data(MPT_ADAPTER * ioc,MPI_SAS_IO_UNIT0_PHY_DATA * phy_data)137d6ecdd63SPrakash, Sathya static void mptsas_print_phy_data(MPT_ADAPTER *ioc,
138d6ecdd63SPrakash, Sathya 					MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
139b5141128SChristoph Hellwig {
14029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
14129dd3609SEric Moore 	    "---- IO UNIT PAGE 0 ------------\n", ioc->name));
14229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Handle=0x%X\n",
14329dd3609SEric Moore 	    ioc->name, le16_to_cpu(phy_data->AttachedDeviceHandle)));
14429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Controller Handle=0x%X\n",
14529dd3609SEric Moore 	    ioc->name, le16_to_cpu(phy_data->ControllerDevHandle)));
14629dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Port=0x%X\n",
14729dd3609SEric Moore 	    ioc->name, phy_data->Port));
14829dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Port Flags=0x%X\n",
14929dd3609SEric Moore 	    ioc->name, phy_data->PortFlags));
15029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Flags=0x%X\n",
15129dd3609SEric Moore 	    ioc->name, phy_data->PhyFlags));
15229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Negotiated Link Rate=0x%X\n",
15329dd3609SEric Moore 	    ioc->name, phy_data->NegotiatedLinkRate));
15429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
15529dd3609SEric Moore 	    "Controller PHY Device Info=0x%X\n", ioc->name,
156d6ecdd63SPrakash, Sathya 	    le32_to_cpu(phy_data->ControllerPhyDeviceInfo)));
15729dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "DiscoveryStatus=0x%X\n\n",
15829dd3609SEric Moore 	    ioc->name, le32_to_cpu(phy_data->DiscoveryStatus)));
159b5141128SChristoph Hellwig }
160b5141128SChristoph Hellwig 
mptsas_print_phy_pg0(MPT_ADAPTER * ioc,SasPhyPage0_t * pg0)161d6ecdd63SPrakash, Sathya static void mptsas_print_phy_pg0(MPT_ADAPTER *ioc, SasPhyPage0_t *pg0)
162b5141128SChristoph Hellwig {
163b5141128SChristoph Hellwig 	__le64 sas_address;
164b5141128SChristoph Hellwig 
165b5141128SChristoph Hellwig 	memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64));
166b5141128SChristoph Hellwig 
16729dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
16829dd3609SEric Moore 	    "---- SAS PHY PAGE 0 ------------\n", ioc->name));
16929dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
17029dd3609SEric Moore 	    "Attached Device Handle=0x%X\n", ioc->name,
171d6ecdd63SPrakash, Sathya 	    le16_to_cpu(pg0->AttachedDevHandle)));
17229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SAS Address=0x%llX\n",
17329dd3609SEric Moore 	    ioc->name, (unsigned long long)le64_to_cpu(sas_address)));
17429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
17529dd3609SEric Moore 	    "Attached PHY Identifier=0x%X\n", ioc->name,
17629dd3609SEric Moore 	    pg0->AttachedPhyIdentifier));
17729dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Attached Device Info=0x%X\n",
17829dd3609SEric Moore 	    ioc->name, le32_to_cpu(pg0->AttachedDeviceInfo)));
17929dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Programmed Link Rate=0x%X\n",
18029dd3609SEric Moore 	    ioc->name,  pg0->ProgrammedLinkRate));
18129dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Change Count=0x%X\n",
18229dd3609SEric Moore 	    ioc->name, pg0->ChangeCount));
18329dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Info=0x%X\n\n",
18429dd3609SEric Moore 	    ioc->name, le32_to_cpu(pg0->PhyInfo)));
185b5141128SChristoph Hellwig }
186b5141128SChristoph Hellwig 
mptsas_print_phy_pg1(MPT_ADAPTER * ioc,SasPhyPage1_t * pg1)187d6ecdd63SPrakash, Sathya static void mptsas_print_phy_pg1(MPT_ADAPTER *ioc, SasPhyPage1_t *pg1)
188b5141128SChristoph Hellwig {
18929dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19029dd3609SEric Moore 	    "---- SAS PHY PAGE 1 ------------\n", ioc->name));
19129dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Invalid Dword Count=0x%x\n",
19229dd3609SEric Moore 	    ioc->name,  pg1->InvalidDwordCount));
19329dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19429dd3609SEric Moore 	    "Running Disparity Error Count=0x%x\n", ioc->name,
195d6ecdd63SPrakash, Sathya 	    pg1->RunningDisparityErrorCount));
19629dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
19729dd3609SEric Moore 	    "Loss Dword Synch Count=0x%x\n", ioc->name,
19829dd3609SEric Moore 	    pg1->LossDwordSynchCount));
19929dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
20029dd3609SEric Moore 	    "PHY Reset Problem Count=0x%x\n\n", ioc->name,
20129dd3609SEric Moore 	    pg1->PhyResetProblemCount));
202b5141128SChristoph Hellwig }
203b5141128SChristoph Hellwig 
mptsas_print_device_pg0(MPT_ADAPTER * ioc,SasDevicePage0_t * pg0)204d6ecdd63SPrakash, Sathya static void mptsas_print_device_pg0(MPT_ADAPTER *ioc, SasDevicePage0_t *pg0)
205b5141128SChristoph Hellwig {
206b5141128SChristoph Hellwig 	__le64 sas_address;
207b5141128SChristoph Hellwig 
208b5141128SChristoph Hellwig 	memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64));
209b5141128SChristoph Hellwig 
21029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
21129dd3609SEric Moore 	    "---- SAS DEVICE PAGE 0 ---------\n", ioc->name));
21229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Handle=0x%X\n",
21329dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->DevHandle)));
21429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Parent Handle=0x%X\n",
21529dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->ParentDevHandle)));
21629dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Enclosure Handle=0x%X\n",
21729dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->EnclosureHandle)));
21829dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Slot=0x%X\n",
21929dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->Slot)));
22029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SAS Address=0x%llX\n",
22129dd3609SEric Moore 	    ioc->name, (unsigned long long)le64_to_cpu(sas_address)));
22229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Target ID=0x%X\n",
22329dd3609SEric Moore 	    ioc->name, pg0->TargetID));
22429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Bus=0x%X\n",
22529dd3609SEric Moore 	    ioc->name, pg0->Bus));
22629dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Parent Phy Num=0x%X\n",
22729dd3609SEric Moore 	    ioc->name, pg0->PhyNum));
22829dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Access Status=0x%X\n",
22929dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->AccessStatus)));
23029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Device Info=0x%X\n",
23129dd3609SEric Moore 	    ioc->name, le32_to_cpu(pg0->DeviceInfo)));
23229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Flags=0x%X\n",
23329dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg0->Flags)));
23429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Physical Port=0x%X\n\n",
23529dd3609SEric Moore 	    ioc->name, pg0->PhysicalPort));
236b5141128SChristoph Hellwig }
237b5141128SChristoph Hellwig 
mptsas_print_expander_pg1(MPT_ADAPTER * ioc,SasExpanderPage1_t * pg1)238d6ecdd63SPrakash, Sathya static void mptsas_print_expander_pg1(MPT_ADAPTER *ioc, SasExpanderPage1_t *pg1)
239b5141128SChristoph Hellwig {
24029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
24129dd3609SEric Moore 	    "---- SAS EXPANDER PAGE 1 ------------\n", ioc->name));
24229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Physical Port=0x%X\n",
24329dd3609SEric Moore 	    ioc->name, pg1->PhysicalPort));
24429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "PHY Identifier=0x%X\n",
24529dd3609SEric Moore 	    ioc->name, pg1->PhyIdentifier));
24629dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Negotiated Link Rate=0x%X\n",
24729dd3609SEric Moore 	    ioc->name, pg1->NegotiatedLinkRate));
24829dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Programmed Link Rate=0x%X\n",
24929dd3609SEric Moore 	    ioc->name, pg1->ProgrammedLinkRate));
25029dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Hardware Link Rate=0x%X\n",
25129dd3609SEric Moore 	    ioc->name, pg1->HwLinkRate));
25229dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Owner Device Handle=0x%X\n",
25329dd3609SEric Moore 	    ioc->name, le16_to_cpu(pg1->OwnerDevHandle)));
25429dd3609SEric Moore 	dsasprintk(ioc, printk(MYIOC_s_DEBUG_FMT
25529dd3609SEric Moore 	    "Attached Device Handle=0x%X\n\n", ioc->name,
256d6ecdd63SPrakash, Sathya 	    le16_to_cpu(pg1->AttachedDevHandle)));
257b5141128SChristoph Hellwig }
258b5141128SChristoph Hellwig 
2593eb0822cSKashyap, Desai /* inhibit sas firmware event handling */
2603eb0822cSKashyap, Desai static void
mptsas_fw_event_off(MPT_ADAPTER * ioc)2613eb0822cSKashyap, Desai mptsas_fw_event_off(MPT_ADAPTER *ioc)
2623eb0822cSKashyap, Desai {
2633eb0822cSKashyap, Desai 	unsigned long flags;
2643eb0822cSKashyap, Desai 
2653eb0822cSKashyap, Desai 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
2663eb0822cSKashyap, Desai 	ioc->fw_events_off = 1;
2673eb0822cSKashyap, Desai 	ioc->sas_discovery_quiesce_io = 0;
2683eb0822cSKashyap, Desai 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
2693eb0822cSKashyap, Desai 
2703eb0822cSKashyap, Desai }
2713eb0822cSKashyap, Desai 
2723eb0822cSKashyap, Desai /* enable sas firmware event handling */
2733eb0822cSKashyap, Desai static void
mptsas_fw_event_on(MPT_ADAPTER * ioc)2743eb0822cSKashyap, Desai mptsas_fw_event_on(MPT_ADAPTER *ioc)
2753eb0822cSKashyap, Desai {
2763eb0822cSKashyap, Desai 	unsigned long flags;
2773eb0822cSKashyap, Desai 
2783eb0822cSKashyap, Desai 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
2793eb0822cSKashyap, Desai 	ioc->fw_events_off = 0;
2803eb0822cSKashyap, Desai 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
2813eb0822cSKashyap, Desai }
2823eb0822cSKashyap, Desai 
2833eb0822cSKashyap, Desai /* queue a sas firmware event */
2843eb0822cSKashyap, Desai static void
mptsas_add_fw_event(MPT_ADAPTER * ioc,struct fw_event_work * fw_event,unsigned long delay)2853eb0822cSKashyap, Desai mptsas_add_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
2863eb0822cSKashyap, Desai     unsigned long delay)
2873eb0822cSKashyap, Desai {
2883eb0822cSKashyap, Desai 	unsigned long flags;
2893eb0822cSKashyap, Desai 
2903eb0822cSKashyap, Desai 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
2913eb0822cSKashyap, Desai 	list_add_tail(&fw_event->list, &ioc->fw_event_list);
292817a7c99SSebastian Andrzej Siewior 	fw_event->users = 1;
2933eb0822cSKashyap, Desai 	INIT_DELAYED_WORK(&fw_event->work, mptsas_firmware_event_work);
294a38ae37fSkashyap.desai@lsi.com 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: add (fw_event=0x%p)"
295a38ae37fSkashyap.desai@lsi.com 		"on cpuid %d\n", ioc->name, __func__,
296a38ae37fSkashyap.desai@lsi.com 		fw_event, smp_processor_id()));
297a38ae37fSkashyap.desai@lsi.com 	queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
298a38ae37fSkashyap.desai@lsi.com 	    &fw_event->work, delay);
2993eb0822cSKashyap, Desai 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
3003eb0822cSKashyap, Desai }
3013eb0822cSKashyap, Desai 
302db7051b2SKashyap, Desai /* requeue a sas firmware event */
303db7051b2SKashyap, Desai static void
mptsas_requeue_fw_event(MPT_ADAPTER * ioc,struct fw_event_work * fw_event,unsigned long delay)304db7051b2SKashyap, Desai mptsas_requeue_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
305db7051b2SKashyap, Desai     unsigned long delay)
306db7051b2SKashyap, Desai {
307db7051b2SKashyap, Desai 	unsigned long flags;
308db7051b2SKashyap, Desai 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
309db7051b2SKashyap, Desai 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: reschedule task "
310a38ae37fSkashyap.desai@lsi.com 	    "(fw_event=0x%p)on cpuid %d\n", ioc->name, __func__,
311a38ae37fSkashyap.desai@lsi.com 		fw_event, smp_processor_id()));
312db7051b2SKashyap, Desai 	fw_event->retries++;
313a38ae37fSkashyap.desai@lsi.com 	queue_delayed_work_on(smp_processor_id(), ioc->fw_event_q,
314a38ae37fSkashyap.desai@lsi.com 	    &fw_event->work, msecs_to_jiffies(delay));
315db7051b2SKashyap, Desai 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
316db7051b2SKashyap, Desai }
317db7051b2SKashyap, Desai 
__mptsas_free_fw_event(MPT_ADAPTER * ioc,struct fw_event_work * fw_event)318817a7c99SSebastian Andrzej Siewior static void __mptsas_free_fw_event(MPT_ADAPTER *ioc,
319817a7c99SSebastian Andrzej Siewior 				   struct fw_event_work *fw_event)
320817a7c99SSebastian Andrzej Siewior {
321817a7c99SSebastian Andrzej Siewior 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: kfree (fw_event=0x%p)\n",
322817a7c99SSebastian Andrzej Siewior 	    ioc->name, __func__, fw_event));
323817a7c99SSebastian Andrzej Siewior 	list_del(&fw_event->list);
324817a7c99SSebastian Andrzej Siewior 	kfree(fw_event);
325817a7c99SSebastian Andrzej Siewior }
326817a7c99SSebastian Andrzej Siewior 
32725985edcSLucas De Marchi /* free memory associated to a sas firmware event */
3283eb0822cSKashyap, Desai static void
mptsas_free_fw_event(MPT_ADAPTER * ioc,struct fw_event_work * fw_event)3293eb0822cSKashyap, Desai mptsas_free_fw_event(MPT_ADAPTER *ioc, struct fw_event_work *fw_event)
3303eb0822cSKashyap, Desai {
3313eb0822cSKashyap, Desai 	unsigned long flags;
3323eb0822cSKashyap, Desai 
3333eb0822cSKashyap, Desai 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
334817a7c99SSebastian Andrzej Siewior 	fw_event->users--;
335817a7c99SSebastian Andrzej Siewior 	if (!fw_event->users)
336817a7c99SSebastian Andrzej Siewior 		__mptsas_free_fw_event(ioc, fw_event);
3373eb0822cSKashyap, Desai 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
3383eb0822cSKashyap, Desai }
3393eb0822cSKashyap, Desai 
3403eb0822cSKashyap, Desai /* walk the firmware event queue, and either stop or wait for
3413eb0822cSKashyap, Desai  * outstanding events to complete */
3423eb0822cSKashyap, Desai static void
mptsas_cleanup_fw_event_q(MPT_ADAPTER * ioc)3433eb0822cSKashyap, Desai mptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
3443eb0822cSKashyap, Desai {
345817a7c99SSebastian Andrzej Siewior 	struct fw_event_work *fw_event;
3463eb0822cSKashyap, Desai 	struct mptsas_target_reset_event *target_reset_list, *n;
3473eb0822cSKashyap, Desai 	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
348817a7c99SSebastian Andrzej Siewior 	unsigned long flags;
3493eb0822cSKashyap, Desai 
3503eb0822cSKashyap, Desai 	/* flush the target_reset_list */
3513eb0822cSKashyap, Desai 	if (!list_empty(&hd->target_reset_list)) {
3523eb0822cSKashyap, Desai 		list_for_each_entry_safe(target_reset_list, n,
3533eb0822cSKashyap, Desai 		    &hd->target_reset_list, list) {
3543eb0822cSKashyap, Desai 			dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
3553eb0822cSKashyap, Desai 			    "%s: removing target reset for id=%d\n",
3563eb0822cSKashyap, Desai 			    ioc->name, __func__,
3573eb0822cSKashyap, Desai 			   target_reset_list->sas_event_data.TargetID));
3583eb0822cSKashyap, Desai 			list_del(&target_reset_list->list);
3593eb0822cSKashyap, Desai 			kfree(target_reset_list);
3603eb0822cSKashyap, Desai 		}
3613eb0822cSKashyap, Desai 	}
3623eb0822cSKashyap, Desai 
363817a7c99SSebastian Andrzej Siewior 	if (list_empty(&ioc->fw_event_list) || !ioc->fw_event_q)
3643eb0822cSKashyap, Desai 		return;
3653eb0822cSKashyap, Desai 
366817a7c99SSebastian Andrzej Siewior 	spin_lock_irqsave(&ioc->fw_event_lock, flags);
367817a7c99SSebastian Andrzej Siewior 
368817a7c99SSebastian Andrzej Siewior 	while (!list_empty(&ioc->fw_event_list)) {
369817a7c99SSebastian Andrzej Siewior 		bool canceled = false;
370817a7c99SSebastian Andrzej Siewior 
371817a7c99SSebastian Andrzej Siewior 		fw_event = list_first_entry(&ioc->fw_event_list,
372817a7c99SSebastian Andrzej Siewior 					    struct fw_event_work, list);
373817a7c99SSebastian Andrzej Siewior 		fw_event->users++;
374817a7c99SSebastian Andrzej Siewior 		spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
375817a7c99SSebastian Andrzej Siewior 		if (cancel_delayed_work_sync(&fw_event->work))
376817a7c99SSebastian Andrzej Siewior 			canceled = true;
377817a7c99SSebastian Andrzej Siewior 
378817a7c99SSebastian Andrzej Siewior 		spin_lock_irqsave(&ioc->fw_event_lock, flags);
379817a7c99SSebastian Andrzej Siewior 		if (canceled)
380817a7c99SSebastian Andrzej Siewior 			fw_event->users--;
381817a7c99SSebastian Andrzej Siewior 		fw_event->users--;
382817a7c99SSebastian Andrzej Siewior 		WARN_ON_ONCE(fw_event->users);
383817a7c99SSebastian Andrzej Siewior 		__mptsas_free_fw_event(ioc, fw_event);
3843eb0822cSKashyap, Desai 	}
385817a7c99SSebastian Andrzej Siewior 	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
3863eb0822cSKashyap, Desai }
3873eb0822cSKashyap, Desai 
3883eb0822cSKashyap, Desai 
phy_to_ioc(struct sas_phy * phy)389e3094447SChristoph Hellwig static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
390e3094447SChristoph Hellwig {
391e3094447SChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
392e3094447SChristoph Hellwig 	return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
393e3094447SChristoph Hellwig }
394e3094447SChristoph Hellwig 
rphy_to_ioc(struct sas_rphy * rphy)395e3094447SChristoph Hellwig static inline MPT_ADAPTER *rphy_to_ioc(struct sas_rphy *rphy)
396e3094447SChristoph Hellwig {
397e3094447SChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
398e3094447SChristoph Hellwig 	return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
399e3094447SChristoph Hellwig }
400e3094447SChristoph Hellwig 
401e6b2d76aSMoore, Eric /*
402e6b2d76aSMoore, Eric  * mptsas_find_portinfo_by_handle
403e6b2d76aSMoore, Eric  *
404e6b2d76aSMoore, Eric  * This function should be called with the sas_topology_mutex already held
405e6b2d76aSMoore, Eric  */
406e6b2d76aSMoore, Eric static struct mptsas_portinfo *
mptsas_find_portinfo_by_handle(MPT_ADAPTER * ioc,u16 handle)407e6b2d76aSMoore, Eric mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle)
408e6b2d76aSMoore, Eric {
409e6b2d76aSMoore, Eric 	struct mptsas_portinfo *port_info, *rc=NULL;
410e6b2d76aSMoore, Eric 	int i;
411e6b2d76aSMoore, Eric 
412e6b2d76aSMoore, Eric 	list_for_each_entry(port_info, &ioc->sas_topology, list)
413e6b2d76aSMoore, Eric 		for (i = 0; i < port_info->num_phys; i++)
414e6b2d76aSMoore, Eric 			if (port_info->phy_info[i].identify.handle == handle) {
415e6b2d76aSMoore, Eric 				rc = port_info;
416e6b2d76aSMoore, Eric 				goto out;
417e6b2d76aSMoore, Eric 			}
418e6b2d76aSMoore, Eric  out:
419e6b2d76aSMoore, Eric 	return rc;
420e6b2d76aSMoore, Eric }
421e6b2d76aSMoore, Eric 
422f9c34022SKashyap, Desai /**
423cdcda465SRandy Dunlap  *	mptsas_find_portinfo_by_sas_address - find and return portinfo for
424cdcda465SRandy Dunlap  *		this sas_address
425f9c34022SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
426cdcda465SRandy Dunlap  *	@sas_address: expander sas address
427f9c34022SKashyap, Desai  *
428cdcda465SRandy Dunlap  *	This function should be called with the sas_topology_mutex already held.
429f9c34022SKashyap, Desai  *
430cdcda465SRandy Dunlap  *	Return: %NULL if not found.
431f9c34022SKashyap, Desai  **/
432f9c34022SKashyap, Desai static struct mptsas_portinfo *
mptsas_find_portinfo_by_sas_address(MPT_ADAPTER * ioc,u64 sas_address)433f9c34022SKashyap, Desai mptsas_find_portinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
434f9c34022SKashyap, Desai {
435f9c34022SKashyap, Desai 	struct mptsas_portinfo *port_info, *rc = NULL;
436f9c34022SKashyap, Desai 	int i;
437f9c34022SKashyap, Desai 
438f9c34022SKashyap, Desai 	if (sas_address >= ioc->hba_port_sas_addr &&
439f9c34022SKashyap, Desai 	    sas_address < (ioc->hba_port_sas_addr +
440f9c34022SKashyap, Desai 	    ioc->hba_port_num_phy))
441f9c34022SKashyap, Desai 		return ioc->hba_port_info;
442f9c34022SKashyap, Desai 
443f9c34022SKashyap, Desai 	mutex_lock(&ioc->sas_topology_mutex);
444f9c34022SKashyap, Desai 	list_for_each_entry(port_info, &ioc->sas_topology, list)
445f9c34022SKashyap, Desai 		for (i = 0; i < port_info->num_phys; i++)
446f9c34022SKashyap, Desai 			if (port_info->phy_info[i].identify.sas_address ==
447f9c34022SKashyap, Desai 			    sas_address) {
448f9c34022SKashyap, Desai 				rc = port_info;
449f9c34022SKashyap, Desai 				goto out;
450f9c34022SKashyap, Desai 			}
451f9c34022SKashyap, Desai  out:
452f9c34022SKashyap, Desai 	mutex_unlock(&ioc->sas_topology_mutex);
453f9c34022SKashyap, Desai 	return rc;
454f9c34022SKashyap, Desai }
455f9c34022SKashyap, Desai 
456bd23e94cSMoore, Eric /*
457bd23e94cSMoore, Eric  * Returns true if there is a scsi end device
458bd23e94cSMoore, Eric  */
459bd23e94cSMoore, Eric static inline int
mptsas_is_end_device(struct mptsas_devinfo * attached)460bd23e94cSMoore, Eric mptsas_is_end_device(struct mptsas_devinfo * attached)
461bd23e94cSMoore, Eric {
462547f9a21SEric Moore 	if ((attached->sas_address) &&
463bd23e94cSMoore, Eric 	    (attached->device_info &
464bd23e94cSMoore, Eric 	    MPI_SAS_DEVICE_INFO_END_DEVICE) &&
465bd23e94cSMoore, Eric 	    ((attached->device_info &
466bd23e94cSMoore, Eric 	    MPI_SAS_DEVICE_INFO_SSP_TARGET) |
467bd23e94cSMoore, Eric 	    (attached->device_info &
468bd23e94cSMoore, Eric 	    MPI_SAS_DEVICE_INFO_STP_TARGET) |
469bd23e94cSMoore, Eric 	    (attached->device_info &
470bd23e94cSMoore, Eric 	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)))
471bd23e94cSMoore, Eric 		return 1;
472bd23e94cSMoore, Eric 	else
473bd23e94cSMoore, Eric 		return 0;
474bd23e94cSMoore, Eric }
475bd23e94cSMoore, Eric 
476547f9a21SEric Moore /* no mutex */
477376ac830SEric Moore static void
mptsas_port_delete(MPT_ADAPTER * ioc,struct mptsas_portinfo_details * port_details)478d6ecdd63SPrakash, Sathya mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_details)
479547f9a21SEric Moore {
480547f9a21SEric Moore 	struct mptsas_portinfo *port_info;
481547f9a21SEric Moore 	struct mptsas_phyinfo *phy_info;
482547f9a21SEric Moore 	u8	i;
483547f9a21SEric Moore 
484547f9a21SEric Moore 	if (!port_details)
485547f9a21SEric Moore 		return;
486547f9a21SEric Moore 
487547f9a21SEric Moore 	port_info = port_details->port_info;
488547f9a21SEric Moore 	phy_info = port_info->phy_info;
489547f9a21SEric Moore 
49029dd3609SEric Moore 	dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: num_phys=%02d "
491cadbd4a5SHarvey Harrison 	    "bitmask=0x%016llX\n", ioc->name, __func__, port_details,
492f99be43bSEric Moore 	    port_details->num_phys, (unsigned long long)
493dc22f16dSEric Moore 	    port_details->phy_bitmask));
494547f9a21SEric Moore 
495547f9a21SEric Moore 	for (i = 0; i < port_info->num_phys; i++, phy_info++) {
496547f9a21SEric Moore 		if(phy_info->port_details != port_details)
497547f9a21SEric Moore 			continue;
498547f9a21SEric Moore 		memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
4993eb0822cSKashyap, Desai 		mptsas_set_rphy(ioc, phy_info, NULL);
500547f9a21SEric Moore 		phy_info->port_details = NULL;
501547f9a21SEric Moore 	}
502547f9a21SEric Moore 	kfree(port_details);
503547f9a21SEric Moore }
504547f9a21SEric Moore 
505547f9a21SEric Moore static inline struct sas_rphy *
mptsas_get_rphy(struct mptsas_phyinfo * phy_info)506547f9a21SEric Moore mptsas_get_rphy(struct mptsas_phyinfo *phy_info)
507547f9a21SEric Moore {
508547f9a21SEric Moore 	if (phy_info->port_details)
509547f9a21SEric Moore 		return phy_info->port_details->rphy;
510547f9a21SEric Moore 	else
511547f9a21SEric Moore 		return NULL;
512547f9a21SEric Moore }
513547f9a21SEric Moore 
514547f9a21SEric Moore static inline void
mptsas_set_rphy(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info,struct sas_rphy * rphy)515d6ecdd63SPrakash, Sathya mptsas_set_rphy(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_rphy *rphy)
516547f9a21SEric Moore {
517547f9a21SEric Moore 	if (phy_info->port_details) {
518547f9a21SEric Moore 		phy_info->port_details->rphy = rphy;
51929dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "sas_rphy_add: rphy=%p\n",
52029dd3609SEric Moore 		    ioc->name, rphy));
521547f9a21SEric Moore 	}
522547f9a21SEric Moore 
523547f9a21SEric Moore 	if (rphy) {
524c51d0beaSEric Moore 		dsaswideprintk(ioc, dev_printk(KERN_DEBUG,
525c51d0beaSEric Moore 		    &rphy->dev, MYIOC_s_FMT "add:", ioc->name));
52629dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "rphy=%p release=%p\n",
52729dd3609SEric Moore 		    ioc->name, rphy, rphy->dev.release));
528547f9a21SEric Moore 	}
529547f9a21SEric Moore }
530547f9a21SEric Moore 
531547f9a21SEric Moore static inline struct sas_port *
mptsas_get_port(struct mptsas_phyinfo * phy_info)532547f9a21SEric Moore mptsas_get_port(struct mptsas_phyinfo *phy_info)
533547f9a21SEric Moore {
534547f9a21SEric Moore 	if (phy_info->port_details)
535547f9a21SEric Moore 		return phy_info->port_details->port;
536547f9a21SEric Moore 	else
537547f9a21SEric Moore 		return NULL;
538547f9a21SEric Moore }
539547f9a21SEric Moore 
540547f9a21SEric Moore static inline void
mptsas_set_port(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info,struct sas_port * port)541d6ecdd63SPrakash, Sathya mptsas_set_port(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, struct sas_port *port)
542547f9a21SEric Moore {
543547f9a21SEric Moore 	if (phy_info->port_details)
544547f9a21SEric Moore 		phy_info->port_details->port = port;
545547f9a21SEric Moore 
546547f9a21SEric Moore 	if (port) {
547c51d0beaSEric Moore 		dsaswideprintk(ioc, dev_printk(KERN_DEBUG,
548c51d0beaSEric Moore 		    &port->dev, MYIOC_s_FMT "add:", ioc->name));
54929dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "port=%p release=%p\n",
55029dd3609SEric Moore 		    ioc->name, port, port->dev.release));
551547f9a21SEric Moore 	}
552547f9a21SEric Moore }
553547f9a21SEric Moore 
554547f9a21SEric Moore static inline struct scsi_target *
mptsas_get_starget(struct mptsas_phyinfo * phy_info)555547f9a21SEric Moore mptsas_get_starget(struct mptsas_phyinfo *phy_info)
556547f9a21SEric Moore {
557547f9a21SEric Moore 	if (phy_info->port_details)
558547f9a21SEric Moore 		return phy_info->port_details->starget;
559547f9a21SEric Moore 	else
560547f9a21SEric Moore 		return NULL;
561547f9a21SEric Moore }
562547f9a21SEric Moore 
563547f9a21SEric Moore static inline void
mptsas_set_starget(struct mptsas_phyinfo * phy_info,struct scsi_target * starget)564547f9a21SEric Moore mptsas_set_starget(struct mptsas_phyinfo *phy_info, struct scsi_target *
565547f9a21SEric Moore starget)
566547f9a21SEric Moore {
567547f9a21SEric Moore 	if (phy_info->port_details)
568547f9a21SEric Moore 		phy_info->port_details->starget = starget;
569547f9a21SEric Moore }
570547f9a21SEric Moore 
5713eb0822cSKashyap, Desai /**
572cdcda465SRandy Dunlap  *	mptsas_add_device_component - adds a new device component to our lists
5733eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
574cdcda465SRandy Dunlap  *	@channel: channel number
575cdcda465SRandy Dunlap  *	@id: Logical Target ID for reset (if appropriate)
576cdcda465SRandy Dunlap  *	@sas_address: expander sas address
577cdcda465SRandy Dunlap  *	@device_info: specific bits (flags) for devices
578cdcda465SRandy Dunlap  *	@slot: enclosure slot ID
579cdcda465SRandy Dunlap  *	@enclosure_logical_id: enclosure WWN
5803eb0822cSKashyap, Desai  *
5813eb0822cSKashyap, Desai  **/
5823eb0822cSKashyap, Desai static void
mptsas_add_device_component(MPT_ADAPTER * ioc,u8 channel,u8 id,u64 sas_address,u32 device_info,u16 slot,u64 enclosure_logical_id)5833eb0822cSKashyap, Desai mptsas_add_device_component(MPT_ADAPTER *ioc, u8 channel, u8 id,
5843eb0822cSKashyap, Desai 	u64 sas_address, u32 device_info, u16 slot, u64 enclosure_logical_id)
5853eb0822cSKashyap, Desai {
5863eb0822cSKashyap, Desai 	struct mptsas_device_info	*sas_info, *next;
5873eb0822cSKashyap, Desai 	struct scsi_device	*sdev;
5883eb0822cSKashyap, Desai 	struct scsi_target	*starget;
5893eb0822cSKashyap, Desai 	struct sas_rphy	*rphy;
5903eb0822cSKashyap, Desai 
5913eb0822cSKashyap, Desai 	/*
5923eb0822cSKashyap, Desai 	 * Delete all matching devices out of the list
5933eb0822cSKashyap, Desai 	 */
5943eb0822cSKashyap, Desai 	mutex_lock(&ioc->sas_device_info_mutex);
5953eb0822cSKashyap, Desai 	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
5963eb0822cSKashyap, Desai 	    list) {
597a7938b0bSKashyap, Desai 		if (!sas_info->is_logical_volume &&
598a7938b0bSKashyap, Desai 		    (sas_info->sas_address == sas_address ||
5993eb0822cSKashyap, Desai 		    (sas_info->fw.channel == channel &&
6003eb0822cSKashyap, Desai 		     sas_info->fw.id == id))) {
6013eb0822cSKashyap, Desai 			list_del(&sas_info->list);
6023eb0822cSKashyap, Desai 			kfree(sas_info);
6033eb0822cSKashyap, Desai 		}
6043eb0822cSKashyap, Desai 	}
6053eb0822cSKashyap, Desai 
6063eb0822cSKashyap, Desai 	sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
6073eb0822cSKashyap, Desai 	if (!sas_info)
6083eb0822cSKashyap, Desai 		goto out;
6093eb0822cSKashyap, Desai 
6103eb0822cSKashyap, Desai 	/*
6113eb0822cSKashyap, Desai 	 * Set Firmware mapping
6123eb0822cSKashyap, Desai 	 */
6133eb0822cSKashyap, Desai 	sas_info->fw.id = id;
6143eb0822cSKashyap, Desai 	sas_info->fw.channel = channel;
6153eb0822cSKashyap, Desai 
6163eb0822cSKashyap, Desai 	sas_info->sas_address = sas_address;
6173eb0822cSKashyap, Desai 	sas_info->device_info = device_info;
6183eb0822cSKashyap, Desai 	sas_info->slot = slot;
6193eb0822cSKashyap, Desai 	sas_info->enclosure_logical_id = enclosure_logical_id;
6203eb0822cSKashyap, Desai 	INIT_LIST_HEAD(&sas_info->list);
6213eb0822cSKashyap, Desai 	list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
6223eb0822cSKashyap, Desai 
6233eb0822cSKashyap, Desai 	/*
6243eb0822cSKashyap, Desai 	 * Set OS mapping
6253eb0822cSKashyap, Desai 	 */
6263eb0822cSKashyap, Desai 	shost_for_each_device(sdev, ioc->sh) {
6273eb0822cSKashyap, Desai 		starget = scsi_target(sdev);
6283eb0822cSKashyap, Desai 		rphy = dev_to_rphy(starget->dev.parent);
6293eb0822cSKashyap, Desai 		if (rphy->identify.sas_address == sas_address) {
6303eb0822cSKashyap, Desai 			sas_info->os.id = starget->id;
6313eb0822cSKashyap, Desai 			sas_info->os.channel = starget->channel;
6323eb0822cSKashyap, Desai 		}
6333eb0822cSKashyap, Desai 	}
6343eb0822cSKashyap, Desai 
6353eb0822cSKashyap, Desai  out:
6363eb0822cSKashyap, Desai 	mutex_unlock(&ioc->sas_device_info_mutex);
6373eb0822cSKashyap, Desai 	return;
6383eb0822cSKashyap, Desai }
6393eb0822cSKashyap, Desai 
6403eb0822cSKashyap, Desai /**
641cdcda465SRandy Dunlap  *	mptsas_add_device_component_by_fw - adds a new device component by FW ID
6423eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
643cdcda465SRandy Dunlap  *	@channel: channel number
644cdcda465SRandy Dunlap  *	@id: Logical Target ID
6453eb0822cSKashyap, Desai  *
6463eb0822cSKashyap, Desai  **/
6473eb0822cSKashyap, Desai static void
mptsas_add_device_component_by_fw(MPT_ADAPTER * ioc,u8 channel,u8 id)6483eb0822cSKashyap, Desai mptsas_add_device_component_by_fw(MPT_ADAPTER *ioc, u8 channel, u8 id)
6493eb0822cSKashyap, Desai {
6503eb0822cSKashyap, Desai 	struct mptsas_devinfo sas_device;
6513eb0822cSKashyap, Desai 	struct mptsas_enclosure enclosure_info;
6523eb0822cSKashyap, Desai 	int rc;
6533eb0822cSKashyap, Desai 
6543eb0822cSKashyap, Desai 	rc = mptsas_sas_device_pg0(ioc, &sas_device,
6553eb0822cSKashyap, Desai 	    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
6563eb0822cSKashyap, Desai 	     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
6573eb0822cSKashyap, Desai 	    (channel << 8) + id);
6583eb0822cSKashyap, Desai 	if (rc)
6593eb0822cSKashyap, Desai 		return;
6603eb0822cSKashyap, Desai 
6613eb0822cSKashyap, Desai 	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
6623eb0822cSKashyap, Desai 	mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
6633eb0822cSKashyap, Desai 	    (MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
6643eb0822cSKashyap, Desai 	     MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
6653eb0822cSKashyap, Desai 	     sas_device.handle_enclosure);
6663eb0822cSKashyap, Desai 
6673eb0822cSKashyap, Desai 	mptsas_add_device_component(ioc, sas_device.channel,
6683eb0822cSKashyap, Desai 	    sas_device.id, sas_device.sas_address, sas_device.device_info,
6693eb0822cSKashyap, Desai 	    sas_device.slot, enclosure_info.enclosure_logical_id);
6703eb0822cSKashyap, Desai }
6713eb0822cSKashyap, Desai 
6723eb0822cSKashyap, Desai /**
673fc847ab4SJames Bottomley  *	mptsas_add_device_component_starget_ir - Handle Integrated RAID, adding each individual device to list
674a7938b0bSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
675cdcda465SRandy Dunlap  *	@starget: SCSI target for this SCSI device
676a7938b0bSKashyap, Desai  *
677a7938b0bSKashyap, Desai  **/
678a7938b0bSKashyap, Desai static void
mptsas_add_device_component_starget_ir(MPT_ADAPTER * ioc,struct scsi_target * starget)679a7938b0bSKashyap, Desai mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc,
680a7938b0bSKashyap, Desai 		struct scsi_target *starget)
681a7938b0bSKashyap, Desai {
682a7938b0bSKashyap, Desai 	CONFIGPARMS			cfg;
683a7938b0bSKashyap, Desai 	ConfigPageHeader_t		hdr;
684a7938b0bSKashyap, Desai 	dma_addr_t			dma_handle;
685a7938b0bSKashyap, Desai 	pRaidVolumePage0_t		buffer = NULL;
686a7938b0bSKashyap, Desai 	int				i;
687a7938b0bSKashyap, Desai 	RaidPhysDiskPage0_t 		phys_disk;
688a7938b0bSKashyap, Desai 	struct mptsas_device_info	*sas_info, *next;
689a7938b0bSKashyap, Desai 
690a7938b0bSKashyap, Desai 	memset(&cfg, 0 , sizeof(CONFIGPARMS));
691a7938b0bSKashyap, Desai 	memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
692a7938b0bSKashyap, Desai 	hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
693a7938b0bSKashyap, Desai 	/* assumption that all volumes on channel = 0 */
694a7938b0bSKashyap, Desai 	cfg.pageAddr = starget->id;
695a7938b0bSKashyap, Desai 	cfg.cfghdr.hdr = &hdr;
696a7938b0bSKashyap, Desai 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
6974b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
698a7938b0bSKashyap, Desai 
699a7938b0bSKashyap, Desai 	if (mpt_config(ioc, &cfg) != 0)
700a7938b0bSKashyap, Desai 		goto out;
701a7938b0bSKashyap, Desai 
702a7938b0bSKashyap, Desai 	if (!hdr.PageLength)
703a7938b0bSKashyap, Desai 		goto out;
704a7938b0bSKashyap, Desai 
70576a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
70676a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
707a7938b0bSKashyap, Desai 
708a7938b0bSKashyap, Desai 	if (!buffer)
709a7938b0bSKashyap, Desai 		goto out;
710a7938b0bSKashyap, Desai 
711a7938b0bSKashyap, Desai 	cfg.physAddr = dma_handle;
712a7938b0bSKashyap, Desai 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
713a7938b0bSKashyap, Desai 
714a7938b0bSKashyap, Desai 	if (mpt_config(ioc, &cfg) != 0)
715a7938b0bSKashyap, Desai 		goto out;
716a7938b0bSKashyap, Desai 
717a7938b0bSKashyap, Desai 	if (!buffer->NumPhysDisks)
718a7938b0bSKashyap, Desai 		goto out;
719a7938b0bSKashyap, Desai 
720a7938b0bSKashyap, Desai 	/*
721a7938b0bSKashyap, Desai 	 * Adding entry for hidden components
722a7938b0bSKashyap, Desai 	 */
723a7938b0bSKashyap, Desai 	for (i = 0; i < buffer->NumPhysDisks; i++) {
724a7938b0bSKashyap, Desai 
725a7938b0bSKashyap, Desai 		if (mpt_raid_phys_disk_pg0(ioc,
726a7938b0bSKashyap, Desai 		    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
727a7938b0bSKashyap, Desai 			continue;
728a7938b0bSKashyap, Desai 
729a7938b0bSKashyap, Desai 		mptsas_add_device_component_by_fw(ioc, phys_disk.PhysDiskBus,
730a7938b0bSKashyap, Desai 		    phys_disk.PhysDiskID);
731a7938b0bSKashyap, Desai 
73257e98513SKashyap, Desai 		mutex_lock(&ioc->sas_device_info_mutex);
73357e98513SKashyap, Desai 		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
73457e98513SKashyap, Desai 		    list) {
73557e98513SKashyap, Desai 			if (!sas_info->is_logical_volume &&
73657e98513SKashyap, Desai 			    (sas_info->fw.channel == phys_disk.PhysDiskBus &&
73757e98513SKashyap, Desai 			    sas_info->fw.id == phys_disk.PhysDiskID)) {
73857e98513SKashyap, Desai 				sas_info->is_hidden_raid_component = 1;
73957e98513SKashyap, Desai 				sas_info->volume_id = starget->id;
74057e98513SKashyap, Desai 			}
74157e98513SKashyap, Desai 		}
74257e98513SKashyap, Desai 		mutex_unlock(&ioc->sas_device_info_mutex);
74357e98513SKashyap, Desai 
744a7938b0bSKashyap, Desai 	}
745a7938b0bSKashyap, Desai 
746a7938b0bSKashyap, Desai 	/*
747a7938b0bSKashyap, Desai 	 * Delete all matching devices out of the list
748a7938b0bSKashyap, Desai 	 */
749a7938b0bSKashyap, Desai 	mutex_lock(&ioc->sas_device_info_mutex);
750a7938b0bSKashyap, Desai 	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
751a7938b0bSKashyap, Desai 	    list) {
752a7938b0bSKashyap, Desai 		if (sas_info->is_logical_volume && sas_info->fw.id ==
753a7938b0bSKashyap, Desai 		    starget->id) {
754a7938b0bSKashyap, Desai 			list_del(&sas_info->list);
755a7938b0bSKashyap, Desai 			kfree(sas_info);
756a7938b0bSKashyap, Desai 		}
757a7938b0bSKashyap, Desai 	}
758a7938b0bSKashyap, Desai 
759a7938b0bSKashyap, Desai 	sas_info = kzalloc(sizeof(struct mptsas_device_info), GFP_KERNEL);
760a7938b0bSKashyap, Desai 	if (sas_info) {
761a7938b0bSKashyap, Desai 		sas_info->fw.id = starget->id;
762a7938b0bSKashyap, Desai 		sas_info->os.id = starget->id;
763a7938b0bSKashyap, Desai 		sas_info->os.channel = starget->channel;
764a7938b0bSKashyap, Desai 		sas_info->is_logical_volume = 1;
765a7938b0bSKashyap, Desai 		INIT_LIST_HEAD(&sas_info->list);
766a7938b0bSKashyap, Desai 		list_add_tail(&sas_info->list, &ioc->sas_device_info_list);
767a7938b0bSKashyap, Desai 	}
768a7938b0bSKashyap, Desai 	mutex_unlock(&ioc->sas_device_info_mutex);
769a7938b0bSKashyap, Desai 
770a7938b0bSKashyap, Desai  out:
771a7938b0bSKashyap, Desai 	if (buffer)
772b114dda6SChristophe JAILLET 		dma_free_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
773b114dda6SChristophe JAILLET 				  buffer, dma_handle);
774a7938b0bSKashyap, Desai }
775a7938b0bSKashyap, Desai 
776a7938b0bSKashyap, Desai /**
777cdcda465SRandy Dunlap  *	mptsas_add_device_component_starget - adds a SCSI target device component
7783eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
779cdcda465SRandy Dunlap  *	@starget: SCSI target for this SCSI device
7803eb0822cSKashyap, Desai  *
7813eb0822cSKashyap, Desai  **/
7823eb0822cSKashyap, Desai static void
mptsas_add_device_component_starget(MPT_ADAPTER * ioc,struct scsi_target * starget)7833eb0822cSKashyap, Desai mptsas_add_device_component_starget(MPT_ADAPTER *ioc,
7843eb0822cSKashyap, Desai 	struct scsi_target *starget)
7853eb0822cSKashyap, Desai {
7863eb0822cSKashyap, Desai 	struct sas_rphy	*rphy;
7873eb0822cSKashyap, Desai 	struct mptsas_phyinfo	*phy_info = NULL;
7883eb0822cSKashyap, Desai 	struct mptsas_enclosure	enclosure_info;
7893eb0822cSKashyap, Desai 
7903eb0822cSKashyap, Desai 	rphy = dev_to_rphy(starget->dev.parent);
7913eb0822cSKashyap, Desai 	phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
7923eb0822cSKashyap, Desai 			rphy->identify.sas_address);
7933eb0822cSKashyap, Desai 	if (!phy_info)
7943eb0822cSKashyap, Desai 		return;
7953eb0822cSKashyap, Desai 
7963eb0822cSKashyap, Desai 	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
7973eb0822cSKashyap, Desai 	mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
7983eb0822cSKashyap, Desai 		(MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
7993eb0822cSKashyap, Desai 		MPI_SAS_ENCLOS_PGAD_FORM_SHIFT),
8003eb0822cSKashyap, Desai 		phy_info->attached.handle_enclosure);
8013eb0822cSKashyap, Desai 
8023eb0822cSKashyap, Desai 	mptsas_add_device_component(ioc, phy_info->attached.channel,
8033eb0822cSKashyap, Desai 		phy_info->attached.id, phy_info->attached.sas_address,
8043eb0822cSKashyap, Desai 		phy_info->attached.device_info,
8053eb0822cSKashyap, Desai 		phy_info->attached.slot, enclosure_info.enclosure_logical_id);
8063eb0822cSKashyap, Desai }
8073eb0822cSKashyap, Desai 
8083eb0822cSKashyap, Desai /**
809fc847ab4SJames Bottomley  *	mptsas_del_device_component_by_os - Once a device has been removed, we mark the entry in the list as being cached
81057e98513SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
81157e98513SKashyap, Desai  *	@channel: os mapped id's
812cdcda465SRandy Dunlap  *	@id: Logical Target ID
81357e98513SKashyap, Desai  *
81457e98513SKashyap, Desai  **/
81557e98513SKashyap, Desai static void
mptsas_del_device_component_by_os(MPT_ADAPTER * ioc,u8 channel,u8 id)81657e98513SKashyap, Desai mptsas_del_device_component_by_os(MPT_ADAPTER *ioc, u8 channel, u8 id)
81757e98513SKashyap, Desai {
81857e98513SKashyap, Desai 	struct mptsas_device_info	*sas_info, *next;
81957e98513SKashyap, Desai 
82057e98513SKashyap, Desai 	/*
82157e98513SKashyap, Desai 	 * Set is_cached flag
82257e98513SKashyap, Desai 	 */
82357e98513SKashyap, Desai 	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
82457e98513SKashyap, Desai 		list) {
82557e98513SKashyap, Desai 		if (sas_info->os.channel == channel && sas_info->os.id == id)
82657e98513SKashyap, Desai 			sas_info->is_cached = 1;
82757e98513SKashyap, Desai 	}
82857e98513SKashyap, Desai }
82957e98513SKashyap, Desai 
83057e98513SKashyap, Desai /**
8313eb0822cSKashyap, Desai  *	mptsas_del_device_components - Cleaning the list
8323eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
8333eb0822cSKashyap, Desai  *
8343eb0822cSKashyap, Desai  **/
8353eb0822cSKashyap, Desai static void
mptsas_del_device_components(MPT_ADAPTER * ioc)8363eb0822cSKashyap, Desai mptsas_del_device_components(MPT_ADAPTER *ioc)
8373eb0822cSKashyap, Desai {
8383eb0822cSKashyap, Desai 	struct mptsas_device_info	*sas_info, *next;
8393eb0822cSKashyap, Desai 
8403eb0822cSKashyap, Desai 	mutex_lock(&ioc->sas_device_info_mutex);
8413eb0822cSKashyap, Desai 	list_for_each_entry_safe(sas_info, next, &ioc->sas_device_info_list,
8423eb0822cSKashyap, Desai 		list) {
8433eb0822cSKashyap, Desai 		list_del(&sas_info->list);
8443eb0822cSKashyap, Desai 		kfree(sas_info);
8453eb0822cSKashyap, Desai 	}
8463eb0822cSKashyap, Desai 	mutex_unlock(&ioc->sas_device_info_mutex);
8473eb0822cSKashyap, Desai }
8483eb0822cSKashyap, Desai 
849547f9a21SEric Moore 
850547f9a21SEric Moore /*
851547f9a21SEric Moore  * mptsas_setup_wide_ports
852547f9a21SEric Moore  *
853547f9a21SEric Moore  * Updates for new and existing narrow/wide port configuration
854547f9a21SEric Moore  * in the sas_topology
855547f9a21SEric Moore  */
856376ac830SEric Moore static void
mptsas_setup_wide_ports(MPT_ADAPTER * ioc,struct mptsas_portinfo * port_info)857547f9a21SEric Moore mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
858547f9a21SEric Moore {
859547f9a21SEric Moore 	struct mptsas_portinfo_details * port_details;
860547f9a21SEric Moore 	struct mptsas_phyinfo *phy_info, *phy_info_cmp;
861547f9a21SEric Moore 	u64	sas_address;
862547f9a21SEric Moore 	int	i, j;
863547f9a21SEric Moore 
864547f9a21SEric Moore 	mutex_lock(&ioc->sas_topology_mutex);
865547f9a21SEric Moore 
866547f9a21SEric Moore 	phy_info = port_info->phy_info;
867547f9a21SEric Moore 	for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) {
868547f9a21SEric Moore 		if (phy_info->attached.handle)
869547f9a21SEric Moore 			continue;
870547f9a21SEric Moore 		port_details = phy_info->port_details;
871547f9a21SEric Moore 		if (!port_details)
872547f9a21SEric Moore 			continue;
873547f9a21SEric Moore 		if (port_details->num_phys < 2)
874547f9a21SEric Moore 			continue;
875547f9a21SEric Moore 		/*
876547f9a21SEric Moore 		 * Removing a phy from a port, letting the last
877547f9a21SEric Moore 		 * phy be removed by firmware events.
878547f9a21SEric Moore 		 */
87929dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
880dc22f16dSEric Moore 		    "%s: [%p]: deleting phy = %d\n",
881cadbd4a5SHarvey Harrison 		    ioc->name, __func__, port_details, i));
882547f9a21SEric Moore 		port_details->num_phys--;
883547f9a21SEric Moore 		port_details->phy_bitmask &= ~ (1 << phy_info->phy_id);
884547f9a21SEric Moore 		memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
8859e39089bSKashyap, Desai 		if (phy_info->phy) {
8869e39089bSKashyap, Desai 			devtprintk(ioc, dev_printk(KERN_DEBUG,
8879e39089bSKashyap, Desai 				&phy_info->phy->dev, MYIOC_s_FMT
8889e39089bSKashyap, Desai 				"delete phy %d, phy-obj (0x%p)\n", ioc->name,
8899e39089bSKashyap, Desai 				phy_info->phy_id, phy_info->phy));
890547f9a21SEric Moore 			sas_port_delete_phy(port_details->port, phy_info->phy);
8919e39089bSKashyap, Desai 		}
892547f9a21SEric Moore 		phy_info->port_details = NULL;
893547f9a21SEric Moore 	}
894547f9a21SEric Moore 
895547f9a21SEric Moore 	/*
896547f9a21SEric Moore 	 * Populate and refresh the tree
897547f9a21SEric Moore 	 */
898547f9a21SEric Moore 	phy_info = port_info->phy_info;
899547f9a21SEric Moore 	for (i = 0 ; i < port_info->num_phys ; i++, phy_info++) {
900547f9a21SEric Moore 		sas_address = phy_info->attached.sas_address;
90129dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "phy_id=%d sas_address=0x%018llX\n",
90229dd3609SEric Moore 		    ioc->name, i, (unsigned long long)sas_address));
903547f9a21SEric Moore 		if (!sas_address)
904547f9a21SEric Moore 			continue;
905547f9a21SEric Moore 		port_details = phy_info->port_details;
906547f9a21SEric Moore 		/*
907547f9a21SEric Moore 		 * Forming a port
908547f9a21SEric Moore 		 */
909547f9a21SEric Moore 		if (!port_details) {
9102f187862SKashyap, Desai 			port_details = kzalloc(sizeof(struct
9112f187862SKashyap, Desai 				mptsas_portinfo_details), GFP_KERNEL);
912547f9a21SEric Moore 			if (!port_details)
913547f9a21SEric Moore 				goto out;
914547f9a21SEric Moore 			port_details->num_phys = 1;
915547f9a21SEric Moore 			port_details->port_info = port_info;
916547f9a21SEric Moore 			if (phy_info->phy_id < 64 )
917547f9a21SEric Moore 				port_details->phy_bitmask |=
918547f9a21SEric Moore 				    (1 << phy_info->phy_id);
919547f9a21SEric Moore 			phy_info->sas_port_add_phy=1;
92029dd3609SEric Moore 			dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tForming port\n\t\t"
921547f9a21SEric Moore 			    "phy_id=%d sas_address=0x%018llX\n",
92229dd3609SEric Moore 			    ioc->name, i, (unsigned long long)sas_address));
923547f9a21SEric Moore 			phy_info->port_details = port_details;
924547f9a21SEric Moore 		}
925547f9a21SEric Moore 
926547f9a21SEric Moore 		if (i == port_info->num_phys - 1)
927547f9a21SEric Moore 			continue;
928547f9a21SEric Moore 		phy_info_cmp = &port_info->phy_info[i + 1];
929547f9a21SEric Moore 		for (j = i + 1 ; j < port_info->num_phys ; j++,
930547f9a21SEric Moore 		    phy_info_cmp++) {
931547f9a21SEric Moore 			if (!phy_info_cmp->attached.sas_address)
932547f9a21SEric Moore 				continue;
933547f9a21SEric Moore 			if (sas_address != phy_info_cmp->attached.sas_address)
934547f9a21SEric Moore 				continue;
935547f9a21SEric Moore 			if (phy_info_cmp->port_details == port_details )
936547f9a21SEric Moore 				continue;
93729dd3609SEric Moore 			dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
938547f9a21SEric Moore 			    "\t\tphy_id=%d sas_address=0x%018llX\n",
93929dd3609SEric Moore 			    ioc->name, j, (unsigned long long)
940f99be43bSEric Moore 			    phy_info_cmp->attached.sas_address));
941547f9a21SEric Moore 			if (phy_info_cmp->port_details) {
942547f9a21SEric Moore 				port_details->rphy =
943547f9a21SEric Moore 				    mptsas_get_rphy(phy_info_cmp);
944547f9a21SEric Moore 				port_details->port =
945547f9a21SEric Moore 				    mptsas_get_port(phy_info_cmp);
946547f9a21SEric Moore 				port_details->starget =
947547f9a21SEric Moore 				    mptsas_get_starget(phy_info_cmp);
948547f9a21SEric Moore 				port_details->num_phys =
949547f9a21SEric Moore 					phy_info_cmp->port_details->num_phys;
950547f9a21SEric Moore 				if (!phy_info_cmp->port_details->num_phys)
951547f9a21SEric Moore 					kfree(phy_info_cmp->port_details);
952547f9a21SEric Moore 			} else
953547f9a21SEric Moore 				phy_info_cmp->sas_port_add_phy=1;
954547f9a21SEric Moore 			/*
955547f9a21SEric Moore 			 * Adding a phy to a port
956547f9a21SEric Moore 			 */
957547f9a21SEric Moore 			phy_info_cmp->port_details = port_details;
958547f9a21SEric Moore 			if (phy_info_cmp->phy_id < 64 )
959547f9a21SEric Moore 				port_details->phy_bitmask |=
960547f9a21SEric Moore 				(1 << phy_info_cmp->phy_id);
961547f9a21SEric Moore 			port_details->num_phys++;
962547f9a21SEric Moore 		}
963547f9a21SEric Moore 	}
964547f9a21SEric Moore 
965547f9a21SEric Moore  out:
966547f9a21SEric Moore 
967547f9a21SEric Moore 	for (i = 0; i < port_info->num_phys; i++) {
968547f9a21SEric Moore 		port_details = port_info->phy_info[i].port_details;
969547f9a21SEric Moore 		if (!port_details)
970547f9a21SEric Moore 			continue;
97129dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
972dc22f16dSEric Moore 		    "%s: [%p]: phy_id=%02d num_phys=%02d "
973cadbd4a5SHarvey Harrison 		    "bitmask=0x%016llX\n", ioc->name, __func__,
974dc22f16dSEric Moore 		    port_details, i, port_details->num_phys,
975f99be43bSEric Moore 		    (unsigned long long)port_details->phy_bitmask));
97629dd3609SEric Moore 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tport = %p rphy=%p\n",
97729dd3609SEric Moore 		    ioc->name, port_details->port, port_details->rphy));
978547f9a21SEric Moore 	}
97929dd3609SEric Moore 	dsaswideprintk(ioc, printk("\n"));
980547f9a21SEric Moore 	mutex_unlock(&ioc->sas_topology_mutex);
981547f9a21SEric Moore }
982547f9a21SEric Moore 
983df9e062aSEric Moore /**
984cdcda465SRandy Dunlap  * mptsas_find_vtarget - find a virtual target device (FC LUN device or
985cdcda465SRandy Dunlap  *				SCSI target device)
986df9e062aSEric Moore  *
987cdcda465SRandy Dunlap  * @ioc: Pointer to MPT_ADAPTER structure
988cdcda465SRandy Dunlap  * @channel: channel number
989cdcda465SRandy Dunlap  * @id: Logical Target ID
990df9e062aSEric Moore  *
991df9e062aSEric Moore  **/
992df9e062aSEric Moore static VirtTarget *
mptsas_find_vtarget(MPT_ADAPTER * ioc,u8 channel,u8 id)993df9e062aSEric Moore mptsas_find_vtarget(MPT_ADAPTER *ioc, u8 channel, u8 id)
994df9e062aSEric Moore {
995df9e062aSEric Moore 	struct scsi_device 		*sdev;
996a69de507SEric Moore 	VirtDevice			*vdevice;
997df9e062aSEric Moore 	VirtTarget 			*vtarget = NULL;
998df9e062aSEric Moore 
999df9e062aSEric Moore 	shost_for_each_device(sdev, ioc->sh) {
1000e7deff33SKashyap, Desai 		vdevice = sdev->hostdata;
1001e7deff33SKashyap, Desai 		if ((vdevice == NULL) ||
1002e7deff33SKashyap, Desai 			(vdevice->vtarget == NULL))
1003df9e062aSEric Moore 			continue;
1004a7938b0bSKashyap, Desai 		if ((vdevice->vtarget->tflags &
1005a7938b0bSKashyap, Desai 		    MPT_TARGET_FLAGS_RAID_COMPONENT ||
1006a7938b0bSKashyap, Desai 		    vdevice->vtarget->raidVolume))
1007a7938b0bSKashyap, Desai 			continue;
1008a69de507SEric Moore 		if (vdevice->vtarget->id == id &&
1009a69de507SEric Moore 			vdevice->vtarget->channel == channel)
1010a69de507SEric Moore 			vtarget = vdevice->vtarget;
1011df9e062aSEric Moore 	}
1012df9e062aSEric Moore 	return vtarget;
1013df9e062aSEric Moore }
1014df9e062aSEric Moore 
10153eb0822cSKashyap, Desai static void
mptsas_queue_device_delete(MPT_ADAPTER * ioc,MpiEventDataSasDeviceStatusChange_t * sas_event_data)10163eb0822cSKashyap, Desai mptsas_queue_device_delete(MPT_ADAPTER *ioc,
10173eb0822cSKashyap, Desai 	MpiEventDataSasDeviceStatusChange_t *sas_event_data)
10183eb0822cSKashyap, Desai {
10193eb0822cSKashyap, Desai 	struct fw_event_work *fw_event;
10203eb0822cSKashyap, Desai 
102132696198SJoe Lawrence 	fw_event = kzalloc(sizeof(*fw_event) +
102232696198SJoe Lawrence 			   sizeof(MpiEventDataSasDeviceStatusChange_t),
102332696198SJoe Lawrence 			   GFP_ATOMIC);
10243eb0822cSKashyap, Desai 	if (!fw_event) {
10253eb0822cSKashyap, Desai 		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
10263eb0822cSKashyap, Desai 		    ioc->name, __func__, __LINE__);
10273eb0822cSKashyap, Desai 		return;
10283eb0822cSKashyap, Desai 	}
10293eb0822cSKashyap, Desai 	memcpy(fw_event->event_data, sas_event_data,
10303eb0822cSKashyap, Desai 	    sizeof(MpiEventDataSasDeviceStatusChange_t));
10313eb0822cSKashyap, Desai 	fw_event->event = MPI_EVENT_SAS_DEVICE_STATUS_CHANGE;
10323eb0822cSKashyap, Desai 	fw_event->ioc = ioc;
10333eb0822cSKashyap, Desai 	mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
10343eb0822cSKashyap, Desai }
10353eb0822cSKashyap, Desai 
1036eedf92b9SKashyap, Desai static void
mptsas_queue_rescan(MPT_ADAPTER * ioc)1037eedf92b9SKashyap, Desai mptsas_queue_rescan(MPT_ADAPTER *ioc)
1038eedf92b9SKashyap, Desai {
1039eedf92b9SKashyap, Desai 	struct fw_event_work *fw_event;
1040eedf92b9SKashyap, Desai 
104132696198SJoe Lawrence 	fw_event = kzalloc(sizeof(*fw_event), GFP_ATOMIC);
1042eedf92b9SKashyap, Desai 	if (!fw_event) {
1043eedf92b9SKashyap, Desai 		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n",
1044eedf92b9SKashyap, Desai 		    ioc->name, __func__, __LINE__);
1045eedf92b9SKashyap, Desai 		return;
1046eedf92b9SKashyap, Desai 	}
1047eedf92b9SKashyap, Desai 	fw_event->event = -1;
1048eedf92b9SKashyap, Desai 	fw_event->ioc = ioc;
1049eedf92b9SKashyap, Desai 	mptsas_add_fw_event(ioc, fw_event, msecs_to_jiffies(1));
1050eedf92b9SKashyap, Desai }
1051eedf92b9SKashyap, Desai 
10523eb0822cSKashyap, Desai 
1053df9e062aSEric Moore /**
1054cdcda465SRandy Dunlap  * mptsas_target_reset - Issues TARGET_RESET to end device using
1055cdcda465SRandy Dunlap  *			 handshaking method
1056df9e062aSEric Moore  *
1057cdcda465SRandy Dunlap  * @ioc: Pointer to MPT_ADAPTER structure
1058cdcda465SRandy Dunlap  * @channel: channel number
1059cdcda465SRandy Dunlap  * @id: Logical Target ID for reset
1060df9e062aSEric Moore  *
1061cdcda465SRandy Dunlap  * Return: (1) success
1062df9e062aSEric Moore  *         (0) failure
1063df9e062aSEric Moore  *
1064df9e062aSEric Moore  **/
1065df9e062aSEric Moore static int
mptsas_target_reset(MPT_ADAPTER * ioc,u8 channel,u8 id)1066df9e062aSEric Moore mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
1067df9e062aSEric Moore {
1068df9e062aSEric Moore 	MPT_FRAME_HDR	*mf;
1069df9e062aSEric Moore 	SCSITaskMgmt_t	*pScsiTm;
1070ea2a788dSKashyap, Desai 	if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0)
1071ea2a788dSKashyap, Desai 		return 0;
1072ea2a788dSKashyap, Desai 
1073df9e062aSEric Moore 
1074e7deff33SKashyap, Desai 	mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
1075e7deff33SKashyap, Desai 	if (mf == NULL) {
1076e7deff33SKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
1077ea2a788dSKashyap, Desai 			"%s, no msg frames @%d!!\n", ioc->name,
1078ea2a788dSKashyap, Desai 			__func__, __LINE__));
1079ea2a788dSKashyap, Desai 		goto out_fail;
1080df9e062aSEric Moore 	}
1081df9e062aSEric Moore 
1082ea2a788dSKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n",
1083ea2a788dSKashyap, Desai 		ioc->name, mf));
1084ea2a788dSKashyap, Desai 
1085df9e062aSEric Moore 	/* Format the Request
1086df9e062aSEric Moore 	 */
1087df9e062aSEric Moore 	pScsiTm = (SCSITaskMgmt_t *) mf;
1088df9e062aSEric Moore 	memset (pScsiTm, 0, sizeof(SCSITaskMgmt_t));
1089df9e062aSEric Moore 	pScsiTm->TargetID = id;
1090df9e062aSEric Moore 	pScsiTm->Bus = channel;
1091df9e062aSEric Moore 	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1092df9e062aSEric Moore 	pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
1093df9e062aSEric Moore 	pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
1094df9e062aSEric Moore 
1095d6ecdd63SPrakash, Sathya 	DBG_DUMP_TM_REQUEST_FRAME(ioc, (u32 *)mf);
1096df9e062aSEric Moore 
1097ea2a788dSKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
1098ea2a788dSKashyap, Desai 	   "TaskMgmt type=%d (sas device delete) fw_channel = %d fw_id = %d)\n",
1099ea2a788dSKashyap, Desai 	   ioc->name, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, channel, id));
1100ea2a788dSKashyap, Desai 
1101e7deff33SKashyap, Desai 	mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
1102df9e062aSEric Moore 
1103df9e062aSEric Moore 	return 1;
1104ea2a788dSKashyap, Desai 
1105ea2a788dSKashyap, Desai  out_fail:
1106ea2a788dSKashyap, Desai 
1107ea2a788dSKashyap, Desai 	mpt_clear_taskmgmt_in_progress_flag(ioc);
1108ea2a788dSKashyap, Desai 	return 0;
1109df9e062aSEric Moore }
1110df9e062aSEric Moore 
111164e155adSKashyap, Desai static void
mptsas_block_io_sdev(struct scsi_device * sdev,void * data)111264e155adSKashyap, Desai mptsas_block_io_sdev(struct scsi_device *sdev, void *data)
111364e155adSKashyap, Desai {
111464e155adSKashyap, Desai 	scsi_device_set_state(sdev, SDEV_BLOCK);
111564e155adSKashyap, Desai }
111664e155adSKashyap, Desai 
111764e155adSKashyap, Desai static void
mptsas_block_io_starget(struct scsi_target * starget)111864e155adSKashyap, Desai mptsas_block_io_starget(struct scsi_target *starget)
111964e155adSKashyap, Desai {
112064e155adSKashyap, Desai 	if (starget)
112164e155adSKashyap, Desai 		starget_for_each_device(starget, NULL, mptsas_block_io_sdev);
112264e155adSKashyap, Desai }
112364e155adSKashyap, Desai 
1124df9e062aSEric Moore /**
1125cdcda465SRandy Dunlap  * mptsas_target_reset_queue - queue a target reset
1126df9e062aSEric Moore  *
1127cdcda465SRandy Dunlap  * @ioc: Pointer to MPT_ADAPTER structure
1128cdcda465SRandy Dunlap  * @sas_event_data: SAS Device Status Change Event data
1129cdcda465SRandy Dunlap  *
1130cdcda465SRandy Dunlap  * Receive request for TARGET_RESET after receiving a firmware
1131df9e062aSEric Moore  * event NOT_RESPONDING_EVENT, then put command in link list
1132df9e062aSEric Moore  * and queue if task_queue already in use.
1133df9e062aSEric Moore  *
1134df9e062aSEric Moore  **/
1135547f9a21SEric Moore static void
mptsas_target_reset_queue(MPT_ADAPTER * ioc,EVENT_DATA_SAS_DEVICE_STATUS_CHANGE * sas_event_data)1136df9e062aSEric Moore mptsas_target_reset_queue(MPT_ADAPTER *ioc,
1137df9e062aSEric Moore     EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data)
1138547f9a21SEric Moore {
1139e7eae9f6SEric Moore 	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
1140df9e062aSEric Moore 	VirtTarget *vtarget = NULL;
1141df9e062aSEric Moore 	struct mptsas_target_reset_event *target_reset_list;
1142df9e062aSEric Moore 	u8		id, channel;
1143547f9a21SEric Moore 
1144df9e062aSEric Moore 	id = sas_event_data->TargetID;
1145df9e062aSEric Moore 	channel = sas_event_data->Bus;
1146df9e062aSEric Moore 
114764e155adSKashyap, Desai 	vtarget = mptsas_find_vtarget(ioc, channel, id);
114864e155adSKashyap, Desai 	if (vtarget) {
114964e155adSKashyap, Desai 		mptsas_block_io_starget(vtarget->starget);
1150df9e062aSEric Moore 		vtarget->deleted = 1; /* block IO */
115164e155adSKashyap, Desai 	}
1152df9e062aSEric Moore 
11532f187862SKashyap, Desai 	target_reset_list = kzalloc(sizeof(struct mptsas_target_reset_event),
1154df9e062aSEric Moore 	    GFP_ATOMIC);
1155df9e062aSEric Moore 	if (!target_reset_list) {
1156e7deff33SKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_WARN_FMT
1157e7deff33SKashyap, Desai 			"%s, failed to allocate mem @%d..!!\n",
1158cadbd4a5SHarvey Harrison 			ioc->name, __func__, __LINE__));
1159df9e062aSEric Moore 		return;
1160547f9a21SEric Moore 	}
1161df9e062aSEric Moore 
1162df9e062aSEric Moore 	memcpy(&target_reset_list->sas_event_data, sas_event_data,
1163df9e062aSEric Moore 		sizeof(*sas_event_data));
1164df9e062aSEric Moore 	list_add_tail(&target_reset_list->list, &hd->target_reset_list);
1165df9e062aSEric Moore 
1166e7deff33SKashyap, Desai 	target_reset_list->time_count = jiffies;
1167df9e062aSEric Moore 
1168df9e062aSEric Moore 	if (mptsas_target_reset(ioc, channel, id)) {
1169df9e062aSEric Moore 		target_reset_list->target_reset_issued = 1;
1170df9e062aSEric Moore 	}
1171df9e062aSEric Moore }
1172df9e062aSEric Moore 
1173df9e062aSEric Moore /**
1174b68bf096SKashyap, Desai  * mptsas_schedule_target_reset- send pending target reset
1175b68bf096SKashyap, Desai  * @iocp: per adapter object
1176b68bf096SKashyap, Desai  *
1177b68bf096SKashyap, Desai  * This function will delete scheduled target reset from the list and
1178b68bf096SKashyap, Desai  * try to send next target reset. This will be called from completion
1179b595076aSUwe Kleine-König  * context of any Task management command.
1180b68bf096SKashyap, Desai  */
1181b68bf096SKashyap, Desai 
1182b68bf096SKashyap, Desai void
mptsas_schedule_target_reset(void * iocp)1183b68bf096SKashyap, Desai mptsas_schedule_target_reset(void *iocp)
1184b68bf096SKashyap, Desai {
1185b68bf096SKashyap, Desai 	MPT_ADAPTER *ioc = (MPT_ADAPTER *)(iocp);
1186b68bf096SKashyap, Desai 	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
1187b68bf096SKashyap, Desai 	struct list_head *head = &hd->target_reset_list;
1188b68bf096SKashyap, Desai 	struct mptsas_target_reset_event	*target_reset_list;
1189b68bf096SKashyap, Desai 	u8		id, channel;
1190b68bf096SKashyap, Desai 	/*
1191b68bf096SKashyap, Desai 	 * issue target reset to next device in the queue
1192b68bf096SKashyap, Desai 	 */
1193b68bf096SKashyap, Desai 
1194b68bf096SKashyap, Desai 	if (list_empty(head))
1195b68bf096SKashyap, Desai 		return;
1196b68bf096SKashyap, Desai 
1197b68bf096SKashyap, Desai 	target_reset_list = list_entry(head->next,
1198b68bf096SKashyap, Desai 		struct mptsas_target_reset_event, list);
1199b68bf096SKashyap, Desai 
1200b68bf096SKashyap, Desai 	id = target_reset_list->sas_event_data.TargetID;
1201b68bf096SKashyap, Desai 	channel = target_reset_list->sas_event_data.Bus;
1202b68bf096SKashyap, Desai 	target_reset_list->time_count = jiffies;
1203b68bf096SKashyap, Desai 
1204b68bf096SKashyap, Desai 	if (mptsas_target_reset(ioc, channel, id))
1205b68bf096SKashyap, Desai 		target_reset_list->target_reset_issued = 1;
1206b68bf096SKashyap, Desai 	return;
1207b68bf096SKashyap, Desai }
1208b68bf096SKashyap, Desai 
1209b68bf096SKashyap, Desai 
1210b68bf096SKashyap, Desai /**
1211fc847ab4SJames Bottomley  *	mptsas_taskmgmt_complete - complete SAS task management function
1212e7deff33SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
1213cdcda465SRandy Dunlap  *	@mf: MPT message frame
1214cdcda465SRandy Dunlap  *	@mr: SCSI Task Management Reply structure ptr (may be %NULL)
1215df9e062aSEric Moore  *
1216fc847ab4SJames Bottomley  *	Completion for TARGET_RESET after NOT_RESPONDING_EVENT, enable work
1217cdcda465SRandy Dunlap  *	queue to finish off removing device from upper layers, then send next
1218fc847ab4SJames Bottomley  *	TARGET_RESET in the queue.
1219df9e062aSEric Moore  **/
1220e7deff33SKashyap, Desai static int
mptsas_taskmgmt_complete(MPT_ADAPTER * ioc,MPT_FRAME_HDR * mf,MPT_FRAME_HDR * mr)1221e7deff33SKashyap, Desai mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
1222df9e062aSEric Moore {
1223e7eae9f6SEric Moore 	MPT_SCSI_HOST	*hd = shost_priv(ioc->sh);
1224df9e062aSEric Moore         struct list_head *head = &hd->target_reset_list;
1225df9e062aSEric Moore 	u8		id, channel;
1226e7deff33SKashyap, Desai 	struct mptsas_target_reset_event	*target_reset_list;
1227e7deff33SKashyap, Desai 	SCSITaskMgmtReply_t *pScsiTmReply;
1228e7deff33SKashyap, Desai 
1229e7deff33SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed: "
1230e7deff33SKashyap, Desai 	    "(mf = %p, mr = %p)\n", ioc->name, mf, mr));
1231e7deff33SKashyap, Desai 
1232e7deff33SKashyap, Desai 	pScsiTmReply = (SCSITaskMgmtReply_t *)mr;
12339f21316fSJoe Lawrence 	if (!pScsiTmReply)
12349f21316fSJoe Lawrence 		return 0;
12359f21316fSJoe Lawrence 
1236e7deff33SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
1237e7deff33SKashyap, Desai 	    "\tTaskMgmt completed: fw_channel = %d, fw_id = %d,\n"
1238e7deff33SKashyap, Desai 	    "\ttask_type = 0x%02X, iocstatus = 0x%04X "
1239e7deff33SKashyap, Desai 	    "loginfo = 0x%08X,\n\tresponse_code = 0x%02X, "
1240e7deff33SKashyap, Desai 	    "term_cmnds = %d\n", ioc->name,
1241e7deff33SKashyap, Desai 	    pScsiTmReply->Bus, pScsiTmReply->TargetID,
1242e7deff33SKashyap, Desai 	    pScsiTmReply->TaskType,
1243e7deff33SKashyap, Desai 	    le16_to_cpu(pScsiTmReply->IOCStatus),
1244e7deff33SKashyap, Desai 	    le32_to_cpu(pScsiTmReply->IOCLogInfo),
1245e7deff33SKashyap, Desai 	    pScsiTmReply->ResponseCode,
1246e7deff33SKashyap, Desai 	    le32_to_cpu(pScsiTmReply->TerminationCount)));
1247e7deff33SKashyap, Desai 
1248e7deff33SKashyap, Desai 	if (pScsiTmReply->ResponseCode)
1249e7deff33SKashyap, Desai 		mptscsih_taskmgmt_response_code(ioc,
1250e7deff33SKashyap, Desai 		pScsiTmReply->ResponseCode);
1251e7deff33SKashyap, Desai 
12529f21316fSJoe Lawrence 	if (pScsiTmReply->TaskType ==
1253e7deff33SKashyap, Desai 	    MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK || pScsiTmReply->TaskType ==
12549f21316fSJoe Lawrence 	     MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET) {
1255e7deff33SKashyap, Desai 		ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
1256e7deff33SKashyap, Desai 		ioc->taskmgmt_cmds.status |= MPT_MGMT_STATUS_RF_VALID;
1257e7deff33SKashyap, Desai 		memcpy(ioc->taskmgmt_cmds.reply, mr,
1258e7deff33SKashyap, Desai 		    min(MPT_DEFAULT_FRAME_SIZE, 4 * mr->u.reply.MsgLength));
1259e7deff33SKashyap, Desai 		if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_PENDING) {
1260e7deff33SKashyap, Desai 			ioc->taskmgmt_cmds.status &= ~MPT_MGMT_STATUS_PENDING;
1261e7deff33SKashyap, Desai 			complete(&ioc->taskmgmt_cmds.done);
1262e7deff33SKashyap, Desai 			return 1;
1263e7deff33SKashyap, Desai 		}
1264e7deff33SKashyap, Desai 		return 0;
1265e7deff33SKashyap, Desai 	}
1266e7deff33SKashyap, Desai 
1267e7deff33SKashyap, Desai 	mpt_clear_taskmgmt_in_progress_flag(ioc);
1268df9e062aSEric Moore 
1269df9e062aSEric Moore 	if (list_empty(head))
1270e7deff33SKashyap, Desai 		return 1;
1271df9e062aSEric Moore 
1272e7deff33SKashyap, Desai 	target_reset_list = list_entry(head->next,
1273e7deff33SKashyap, Desai 	    struct mptsas_target_reset_event, list);
1274e7deff33SKashyap, Desai 
1275e7deff33SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
1276e7deff33SKashyap, Desai 	    "TaskMgmt: completed (%d seconds)\n",
1277e7deff33SKashyap, Desai 	    ioc->name, jiffies_to_msecs(jiffies -
1278e7deff33SKashyap, Desai 	    target_reset_list->time_count)/1000));
1279df9e062aSEric Moore 
1280e7deff33SKashyap, Desai 	id = pScsiTmReply->TargetID;
1281e7deff33SKashyap, Desai 	channel = pScsiTmReply->Bus;
1282e7deff33SKashyap, Desai 	target_reset_list->time_count = jiffies;
1283df9e062aSEric Moore 
1284df9e062aSEric Moore 	/*
1285df9e062aSEric Moore 	 * retry target reset
1286df9e062aSEric Moore 	 */
1287df9e062aSEric Moore 	if (!target_reset_list->target_reset_issued) {
1288e7deff33SKashyap, Desai 		if (mptsas_target_reset(ioc, channel, id))
1289df9e062aSEric Moore 			target_reset_list->target_reset_issued = 1;
1290e7deff33SKashyap, Desai 		return 1;
1291df9e062aSEric Moore 	}
1292df9e062aSEric Moore 
1293df9e062aSEric Moore 	/*
1294df9e062aSEric Moore 	 * enable work queue to remove device from upper layers
1295df9e062aSEric Moore 	 */
1296df9e062aSEric Moore 	list_del(&target_reset_list->list);
12973e84bebaSKei Tokunaga 	if (!ioc->fw_events_off)
12983eb0822cSKashyap, Desai 		mptsas_queue_device_delete(ioc,
12993eb0822cSKashyap, Desai 			&target_reset_list->sas_event_data);
1300ea2a788dSKashyap, Desai 
1301e7deff33SKashyap, Desai 
1302b68bf096SKashyap, Desai 	ioc->schedule_target_reset(ioc);
1303df9e062aSEric Moore 
1304e7deff33SKashyap, Desai 	return 1;
1305df9e062aSEric Moore }
1306df9e062aSEric Moore 
1307df9e062aSEric Moore /**
1308cdcda465SRandy Dunlap  * mptsas_ioc_reset - issue an IOC reset for this reset phase
1309df9e062aSEric Moore  *
1310cdcda465SRandy Dunlap  * @ioc: Pointer to MPT_ADAPTER structure
1311cdcda465SRandy Dunlap  * @reset_phase: id of phase of reset
1312df9e062aSEric Moore  *
1313df9e062aSEric Moore  **/
1314df9e062aSEric Moore static int
mptsas_ioc_reset(MPT_ADAPTER * ioc,int reset_phase)1315df9e062aSEric Moore mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
1316df9e062aSEric Moore {
1317ba76ef24SJudith Lebzelter 	MPT_SCSI_HOST	*hd;
1318df9e062aSEric Moore 	int rc;
1319df9e062aSEric Moore 
1320df9e062aSEric Moore 	rc = mptscsih_ioc_reset(ioc, reset_phase);
13213eb0822cSKashyap, Desai 	if ((ioc->bus_type != SAS) || (!rc))
13223eb0822cSKashyap, Desai 		return rc;
1323df9e062aSEric Moore 
1324e7eae9f6SEric Moore 	hd = shost_priv(ioc->sh);
1325ba76ef24SJudith Lebzelter 	if (!hd->ioc)
1326df9e062aSEric Moore 		goto out;
1327df9e062aSEric Moore 
13283eb0822cSKashyap, Desai 	switch (reset_phase) {
13293eb0822cSKashyap, Desai 	case MPT_IOC_SETUP_RESET:
13303eb0822cSKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
13313eb0822cSKashyap, Desai 		    "%s: MPT_IOC_SETUP_RESET\n", ioc->name, __func__));
13323eb0822cSKashyap, Desai 		mptsas_fw_event_off(ioc);
13333eb0822cSKashyap, Desai 		break;
13343eb0822cSKashyap, Desai 	case MPT_IOC_PRE_RESET:
13353eb0822cSKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
13363eb0822cSKashyap, Desai 		    "%s: MPT_IOC_PRE_RESET\n", ioc->name, __func__));
13373eb0822cSKashyap, Desai 		break;
13383eb0822cSKashyap, Desai 	case MPT_IOC_POST_RESET:
13393eb0822cSKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
13403eb0822cSKashyap, Desai 		    "%s: MPT_IOC_POST_RESET\n", ioc->name, __func__));
13413eb0822cSKashyap, Desai 		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
13423eb0822cSKashyap, Desai 			ioc->sas_mgmt.status |= MPT_MGMT_STATUS_DID_IOCRESET;
13433eb0822cSKashyap, Desai 			complete(&ioc->sas_mgmt.done);
13443eb0822cSKashyap, Desai 		}
13453eb0822cSKashyap, Desai 		mptsas_cleanup_fw_event_q(ioc);
1346eedf92b9SKashyap, Desai 		mptsas_queue_rescan(ioc);
13473eb0822cSKashyap, Desai 		break;
13483eb0822cSKashyap, Desai 	default:
13493eb0822cSKashyap, Desai 		break;
1350df9e062aSEric Moore 	}
1351df9e062aSEric Moore 
1352df9e062aSEric Moore  out:
1353df9e062aSEric Moore 	return rc;
1354547f9a21SEric Moore }
1355547f9a21SEric Moore 
13563eb0822cSKashyap, Desai 
13573eb0822cSKashyap, Desai /**
1358cdcda465SRandy Dunlap  * enum device_state - TUR device state
13593eb0822cSKashyap, Desai  * @DEVICE_RETRY: need to retry the TUR
13603eb0822cSKashyap, Desai  * @DEVICE_ERROR: TUR return error, don't add device
13613eb0822cSKashyap, Desai  * @DEVICE_READY: device can be added
13623eb0822cSKashyap, Desai  *
13633eb0822cSKashyap, Desai  */
13643eb0822cSKashyap, Desai enum device_state{
13653eb0822cSKashyap, Desai 	DEVICE_RETRY,
13663eb0822cSKashyap, Desai 	DEVICE_ERROR,
13673eb0822cSKashyap, Desai 	DEVICE_READY,
13683eb0822cSKashyap, Desai };
13693eb0822cSKashyap, Desai 
1370e3094447SChristoph Hellwig static int
mptsas_sas_enclosure_pg0(MPT_ADAPTER * ioc,struct mptsas_enclosure * enclosure,u32 form,u32 form_specific)137152435430SMoore, Eric mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
1372e3094447SChristoph Hellwig 		u32 form, u32 form_specific)
1373e3094447SChristoph Hellwig {
1374e3094447SChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
1375e3094447SChristoph Hellwig 	CONFIGPARMS cfg;
1376e3094447SChristoph Hellwig 	SasEnclosurePage0_t *buffer;
1377e3094447SChristoph Hellwig 	dma_addr_t dma_handle;
1378e3094447SChristoph Hellwig 	int error;
1379e3094447SChristoph Hellwig 	__le64 le_identifier;
1380e3094447SChristoph Hellwig 
1381e3094447SChristoph Hellwig 	memset(&hdr, 0, sizeof(hdr));
1382e3094447SChristoph Hellwig 	hdr.PageVersion = MPI_SASENCLOSURE0_PAGEVERSION;
1383e3094447SChristoph Hellwig 	hdr.PageNumber = 0;
1384e3094447SChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
1385e3094447SChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_ENCLOSURE;
1386e3094447SChristoph Hellwig 
1387e3094447SChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
1388e3094447SChristoph Hellwig 	cfg.physAddr = -1;
1389e3094447SChristoph Hellwig 	cfg.pageAddr = form + form_specific;
1390e3094447SChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
1391e3094447SChristoph Hellwig 	cfg.dir = 0;	/* read */
13924b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
1393e3094447SChristoph Hellwig 
1394e3094447SChristoph Hellwig 	error = mpt_config(ioc, &cfg);
1395e3094447SChristoph Hellwig 	if (error)
1396e3094447SChristoph Hellwig 		goto out;
1397e3094447SChristoph Hellwig 	if (!hdr.ExtPageLength) {
1398e3094447SChristoph Hellwig 		error = -ENXIO;
1399e3094447SChristoph Hellwig 		goto out;
1400e3094447SChristoph Hellwig 	}
1401e3094447SChristoph Hellwig 
140276a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
140376a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
1404e3094447SChristoph Hellwig 	if (!buffer) {
1405e3094447SChristoph Hellwig 		error = -ENOMEM;
1406e3094447SChristoph Hellwig 		goto out;
1407e3094447SChristoph Hellwig 	}
1408e3094447SChristoph Hellwig 
1409e3094447SChristoph Hellwig 	cfg.physAddr = dma_handle;
1410e3094447SChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
1411e3094447SChristoph Hellwig 
1412e3094447SChristoph Hellwig 	error = mpt_config(ioc, &cfg);
1413e3094447SChristoph Hellwig 	if (error)
1414e3094447SChristoph Hellwig 		goto out_free_consistent;
1415e3094447SChristoph Hellwig 
1416e3094447SChristoph Hellwig 	/* save config data */
1417e3094447SChristoph Hellwig 	memcpy(&le_identifier, &buffer->EnclosureLogicalID, sizeof(__le64));
1418e3094447SChristoph Hellwig 	enclosure->enclosure_logical_id = le64_to_cpu(le_identifier);
1419e3094447SChristoph Hellwig 	enclosure->enclosure_handle = le16_to_cpu(buffer->EnclosureHandle);
1420e3094447SChristoph Hellwig 	enclosure->flags = le16_to_cpu(buffer->Flags);
1421e3094447SChristoph Hellwig 	enclosure->num_slot = le16_to_cpu(buffer->NumSlots);
1422e3094447SChristoph Hellwig 	enclosure->start_slot = le16_to_cpu(buffer->StartSlot);
1423e3094447SChristoph Hellwig 	enclosure->start_id = buffer->StartTargetID;
1424e3094447SChristoph Hellwig 	enclosure->start_channel = buffer->StartBus;
1425e3094447SChristoph Hellwig 	enclosure->sep_id = buffer->SEPTargetID;
1426e3094447SChristoph Hellwig 	enclosure->sep_channel = buffer->SEPBus;
1427e3094447SChristoph Hellwig 
1428e3094447SChristoph Hellwig  out_free_consistent:
1429b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
1430b114dda6SChristophe JAILLET 			  dma_handle);
1431e3094447SChristoph Hellwig  out:
1432e3094447SChristoph Hellwig 	return error;
1433e3094447SChristoph Hellwig }
1434b5141128SChristoph Hellwig 
14353eb0822cSKashyap, Desai /**
14363eb0822cSKashyap, Desai  *	mptsas_add_end_device - report a new end device to sas transport layer
14373eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
143825985edcSLucas De Marchi  *	@phy_info: describes attached device
14393eb0822cSKashyap, Desai  *
14403eb0822cSKashyap, Desai  *	return (0) success (1) failure
14413eb0822cSKashyap, Desai  *
14423eb0822cSKashyap, Desai  **/
14433eb0822cSKashyap, Desai static int
mptsas_add_end_device(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info)14443eb0822cSKashyap, Desai mptsas_add_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
14453eb0822cSKashyap, Desai {
14463eb0822cSKashyap, Desai 	struct sas_rphy *rphy;
14473eb0822cSKashyap, Desai 	struct sas_port *port;
14483eb0822cSKashyap, Desai 	struct sas_identify identify;
14493eb0822cSKashyap, Desai 	char *ds = NULL;
14503eb0822cSKashyap, Desai 	u8 fw_id;
14513eb0822cSKashyap, Desai 
14523eb0822cSKashyap, Desai 	if (!phy_info) {
14533eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
14543eb0822cSKashyap, Desai 			"%s: exit at line=%d\n", ioc->name,
14553eb0822cSKashyap, Desai 			 __func__, __LINE__));
14563eb0822cSKashyap, Desai 		return 1;
14573eb0822cSKashyap, Desai 	}
14583eb0822cSKashyap, Desai 
14593eb0822cSKashyap, Desai 	fw_id = phy_info->attached.id;
14603eb0822cSKashyap, Desai 
14613eb0822cSKashyap, Desai 	if (mptsas_get_rphy(phy_info)) {
14623eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
14633eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
14643eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
14653eb0822cSKashyap, Desai 		return 2;
14663eb0822cSKashyap, Desai 	}
14673eb0822cSKashyap, Desai 
14683eb0822cSKashyap, Desai 	port = mptsas_get_port(phy_info);
14693eb0822cSKashyap, Desai 	if (!port) {
14703eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
14713eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
14723eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
14733eb0822cSKashyap, Desai 		return 3;
14743eb0822cSKashyap, Desai 	}
14753eb0822cSKashyap, Desai 
14763eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
14773eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_SSP_TARGET)
14783eb0822cSKashyap, Desai 		ds = "ssp";
14793eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
14803eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_STP_TARGET)
14813eb0822cSKashyap, Desai 		ds = "stp";
14823eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
14833eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)
14843eb0822cSKashyap, Desai 		ds = "sata";
14853eb0822cSKashyap, Desai 
14863eb0822cSKashyap, Desai 	printk(MYIOC_s_INFO_FMT "attaching %s device: fw_channel %d, fw_id %d,"
14873eb0822cSKashyap, Desai 	    " phy %d, sas_addr 0x%llx\n", ioc->name, ds,
14883eb0822cSKashyap, Desai 	    phy_info->attached.channel, phy_info->attached.id,
14893eb0822cSKashyap, Desai 	    phy_info->attached.phy_id, (unsigned long long)
14903eb0822cSKashyap, Desai 	    phy_info->attached.sas_address);
14913eb0822cSKashyap, Desai 
14923eb0822cSKashyap, Desai 	mptsas_parse_device_info(&identify, &phy_info->attached);
14933eb0822cSKashyap, Desai 	rphy = sas_end_device_alloc(port);
14943eb0822cSKashyap, Desai 	if (!rphy) {
14953eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
14963eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
14973eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
14983eb0822cSKashyap, Desai 		return 5; /* non-fatal: an rphy can be added later */
14993eb0822cSKashyap, Desai 	}
15003eb0822cSKashyap, Desai 
15013eb0822cSKashyap, Desai 	rphy->identify = identify;
15023eb0822cSKashyap, Desai 	if (sas_rphy_add(rphy)) {
15033eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
15043eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
15053eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
15063eb0822cSKashyap, Desai 		sas_rphy_free(rphy);
15073eb0822cSKashyap, Desai 		return 6;
15083eb0822cSKashyap, Desai 	}
15093eb0822cSKashyap, Desai 	mptsas_set_rphy(ioc, phy_info, rphy);
15103eb0822cSKashyap, Desai 	return 0;
15113eb0822cSKashyap, Desai }
15123eb0822cSKashyap, Desai 
15133eb0822cSKashyap, Desai /**
1514fc847ab4SJames Bottomley  *	mptsas_del_end_device - report a deleted end device to sas transport layer
15153eb0822cSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
151625985edcSLucas De Marchi  *	@phy_info: describes attached device
15173eb0822cSKashyap, Desai  *
15183eb0822cSKashyap, Desai  **/
15193eb0822cSKashyap, Desai static void
mptsas_del_end_device(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info)15203eb0822cSKashyap, Desai mptsas_del_end_device(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info)
15213eb0822cSKashyap, Desai {
15223eb0822cSKashyap, Desai 	struct sas_rphy *rphy;
15233eb0822cSKashyap, Desai 	struct sas_port *port;
15243eb0822cSKashyap, Desai 	struct mptsas_portinfo *port_info;
15253eb0822cSKashyap, Desai 	struct mptsas_phyinfo *phy_info_parent;
15263eb0822cSKashyap, Desai 	int i;
15273eb0822cSKashyap, Desai 	char *ds = NULL;
15283eb0822cSKashyap, Desai 	u8 fw_id;
15293eb0822cSKashyap, Desai 	u64 sas_address;
15303eb0822cSKashyap, Desai 
15313eb0822cSKashyap, Desai 	if (!phy_info)
15323eb0822cSKashyap, Desai 		return;
15333eb0822cSKashyap, Desai 
15343eb0822cSKashyap, Desai 	fw_id = phy_info->attached.id;
15353eb0822cSKashyap, Desai 	sas_address = phy_info->attached.sas_address;
15363eb0822cSKashyap, Desai 
15373eb0822cSKashyap, Desai 	if (!phy_info->port_details) {
15383eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
15393eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
15403eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
15413eb0822cSKashyap, Desai 		return;
15423eb0822cSKashyap, Desai 	}
15433eb0822cSKashyap, Desai 	rphy = mptsas_get_rphy(phy_info);
15443eb0822cSKashyap, Desai 	if (!rphy) {
15453eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
15463eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
15473eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
15483eb0822cSKashyap, Desai 		return;
15493eb0822cSKashyap, Desai 	}
15503eb0822cSKashyap, Desai 
15513eb0822cSKashyap, Desai 	if (phy_info->attached.device_info & MPI_SAS_DEVICE_INFO_SSP_INITIATOR
15523eb0822cSKashyap, Desai 		|| phy_info->attached.device_info
15533eb0822cSKashyap, Desai 			& MPI_SAS_DEVICE_INFO_SMP_INITIATOR
15543eb0822cSKashyap, Desai 		|| phy_info->attached.device_info
15553eb0822cSKashyap, Desai 			& MPI_SAS_DEVICE_INFO_STP_INITIATOR)
15563eb0822cSKashyap, Desai 		ds = "initiator";
15573eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
15583eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_SSP_TARGET)
15593eb0822cSKashyap, Desai 		ds = "ssp";
15603eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
15613eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_STP_TARGET)
15623eb0822cSKashyap, Desai 		ds = "stp";
15633eb0822cSKashyap, Desai 	if (phy_info->attached.device_info &
15643eb0822cSKashyap, Desai 	    MPI_SAS_DEVICE_INFO_SATA_DEVICE)
15653eb0822cSKashyap, Desai 		ds = "sata";
15663eb0822cSKashyap, Desai 
15673eb0822cSKashyap, Desai 	dev_printk(KERN_DEBUG, &rphy->dev, MYIOC_s_FMT
15683eb0822cSKashyap, Desai 	    "removing %s device: fw_channel %d, fw_id %d, phy %d,"
15693eb0822cSKashyap, Desai 	    "sas_addr 0x%llx\n", ioc->name, ds, phy_info->attached.channel,
15703eb0822cSKashyap, Desai 	    phy_info->attached.id, phy_info->attached.phy_id,
15713eb0822cSKashyap, Desai 	    (unsigned long long) sas_address);
15723eb0822cSKashyap, Desai 
15733eb0822cSKashyap, Desai 	port = mptsas_get_port(phy_info);
15743eb0822cSKashyap, Desai 	if (!port) {
15753eb0822cSKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
15763eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
15773eb0822cSKashyap, Desai 			 __func__, fw_id, __LINE__));
15783eb0822cSKashyap, Desai 		return;
15793eb0822cSKashyap, Desai 	}
15803eb0822cSKashyap, Desai 	port_info = phy_info->portinfo;
15813eb0822cSKashyap, Desai 	phy_info_parent = port_info->phy_info;
15823eb0822cSKashyap, Desai 	for (i = 0; i < port_info->num_phys; i++, phy_info_parent++) {
15833eb0822cSKashyap, Desai 		if (!phy_info_parent->phy)
15843eb0822cSKashyap, Desai 			continue;
15853eb0822cSKashyap, Desai 		if (phy_info_parent->attached.sas_address !=
15863eb0822cSKashyap, Desai 		    sas_address)
15873eb0822cSKashyap, Desai 			continue;
15883eb0822cSKashyap, Desai 		dev_printk(KERN_DEBUG, &phy_info_parent->phy->dev,
15893eb0822cSKashyap, Desai 		    MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n",
15903eb0822cSKashyap, Desai 		    ioc->name, phy_info_parent->phy_id,
15913eb0822cSKashyap, Desai 		    phy_info_parent->phy);
15923eb0822cSKashyap, Desai 		sas_port_delete_phy(port, phy_info_parent->phy);
15933eb0822cSKashyap, Desai 	}
15943eb0822cSKashyap, Desai 
15953eb0822cSKashyap, Desai 	dev_printk(KERN_DEBUG, &port->dev, MYIOC_s_FMT
15963eb0822cSKashyap, Desai 	    "delete port %d, sas_addr (0x%llx)\n", ioc->name,
15973eb0822cSKashyap, Desai 	     port->port_identifier, (unsigned long long)sas_address);
15983eb0822cSKashyap, Desai 	sas_port_delete(port);
15993eb0822cSKashyap, Desai 	mptsas_set_port(ioc, phy_info, NULL);
16003eb0822cSKashyap, Desai 	mptsas_port_delete(ioc, phy_info->port_details);
16013eb0822cSKashyap, Desai }
16023eb0822cSKashyap, Desai 
16035767d25fSJoe Lawrence static struct mptsas_phyinfo *
mptsas_refreshing_device_handles(MPT_ADAPTER * ioc,struct mptsas_devinfo * sas_device)16043eb0822cSKashyap, Desai mptsas_refreshing_device_handles(MPT_ADAPTER *ioc,
16053eb0822cSKashyap, Desai 	struct mptsas_devinfo *sas_device)
16063eb0822cSKashyap, Desai {
16073eb0822cSKashyap, Desai 	struct mptsas_phyinfo *phy_info;
16083eb0822cSKashyap, Desai 	struct mptsas_portinfo *port_info;
16093eb0822cSKashyap, Desai 	int i;
16103eb0822cSKashyap, Desai 
16113eb0822cSKashyap, Desai 	phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
16123eb0822cSKashyap, Desai 	    sas_device->sas_address);
16133eb0822cSKashyap, Desai 	if (!phy_info)
16143eb0822cSKashyap, Desai 		goto out;
16153eb0822cSKashyap, Desai 	port_info = phy_info->portinfo;
16163eb0822cSKashyap, Desai 	if (!port_info)
16173eb0822cSKashyap, Desai 		goto out;
16183eb0822cSKashyap, Desai 	mutex_lock(&ioc->sas_topology_mutex);
16193eb0822cSKashyap, Desai 	for (i = 0; i < port_info->num_phys; i++) {
16203eb0822cSKashyap, Desai 		if (port_info->phy_info[i].attached.sas_address !=
16213eb0822cSKashyap, Desai 			sas_device->sas_address)
16223eb0822cSKashyap, Desai 			continue;
16233eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.channel = sas_device->channel;
16243eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.id = sas_device->id;
16253eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.sas_address =
16263eb0822cSKashyap, Desai 		    sas_device->sas_address;
16273eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.handle = sas_device->handle;
16283eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.handle_parent =
16293eb0822cSKashyap, Desai 		    sas_device->handle_parent;
16303eb0822cSKashyap, Desai 		port_info->phy_info[i].attached.handle_enclosure =
16313eb0822cSKashyap, Desai 		    sas_device->handle_enclosure;
16323eb0822cSKashyap, Desai 	}
16333eb0822cSKashyap, Desai 	mutex_unlock(&ioc->sas_topology_mutex);
16343eb0822cSKashyap, Desai  out:
16353eb0822cSKashyap, Desai 	return phy_info;
16363eb0822cSKashyap, Desai }
16373eb0822cSKashyap, Desai 
16383eb0822cSKashyap, Desai /**
16393eb0822cSKashyap, Desai  * mptsas_firmware_event_work - work thread for processing fw events
16403eb0822cSKashyap, Desai  * @work: work queue payload containing info describing the event
16413eb0822cSKashyap, Desai  * Context: user
16423eb0822cSKashyap, Desai  *
16433eb0822cSKashyap, Desai  */
16443eb0822cSKashyap, Desai static void
mptsas_firmware_event_work(struct work_struct * work)16453eb0822cSKashyap, Desai mptsas_firmware_event_work(struct work_struct *work)
16463eb0822cSKashyap, Desai {
16473eb0822cSKashyap, Desai 	struct fw_event_work *fw_event =
16483eb0822cSKashyap, Desai 		container_of(work, struct fw_event_work, work.work);
16493eb0822cSKashyap, Desai 	MPT_ADAPTER *ioc = fw_event->ioc;
16503eb0822cSKashyap, Desai 
1651eedf92b9SKashyap, Desai 	/* special rescan topology handling */
1652eedf92b9SKashyap, Desai 	if (fw_event->event == -1) {
1653eedf92b9SKashyap, Desai 		if (ioc->in_rescan) {
1654eedf92b9SKashyap, Desai 			devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
1655eedf92b9SKashyap, Desai 				"%s: rescan ignored as it is in progress\n",
1656eedf92b9SKashyap, Desai 				ioc->name, __func__));
1657eedf92b9SKashyap, Desai 			return;
1658eedf92b9SKashyap, Desai 		}
1659eedf92b9SKashyap, Desai 		devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: rescan after "
1660eedf92b9SKashyap, Desai 		    "reset\n", ioc->name, __func__));
1661eedf92b9SKashyap, Desai 		ioc->in_rescan = 1;
1662eedf92b9SKashyap, Desai 		mptsas_not_responding_devices(ioc);
1663eedf92b9SKashyap, Desai 		mptsas_scan_sas_topology(ioc);
1664eedf92b9SKashyap, Desai 		ioc->in_rescan = 0;
1665eedf92b9SKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
16669766096dSKashyap, Desai 		mptsas_fw_event_on(ioc);
1667eedf92b9SKashyap, Desai 		return;
1668eedf92b9SKashyap, Desai 	}
16693eb0822cSKashyap, Desai 
16703eb0822cSKashyap, Desai 	/* events handling turned off during host reset */
16713eb0822cSKashyap, Desai 	if (ioc->fw_events_off) {
16723eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
16733eb0822cSKashyap, Desai 		return;
16743eb0822cSKashyap, Desai 	}
16753eb0822cSKashyap, Desai 
16763eb0822cSKashyap, Desai 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: fw_event=(0x%p), "
16773eb0822cSKashyap, Desai 	    "event = (0x%02x)\n", ioc->name, __func__, fw_event,
16783eb0822cSKashyap, Desai 	    (fw_event->event & 0xFF)));
16793eb0822cSKashyap, Desai 
16803eb0822cSKashyap, Desai 	switch (fw_event->event) {
16813eb0822cSKashyap, Desai 	case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
16823eb0822cSKashyap, Desai 		mptsas_send_sas_event(fw_event);
16833eb0822cSKashyap, Desai 		break;
16843eb0822cSKashyap, Desai 	case MPI_EVENT_INTEGRATED_RAID:
16853eb0822cSKashyap, Desai 		mptsas_send_raid_event(fw_event);
16863eb0822cSKashyap, Desai 		break;
16873eb0822cSKashyap, Desai 	case MPI_EVENT_IR2:
16883eb0822cSKashyap, Desai 		mptsas_send_ir2_event(fw_event);
16893eb0822cSKashyap, Desai 		break;
16903eb0822cSKashyap, Desai 	case MPI_EVENT_PERSISTENT_TABLE_FULL:
16913eb0822cSKashyap, Desai 		mptbase_sas_persist_operation(ioc,
16923eb0822cSKashyap, Desai 		    MPI_SAS_OP_CLEAR_NOT_PRESENT);
16933eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
16943eb0822cSKashyap, Desai 		break;
1695db7051b2SKashyap, Desai 	case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
169694e989deSColin Ian King 		mptsas_broadcast_primitive_work(fw_event);
1697db7051b2SKashyap, Desai 		break;
1698f9c34022SKashyap, Desai 	case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
1699f9c34022SKashyap, Desai 		mptsas_send_expander_event(fw_event);
1700f9c34022SKashyap, Desai 		break;
1701f9c34022SKashyap, Desai 	case MPI_EVENT_SAS_PHY_LINK_STATUS:
1702f9c34022SKashyap, Desai 		mptsas_send_link_status_event(fw_event);
1703f9c34022SKashyap, Desai 		break;
170457e98513SKashyap, Desai 	case MPI_EVENT_QUEUE_FULL:
170557e98513SKashyap, Desai 		mptsas_handle_queue_full_event(fw_event);
170657e98513SKashyap, Desai 		break;
17073eb0822cSKashyap, Desai 	}
17083eb0822cSKashyap, Desai }
17093eb0822cSKashyap, Desai 
17103eb0822cSKashyap, Desai 
17113eb0822cSKashyap, Desai 
1712f013db32SJames Bottomley static int
mptsas_slave_configure(struct scsi_device * sdev)1713f013db32SJames Bottomley mptsas_slave_configure(struct scsi_device *sdev)
1714f013db32SJames Bottomley {
17153eb0822cSKashyap, Desai 	struct Scsi_Host	*host = sdev->host;
17163eb0822cSKashyap, Desai 	MPT_SCSI_HOST	*hd = shost_priv(host);
17173eb0822cSKashyap, Desai 	MPT_ADAPTER	*ioc = hd->ioc;
1718a7938b0bSKashyap, Desai 	VirtDevice	*vdevice = sdev->hostdata;
17193c0c25b9SMoore, Eric 
1720a7938b0bSKashyap, Desai 	if (vdevice->vtarget->deleted) {
1721a7938b0bSKashyap, Desai 		sdev_printk(KERN_INFO, sdev, "clearing deleted flag\n");
1722a7938b0bSKashyap, Desai 		vdevice->vtarget->deleted = 0;
1723a7938b0bSKashyap, Desai 	}
1724a7938b0bSKashyap, Desai 
1725a7938b0bSKashyap, Desai 	/*
1726a7938b0bSKashyap, Desai 	 * RAID volumes placed beyond the last expected port.
1727a7938b0bSKashyap, Desai 	 * Ignore sending sas mode pages in that case..
1728a7938b0bSKashyap, Desai 	 */
1729a7938b0bSKashyap, Desai 	if (sdev->channel == MPTSAS_RAID_CHANNEL) {
1730a7938b0bSKashyap, Desai 		mptsas_add_device_component_starget_ir(ioc, scsi_target(sdev));
1731e8bf3941SJames Bottomley 		goto out;
1732a7938b0bSKashyap, Desai 	}
1733e8bf3941SJames Bottomley 
1734f013db32SJames Bottomley 	sas_read_port_mode_page(sdev);
1735f013db32SJames Bottomley 
17363eb0822cSKashyap, Desai 	mptsas_add_device_component_starget(ioc, scsi_target(sdev));
17373eb0822cSKashyap, Desai 
1738e8bf3941SJames Bottomley  out:
1739f013db32SJames Bottomley 	return mptscsih_slave_configure(sdev);
1740f013db32SJames Bottomley }
1741f013db32SJames Bottomley 
1742547f9a21SEric Moore static int
mptsas_target_alloc(struct scsi_target * starget)1743547f9a21SEric Moore mptsas_target_alloc(struct scsi_target *starget)
1744547f9a21SEric Moore {
1745547f9a21SEric Moore 	struct Scsi_Host *host = dev_to_shost(&starget->dev);
1746e7eae9f6SEric Moore 	MPT_SCSI_HOST		*hd = shost_priv(host);
1747547f9a21SEric Moore 	VirtTarget		*vtarget;
1748793955f5SEric Moore 	u8			id, channel;
1749547f9a21SEric Moore 	struct sas_rphy		*rphy;
1750547f9a21SEric Moore 	struct mptsas_portinfo	*p;
1751547f9a21SEric Moore 	int 			 i;
1752e80b002bSEric Moore 	MPT_ADAPTER		*ioc = hd->ioc;
1753547f9a21SEric Moore 
1754547f9a21SEric Moore 	vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
1755547f9a21SEric Moore 	if (!vtarget)
1756547f9a21SEric Moore 		return -ENOMEM;
1757547f9a21SEric Moore 
1758547f9a21SEric Moore 	vtarget->starget = starget;
1759e80b002bSEric Moore 	vtarget->ioc_id = ioc->id;
1760793955f5SEric Moore 	vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
1761793955f5SEric Moore 	id = starget->id;
1762547f9a21SEric Moore 	channel = 0;
1763547f9a21SEric Moore 
1764793955f5SEric Moore 	/*
1765793955f5SEric Moore 	 * RAID volumes placed beyond the last expected port.
1766793955f5SEric Moore 	 */
1767793955f5SEric Moore 	if (starget->channel == MPTSAS_RAID_CHANNEL) {
1768a7938b0bSKashyap, Desai 		if (!ioc->raid_data.pIocPg2) {
1769a7938b0bSKashyap, Desai 			kfree(vtarget);
1770a7938b0bSKashyap, Desai 			return -ENXIO;
1771a7938b0bSKashyap, Desai 		}
1772a7938b0bSKashyap, Desai 		for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
1773a7938b0bSKashyap, Desai 			if (id == ioc->raid_data.pIocPg2->
1774a7938b0bSKashyap, Desai 					RaidVolume[i].VolumeID) {
1775a7938b0bSKashyap, Desai 				channel = ioc->raid_data.pIocPg2->
1776a7938b0bSKashyap, Desai 					RaidVolume[i].VolumeBus;
1777a7938b0bSKashyap, Desai 			}
1778a7938b0bSKashyap, Desai 		}
1779a7938b0bSKashyap, Desai 		vtarget->raidVolume = 1;
1780547f9a21SEric Moore 		goto out;
1781793955f5SEric Moore 	}
1782547f9a21SEric Moore 
1783547f9a21SEric Moore 	rphy = dev_to_rphy(starget->dev.parent);
1784e80b002bSEric Moore 	mutex_lock(&ioc->sas_topology_mutex);
1785e80b002bSEric Moore 	list_for_each_entry(p, &ioc->sas_topology, list) {
1786547f9a21SEric Moore 		for (i = 0; i < p->num_phys; i++) {
1787547f9a21SEric Moore 			if (p->phy_info[i].attached.sas_address !=
1788547f9a21SEric Moore 					rphy->identify.sas_address)
1789547f9a21SEric Moore 				continue;
1790793955f5SEric Moore 			id = p->phy_info[i].attached.id;
1791547f9a21SEric Moore 			channel = p->phy_info[i].attached.channel;
1792547f9a21SEric Moore 			mptsas_set_starget(&p->phy_info[i], starget);
1793547f9a21SEric Moore 
1794547f9a21SEric Moore 			/*
1795547f9a21SEric Moore 			 * Exposing hidden raid components
1796547f9a21SEric Moore 			 */
1797e80b002bSEric Moore 			if (mptscsih_is_phys_disk(ioc, channel, id)) {
1798e80b002bSEric Moore 				id = mptscsih_raid_id_to_num(ioc,
1799793955f5SEric Moore 						channel, id);
1800547f9a21SEric Moore 				vtarget->tflags |=
1801547f9a21SEric Moore 				    MPT_TARGET_FLAGS_RAID_COMPONENT;
1802b506ade9SEric Moore 				p->phy_info[i].attached.phys_disk_num = id;
1803547f9a21SEric Moore 			}
1804e80b002bSEric Moore 			mutex_unlock(&ioc->sas_topology_mutex);
1805547f9a21SEric Moore 			goto out;
1806547f9a21SEric Moore 		}
1807547f9a21SEric Moore 	}
1808e80b002bSEric Moore 	mutex_unlock(&ioc->sas_topology_mutex);
1809547f9a21SEric Moore 
1810547f9a21SEric Moore 	kfree(vtarget);
1811547f9a21SEric Moore 	return -ENXIO;
1812547f9a21SEric Moore 
1813547f9a21SEric Moore  out:
1814793955f5SEric Moore 	vtarget->id = id;
1815793955f5SEric Moore 	vtarget->channel = channel;
1816547f9a21SEric Moore 	starget->hostdata = vtarget;
1817547f9a21SEric Moore 	return 0;
1818547f9a21SEric Moore }
1819547f9a21SEric Moore 
1820547f9a21SEric Moore static void
mptsas_target_destroy(struct scsi_target * starget)1821547f9a21SEric Moore mptsas_target_destroy(struct scsi_target *starget)
1822547f9a21SEric Moore {
1823547f9a21SEric Moore 	struct Scsi_Host *host = dev_to_shost(&starget->dev);
1824e7eae9f6SEric Moore 	MPT_SCSI_HOST		*hd = shost_priv(host);
1825547f9a21SEric Moore 	struct sas_rphy		*rphy;
1826547f9a21SEric Moore 	struct mptsas_portinfo	*p;
1827547f9a21SEric Moore 	int 			 i;
1828e80b002bSEric Moore 	MPT_ADAPTER	*ioc = hd->ioc;
18293eb0822cSKashyap, Desai 	VirtTarget	*vtarget;
1830547f9a21SEric Moore 
1831547f9a21SEric Moore 	if (!starget->hostdata)
1832547f9a21SEric Moore 		return;
1833547f9a21SEric Moore 
18343eb0822cSKashyap, Desai 	vtarget = starget->hostdata;
18353eb0822cSKashyap, Desai 
183657e98513SKashyap, Desai 	mptsas_del_device_component_by_os(ioc, starget->channel,
183757e98513SKashyap, Desai 	    starget->id);
183857e98513SKashyap, Desai 
18393eb0822cSKashyap, Desai 
1840e8bf3941SJames Bottomley 	if (starget->channel == MPTSAS_RAID_CHANNEL)
1841547f9a21SEric Moore 		goto out;
1842547f9a21SEric Moore 
1843547f9a21SEric Moore 	rphy = dev_to_rphy(starget->dev.parent);
1844e80b002bSEric Moore 	list_for_each_entry(p, &ioc->sas_topology, list) {
1845547f9a21SEric Moore 		for (i = 0; i < p->num_phys; i++) {
1846547f9a21SEric Moore 			if (p->phy_info[i].attached.sas_address !=
1847547f9a21SEric Moore 					rphy->identify.sas_address)
1848547f9a21SEric Moore 				continue;
18493eb0822cSKashyap, Desai 
18503eb0822cSKashyap, Desai 			starget_printk(KERN_INFO, starget, MYIOC_s_FMT
18513eb0822cSKashyap, Desai 			"delete device: fw_channel %d, fw_id %d, phy %d, "
18523eb0822cSKashyap, Desai 			"sas_addr 0x%llx\n", ioc->name,
18533eb0822cSKashyap, Desai 			p->phy_info[i].attached.channel,
18543eb0822cSKashyap, Desai 			p->phy_info[i].attached.id,
18553eb0822cSKashyap, Desai 			p->phy_info[i].attached.phy_id, (unsigned long long)
18563eb0822cSKashyap, Desai 			p->phy_info[i].attached.sas_address);
18573eb0822cSKashyap, Desai 
1858547f9a21SEric Moore 			mptsas_set_starget(&p->phy_info[i], NULL);
1859547f9a21SEric Moore 		}
1860547f9a21SEric Moore 	}
1861547f9a21SEric Moore 
1862547f9a21SEric Moore  out:
18633eb0822cSKashyap, Desai 	vtarget->starget = NULL;
1864547f9a21SEric Moore 	kfree(starget->hostdata);
1865547f9a21SEric Moore 	starget->hostdata = NULL;
1866547f9a21SEric Moore }
1867547f9a21SEric Moore 
1868547f9a21SEric Moore 
18690c33b27dSChristoph Hellwig static int
mptsas_slave_alloc(struct scsi_device * sdev)1870c7c82987SMoore, Eric Dean mptsas_slave_alloc(struct scsi_device *sdev)
18710c33b27dSChristoph Hellwig {
1872c7c82987SMoore, Eric Dean 	struct Scsi_Host	*host = sdev->host;
1873e7eae9f6SEric Moore 	MPT_SCSI_HOST		*hd = shost_priv(host);
18740c33b27dSChristoph Hellwig 	struct sas_rphy		*rphy;
18750c33b27dSChristoph Hellwig 	struct mptsas_portinfo	*p;
1876a69de507SEric Moore 	VirtDevice		*vdevice;
1877c7c82987SMoore, Eric Dean 	struct scsi_target 	*starget;
18780c33b27dSChristoph Hellwig 	int 			i;
1879e80b002bSEric Moore 	MPT_ADAPTER *ioc = hd->ioc;
18800c33b27dSChristoph Hellwig 
1881a69de507SEric Moore 	vdevice = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
1882a69de507SEric Moore 	if (!vdevice) {
1883547f9a21SEric Moore 		printk(MYIOC_s_ERR_FMT "slave_alloc kzalloc(%zd) FAILED!\n",
1884e80b002bSEric Moore 				ioc->name, sizeof(VirtDevice));
18850c33b27dSChristoph Hellwig 		return -ENOMEM;
18860c33b27dSChristoph Hellwig 	}
1887c7c82987SMoore, Eric Dean 	starget = scsi_target(sdev);
1888a69de507SEric Moore 	vdevice->vtarget = starget->hostdata;
18890c33b27dSChristoph Hellwig 
1890e8bf3941SJames Bottomley 	if (sdev->channel == MPTSAS_RAID_CHANNEL)
1891816aa907SMoore, Eric 		goto out;
1892816aa907SMoore, Eric 
1893c7c82987SMoore, Eric Dean 	rphy = dev_to_rphy(sdev->sdev_target->dev.parent);
1894e80b002bSEric Moore 	mutex_lock(&ioc->sas_topology_mutex);
1895e80b002bSEric Moore 	list_for_each_entry(p, &ioc->sas_topology, list) {
18960c33b27dSChristoph Hellwig 		for (i = 0; i < p->num_phys; i++) {
1897547f9a21SEric Moore 			if (p->phy_info[i].attached.sas_address !=
1898547f9a21SEric Moore 					rphy->identify.sas_address)
1899547f9a21SEric Moore 				continue;
1900a69de507SEric Moore 			vdevice->lun = sdev->lun;
1901f44e5461SMoore, Eric 			/*
1902547f9a21SEric Moore 			 * Exposing hidden raid components
1903f44e5461SMoore, Eric 			 */
1904e80b002bSEric Moore 			if (mptscsih_is_phys_disk(ioc,
1905793955f5SEric Moore 			    p->phy_info[i].attached.channel,
1906547f9a21SEric Moore 			    p->phy_info[i].attached.id))
1907f44e5461SMoore, Eric 				sdev->no_uld_attach = 1;
1908e80b002bSEric Moore 			mutex_unlock(&ioc->sas_topology_mutex);
19090c33b27dSChristoph Hellwig 			goto out;
19100c33b27dSChristoph Hellwig 		}
19110c33b27dSChristoph Hellwig 	}
1912e80b002bSEric Moore 	mutex_unlock(&ioc->sas_topology_mutex);
19130c33b27dSChristoph Hellwig 
1914a69de507SEric Moore 	kfree(vdevice);
191523f236edSChristoph Hellwig 	return -ENXIO;
19160c33b27dSChristoph Hellwig 
19170c33b27dSChristoph Hellwig  out:
1918a69de507SEric Moore 	vdevice->vtarget->num_luns++;
1919a69de507SEric Moore 	sdev->hostdata = vdevice;
19200c33b27dSChristoph Hellwig 	return 0;
19210c33b27dSChristoph Hellwig }
19220c33b27dSChristoph Hellwig 
1923547f9a21SEric Moore static int
mptsas_qcmd(struct Scsi_Host * shost,struct scsi_cmnd * SCpnt)1924a48ac9e5SMatthew Wilcox mptsas_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *SCpnt)
19259a28f49aSChristoph Hellwig {
19267b5a65b9SKashyap, Desai 	MPT_SCSI_HOST	*hd;
19277b5a65b9SKashyap, Desai 	MPT_ADAPTER	*ioc;
1928a69de507SEric Moore 	VirtDevice	*vdevice = SCpnt->device->hostdata;
19299a28f49aSChristoph Hellwig 
1930a69de507SEric Moore 	if (!vdevice || !vdevice->vtarget || vdevice->vtarget->deleted) {
1931547f9a21SEric Moore 		SCpnt->result = DID_NO_CONNECT << 16;
19321ae6d167SBart Van Assche 		scsi_done(SCpnt);
1933547f9a21SEric Moore 		return 0;
1934547f9a21SEric Moore 	}
19357d3eecf7SMoore, Eric 
1936a48ac9e5SMatthew Wilcox 	hd = shost_priv(shost);
19377b5a65b9SKashyap, Desai 	ioc = hd->ioc;
19387b5a65b9SKashyap, Desai 
19397b5a65b9SKashyap, Desai 	if (ioc->sas_discovery_quiesce_io)
19407b5a65b9SKashyap, Desai 		return SCSI_MLQUEUE_HOST_BUSY;
19417b5a65b9SKashyap, Desai 
194264e155adSKashyap, Desai 	if (ioc->debug_level & MPT_DEBUG_SCSI)
194364e155adSKashyap, Desai 		scsi_print_command(SCpnt);
1944793955f5SEric Moore 
1945a48ac9e5SMatthew Wilcox 	return mptscsih_qcmd(SCpnt);
1946547f9a21SEric Moore }
19477d3eecf7SMoore, Eric 
1948c9de7dc4SKashyap, Desai /**
1949cdcda465SRandy Dunlap  *	mptsas_eh_timed_out - resets the scsi_cmnd timeout
1950c9de7dc4SKashyap, Desai  *		if the device under question is currently in the
1951c9de7dc4SKashyap, Desai  *		device removal delay.
1952c9de7dc4SKashyap, Desai  *	@sc: scsi command that the midlayer is about to time out
1953c9de7dc4SKashyap, Desai  *
1954c9de7dc4SKashyap, Desai  **/
mptsas_eh_timed_out(struct scsi_cmnd * sc)1955dee7121eSBart Van Assche static enum scsi_timeout_action mptsas_eh_timed_out(struct scsi_cmnd *sc)
1956c9de7dc4SKashyap, Desai {
1957c9de7dc4SKashyap, Desai 	MPT_SCSI_HOST *hd;
1958c9de7dc4SKashyap, Desai 	MPT_ADAPTER   *ioc;
1959c9de7dc4SKashyap, Desai 	VirtDevice    *vdevice;
1960dee7121eSBart Van Assche 	enum scsi_timeout_action rc = SCSI_EH_NOT_HANDLED;
1961c9de7dc4SKashyap, Desai 
1962c9de7dc4SKashyap, Desai 	hd = shost_priv(sc->device->host);
1963c9de7dc4SKashyap, Desai 	if (hd == NULL) {
1964c9de7dc4SKashyap, Desai 		printk(KERN_ERR MYNAM ": %s: Can't locate host! (sc=%p)\n",
1965c9de7dc4SKashyap, Desai 		    __func__, sc);
1966c9de7dc4SKashyap, Desai 		goto done;
1967c9de7dc4SKashyap, Desai 	}
1968c9de7dc4SKashyap, Desai 
1969c9de7dc4SKashyap, Desai 	ioc = hd->ioc;
1970c9de7dc4SKashyap, Desai 	if (ioc->bus_type != SAS) {
1971c9de7dc4SKashyap, Desai 		printk(KERN_ERR MYNAM ": %s: Wrong bus type (sc=%p)\n",
1972c9de7dc4SKashyap, Desai 		    __func__, sc);
1973c9de7dc4SKashyap, Desai 		goto done;
1974c9de7dc4SKashyap, Desai 	}
1975c9de7dc4SKashyap, Desai 
197698cbe371Skashyap.desai@lsi.com 	/* In case if IOC is in reset from internal context.
197798cbe371Skashyap.desai@lsi.com 	*  Do not execute EEH for the same IOC. SML should to reset timer.
197898cbe371Skashyap.desai@lsi.com 	*/
197998cbe371Skashyap.desai@lsi.com 	if (ioc->ioc_reset_in_progress) {
198098cbe371Skashyap.desai@lsi.com 		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT ": %s: ioc is in reset,"
198198cbe371Skashyap.desai@lsi.com 		    "SML need to reset the timer (sc=%p)\n",
198298cbe371Skashyap.desai@lsi.com 		    ioc->name, __func__, sc));
1983dee7121eSBart Van Assche 		rc = SCSI_EH_RESET_TIMER;
198498cbe371Skashyap.desai@lsi.com 	}
1985c9de7dc4SKashyap, Desai 	vdevice = sc->device->hostdata;
1986c9de7dc4SKashyap, Desai 	if (vdevice && vdevice->vtarget && (vdevice->vtarget->inDMD
1987c9de7dc4SKashyap, Desai 		|| vdevice->vtarget->deleted)) {
1988c9de7dc4SKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT ": %s: target removed "
1989c9de7dc4SKashyap, Desai 		    "or in device removal delay (sc=%p)\n",
1990c9de7dc4SKashyap, Desai 		    ioc->name, __func__, sc));
1991dee7121eSBart Van Assche 		rc = SCSI_EH_RESET_TIMER;
1992c9de7dc4SKashyap, Desai 		goto done;
1993c9de7dc4SKashyap, Desai 	}
1994c9de7dc4SKashyap, Desai 
1995c9de7dc4SKashyap, Desai done:
1996c9de7dc4SKashyap, Desai 	return rc;
1997c9de7dc4SKashyap, Desai }
1998c9de7dc4SKashyap, Desai 
19999a28f49aSChristoph Hellwig 
200095a24cf1SBart Van Assche static const struct scsi_host_template mptsas_driver_template = {
2001f78496daSMoore, Eric Dean 	.module				= THIS_MODULE,
20020c33b27dSChristoph Hellwig 	.proc_name			= "mptsas",
2003cac19703SAl Viro 	.show_info			= mptscsih_show_info,
2004568da769SKashyap, Desai 	.name				= "MPT SAS Host",
20050c33b27dSChristoph Hellwig 	.info				= mptscsih_info,
2006547f9a21SEric Moore 	.queuecommand			= mptsas_qcmd,
2007547f9a21SEric Moore 	.target_alloc			= mptsas_target_alloc,
20080c33b27dSChristoph Hellwig 	.slave_alloc			= mptsas_slave_alloc,
2009f013db32SJames Bottomley 	.slave_configure		= mptsas_slave_configure,
2010547f9a21SEric Moore 	.target_destroy			= mptsas_target_destroy,
2011547f9a21SEric Moore 	.slave_destroy			= mptscsih_slave_destroy,
20120c33b27dSChristoph Hellwig 	.change_queue_depth 		= mptscsih_change_queue_depth,
2013b6a05c82SChristoph Hellwig 	.eh_timed_out			= mptsas_eh_timed_out,
20140c33b27dSChristoph Hellwig 	.eh_abort_handler		= mptscsih_abort,
20150c33b27dSChristoph Hellwig 	.eh_device_reset_handler	= mptscsih_dev_reset,
20160c33b27dSChristoph Hellwig 	.eh_host_reset_handler		= mptscsih_host_reset,
20170c33b27dSChristoph Hellwig 	.bios_param			= mptscsih_bios_param,
20189d2e9d66SKashyap, Desai 	.can_queue			= MPT_SAS_CAN_QUEUE,
20190c33b27dSChristoph Hellwig 	.this_id			= -1,
20200c33b27dSChristoph Hellwig 	.sg_tablesize			= MPT_SCSI_SG_DEPTH,
20210c33b27dSChristoph Hellwig 	.max_sectors			= 8192,
20220c33b27dSChristoph Hellwig 	.cmd_per_lun			= 7,
20232899836fSBart Van Assche 	.shost_groups			= mptscsih_host_attr_groups,
202494e5395dSMartin K. Petersen 	.no_write_same			= 1,
20250c33b27dSChristoph Hellwig };
20260c33b27dSChristoph Hellwig 
mptsas_get_linkerrors(struct sas_phy * phy)2027b5141128SChristoph Hellwig static int mptsas_get_linkerrors(struct sas_phy *phy)
2028b5141128SChristoph Hellwig {
2029b5141128SChristoph Hellwig 	MPT_ADAPTER *ioc = phy_to_ioc(phy);
2030b5141128SChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
2031b5141128SChristoph Hellwig 	CONFIGPARMS cfg;
2032b5141128SChristoph Hellwig 	SasPhyPage1_t *buffer;
2033b5141128SChristoph Hellwig 	dma_addr_t dma_handle;
2034b5141128SChristoph Hellwig 	int error;
2035b5141128SChristoph Hellwig 
2036f4ad7b58SJames Bottomley 	/* FIXME: only have link errors on local phys */
2037f4ad7b58SJames Bottomley 	if (!scsi_is_sas_phy_local(phy))
2038f4ad7b58SJames Bottomley 		return -EINVAL;
2039f4ad7b58SJames Bottomley 
2040b5141128SChristoph Hellwig 	hdr.PageVersion = MPI_SASPHY1_PAGEVERSION;
2041b5141128SChristoph Hellwig 	hdr.ExtPageLength = 0;
2042b5141128SChristoph Hellwig 	hdr.PageNumber = 1 /* page number 1*/;
2043b5141128SChristoph Hellwig 	hdr.Reserved1 = 0;
2044b5141128SChristoph Hellwig 	hdr.Reserved2 = 0;
2045b5141128SChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
2046b5141128SChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY;
2047b5141128SChristoph Hellwig 
2048b5141128SChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
2049b5141128SChristoph Hellwig 	cfg.physAddr = -1;
2050b5141128SChristoph Hellwig 	cfg.pageAddr = phy->identify.phy_identifier;
2051b5141128SChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
2052b5141128SChristoph Hellwig 	cfg.dir = 0;    /* read */
20534b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
2054b5141128SChristoph Hellwig 
2055b5141128SChristoph Hellwig 	error = mpt_config(ioc, &cfg);
2056b5141128SChristoph Hellwig 	if (error)
2057b5141128SChristoph Hellwig 		return error;
2058b5141128SChristoph Hellwig 	if (!hdr.ExtPageLength)
2059b5141128SChristoph Hellwig 		return -ENXIO;
2060b5141128SChristoph Hellwig 
206176a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
206276a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
2063b5141128SChristoph Hellwig 	if (!buffer)
2064b5141128SChristoph Hellwig 		return -ENOMEM;
2065b5141128SChristoph Hellwig 
2066b5141128SChristoph Hellwig 	cfg.physAddr = dma_handle;
2067b5141128SChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
2068b5141128SChristoph Hellwig 
2069b5141128SChristoph Hellwig 	error = mpt_config(ioc, &cfg);
2070b5141128SChristoph Hellwig 	if (error)
2071b5141128SChristoph Hellwig 		goto out_free_consistent;
2072b5141128SChristoph Hellwig 
2073d6ecdd63SPrakash, Sathya 	mptsas_print_phy_pg1(ioc, buffer);
2074b5141128SChristoph Hellwig 
2075b5141128SChristoph Hellwig 	phy->invalid_dword_count = le32_to_cpu(buffer->InvalidDwordCount);
2076b5141128SChristoph Hellwig 	phy->running_disparity_error_count =
2077b5141128SChristoph Hellwig 		le32_to_cpu(buffer->RunningDisparityErrorCount);
2078b5141128SChristoph Hellwig 	phy->loss_of_dword_sync_count =
2079b5141128SChristoph Hellwig 		le32_to_cpu(buffer->LossDwordSynchCount);
2080b5141128SChristoph Hellwig 	phy->phy_reset_problem_count =
2081b5141128SChristoph Hellwig 		le32_to_cpu(buffer->PhyResetProblemCount);
2082b5141128SChristoph Hellwig 
2083b5141128SChristoph Hellwig  out_free_consistent:
2084b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2085b114dda6SChristophe JAILLET 			  dma_handle);
2086b5141128SChristoph Hellwig 	return error;
2087b5141128SChristoph Hellwig }
2088b5141128SChristoph Hellwig 
mptsas_mgmt_done(MPT_ADAPTER * ioc,MPT_FRAME_HDR * req,MPT_FRAME_HDR * reply)2089da4fa655SChristoph Hellwig static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
2090da4fa655SChristoph Hellwig 		MPT_FRAME_HDR *reply)
2091da4fa655SChristoph Hellwig {
2092f0f09d3bSKashyap, Desai 	ioc->sas_mgmt.status |= MPT_MGMT_STATUS_COMMAND_GOOD;
2093da4fa655SChristoph Hellwig 	if (reply != NULL) {
2094f0f09d3bSKashyap, Desai 		ioc->sas_mgmt.status |= MPT_MGMT_STATUS_RF_VALID;
2095da4fa655SChristoph Hellwig 		memcpy(ioc->sas_mgmt.reply, reply,
2096da4fa655SChristoph Hellwig 		    min(ioc->reply_sz, 4 * reply->u.reply.MsgLength));
2097da4fa655SChristoph Hellwig 	}
20982f187862SKashyap, Desai 
20992f187862SKashyap, Desai 	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_PENDING) {
21002f187862SKashyap, Desai 		ioc->sas_mgmt.status &= ~MPT_MGMT_STATUS_PENDING;
2101da4fa655SChristoph Hellwig 		complete(&ioc->sas_mgmt.done);
2102da4fa655SChristoph Hellwig 		return 1;
2103da4fa655SChristoph Hellwig 	}
21042f187862SKashyap, Desai 	return 0;
21052f187862SKashyap, Desai }
2106da4fa655SChristoph Hellwig 
mptsas_phy_reset(struct sas_phy * phy,int hard_reset)2107da4fa655SChristoph Hellwig static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
2108da4fa655SChristoph Hellwig {
2109da4fa655SChristoph Hellwig 	MPT_ADAPTER *ioc = phy_to_ioc(phy);
2110da4fa655SChristoph Hellwig 	SasIoUnitControlRequest_t *req;
2111da4fa655SChristoph Hellwig 	SasIoUnitControlReply_t *reply;
2112da4fa655SChristoph Hellwig 	MPT_FRAME_HDR *mf;
2113da4fa655SChristoph Hellwig 	MPIHeader_t *hdr;
2114da4fa655SChristoph Hellwig 	unsigned long timeleft;
2115da4fa655SChristoph Hellwig 	int error = -ERESTARTSYS;
2116da4fa655SChristoph Hellwig 
2117f4ad7b58SJames Bottomley 	/* FIXME: fusion doesn't allow non-local phy reset */
2118f4ad7b58SJames Bottomley 	if (!scsi_is_sas_phy_local(phy))
2119f4ad7b58SJames Bottomley 		return -EINVAL;
2120f4ad7b58SJames Bottomley 
2121da4fa655SChristoph Hellwig 	/* not implemented for expanders */
2122da4fa655SChristoph Hellwig 	if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP)
2123da4fa655SChristoph Hellwig 		return -ENXIO;
2124da4fa655SChristoph Hellwig 
2125eeb846ceSChristoph Hellwig 	if (mutex_lock_interruptible(&ioc->sas_mgmt.mutex))
2126da4fa655SChristoph Hellwig 		goto out;
2127da4fa655SChristoph Hellwig 
2128da4fa655SChristoph Hellwig 	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
2129da4fa655SChristoph Hellwig 	if (!mf) {
2130da4fa655SChristoph Hellwig 		error = -ENOMEM;
2131da4fa655SChristoph Hellwig 		goto out_unlock;
2132da4fa655SChristoph Hellwig 	}
2133da4fa655SChristoph Hellwig 
2134da4fa655SChristoph Hellwig 	hdr = (MPIHeader_t *) mf;
2135da4fa655SChristoph Hellwig 	req = (SasIoUnitControlRequest_t *)mf;
2136da4fa655SChristoph Hellwig 	memset(req, 0, sizeof(SasIoUnitControlRequest_t));
2137da4fa655SChristoph Hellwig 	req->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL;
2138da4fa655SChristoph Hellwig 	req->MsgContext = hdr->MsgContext;
2139da4fa655SChristoph Hellwig 	req->Operation = hard_reset ?
2140da4fa655SChristoph Hellwig 		MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET;
2141da4fa655SChristoph Hellwig 	req->PhyNum = phy->identify.phy_identifier;
2142da4fa655SChristoph Hellwig 
21432f187862SKashyap, Desai 	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
2144da4fa655SChristoph Hellwig 	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
2145da4fa655SChristoph Hellwig 
2146da4fa655SChristoph Hellwig 	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done,
2147da4fa655SChristoph Hellwig 			10 * HZ);
2148568da769SKashyap, Desai 	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
2149568da769SKashyap, Desai 		error = -ETIME;
2150da4fa655SChristoph Hellwig 		mpt_free_msg_frame(ioc, mf);
2151568da769SKashyap, Desai 		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
2152568da769SKashyap, Desai 			goto out_unlock;
2153568da769SKashyap, Desai 		if (!timeleft)
2154d0f698c4SKashyap, Desai 			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
2155da4fa655SChristoph Hellwig 		goto out_unlock;
2156da4fa655SChristoph Hellwig 	}
2157da4fa655SChristoph Hellwig 
2158da4fa655SChristoph Hellwig 	/* a reply frame is expected */
2159da4fa655SChristoph Hellwig 	if ((ioc->sas_mgmt.status &
2160f0f09d3bSKashyap, Desai 	    MPT_MGMT_STATUS_RF_VALID) == 0) {
2161da4fa655SChristoph Hellwig 		error = -ENXIO;
2162da4fa655SChristoph Hellwig 		goto out_unlock;
2163da4fa655SChristoph Hellwig 	}
2164da4fa655SChristoph Hellwig 
2165da4fa655SChristoph Hellwig 	/* process the completed Reply Message Frame */
2166da4fa655SChristoph Hellwig 	reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply;
2167da4fa655SChristoph Hellwig 	if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) {
216829dd3609SEric Moore 		printk(MYIOC_s_INFO_FMT "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
2169cadbd4a5SHarvey Harrison 		    ioc->name, __func__, reply->IOCStatus, reply->IOCLogInfo);
2170da4fa655SChristoph Hellwig 		error = -ENXIO;
2171da4fa655SChristoph Hellwig 		goto out_unlock;
2172da4fa655SChristoph Hellwig 	}
2173da4fa655SChristoph Hellwig 
2174da4fa655SChristoph Hellwig 	error = 0;
2175da4fa655SChristoph Hellwig 
2176da4fa655SChristoph Hellwig  out_unlock:
21772f187862SKashyap, Desai 	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
2178eeb846ceSChristoph Hellwig 	mutex_unlock(&ioc->sas_mgmt.mutex);
2179da4fa655SChristoph Hellwig  out:
2180da4fa655SChristoph Hellwig 	return error;
2181da4fa655SChristoph Hellwig }
2182b5141128SChristoph Hellwig 
2183e3094447SChristoph Hellwig static int
mptsas_get_enclosure_identifier(struct sas_rphy * rphy,u64 * identifier)2184e3094447SChristoph Hellwig mptsas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
2185e3094447SChristoph Hellwig {
2186e3094447SChristoph Hellwig 	MPT_ADAPTER *ioc = rphy_to_ioc(rphy);
2187e3094447SChristoph Hellwig 	int i, error;
2188e3094447SChristoph Hellwig 	struct mptsas_portinfo *p;
2189e3094447SChristoph Hellwig 	struct mptsas_enclosure enclosure_info;
2190e3094447SChristoph Hellwig 	u64 enclosure_handle;
2191e3094447SChristoph Hellwig 
2192e3094447SChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
2193e3094447SChristoph Hellwig 	list_for_each_entry(p, &ioc->sas_topology, list) {
2194e3094447SChristoph Hellwig 		for (i = 0; i < p->num_phys; i++) {
2195e3094447SChristoph Hellwig 			if (p->phy_info[i].attached.sas_address ==
2196e3094447SChristoph Hellwig 			    rphy->identify.sas_address) {
2197e3094447SChristoph Hellwig 				enclosure_handle = p->phy_info[i].
2198e3094447SChristoph Hellwig 					attached.handle_enclosure;
2199e3094447SChristoph Hellwig 				goto found_info;
2200e3094447SChristoph Hellwig 			}
2201e3094447SChristoph Hellwig 		}
2202e3094447SChristoph Hellwig 	}
2203e3094447SChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
2204e3094447SChristoph Hellwig 	return -ENXIO;
2205e3094447SChristoph Hellwig 
2206e3094447SChristoph Hellwig  found_info:
2207e3094447SChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
2208e3094447SChristoph Hellwig 	memset(&enclosure_info, 0, sizeof(struct mptsas_enclosure));
220952435430SMoore, Eric 	error = mptsas_sas_enclosure_pg0(ioc, &enclosure_info,
2210e3094447SChristoph Hellwig 			(MPI_SAS_ENCLOS_PGAD_FORM_HANDLE <<
2211e3094447SChristoph Hellwig 			 MPI_SAS_ENCLOS_PGAD_FORM_SHIFT), enclosure_handle);
2212e3094447SChristoph Hellwig 	if (!error)
2213e3094447SChristoph Hellwig 		*identifier = enclosure_info.enclosure_logical_id;
2214e3094447SChristoph Hellwig 	return error;
2215e3094447SChristoph Hellwig }
2216e3094447SChristoph Hellwig 
2217e3094447SChristoph Hellwig static int
mptsas_get_bay_identifier(struct sas_rphy * rphy)2218e3094447SChristoph Hellwig mptsas_get_bay_identifier(struct sas_rphy *rphy)
2219e3094447SChristoph Hellwig {
2220e3094447SChristoph Hellwig 	MPT_ADAPTER *ioc = rphy_to_ioc(rphy);
2221e3094447SChristoph Hellwig 	struct mptsas_portinfo *p;
2222e3094447SChristoph Hellwig 	int i, rc;
2223e3094447SChristoph Hellwig 
2224e3094447SChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
2225e3094447SChristoph Hellwig 	list_for_each_entry(p, &ioc->sas_topology, list) {
2226e3094447SChristoph Hellwig 		for (i = 0; i < p->num_phys; i++) {
2227e3094447SChristoph Hellwig 			if (p->phy_info[i].attached.sas_address ==
2228e3094447SChristoph Hellwig 			    rphy->identify.sas_address) {
2229e3094447SChristoph Hellwig 				rc = p->phy_info[i].attached.slot;
2230e3094447SChristoph Hellwig 				goto out;
2231e3094447SChristoph Hellwig 			}
2232e3094447SChristoph Hellwig 		}
2233e3094447SChristoph Hellwig 	}
2234e3094447SChristoph Hellwig 	rc = -ENXIO;
2235e3094447SChristoph Hellwig  out:
2236e3094447SChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
2237e3094447SChristoph Hellwig 	return rc;
2238e3094447SChristoph Hellwig }
2239e3094447SChristoph Hellwig 
mptsas_smp_handler(struct bsg_job * job,struct Scsi_Host * shost,struct sas_rphy * rphy)2240651a0136SChristoph Hellwig static void mptsas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
2241651a0136SChristoph Hellwig 		struct sas_rphy *rphy)
2242159e36feSFUJITA Tomonori {
2243159e36feSFUJITA Tomonori 	MPT_ADAPTER *ioc = ((MPT_SCSI_HOST *) shost->hostdata)->ioc;
2244159e36feSFUJITA Tomonori 	MPT_FRAME_HDR *mf;
2245159e36feSFUJITA Tomonori 	SmpPassthroughRequest_t *smpreq;
2246159e36feSFUJITA Tomonori 	int flagsLength;
2247159e36feSFUJITA Tomonori 	unsigned long timeleft;
2248159e36feSFUJITA Tomonori 	char *psge;
2249159e36feSFUJITA Tomonori 	u64 sas_address = 0;
2250651a0136SChristoph Hellwig 	unsigned int reslen = 0;
2251651a0136SChristoph Hellwig 	int ret = -EINVAL;
2252159e36feSFUJITA Tomonori 
2253159e36feSFUJITA Tomonori 	/* do we need to support multiple segments? */
2254651a0136SChristoph Hellwig 	if (job->request_payload.sg_cnt > 1 ||
2255651a0136SChristoph Hellwig 	    job->reply_payload.sg_cnt > 1) {
2256458b76edSKent Overstreet 		printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u, rsp %u\n",
2257651a0136SChristoph Hellwig 		    ioc->name, __func__, job->request_payload.payload_len,
2258651a0136SChristoph Hellwig 		    job->reply_payload.payload_len);
2259651a0136SChristoph Hellwig 		goto out;
2260159e36feSFUJITA Tomonori 	}
2261159e36feSFUJITA Tomonori 
2262159e36feSFUJITA Tomonori 	ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
2263159e36feSFUJITA Tomonori 	if (ret)
2264159e36feSFUJITA Tomonori 		goto out;
2265159e36feSFUJITA Tomonori 
2266159e36feSFUJITA Tomonori 	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
2267159e36feSFUJITA Tomonori 	if (!mf) {
2268159e36feSFUJITA Tomonori 		ret = -ENOMEM;
2269159e36feSFUJITA Tomonori 		goto out_unlock;
2270159e36feSFUJITA Tomonori 	}
2271159e36feSFUJITA Tomonori 
2272159e36feSFUJITA Tomonori 	smpreq = (SmpPassthroughRequest_t *)mf;
2273159e36feSFUJITA Tomonori 	memset(smpreq, 0, sizeof(*smpreq));
2274159e36feSFUJITA Tomonori 
2275651a0136SChristoph Hellwig 	smpreq->RequestDataLength =
2276651a0136SChristoph Hellwig 		cpu_to_le16(job->request_payload.payload_len - 4);
2277159e36feSFUJITA Tomonori 	smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
2278159e36feSFUJITA Tomonori 
2279159e36feSFUJITA Tomonori 	if (rphy)
2280159e36feSFUJITA Tomonori 		sas_address = rphy->identify.sas_address;
2281159e36feSFUJITA Tomonori 	else {
2282159e36feSFUJITA Tomonori 		struct mptsas_portinfo *port_info;
2283159e36feSFUJITA Tomonori 
2284159e36feSFUJITA Tomonori 		mutex_lock(&ioc->sas_topology_mutex);
2285f9c34022SKashyap, Desai 		port_info = ioc->hba_port_info;
2286159e36feSFUJITA Tomonori 		if (port_info && port_info->phy_info)
2287159e36feSFUJITA Tomonori 			sas_address =
2288159e36feSFUJITA Tomonori 				port_info->phy_info[0].phy->identify.sas_address;
2289159e36feSFUJITA Tomonori 		mutex_unlock(&ioc->sas_topology_mutex);
2290159e36feSFUJITA Tomonori 	}
2291159e36feSFUJITA Tomonori 
2292159e36feSFUJITA Tomonori 	*((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address);
2293159e36feSFUJITA Tomonori 
2294159e36feSFUJITA Tomonori 	psge = (char *)
2295159e36feSFUJITA Tomonori 		(((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4));
2296159e36feSFUJITA Tomonori 
2297159e36feSFUJITA Tomonori 	/* request */
2298159e36feSFUJITA Tomonori 	flagsLength = (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
2299159e36feSFUJITA Tomonori 		       MPI_SGE_FLAGS_END_OF_BUFFER |
230014d0f0b0SKashyap, Desai 		       MPI_SGE_FLAGS_DIRECTION)
230114d0f0b0SKashyap, Desai 		       << MPI_SGE_FLAGS_SHIFT;
2302159e36feSFUJITA Tomonori 
2303651a0136SChristoph Hellwig 	if (!dma_map_sg(&ioc->pcidev->dev, job->request_payload.sg_list,
2304b114dda6SChristophe JAILLET 			1, DMA_BIDIRECTIONAL))
2305159e36feSFUJITA Tomonori 		goto put_mf;
2306651a0136SChristoph Hellwig 
2307651a0136SChristoph Hellwig 	flagsLength |= (sg_dma_len(job->request_payload.sg_list) - 4);
2308651a0136SChristoph Hellwig 	ioc->add_sge(psge, flagsLength,
2309651a0136SChristoph Hellwig 			sg_dma_address(job->request_payload.sg_list));
23102f187862SKashyap, Desai 	psge += ioc->SGE_size;
2311159e36feSFUJITA Tomonori 
2312159e36feSFUJITA Tomonori 	/* response */
23132f187862SKashyap, Desai 	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
23142f187862SKashyap, Desai 		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
23152f187862SKashyap, Desai 		MPI_SGE_FLAGS_IOC_TO_HOST |
23162f187862SKashyap, Desai 		MPI_SGE_FLAGS_END_OF_BUFFER;
23172f187862SKashyap, Desai 
23182f187862SKashyap, Desai 	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
2319651a0136SChristoph Hellwig 
2320651a0136SChristoph Hellwig 	if (!dma_map_sg(&ioc->pcidev->dev, job->reply_payload.sg_list,
2321b114dda6SChristophe JAILLET 			1, DMA_BIDIRECTIONAL))
2322651a0136SChristoph Hellwig 		goto unmap_out;
2323651a0136SChristoph Hellwig 	flagsLength |= sg_dma_len(job->reply_payload.sg_list) + 4;
2324651a0136SChristoph Hellwig 	ioc->add_sge(psge, flagsLength,
2325651a0136SChristoph Hellwig 			sg_dma_address(job->reply_payload.sg_list));
2326159e36feSFUJITA Tomonori 
23272f187862SKashyap, Desai 	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
2328159e36feSFUJITA Tomonori 	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
2329159e36feSFUJITA Tomonori 
2330159e36feSFUJITA Tomonori 	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
2331568da769SKashyap, Desai 	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
2332568da769SKashyap, Desai 		ret = -ETIME;
2333568da769SKashyap, Desai 		mpt_free_msg_frame(ioc, mf);
2334568da769SKashyap, Desai 		mf = NULL;
2335568da769SKashyap, Desai 		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
2336651a0136SChristoph Hellwig 			goto unmap_in;
2337568da769SKashyap, Desai 		if (!timeleft)
2338d0f698c4SKashyap, Desai 			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
2339651a0136SChristoph Hellwig 		goto unmap_in;
2340159e36feSFUJITA Tomonori 	}
2341159e36feSFUJITA Tomonori 	mf = NULL;
2342159e36feSFUJITA Tomonori 
2343f0f09d3bSKashyap, Desai 	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
2344159e36feSFUJITA Tomonori 		SmpPassthroughReply_t *smprep;
2345159e36feSFUJITA Tomonori 
2346159e36feSFUJITA Tomonori 		smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
2347651a0136SChristoph Hellwig 		memcpy(job->reply, smprep, sizeof(*smprep));
2348651a0136SChristoph Hellwig 		job->reply_len = sizeof(*smprep);
2349651a0136SChristoph Hellwig 		reslen = smprep->ResponseDataLength;
2350159e36feSFUJITA Tomonori 	} else {
23512f187862SKashyap, Desai 		printk(MYIOC_s_ERR_FMT
23522f187862SKashyap, Desai 		    "%s: smp passthru reply failed to be returned\n",
2353cadbd4a5SHarvey Harrison 		    ioc->name, __func__);
2354159e36feSFUJITA Tomonori 		ret = -ENXIO;
2355159e36feSFUJITA Tomonori 	}
2356651a0136SChristoph Hellwig 
2357651a0136SChristoph Hellwig unmap_in:
2358651a0136SChristoph Hellwig 	dma_unmap_sg(&ioc->pcidev->dev, job->reply_payload.sg_list, 1,
2359b114dda6SChristophe JAILLET 			DMA_BIDIRECTIONAL);
2360651a0136SChristoph Hellwig unmap_out:
2361651a0136SChristoph Hellwig 	dma_unmap_sg(&ioc->pcidev->dev, job->request_payload.sg_list, 1,
2362b114dda6SChristophe JAILLET 			DMA_BIDIRECTIONAL);
2363159e36feSFUJITA Tomonori put_mf:
2364159e36feSFUJITA Tomonori 	if (mf)
2365159e36feSFUJITA Tomonori 		mpt_free_msg_frame(ioc, mf);
2366159e36feSFUJITA Tomonori out_unlock:
23672f187862SKashyap, Desai 	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
2368159e36feSFUJITA Tomonori 	mutex_unlock(&ioc->sas_mgmt.mutex);
2369159e36feSFUJITA Tomonori out:
2370651a0136SChristoph Hellwig 	bsg_job_done(job, ret, reslen);
2371159e36feSFUJITA Tomonori }
2372159e36feSFUJITA Tomonori 
23730c33b27dSChristoph Hellwig static struct sas_function_template mptsas_transport_functions = {
2374b5141128SChristoph Hellwig 	.get_linkerrors		= mptsas_get_linkerrors,
2375e3094447SChristoph Hellwig 	.get_enclosure_identifier = mptsas_get_enclosure_identifier,
2376e3094447SChristoph Hellwig 	.get_bay_identifier	= mptsas_get_bay_identifier,
2377da4fa655SChristoph Hellwig 	.phy_reset		= mptsas_phy_reset,
2378159e36feSFUJITA Tomonori 	.smp_handler		= mptsas_smp_handler,
23790c33b27dSChristoph Hellwig };
23800c33b27dSChristoph Hellwig 
23810c33b27dSChristoph Hellwig static struct scsi_transport_template *mptsas_transport_template;
23820c33b27dSChristoph Hellwig 
23830c33b27dSChristoph Hellwig static int
mptsas_sas_io_unit_pg0(MPT_ADAPTER * ioc,struct mptsas_portinfo * port_info)23840c33b27dSChristoph Hellwig mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
23850c33b27dSChristoph Hellwig {
23860c33b27dSChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
23870c33b27dSChristoph Hellwig 	CONFIGPARMS cfg;
23880c33b27dSChristoph Hellwig 	SasIOUnitPage0_t *buffer;
23890c33b27dSChristoph Hellwig 	dma_addr_t dma_handle;
23900c33b27dSChristoph Hellwig 	int error, i;
23910c33b27dSChristoph Hellwig 
23920c33b27dSChristoph Hellwig 	hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION;
23930c33b27dSChristoph Hellwig 	hdr.ExtPageLength = 0;
23940c33b27dSChristoph Hellwig 	hdr.PageNumber = 0;
23950c33b27dSChristoph Hellwig 	hdr.Reserved1 = 0;
23960c33b27dSChristoph Hellwig 	hdr.Reserved2 = 0;
23970c33b27dSChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
23980c33b27dSChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
23990c33b27dSChristoph Hellwig 
24000c33b27dSChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
24010c33b27dSChristoph Hellwig 	cfg.physAddr = -1;
24020c33b27dSChristoph Hellwig 	cfg.pageAddr = 0;
24030c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
24040c33b27dSChristoph Hellwig 	cfg.dir = 0;	/* read */
24054b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
24060c33b27dSChristoph Hellwig 
24070c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
24080c33b27dSChristoph Hellwig 	if (error)
24090c33b27dSChristoph Hellwig 		goto out;
24100c33b27dSChristoph Hellwig 	if (!hdr.ExtPageLength) {
24110c33b27dSChristoph Hellwig 		error = -ENXIO;
24120c33b27dSChristoph Hellwig 		goto out;
24130c33b27dSChristoph Hellwig 	}
24140c33b27dSChristoph Hellwig 
241576a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
241676a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
24170c33b27dSChristoph Hellwig 	if (!buffer) {
24180c33b27dSChristoph Hellwig 		error = -ENOMEM;
24190c33b27dSChristoph Hellwig 		goto out;
24200c33b27dSChristoph Hellwig 	}
24210c33b27dSChristoph Hellwig 
24220c33b27dSChristoph Hellwig 	cfg.physAddr = dma_handle;
24230c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
24240c33b27dSChristoph Hellwig 
24250c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
24260c33b27dSChristoph Hellwig 	if (error)
24270c33b27dSChristoph Hellwig 		goto out_free_consistent;
24280c33b27dSChristoph Hellwig 
24290c33b27dSChristoph Hellwig 	port_info->num_phys = buffer->NumPhys;
24300c33b27dSChristoph Hellwig 	port_info->phy_info = kcalloc(port_info->num_phys,
24312f187862SKashyap, Desai 		sizeof(struct mptsas_phyinfo), GFP_KERNEL);
24320c33b27dSChristoph Hellwig 	if (!port_info->phy_info) {
24330c33b27dSChristoph Hellwig 		error = -ENOMEM;
24340c33b27dSChristoph Hellwig 		goto out_free_consistent;
24350c33b27dSChristoph Hellwig 	}
24360c33b27dSChristoph Hellwig 
2437edb9068dSPrakash, Sathya 	ioc->nvdata_version_persistent =
2438edb9068dSPrakash, Sathya 	    le16_to_cpu(buffer->NvdataVersionPersistent);
2439edb9068dSPrakash, Sathya 	ioc->nvdata_version_default =
2440edb9068dSPrakash, Sathya 	    le16_to_cpu(buffer->NvdataVersionDefault);
2441edb9068dSPrakash, Sathya 
24420c33b27dSChristoph Hellwig 	for (i = 0; i < port_info->num_phys; i++) {
2443d6ecdd63SPrakash, Sathya 		mptsas_print_phy_data(ioc, &buffer->PhyData[i]);
24440c33b27dSChristoph Hellwig 		port_info->phy_info[i].phy_id = i;
24450c33b27dSChristoph Hellwig 		port_info->phy_info[i].port_id =
24460c33b27dSChristoph Hellwig 		    buffer->PhyData[i].Port;
24470c33b27dSChristoph Hellwig 		port_info->phy_info[i].negotiated_link_rate =
24480c33b27dSChristoph Hellwig 		    buffer->PhyData[i].NegotiatedLinkRate;
2449547f9a21SEric Moore 		port_info->phy_info[i].portinfo = port_info;
24502ecce492SEric Moore 		port_info->phy_info[i].handle =
24512ecce492SEric Moore 		    le16_to_cpu(buffer->PhyData[i].ControllerDevHandle);
24520c33b27dSChristoph Hellwig 	}
24530c33b27dSChristoph Hellwig 
24540c33b27dSChristoph Hellwig  out_free_consistent:
2455b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2456b114dda6SChristophe JAILLET 			  dma_handle);
24570c33b27dSChristoph Hellwig  out:
24580c33b27dSChristoph Hellwig 	return error;
24590c33b27dSChristoph Hellwig }
24600c33b27dSChristoph Hellwig 
24610c33b27dSChristoph Hellwig static int
mptsas_sas_io_unit_pg1(MPT_ADAPTER * ioc)2462edb9068dSPrakash, Sathya mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc)
2463edb9068dSPrakash, Sathya {
2464edb9068dSPrakash, Sathya 	ConfigExtendedPageHeader_t hdr;
2465edb9068dSPrakash, Sathya 	CONFIGPARMS cfg;
2466edb9068dSPrakash, Sathya 	SasIOUnitPage1_t *buffer;
2467edb9068dSPrakash, Sathya 	dma_addr_t dma_handle;
2468edb9068dSPrakash, Sathya 	int error;
2469aca794ddSKashyap, Desai 	u8 device_missing_delay;
2470edb9068dSPrakash, Sathya 
2471edb9068dSPrakash, Sathya 	memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t));
2472edb9068dSPrakash, Sathya 	memset(&cfg, 0, sizeof(CONFIGPARMS));
2473edb9068dSPrakash, Sathya 
2474edb9068dSPrakash, Sathya 	cfg.cfghdr.ehdr = &hdr;
2475edb9068dSPrakash, Sathya 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
24764b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
2477edb9068dSPrakash, Sathya 	cfg.cfghdr.ehdr->PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
2478edb9068dSPrakash, Sathya 	cfg.cfghdr.ehdr->ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
2479edb9068dSPrakash, Sathya 	cfg.cfghdr.ehdr->PageVersion = MPI_SASIOUNITPAGE1_PAGEVERSION;
2480edb9068dSPrakash, Sathya 	cfg.cfghdr.ehdr->PageNumber = 1;
2481edb9068dSPrakash, Sathya 
2482edb9068dSPrakash, Sathya 	error = mpt_config(ioc, &cfg);
2483edb9068dSPrakash, Sathya 	if (error)
2484edb9068dSPrakash, Sathya 		goto out;
2485edb9068dSPrakash, Sathya 	if (!hdr.ExtPageLength) {
2486edb9068dSPrakash, Sathya 		error = -ENXIO;
2487edb9068dSPrakash, Sathya 		goto out;
2488edb9068dSPrakash, Sathya 	}
2489edb9068dSPrakash, Sathya 
249076a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
249176a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
2492edb9068dSPrakash, Sathya 	if (!buffer) {
2493edb9068dSPrakash, Sathya 		error = -ENOMEM;
2494edb9068dSPrakash, Sathya 		goto out;
2495edb9068dSPrakash, Sathya 	}
2496edb9068dSPrakash, Sathya 
2497edb9068dSPrakash, Sathya 	cfg.physAddr = dma_handle;
2498edb9068dSPrakash, Sathya 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
2499edb9068dSPrakash, Sathya 
2500edb9068dSPrakash, Sathya 	error = mpt_config(ioc, &cfg);
2501edb9068dSPrakash, Sathya 	if (error)
2502edb9068dSPrakash, Sathya 		goto out_free_consistent;
2503edb9068dSPrakash, Sathya 
2504edb9068dSPrakash, Sathya 	ioc->io_missing_delay  =
2505edb9068dSPrakash, Sathya 	    le16_to_cpu(buffer->IODeviceMissingDelay);
2506aca794ddSKashyap, Desai 	device_missing_delay = buffer->ReportDeviceMissingDelay;
2507edb9068dSPrakash, Sathya 	ioc->device_missing_delay = (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_UNIT_16) ?
2508edb9068dSPrakash, Sathya 	    (device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16 :
2509edb9068dSPrakash, Sathya 	    device_missing_delay & MPI_SAS_IOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
2510edb9068dSPrakash, Sathya 
2511edb9068dSPrakash, Sathya  out_free_consistent:
2512b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2513b114dda6SChristophe JAILLET 			  dma_handle);
2514edb9068dSPrakash, Sathya  out:
2515edb9068dSPrakash, Sathya 	return error;
2516edb9068dSPrakash, Sathya }
2517edb9068dSPrakash, Sathya 
2518edb9068dSPrakash, Sathya static int
mptsas_sas_phy_pg0(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info,u32 form,u32 form_specific)25190c33b27dSChristoph Hellwig mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
25200c33b27dSChristoph Hellwig 		u32 form, u32 form_specific)
25210c33b27dSChristoph Hellwig {
25220c33b27dSChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
25230c33b27dSChristoph Hellwig 	CONFIGPARMS cfg;
25240c33b27dSChristoph Hellwig 	SasPhyPage0_t *buffer;
25250c33b27dSChristoph Hellwig 	dma_addr_t dma_handle;
25260c33b27dSChristoph Hellwig 	int error;
25270c33b27dSChristoph Hellwig 
25280c33b27dSChristoph Hellwig 	hdr.PageVersion = MPI_SASPHY0_PAGEVERSION;
25290c33b27dSChristoph Hellwig 	hdr.ExtPageLength = 0;
25300c33b27dSChristoph Hellwig 	hdr.PageNumber = 0;
25310c33b27dSChristoph Hellwig 	hdr.Reserved1 = 0;
25320c33b27dSChristoph Hellwig 	hdr.Reserved2 = 0;
25330c33b27dSChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
25340c33b27dSChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY;
25350c33b27dSChristoph Hellwig 
25360c33b27dSChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
25370c33b27dSChristoph Hellwig 	cfg.dir = 0;	/* read */
25384b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
25390c33b27dSChristoph Hellwig 
25400c33b27dSChristoph Hellwig 	/* Get Phy Pg 0 for each Phy. */
25410c33b27dSChristoph Hellwig 	cfg.physAddr = -1;
25420c33b27dSChristoph Hellwig 	cfg.pageAddr = form + form_specific;
25430c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
25440c33b27dSChristoph Hellwig 
25450c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
25460c33b27dSChristoph Hellwig 	if (error)
25470c33b27dSChristoph Hellwig 		goto out;
25480c33b27dSChristoph Hellwig 
25490c33b27dSChristoph Hellwig 	if (!hdr.ExtPageLength) {
25500c33b27dSChristoph Hellwig 		error = -ENXIO;
25510c33b27dSChristoph Hellwig 		goto out;
25520c33b27dSChristoph Hellwig 	}
25530c33b27dSChristoph Hellwig 
255476a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
255576a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
25560c33b27dSChristoph Hellwig 	if (!buffer) {
25570c33b27dSChristoph Hellwig 		error = -ENOMEM;
25580c33b27dSChristoph Hellwig 		goto out;
25590c33b27dSChristoph Hellwig 	}
25600c33b27dSChristoph Hellwig 
25610c33b27dSChristoph Hellwig 	cfg.physAddr = dma_handle;
25620c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
25630c33b27dSChristoph Hellwig 
25640c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
25650c33b27dSChristoph Hellwig 	if (error)
25660c33b27dSChristoph Hellwig 		goto out_free_consistent;
25670c33b27dSChristoph Hellwig 
2568d6ecdd63SPrakash, Sathya 	mptsas_print_phy_pg0(ioc, buffer);
25690c33b27dSChristoph Hellwig 
25700c33b27dSChristoph Hellwig 	phy_info->hw_link_rate = buffer->HwLinkRate;
25710c33b27dSChristoph Hellwig 	phy_info->programmed_link_rate = buffer->ProgrammedLinkRate;
25720c33b27dSChristoph Hellwig 	phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle);
25730c33b27dSChristoph Hellwig 	phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle);
25740c33b27dSChristoph Hellwig 
25750c33b27dSChristoph Hellwig  out_free_consistent:
2576b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2577b114dda6SChristophe JAILLET 			  dma_handle);
25780c33b27dSChristoph Hellwig  out:
25790c33b27dSChristoph Hellwig 	return error;
25800c33b27dSChristoph Hellwig }
25810c33b27dSChristoph Hellwig 
25820c33b27dSChristoph Hellwig static int
mptsas_sas_device_pg0(MPT_ADAPTER * ioc,struct mptsas_devinfo * device_info,u32 form,u32 form_specific)25830c33b27dSChristoph Hellwig mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
25840c33b27dSChristoph Hellwig 		u32 form, u32 form_specific)
25850c33b27dSChristoph Hellwig {
25860c33b27dSChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
25870c33b27dSChristoph Hellwig 	CONFIGPARMS cfg;
25880c33b27dSChristoph Hellwig 	SasDevicePage0_t *buffer;
25890c33b27dSChristoph Hellwig 	dma_addr_t dma_handle;
25900c33b27dSChristoph Hellwig 	__le64 sas_address;
2591bd23e94cSMoore, Eric 	int error=0;
2592bd23e94cSMoore, Eric 
25930c33b27dSChristoph Hellwig 	hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION;
25940c33b27dSChristoph Hellwig 	hdr.ExtPageLength = 0;
25950c33b27dSChristoph Hellwig 	hdr.PageNumber = 0;
25960c33b27dSChristoph Hellwig 	hdr.Reserved1 = 0;
25970c33b27dSChristoph Hellwig 	hdr.Reserved2 = 0;
25980c33b27dSChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
25990c33b27dSChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE;
26000c33b27dSChristoph Hellwig 
26010c33b27dSChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
26020c33b27dSChristoph Hellwig 	cfg.pageAddr = form + form_specific;
26030c33b27dSChristoph Hellwig 	cfg.physAddr = -1;
26040c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
26050c33b27dSChristoph Hellwig 	cfg.dir = 0;	/* read */
26064b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
26070c33b27dSChristoph Hellwig 
2608db9c9174SMoore, Eric 	memset(device_info, 0, sizeof(struct mptsas_devinfo));
26090c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
26100c33b27dSChristoph Hellwig 	if (error)
26110c33b27dSChristoph Hellwig 		goto out;
26120c33b27dSChristoph Hellwig 	if (!hdr.ExtPageLength) {
26130c33b27dSChristoph Hellwig 		error = -ENXIO;
26140c33b27dSChristoph Hellwig 		goto out;
26150c33b27dSChristoph Hellwig 	}
26160c33b27dSChristoph Hellwig 
261776a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
261876a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
26190c33b27dSChristoph Hellwig 	if (!buffer) {
26200c33b27dSChristoph Hellwig 		error = -ENOMEM;
26210c33b27dSChristoph Hellwig 		goto out;
26220c33b27dSChristoph Hellwig 	}
26230c33b27dSChristoph Hellwig 
26240c33b27dSChristoph Hellwig 	cfg.physAddr = dma_handle;
26250c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
26260c33b27dSChristoph Hellwig 
26270c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
26280cf0f23cSKashyap, Desai 
26290cf0f23cSKashyap, Desai 	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
26300cf0f23cSKashyap, Desai 		error = -ENODEV;
26310cf0f23cSKashyap, Desai 		goto out_free_consistent;
26320cf0f23cSKashyap, Desai 	}
26330cf0f23cSKashyap, Desai 
26340c33b27dSChristoph Hellwig 	if (error)
26350c33b27dSChristoph Hellwig 		goto out_free_consistent;
26360c33b27dSChristoph Hellwig 
2637d6ecdd63SPrakash, Sathya 	mptsas_print_device_pg0(ioc, buffer);
26380c33b27dSChristoph Hellwig 
26392f187862SKashyap, Desai 	memset(device_info, 0, sizeof(struct mptsas_devinfo));
26400c33b27dSChristoph Hellwig 	device_info->handle = le16_to_cpu(buffer->DevHandle);
2641c73787eeSMoore, Eric 	device_info->handle_parent = le16_to_cpu(buffer->ParentDevHandle);
2642e3094447SChristoph Hellwig 	device_info->handle_enclosure =
2643e3094447SChristoph Hellwig 	    le16_to_cpu(buffer->EnclosureHandle);
2644e3094447SChristoph Hellwig 	device_info->slot = le16_to_cpu(buffer->Slot);
26450c33b27dSChristoph Hellwig 	device_info->phy_id = buffer->PhyNum;
26460c33b27dSChristoph Hellwig 	device_info->port_id = buffer->PhysicalPort;
26479a28f49aSChristoph Hellwig 	device_info->id = buffer->TargetID;
2648b506ade9SEric Moore 	device_info->phys_disk_num = ~0;
26499a28f49aSChristoph Hellwig 	device_info->channel = buffer->Bus;
26500c33b27dSChristoph Hellwig 	memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
26510c33b27dSChristoph Hellwig 	device_info->sas_address = le64_to_cpu(sas_address);
26520c33b27dSChristoph Hellwig 	device_info->device_info =
26530c33b27dSChristoph Hellwig 	    le32_to_cpu(buffer->DeviceInfo);
265451106ab5SKashyap, Desai 	device_info->flags = le16_to_cpu(buffer->Flags);
26550c33b27dSChristoph Hellwig 
26560c33b27dSChristoph Hellwig  out_free_consistent:
2657b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2658b114dda6SChristophe JAILLET 			  dma_handle);
26590c33b27dSChristoph Hellwig  out:
26600c33b27dSChristoph Hellwig 	return error;
26610c33b27dSChristoph Hellwig }
26620c33b27dSChristoph Hellwig 
26630c33b27dSChristoph Hellwig static int
mptsas_sas_expander_pg0(MPT_ADAPTER * ioc,struct mptsas_portinfo * port_info,u32 form,u32 form_specific)26640c33b27dSChristoph Hellwig mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
26650c33b27dSChristoph Hellwig 		u32 form, u32 form_specific)
26660c33b27dSChristoph Hellwig {
26670c33b27dSChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
26680c33b27dSChristoph Hellwig 	CONFIGPARMS cfg;
26690c33b27dSChristoph Hellwig 	SasExpanderPage0_t *buffer;
26700c33b27dSChristoph Hellwig 	dma_addr_t dma_handle;
2671547f9a21SEric Moore 	int i, error;
26722f187862SKashyap, Desai 	__le64 sas_address;
26730c33b27dSChristoph Hellwig 
26742f187862SKashyap, Desai 	memset(port_info, 0, sizeof(struct mptsas_portinfo));
26750c33b27dSChristoph Hellwig 	hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION;
26760c33b27dSChristoph Hellwig 	hdr.ExtPageLength = 0;
26770c33b27dSChristoph Hellwig 	hdr.PageNumber = 0;
26780c33b27dSChristoph Hellwig 	hdr.Reserved1 = 0;
26790c33b27dSChristoph Hellwig 	hdr.Reserved2 = 0;
26800c33b27dSChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
26810c33b27dSChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
26820c33b27dSChristoph Hellwig 
26830c33b27dSChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
26840c33b27dSChristoph Hellwig 	cfg.physAddr = -1;
26850c33b27dSChristoph Hellwig 	cfg.pageAddr = form + form_specific;
26860c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
26870c33b27dSChristoph Hellwig 	cfg.dir = 0;	/* read */
26884b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
26890c33b27dSChristoph Hellwig 
2690db9c9174SMoore, Eric 	memset(port_info, 0, sizeof(struct mptsas_portinfo));
26910c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
26920c33b27dSChristoph Hellwig 	if (error)
26930c33b27dSChristoph Hellwig 		goto out;
26940c33b27dSChristoph Hellwig 
26950c33b27dSChristoph Hellwig 	if (!hdr.ExtPageLength) {
26960c33b27dSChristoph Hellwig 		error = -ENXIO;
26970c33b27dSChristoph Hellwig 		goto out;
26980c33b27dSChristoph Hellwig 	}
26990c33b27dSChristoph Hellwig 
270076a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
270176a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
27020c33b27dSChristoph Hellwig 	if (!buffer) {
27030c33b27dSChristoph Hellwig 		error = -ENOMEM;
27040c33b27dSChristoph Hellwig 		goto out;
27050c33b27dSChristoph Hellwig 	}
27060c33b27dSChristoph Hellwig 
27070c33b27dSChristoph Hellwig 	cfg.physAddr = dma_handle;
27080c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
27090c33b27dSChristoph Hellwig 
27100c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
27110cf0f23cSKashyap, Desai 	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
271251f39eaeSKrzysztof Oledzki 		error = -ENODEV;
271351f39eaeSKrzysztof Oledzki 		goto out_free_consistent;
271451f39eaeSKrzysztof Oledzki 	}
271551f39eaeSKrzysztof Oledzki 
27160cf0f23cSKashyap, Desai 	if (error)
27170cf0f23cSKashyap, Desai 		goto out_free_consistent;
27180cf0f23cSKashyap, Desai 
27190c33b27dSChristoph Hellwig 	/* save config data */
27202f187862SKashyap, Desai 	port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1;
27210c33b27dSChristoph Hellwig 	port_info->phy_info = kcalloc(port_info->num_phys,
27222f187862SKashyap, Desai 		sizeof(struct mptsas_phyinfo), GFP_KERNEL);
27230c33b27dSChristoph Hellwig 	if (!port_info->phy_info) {
27240c33b27dSChristoph Hellwig 		error = -ENOMEM;
27250c33b27dSChristoph Hellwig 		goto out_free_consistent;
27260c33b27dSChristoph Hellwig 	}
27270c33b27dSChristoph Hellwig 
27282f187862SKashyap, Desai 	memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64));
27292ecce492SEric Moore 	for (i = 0; i < port_info->num_phys; i++) {
2730547f9a21SEric Moore 		port_info->phy_info[i].portinfo = port_info;
27312ecce492SEric Moore 		port_info->phy_info[i].handle =
27322ecce492SEric Moore 		    le16_to_cpu(buffer->DevHandle);
27332f187862SKashyap, Desai 		port_info->phy_info[i].identify.sas_address =
27342f187862SKashyap, Desai 		    le64_to_cpu(sas_address);
27352f187862SKashyap, Desai 		port_info->phy_info[i].identify.handle_parent =
27362f187862SKashyap, Desai 		    le16_to_cpu(buffer->ParentDevHandle);
27372ecce492SEric Moore 	}
2738547f9a21SEric Moore 
27390c33b27dSChristoph Hellwig  out_free_consistent:
2740b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2741b114dda6SChristophe JAILLET 			  dma_handle);
27420c33b27dSChristoph Hellwig  out:
27430c33b27dSChristoph Hellwig 	return error;
27440c33b27dSChristoph Hellwig }
27450c33b27dSChristoph Hellwig 
27460c33b27dSChristoph Hellwig static int
mptsas_sas_expander_pg1(MPT_ADAPTER * ioc,struct mptsas_phyinfo * phy_info,u32 form,u32 form_specific)27470c33b27dSChristoph Hellwig mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
27480c33b27dSChristoph Hellwig 		u32 form, u32 form_specific)
27490c33b27dSChristoph Hellwig {
27500c33b27dSChristoph Hellwig 	ConfigExtendedPageHeader_t hdr;
27510c33b27dSChristoph Hellwig 	CONFIGPARMS cfg;
27520c33b27dSChristoph Hellwig 	SasExpanderPage1_t *buffer;
27530c33b27dSChristoph Hellwig 	dma_addr_t dma_handle;
2754bd23e94cSMoore, Eric 	int error=0;
2755bd23e94cSMoore, Eric 
27562f187862SKashyap, Desai 	hdr.PageVersion = MPI_SASEXPANDER1_PAGEVERSION;
27570c33b27dSChristoph Hellwig 	hdr.ExtPageLength = 0;
27580c33b27dSChristoph Hellwig 	hdr.PageNumber = 1;
27590c33b27dSChristoph Hellwig 	hdr.Reserved1 = 0;
27600c33b27dSChristoph Hellwig 	hdr.Reserved2 = 0;
27610c33b27dSChristoph Hellwig 	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
27620c33b27dSChristoph Hellwig 	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
27630c33b27dSChristoph Hellwig 
27640c33b27dSChristoph Hellwig 	cfg.cfghdr.ehdr = &hdr;
27650c33b27dSChristoph Hellwig 	cfg.physAddr = -1;
27660c33b27dSChristoph Hellwig 	cfg.pageAddr = form + form_specific;
27670c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
27680c33b27dSChristoph Hellwig 	cfg.dir = 0;	/* read */
27694b97650bSKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
27700c33b27dSChristoph Hellwig 
27710c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
27720c33b27dSChristoph Hellwig 	if (error)
27730c33b27dSChristoph Hellwig 		goto out;
27740c33b27dSChristoph Hellwig 
27750c33b27dSChristoph Hellwig 	if (!hdr.ExtPageLength) {
27760c33b27dSChristoph Hellwig 		error = -ENXIO;
27770c33b27dSChristoph Hellwig 		goto out;
27780c33b27dSChristoph Hellwig 	}
27790c33b27dSChristoph Hellwig 
278076a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,
278176a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
27820c33b27dSChristoph Hellwig 	if (!buffer) {
27830c33b27dSChristoph Hellwig 		error = -ENOMEM;
27840c33b27dSChristoph Hellwig 		goto out;
27850c33b27dSChristoph Hellwig 	}
27860c33b27dSChristoph Hellwig 
27870c33b27dSChristoph Hellwig 	cfg.physAddr = dma_handle;
27880c33b27dSChristoph Hellwig 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
27890c33b27dSChristoph Hellwig 
27900c33b27dSChristoph Hellwig 	error = mpt_config(ioc, &cfg);
27912f187862SKashyap, Desai 
27922f187862SKashyap, Desai 	if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) {
27932f187862SKashyap, Desai 		error = -ENODEV;
27940cf0f23cSKashyap, Desai 		goto out_free_consistent;
27952f187862SKashyap, Desai 	}
27962f187862SKashyap, Desai 
27970c33b27dSChristoph Hellwig 	if (error)
27980c33b27dSChristoph Hellwig 		goto out_free_consistent;
27990c33b27dSChristoph Hellwig 
28000c33b27dSChristoph Hellwig 
2801d6ecdd63SPrakash, Sathya 	mptsas_print_expander_pg1(ioc, buffer);
28020c33b27dSChristoph Hellwig 
28030c33b27dSChristoph Hellwig 	/* save config data */
2804024358eeSEric Moore 	phy_info->phy_id = buffer->PhyIdentifier;
28050c33b27dSChristoph Hellwig 	phy_info->port_id = buffer->PhysicalPort;
28060c33b27dSChristoph Hellwig 	phy_info->negotiated_link_rate = buffer->NegotiatedLinkRate;
28070c33b27dSChristoph Hellwig 	phy_info->programmed_link_rate = buffer->ProgrammedLinkRate;
28080c33b27dSChristoph Hellwig 	phy_info->hw_link_rate = buffer->HwLinkRate;
28090c33b27dSChristoph Hellwig 	phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle);
28100c33b27dSChristoph Hellwig 	phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle);
28110c33b27dSChristoph Hellwig 
28120c33b27dSChristoph Hellwig  out_free_consistent:
2813b114dda6SChristophe JAILLET 	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,
2814b114dda6SChristophe JAILLET 			  dma_handle);
28150c33b27dSChristoph Hellwig  out:
28160c33b27dSChristoph Hellwig 	return error;
28170c33b27dSChristoph Hellwig }
28180c33b27dSChristoph Hellwig 
2819e0f553abSKashyap, Desai struct rep_manu_request{
2820e0f553abSKashyap, Desai 	u8 smp_frame_type;
2821e0f553abSKashyap, Desai 	u8 function;
2822e0f553abSKashyap, Desai 	u8 reserved;
2823e0f553abSKashyap, Desai 	u8 request_length;
2824e0f553abSKashyap, Desai };
2825e0f553abSKashyap, Desai 
2826e0f553abSKashyap, Desai struct rep_manu_reply{
2827e0f553abSKashyap, Desai 	u8 smp_frame_type; /* 0x41 */
2828e0f553abSKashyap, Desai 	u8 function; /* 0x01 */
2829e0f553abSKashyap, Desai 	u8 function_result;
2830e0f553abSKashyap, Desai 	u8 response_length;
2831e0f553abSKashyap, Desai 	u16 expander_change_count;
2832e0f553abSKashyap, Desai 	u8 reserved0[2];
2833e0f553abSKashyap, Desai 	u8 sas_format:1;
2834e0f553abSKashyap, Desai 	u8 reserved1:7;
2835e0f553abSKashyap, Desai 	u8 reserved2[3];
2836e0f553abSKashyap, Desai 	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
2837e0f553abSKashyap, Desai 	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
2838e0f553abSKashyap, Desai 	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
2839e0f553abSKashyap, Desai 	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
2840e0f553abSKashyap, Desai 	u16 component_id;
2841e0f553abSKashyap, Desai 	u8 component_revision_id;
2842e0f553abSKashyap, Desai 	u8 reserved3;
2843e0f553abSKashyap, Desai 	u8 vendor_specific[8];
2844e0f553abSKashyap, Desai };
2845e0f553abSKashyap, Desai 
2846e0f553abSKashyap, Desai /**
2847cdcda465SRandy Dunlap   * mptsas_exp_repmanufacture_info - sets expander manufacturer info
2848e0f553abSKashyap, Desai   * @ioc: per adapter object
2849e0f553abSKashyap, Desai   * @sas_address: expander sas address
2850e0f553abSKashyap, Desai   * @edev: the sas_expander_device object
2851e0f553abSKashyap, Desai   *
2852cdcda465SRandy Dunlap   * For an edge expander or a fanout expander:
2853cdcda465SRandy Dunlap   * fills in the sas_expander_device object when SMP port is created.
2854e0f553abSKashyap, Desai   *
2855cdcda465SRandy Dunlap   * Return: 0 for success, non-zero for failure.
2856e0f553abSKashyap, Desai   */
2857e0f553abSKashyap, Desai static int
mptsas_exp_repmanufacture_info(MPT_ADAPTER * ioc,u64 sas_address,struct sas_expander_device * edev)2858e0f553abSKashyap, Desai mptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc,
2859e0f553abSKashyap, Desai 	u64 sas_address, struct sas_expander_device *edev)
2860e0f553abSKashyap, Desai {
2861e0f553abSKashyap, Desai 	MPT_FRAME_HDR *mf;
2862e0f553abSKashyap, Desai 	SmpPassthroughRequest_t *smpreq;
2863e0f553abSKashyap, Desai 	SmpPassthroughReply_t *smprep;
2864e0f553abSKashyap, Desai 	struct rep_manu_reply *manufacture_reply;
2865e0f553abSKashyap, Desai 	struct rep_manu_request *manufacture_request;
2866e0f553abSKashyap, Desai 	int ret;
2867e0f553abSKashyap, Desai 	int flagsLength;
2868e0f553abSKashyap, Desai 	unsigned long timeleft;
2869e0f553abSKashyap, Desai 	char *psge;
2870e0f553abSKashyap, Desai 	unsigned long flags;
2871e0f553abSKashyap, Desai 	void *data_out = NULL;
2872e0f553abSKashyap, Desai 	dma_addr_t data_out_dma = 0;
2873e0f553abSKashyap, Desai 	u32 sz;
2874e0f553abSKashyap, Desai 
2875e0f553abSKashyap, Desai 	spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
2876e0f553abSKashyap, Desai 	if (ioc->ioc_reset_in_progress) {
2877e0f553abSKashyap, Desai 		spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
2878e0f553abSKashyap, Desai 		printk(MYIOC_s_INFO_FMT "%s: host reset in progress!\n",
2879e0f553abSKashyap, Desai 			__func__, ioc->name);
2880e0f553abSKashyap, Desai 		return -EFAULT;
2881e0f553abSKashyap, Desai 	}
2882e0f553abSKashyap, Desai 	spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
2883e0f553abSKashyap, Desai 
2884e0f553abSKashyap, Desai 	ret = mutex_lock_interruptible(&ioc->sas_mgmt.mutex);
2885e0f553abSKashyap, Desai 	if (ret)
2886e0f553abSKashyap, Desai 		goto out;
2887e0f553abSKashyap, Desai 
2888e0f553abSKashyap, Desai 	mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
2889e0f553abSKashyap, Desai 	if (!mf) {
2890e0f553abSKashyap, Desai 		ret = -ENOMEM;
2891e0f553abSKashyap, Desai 		goto out_unlock;
2892e0f553abSKashyap, Desai 	}
2893e0f553abSKashyap, Desai 
2894e0f553abSKashyap, Desai 	smpreq = (SmpPassthroughRequest_t *)mf;
2895e0f553abSKashyap, Desai 	memset(smpreq, 0, sizeof(*smpreq));
2896e0f553abSKashyap, Desai 
2897e0f553abSKashyap, Desai 	sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply);
2898e0f553abSKashyap, Desai 
28997a960b3aSChristophe JAILLET 	data_out = dma_alloc_coherent(&ioc->pcidev->dev, sz, &data_out_dma,
29007a960b3aSChristophe JAILLET 				      GFP_KERNEL);
2901e0f553abSKashyap, Desai 	if (!data_out) {
2902e0f553abSKashyap, Desai 		printk(KERN_ERR "Memory allocation failure at %s:%d/%s()!\n",
2903e0f553abSKashyap, Desai 			__FILE__, __LINE__, __func__);
2904e0f553abSKashyap, Desai 		ret = -ENOMEM;
2905e0f553abSKashyap, Desai 		goto put_mf;
2906e0f553abSKashyap, Desai 	}
2907e0f553abSKashyap, Desai 
2908e0f553abSKashyap, Desai 	manufacture_request = data_out;
2909e0f553abSKashyap, Desai 	manufacture_request->smp_frame_type = 0x40;
2910e0f553abSKashyap, Desai 	manufacture_request->function = 1;
2911e0f553abSKashyap, Desai 	manufacture_request->reserved = 0;
2912e0f553abSKashyap, Desai 	manufacture_request->request_length = 0;
2913e0f553abSKashyap, Desai 
2914e0f553abSKashyap, Desai 	smpreq->Function = MPI_FUNCTION_SMP_PASSTHROUGH;
2915e0f553abSKashyap, Desai 	smpreq->PhysicalPort = 0xFF;
2916e0f553abSKashyap, Desai 	*((u64 *)&smpreq->SASAddress) = cpu_to_le64(sas_address);
2917e0f553abSKashyap, Desai 	smpreq->RequestDataLength = sizeof(struct rep_manu_request);
2918e0f553abSKashyap, Desai 
2919e0f553abSKashyap, Desai 	psge = (char *)
2920e0f553abSKashyap, Desai 		(((int *) mf) + (offsetof(SmpPassthroughRequest_t, SGL) / 4));
2921e0f553abSKashyap, Desai 
2922e0f553abSKashyap, Desai 	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
2923e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
2924e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_HOST_TO_IOC |
2925e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_END_OF_BUFFER;
2926e0f553abSKashyap, Desai 	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
2927e0f553abSKashyap, Desai 	flagsLength |= sizeof(struct rep_manu_request);
2928e0f553abSKashyap, Desai 
2929e0f553abSKashyap, Desai 	ioc->add_sge(psge, flagsLength, data_out_dma);
2930e0f553abSKashyap, Desai 	psge += ioc->SGE_size;
2931e0f553abSKashyap, Desai 
2932e0f553abSKashyap, Desai 	flagsLength = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
2933e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_SYSTEM_ADDRESS |
2934e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_IOC_TO_HOST |
2935e0f553abSKashyap, Desai 		MPI_SGE_FLAGS_END_OF_BUFFER;
2936e0f553abSKashyap, Desai 	flagsLength = flagsLength << MPI_SGE_FLAGS_SHIFT;
2937e0f553abSKashyap, Desai 	flagsLength |= sizeof(struct rep_manu_reply);
2938e0f553abSKashyap, Desai 	ioc->add_sge(psge, flagsLength, data_out_dma +
2939e0f553abSKashyap, Desai 	sizeof(struct rep_manu_request));
2940e0f553abSKashyap, Desai 
2941e0f553abSKashyap, Desai 	INITIALIZE_MGMT_STATUS(ioc->sas_mgmt.status)
2942e0f553abSKashyap, Desai 	mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
2943e0f553abSKashyap, Desai 
2944e0f553abSKashyap, Desai 	timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
2945e0f553abSKashyap, Desai 	if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
2946e0f553abSKashyap, Desai 		ret = -ETIME;
2947e0f553abSKashyap, Desai 		mpt_free_msg_frame(ioc, mf);
2948e0f553abSKashyap, Desai 		mf = NULL;
2949e0f553abSKashyap, Desai 		if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET)
2950e0f553abSKashyap, Desai 			goto out_free;
2951e0f553abSKashyap, Desai 		if (!timeleft)
2952d0f698c4SKashyap, Desai 			mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
2953e0f553abSKashyap, Desai 		goto out_free;
2954e0f553abSKashyap, Desai 	}
2955e0f553abSKashyap, Desai 
2956e0f553abSKashyap, Desai 	mf = NULL;
2957e0f553abSKashyap, Desai 
2958e0f553abSKashyap, Desai 	if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_RF_VALID) {
2959e0f553abSKashyap, Desai 		u8 *tmp;
2960e0f553abSKashyap, Desai 
2961e0f553abSKashyap, Desai 		smprep = (SmpPassthroughReply_t *)ioc->sas_mgmt.reply;
2962e0f553abSKashyap, Desai 		if (le16_to_cpu(smprep->ResponseDataLength) !=
2963e0f553abSKashyap, Desai 		    sizeof(struct rep_manu_reply))
2964e0f553abSKashyap, Desai 			goto out_free;
2965e0f553abSKashyap, Desai 
2966e0f553abSKashyap, Desai 		manufacture_reply = data_out + sizeof(struct rep_manu_request);
2967e0f553abSKashyap, Desai 		strncpy(edev->vendor_id, manufacture_reply->vendor_id,
2968e0f553abSKashyap, Desai 			SAS_EXPANDER_VENDOR_ID_LEN);
2969e0f553abSKashyap, Desai 		strncpy(edev->product_id, manufacture_reply->product_id,
2970e0f553abSKashyap, Desai 			SAS_EXPANDER_PRODUCT_ID_LEN);
2971e0f553abSKashyap, Desai 		strncpy(edev->product_rev, manufacture_reply->product_rev,
2972e0f553abSKashyap, Desai 			SAS_EXPANDER_PRODUCT_REV_LEN);
2973e0f553abSKashyap, Desai 		edev->level = manufacture_reply->sas_format;
2974e0f553abSKashyap, Desai 		if (manufacture_reply->sas_format) {
2975e0f553abSKashyap, Desai 			strncpy(edev->component_vendor_id,
2976e0f553abSKashyap, Desai 				manufacture_reply->component_vendor_id,
2977e0f553abSKashyap, Desai 				SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
2978e0f553abSKashyap, Desai 			tmp = (u8 *)&manufacture_reply->component_id;
2979e0f553abSKashyap, Desai 			edev->component_id = tmp[0] << 8 | tmp[1];
2980e0f553abSKashyap, Desai 			edev->component_revision_id =
2981e0f553abSKashyap, Desai 				manufacture_reply->component_revision_id;
2982e0f553abSKashyap, Desai 		}
2983e0f553abSKashyap, Desai 	} else {
2984e0f553abSKashyap, Desai 		printk(MYIOC_s_ERR_FMT
2985e0f553abSKashyap, Desai 			"%s: smp passthru reply failed to be returned\n",
2986e0f553abSKashyap, Desai 			ioc->name, __func__);
2987e0f553abSKashyap, Desai 		ret = -ENXIO;
2988e0f553abSKashyap, Desai 	}
2989e0f553abSKashyap, Desai out_free:
2990e0f553abSKashyap, Desai 	if (data_out_dma)
2991b114dda6SChristophe JAILLET 		dma_free_coherent(&ioc->pcidev->dev, sz, data_out,
2992b114dda6SChristophe JAILLET 				  data_out_dma);
2993e0f553abSKashyap, Desai put_mf:
2994e0f553abSKashyap, Desai 	if (mf)
2995e0f553abSKashyap, Desai 		mpt_free_msg_frame(ioc, mf);
2996e0f553abSKashyap, Desai out_unlock:
2997e0f553abSKashyap, Desai 	CLEAR_MGMT_STATUS(ioc->sas_mgmt.status)
2998e0f553abSKashyap, Desai 	mutex_unlock(&ioc->sas_mgmt.mutex);
2999e0f553abSKashyap, Desai out:
3000e0f553abSKashyap, Desai 	return ret;
3001e0f553abSKashyap, Desai }
3002e0f553abSKashyap, Desai 
30030c33b27dSChristoph Hellwig static void
mptsas_parse_device_info(struct sas_identify * identify,struct mptsas_devinfo * device_info)30040c33b27dSChristoph Hellwig mptsas_parse_device_info(struct sas_identify *identify,
30050c33b27dSChristoph Hellwig 		struct mptsas_devinfo *device_info)
30060c33b27dSChristoph Hellwig {
30070c33b27dSChristoph Hellwig 	u16 protocols;
30080c33b27dSChristoph Hellwig 
30090c33b27dSChristoph Hellwig 	identify->sas_address = device_info->sas_address;
30100c33b27dSChristoph Hellwig 	identify->phy_identifier = device_info->phy_id;
30110c33b27dSChristoph Hellwig 
30120c33b27dSChristoph Hellwig 	/*
30130c33b27dSChristoph Hellwig 	 * Fill in Phy Initiator Port Protocol.
30140c33b27dSChristoph Hellwig 	 * Bits 6:3, more than one bit can be set, fall through cases.
30150c33b27dSChristoph Hellwig 	 */
30160c33b27dSChristoph Hellwig 	protocols = device_info->device_info & 0x78;
30170c33b27dSChristoph Hellwig 	identify->initiator_port_protocols = 0;
30180c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SSP_INITIATOR)
30190c33b27dSChristoph Hellwig 		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
30200c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_STP_INITIATOR)
30210c33b27dSChristoph Hellwig 		identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
30220c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SMP_INITIATOR)
30230c33b27dSChristoph Hellwig 		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
30240c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SATA_HOST)
30250c33b27dSChristoph Hellwig 		identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
30260c33b27dSChristoph Hellwig 
30270c33b27dSChristoph Hellwig 	/*
30280c33b27dSChristoph Hellwig 	 * Fill in Phy Target Port Protocol.
30290c33b27dSChristoph Hellwig 	 * Bits 10:7, more than one bit can be set, fall through cases.
30300c33b27dSChristoph Hellwig 	 */
30310c33b27dSChristoph Hellwig 	protocols = device_info->device_info & 0x780;
30320c33b27dSChristoph Hellwig 	identify->target_port_protocols = 0;
30330c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SSP_TARGET)
30340c33b27dSChristoph Hellwig 		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
30350c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_STP_TARGET)
30360c33b27dSChristoph Hellwig 		identify->target_port_protocols |= SAS_PROTOCOL_STP;
30370c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SMP_TARGET)
30380c33b27dSChristoph Hellwig 		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
30390c33b27dSChristoph Hellwig 	if (protocols & MPI_SAS_DEVICE_INFO_SATA_DEVICE)
30400c33b27dSChristoph Hellwig 		identify->target_port_protocols |= SAS_PROTOCOL_SATA;
30410c33b27dSChristoph Hellwig 
30420c33b27dSChristoph Hellwig 	/*
30430c33b27dSChristoph Hellwig 	 * Fill in Attached device type.
30440c33b27dSChristoph Hellwig 	 */
30450c33b27dSChristoph Hellwig 	switch (device_info->device_info &
30460c33b27dSChristoph Hellwig 			MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
30470c33b27dSChristoph Hellwig 	case MPI_SAS_DEVICE_INFO_NO_DEVICE:
30480c33b27dSChristoph Hellwig 		identify->device_type = SAS_PHY_UNUSED;
30490c33b27dSChristoph Hellwig 		break;
30500c33b27dSChristoph Hellwig 	case MPI_SAS_DEVICE_INFO_END_DEVICE:
30510c33b27dSChristoph Hellwig 		identify->device_type = SAS_END_DEVICE;
30520c33b27dSChristoph Hellwig 		break;
30530c33b27dSChristoph Hellwig 	case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER:
30540c33b27dSChristoph Hellwig 		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
30550c33b27dSChristoph Hellwig 		break;
30560c33b27dSChristoph Hellwig 	case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER:
30570c33b27dSChristoph Hellwig 		identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
30580c33b27dSChristoph Hellwig 		break;
30590c33b27dSChristoph Hellwig 	}
30600c33b27dSChristoph Hellwig }
30610c33b27dSChristoph Hellwig 
mptsas_probe_one_phy(struct device * dev,struct mptsas_phyinfo * phy_info,int index,int local)30620c33b27dSChristoph Hellwig static int mptsas_probe_one_phy(struct device *dev,
3063ac01bbbdSChristoph Hellwig 		struct mptsas_phyinfo *phy_info, int index, int local)
30640c33b27dSChristoph Hellwig {
3065e6b2d76aSMoore, Eric 	MPT_ADAPTER *ioc;
30669a28f49aSChristoph Hellwig 	struct sas_phy *phy;
3067547f9a21SEric Moore 	struct sas_port *port;
3068547f9a21SEric Moore 	int error = 0;
3069c9de7dc4SKashyap, Desai 	VirtTarget *vtarget;
30700c33b27dSChristoph Hellwig 
3071547f9a21SEric Moore 	if (!dev) {
3072547f9a21SEric Moore 		error = -ENODEV;
3073547f9a21SEric Moore 		goto out;
3074547f9a21SEric Moore 	}
3075e6b2d76aSMoore, Eric 
3076e6b2d76aSMoore, Eric 	if (!phy_info->phy) {
30779a28f49aSChristoph Hellwig 		phy = sas_phy_alloc(dev, index);
3078547f9a21SEric Moore 		if (!phy) {
3079547f9a21SEric Moore 			error = -ENOMEM;
3080547f9a21SEric Moore 			goto out;
3081547f9a21SEric Moore 		}
3082e6b2d76aSMoore, Eric 	} else
3083e6b2d76aSMoore, Eric 		phy = phy_info->phy;
30840c33b27dSChristoph Hellwig 
30859a28f49aSChristoph Hellwig 	mptsas_parse_device_info(&phy->identify, &phy_info->identify);
30860c33b27dSChristoph Hellwig 
30870c33b27dSChristoph Hellwig 	/*
30880c33b27dSChristoph Hellwig 	 * Set Negotiated link rate.
30890c33b27dSChristoph Hellwig 	 */
30900c33b27dSChristoph Hellwig 	switch (phy_info->negotiated_link_rate) {
30910c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED:
30929a28f49aSChristoph Hellwig 		phy->negotiated_linkrate = SAS_PHY_DISABLED;
30930c33b27dSChristoph Hellwig 		break;
30940c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION:
30959a28f49aSChristoph Hellwig 		phy->negotiated_linkrate = SAS_LINK_RATE_FAILED;
30960c33b27dSChristoph Hellwig 		break;
30970c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_1_5:
30989a28f49aSChristoph Hellwig 		phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
30990c33b27dSChristoph Hellwig 		break;
31000c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_3_0:
31019a28f49aSChristoph Hellwig 		phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
31020c33b27dSChristoph Hellwig 		break;
3103d75733d5SKashyap, Desai 	case MPI_SAS_IOUNIT0_RATE_6_0:
3104d75733d5SKashyap, Desai 		phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
3105d75733d5SKashyap, Desai 		break;
31060c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE:
31070c33b27dSChristoph Hellwig 	case MPI_SAS_IOUNIT0_RATE_UNKNOWN:
31080c33b27dSChristoph Hellwig 	default:
31099a28f49aSChristoph Hellwig 		phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
31100c33b27dSChristoph Hellwig 		break;
31110c33b27dSChristoph Hellwig 	}
31120c33b27dSChristoph Hellwig 
31130c33b27dSChristoph Hellwig 	/*
31140c33b27dSChristoph Hellwig 	 * Set Max hardware link rate.
31150c33b27dSChristoph Hellwig 	 */
31160c33b27dSChristoph Hellwig 	switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
31170c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5:
31189a28f49aSChristoph Hellwig 		phy->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
31190c33b27dSChristoph Hellwig 		break;
31200c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
31219a28f49aSChristoph Hellwig 		phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
31220c33b27dSChristoph Hellwig 		break;
31230c33b27dSChristoph Hellwig 	default:
31240c33b27dSChristoph Hellwig 		break;
31250c33b27dSChristoph Hellwig 	}
31260c33b27dSChristoph Hellwig 
31270c33b27dSChristoph Hellwig 	/*
31280c33b27dSChristoph Hellwig 	 * Set Max programmed link rate.
31290c33b27dSChristoph Hellwig 	 */
31300c33b27dSChristoph Hellwig 	switch (phy_info->programmed_link_rate &
31310c33b27dSChristoph Hellwig 			MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) {
31320c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5:
31339a28f49aSChristoph Hellwig 		phy->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS;
31340c33b27dSChristoph Hellwig 		break;
31350c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0:
31369a28f49aSChristoph Hellwig 		phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
31370c33b27dSChristoph Hellwig 		break;
31380c33b27dSChristoph Hellwig 	default:
31390c33b27dSChristoph Hellwig 		break;
31400c33b27dSChristoph Hellwig 	}
31410c33b27dSChristoph Hellwig 
31420c33b27dSChristoph Hellwig 	/*
31430c33b27dSChristoph Hellwig 	 * Set Min hardware link rate.
31440c33b27dSChristoph Hellwig 	 */
31450c33b27dSChristoph Hellwig 	switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) {
31460c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5:
31479a28f49aSChristoph Hellwig 		phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
31480c33b27dSChristoph Hellwig 		break;
31490c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
31509a28f49aSChristoph Hellwig 		phy->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
31510c33b27dSChristoph Hellwig 		break;
31520c33b27dSChristoph Hellwig 	default:
31530c33b27dSChristoph Hellwig 		break;
31540c33b27dSChristoph Hellwig 	}
31550c33b27dSChristoph Hellwig 
31560c33b27dSChristoph Hellwig 	/*
31570c33b27dSChristoph Hellwig 	 * Set Min programmed link rate.
31580c33b27dSChristoph Hellwig 	 */
31590c33b27dSChristoph Hellwig 	switch (phy_info->programmed_link_rate &
31600c33b27dSChristoph Hellwig 			MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) {
31610c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5:
31629a28f49aSChristoph Hellwig 		phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
31630c33b27dSChristoph Hellwig 		break;
31640c33b27dSChristoph Hellwig 	case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0:
31659a28f49aSChristoph Hellwig 		phy->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS;
31660c33b27dSChristoph Hellwig 		break;
31670c33b27dSChristoph Hellwig 	default:
31680c33b27dSChristoph Hellwig 		break;
31690c33b27dSChristoph Hellwig 	}
31700c33b27dSChristoph Hellwig 
3171e6b2d76aSMoore, Eric 	if (!phy_info->phy) {
3172e6b2d76aSMoore, Eric 
31739a28f49aSChristoph Hellwig 		error = sas_phy_add(phy);
31740c33b27dSChristoph Hellwig 		if (error) {
31759a28f49aSChristoph Hellwig 			sas_phy_free(phy);
3176547f9a21SEric Moore 			goto out;
31770c33b27dSChristoph Hellwig 		}
31789a28f49aSChristoph Hellwig 		phy_info->phy = phy;
3179e6b2d76aSMoore, Eric 	}
31800c33b27dSChristoph Hellwig 
3181547f9a21SEric Moore 	if (!phy_info->attached.handle ||
3182547f9a21SEric Moore 			!phy_info->port_details)
3183547f9a21SEric Moore 		goto out;
3184547f9a21SEric Moore 
3185547f9a21SEric Moore 	port = mptsas_get_port(phy_info);
3186547f9a21SEric Moore 	ioc = phy_to_ioc(phy_info->phy);
3187547f9a21SEric Moore 
3188547f9a21SEric Moore 	if (phy_info->sas_port_add_phy) {
3189547f9a21SEric Moore 
3190547f9a21SEric Moore 		if (!port) {
3191dc22f16dSEric Moore 			port = sas_port_alloc_num(dev);
3192547f9a21SEric Moore 			if (!port) {
3193547f9a21SEric Moore 				error = -ENOMEM;
3194547f9a21SEric Moore 				goto out;
3195547f9a21SEric Moore 			}
3196547f9a21SEric Moore 			error = sas_port_add(port);
3197547f9a21SEric Moore 			if (error) {
3198d6ecdd63SPrakash, Sathya 				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
3199547f9a21SEric Moore 					"%s: exit at line=%d\n", ioc->name,
3200cadbd4a5SHarvey Harrison 					__func__, __LINE__));
3201547f9a21SEric Moore 				goto out;
3202547f9a21SEric Moore 			}
3203d6ecdd63SPrakash, Sathya 			mptsas_set_port(ioc, phy_info, port);
32042f187862SKashyap, Desai 			devtprintk(ioc, dev_printk(KERN_DEBUG, &port->dev,
32052f187862SKashyap, Desai 			    MYIOC_s_FMT "add port %d, sas_addr (0x%llx)\n",
32062f187862SKashyap, Desai 			    ioc->name, port->port_identifier,
32072f187862SKashyap, Desai 			    (unsigned long long)phy_info->
32082f187862SKashyap, Desai 			    attached.sas_address));
3209547f9a21SEric Moore 		}
32102f187862SKashyap, Desai 		dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
32112f187862SKashyap, Desai 			"sas_port_add_phy: phy_id=%d\n",
321229dd3609SEric Moore 			ioc->name, phy_info->phy_id));
3213547f9a21SEric Moore 		sas_port_add_phy(port, phy_info->phy);
3214547f9a21SEric Moore 		phy_info->sas_port_add_phy = 0;
32152f187862SKashyap, Desai 		devtprintk(ioc, dev_printk(KERN_DEBUG, &phy_info->phy->dev,
32162f187862SKashyap, Desai 		    MYIOC_s_FMT "add phy %d, phy-obj (0x%p)\n", ioc->name,
32172f187862SKashyap, Desai 		     phy_info->phy_id, phy_info->phy));
3218547f9a21SEric Moore 	}
3219547f9a21SEric Moore 	if (!mptsas_get_rphy(phy_info) && port && !port->rphy) {
3220e6b2d76aSMoore, Eric 
32210c33b27dSChristoph Hellwig 		struct sas_rphy *rphy;
32222686de27SJames Bottomley 		struct device *parent;
3223f013db32SJames Bottomley 		struct sas_identify identify;
32240c33b27dSChristoph Hellwig 
32252686de27SJames Bottomley 		parent = dev->parent->parent;
3226e6b2d76aSMoore, Eric 		/*
3227e6b2d76aSMoore, Eric 		 * Let the hotplug_work thread handle processing
3228e6b2d76aSMoore, Eric 		 * the adding/removing of devices that occur
3229e6b2d76aSMoore, Eric 		 * after start of day.
3230e6b2d76aSMoore, Eric 		 */
32312f187862SKashyap, Desai 		if (mptsas_is_end_device(&phy_info->attached) &&
32322f187862SKashyap, Desai 		    phy_info->attached.handle_parent) {
3233547f9a21SEric Moore 			goto out;
32342f187862SKashyap, Desai 		}
3235e6b2d76aSMoore, Eric 
3236f013db32SJames Bottomley 		mptsas_parse_device_info(&identify, &phy_info->attached);
32372686de27SJames Bottomley 		if (scsi_is_host_device(parent)) {
32382686de27SJames Bottomley 			struct mptsas_portinfo *port_info;
32392686de27SJames Bottomley 			int i;
32402686de27SJames Bottomley 
3241f9c34022SKashyap, Desai 			port_info = ioc->hba_port_info;
32422686de27SJames Bottomley 
32432686de27SJames Bottomley 			for (i = 0; i < port_info->num_phys; i++)
32442686de27SJames Bottomley 				if (port_info->phy_info[i].identify.sas_address ==
32450c269e6dSJames Bottomley 				    identify.sas_address) {
32460c269e6dSJames Bottomley 					sas_port_mark_backlink(port);
32472686de27SJames Bottomley 					goto out;
32480c269e6dSJames Bottomley 				}
32492686de27SJames Bottomley 
32502686de27SJames Bottomley 		} else if (scsi_is_sas_rphy(parent)) {
32512686de27SJames Bottomley 			struct sas_rphy *parent_rphy = dev_to_rphy(parent);
32522686de27SJames Bottomley 			if (identify.sas_address ==
32530c269e6dSJames Bottomley 			    parent_rphy->identify.sas_address) {
32540c269e6dSJames Bottomley 				sas_port_mark_backlink(port);
32552686de27SJames Bottomley 				goto out;
32562686de27SJames Bottomley 			}
32570c269e6dSJames Bottomley 		}
32582686de27SJames Bottomley 
3259f013db32SJames Bottomley 		switch (identify.device_type) {
3260f013db32SJames Bottomley 		case SAS_END_DEVICE:
3261547f9a21SEric Moore 			rphy = sas_end_device_alloc(port);
3262f013db32SJames Bottomley 			break;
3263f013db32SJames Bottomley 		case SAS_EDGE_EXPANDER_DEVICE:
3264f013db32SJames Bottomley 		case SAS_FANOUT_EXPANDER_DEVICE:
3265547f9a21SEric Moore 			rphy = sas_expander_alloc(port, identify.device_type);
3266f013db32SJames Bottomley 			break;
3267f013db32SJames Bottomley 		default:
3268f013db32SJames Bottomley 			rphy = NULL;
3269f013db32SJames Bottomley 			break;
3270f013db32SJames Bottomley 		}
3271547f9a21SEric Moore 		if (!rphy) {
3272d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
3273547f9a21SEric Moore 				"%s: exit at line=%d\n", ioc->name,
3274cadbd4a5SHarvey Harrison 				__func__, __LINE__));
3275547f9a21SEric Moore 			goto out;
3276547f9a21SEric Moore 		}
32770c33b27dSChristoph Hellwig 
3278f013db32SJames Bottomley 		rphy->identify = identify;
32790c33b27dSChristoph Hellwig 		error = sas_rphy_add(rphy);
32800c33b27dSChristoph Hellwig 		if (error) {
3281d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
3282547f9a21SEric Moore 				"%s: exit at line=%d\n", ioc->name,
3283cadbd4a5SHarvey Harrison 				__func__, __LINE__));
32840c33b27dSChristoph Hellwig 			sas_rphy_free(rphy);
3285547f9a21SEric Moore 			goto out;
3286547f9a21SEric Moore 		}
3287d6ecdd63SPrakash, Sathya 		mptsas_set_rphy(ioc, phy_info, rphy);
3288e0f553abSKashyap, Desai 		if (identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
3289e0f553abSKashyap, Desai 			identify.device_type == SAS_FANOUT_EXPANDER_DEVICE)
3290e0f553abSKashyap, Desai 				mptsas_exp_repmanufacture_info(ioc,
3291e0f553abSKashyap, Desai 					identify.sas_address,
3292e0f553abSKashyap, Desai 					rphy_to_expander_device(rphy));
3293547f9a21SEric Moore 	}
3294547f9a21SEric Moore 
3295c9de7dc4SKashyap, Desai 	/* If the device exists, verify it wasn't previously flagged
3296c9de7dc4SKashyap, Desai 	as a missing device.  If so, clear it */
3297c9de7dc4SKashyap, Desai 	vtarget = mptsas_find_vtarget(ioc,
3298c9de7dc4SKashyap, Desai 	    phy_info->attached.channel,
3299c9de7dc4SKashyap, Desai 	    phy_info->attached.id);
3300c9de7dc4SKashyap, Desai 	if (vtarget && vtarget->inDMD) {
3301c9de7dc4SKashyap, Desai 		printk(KERN_INFO "Device returned, unsetting inDMD\n");
3302c9de7dc4SKashyap, Desai 		vtarget->inDMD = 0;
3303c9de7dc4SKashyap, Desai 	}
3304c9de7dc4SKashyap, Desai 
3305547f9a21SEric Moore  out:
33060c33b27dSChristoph Hellwig 	return error;
33070c33b27dSChristoph Hellwig }
33080c33b27dSChristoph Hellwig 
33090c33b27dSChristoph Hellwig static int
mptsas_probe_hba_phys(MPT_ADAPTER * ioc)3310e6b2d76aSMoore, Eric mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
33110c33b27dSChristoph Hellwig {
3312e6b2d76aSMoore, Eric 	struct mptsas_portinfo *port_info, *hba;
33130c33b27dSChristoph Hellwig 	int error = -ENOMEM, i;
33140c33b27dSChristoph Hellwig 
3315f9c34022SKashyap, Desai 	hba = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
3316e6b2d76aSMoore, Eric 	if (! hba)
33170c33b27dSChristoph Hellwig 		goto out;
33180c33b27dSChristoph Hellwig 
3319e6b2d76aSMoore, Eric 	error = mptsas_sas_io_unit_pg0(ioc, hba);
33200c33b27dSChristoph Hellwig 	if (error)
33210c33b27dSChristoph Hellwig 		goto out_free_port_info;
33220c33b27dSChristoph Hellwig 
3323edb9068dSPrakash, Sathya 	mptsas_sas_io_unit_pg1(ioc);
33249a28f49aSChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
3325f9c34022SKashyap, Desai 	port_info = ioc->hba_port_info;
3326e6b2d76aSMoore, Eric 	if (!port_info) {
3327f9c34022SKashyap, Desai 		ioc->hba_port_info = port_info = hba;
3328f9c34022SKashyap, Desai 		ioc->hba_port_num_phy = port_info->num_phys;
33290c33b27dSChristoph Hellwig 		list_add_tail(&port_info->list, &ioc->sas_topology);
3330e6b2d76aSMoore, Eric 	} else {
33312ecce492SEric Moore 		for (i = 0; i < hba->num_phys; i++) {
3332e6b2d76aSMoore, Eric 			port_info->phy_info[i].negotiated_link_rate =
3333e6b2d76aSMoore, Eric 				hba->phy_info[i].negotiated_link_rate;
33342ecce492SEric Moore 			port_info->phy_info[i].handle =
33352ecce492SEric Moore 				hba->phy_info[i].handle;
33362ecce492SEric Moore 			port_info->phy_info[i].port_id =
33372ecce492SEric Moore 				hba->phy_info[i].port_id;
33382ecce492SEric Moore 		}
3339e6b2d76aSMoore, Eric 		kfree(hba->phy_info);
3340e6b2d76aSMoore, Eric 		kfree(hba);
3341e6b2d76aSMoore, Eric 		hba = NULL;
3342e6b2d76aSMoore, Eric 	}
33439a28f49aSChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
3344f9c34022SKashyap, Desai #if defined(CPQ_CIM)
3345f9c34022SKashyap, Desai 	ioc->num_ports = port_info->num_phys;
3346f9c34022SKashyap, Desai #endif
33470c33b27dSChristoph Hellwig 	for (i = 0; i < port_info->num_phys; i++) {
33480c33b27dSChristoph Hellwig 		mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
33490c33b27dSChristoph Hellwig 			(MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
33500c33b27dSChristoph Hellwig 			 MPI_SAS_PHY_PGAD_FORM_SHIFT), i);
3351f9c34022SKashyap, Desai 		port_info->phy_info[i].identify.handle =
3352f9c34022SKashyap, Desai 		    port_info->phy_info[i].handle;
33530c33b27dSChristoph Hellwig 		mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify,
33542ecce492SEric Moore 			(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
33552ecce492SEric Moore 			 MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
3356f9c34022SKashyap, Desai 			 port_info->phy_info[i].identify.handle);
3357f9c34022SKashyap, Desai 		if (!ioc->hba_port_sas_addr)
3358f9c34022SKashyap, Desai 			ioc->hba_port_sas_addr =
3359f9c34022SKashyap, Desai 			    port_info->phy_info[i].identify.sas_address;
3360024358eeSEric Moore 		port_info->phy_info[i].identify.phy_id =
33612ecce492SEric Moore 		    port_info->phy_info[i].phy_id = i;
3362547f9a21SEric Moore 		if (port_info->phy_info[i].attached.handle)
33630c33b27dSChristoph Hellwig 			mptsas_sas_device_pg0(ioc,
33640c33b27dSChristoph Hellwig 				&port_info->phy_info[i].attached,
33650c33b27dSChristoph Hellwig 				(MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
33660c33b27dSChristoph Hellwig 				 MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
33670c33b27dSChristoph Hellwig 				port_info->phy_info[i].attached.handle);
33680c33b27dSChristoph Hellwig 	}
33690c33b27dSChristoph Hellwig 
3370547f9a21SEric Moore 	mptsas_setup_wide_ports(ioc, port_info);
3371547f9a21SEric Moore 
3372547f9a21SEric Moore 	for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
33730c33b27dSChristoph Hellwig 		mptsas_probe_one_phy(&ioc->sh->shost_gendev,
3374e6b2d76aSMoore, Eric 		    &port_info->phy_info[i], ioc->sas_index, 1);
33750c33b27dSChristoph Hellwig 
33760c33b27dSChristoph Hellwig 	return 0;
33770c33b27dSChristoph Hellwig 
33780c33b27dSChristoph Hellwig  out_free_port_info:
3379e6b2d76aSMoore, Eric 	kfree(hba);
33800c33b27dSChristoph Hellwig  out:
33810c33b27dSChristoph Hellwig 	return error;
33820c33b27dSChristoph Hellwig }
33830c33b27dSChristoph Hellwig 
3384f9c34022SKashyap, Desai static void
mptsas_expander_refresh(MPT_ADAPTER * ioc,struct mptsas_portinfo * port_info)3385f9c34022SKashyap, Desai mptsas_expander_refresh(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
33860c33b27dSChristoph Hellwig {
3387f9c34022SKashyap, Desai 	struct mptsas_portinfo *parent;
3388f9c34022SKashyap, Desai 	struct device *parent_dev;
3389547f9a21SEric Moore 	struct sas_rphy	*rphy;
3390f9c34022SKashyap, Desai 	int		i;
3391f9c34022SKashyap, Desai 	u64		sas_address; /* expander sas address */
3392f9c34022SKashyap, Desai 	u32		handle;
33930c33b27dSChristoph Hellwig 
3394f9c34022SKashyap, Desai 	handle = port_info->phy_info[0].handle;
3395f9c34022SKashyap, Desai 	sas_address = port_info->phy_info[0].identify.sas_address;
33960c33b27dSChristoph Hellwig 	for (i = 0; i < port_info->num_phys; i++) {
33970c33b27dSChristoph Hellwig 		mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i],
33980c33b27dSChristoph Hellwig 		    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM <<
3399f9c34022SKashyap, Desai 		    MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + handle);
34000c33b27dSChristoph Hellwig 
34010c33b27dSChristoph Hellwig 		mptsas_sas_device_pg0(ioc,
34020c33b27dSChristoph Hellwig 		    &port_info->phy_info[i].identify,
34030c33b27dSChristoph Hellwig 		    (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
34040c33b27dSChristoph Hellwig 		    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
34050c33b27dSChristoph Hellwig 		    port_info->phy_info[i].identify.handle);
3406024358eeSEric Moore 		port_info->phy_info[i].identify.phy_id =
3407024358eeSEric Moore 		    port_info->phy_info[i].phy_id;
34080c33b27dSChristoph Hellwig 
34090c33b27dSChristoph Hellwig 		if (port_info->phy_info[i].attached.handle) {
34100c33b27dSChristoph Hellwig 			mptsas_sas_device_pg0(ioc,
34110c33b27dSChristoph Hellwig 			    &port_info->phy_info[i].attached,
34120c33b27dSChristoph Hellwig 			    (MPI_SAS_DEVICE_PGAD_FORM_HANDLE <<
34130c33b27dSChristoph Hellwig 			     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
34140c33b27dSChristoph Hellwig 			    port_info->phy_info[i].attached.handle);
3415db9c9174SMoore, Eric 			port_info->phy_info[i].attached.phy_id =
3416db9c9174SMoore, Eric 			    port_info->phy_info[i].phy_id;
34170c33b27dSChristoph Hellwig 		}
3418547f9a21SEric Moore 	}
34190c33b27dSChristoph Hellwig 
34209a28f49aSChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
3421f9c34022SKashyap, Desai 	parent = mptsas_find_portinfo_by_handle(ioc,
3422f9c34022SKashyap, Desai 	    port_info->phy_info[0].identify.handle_parent);
3423f9c34022SKashyap, Desai 	if (!parent) {
3424f9c34022SKashyap, Desai 		mutex_unlock(&ioc->sas_topology_mutex);
3425f9c34022SKashyap, Desai 		return;
3426f9c34022SKashyap, Desai 	}
3427f9c34022SKashyap, Desai 	for (i = 0, parent_dev = NULL; i < parent->num_phys && !parent_dev;
3428f9c34022SKashyap, Desai 	    i++) {
3429f9c34022SKashyap, Desai 		if (parent->phy_info[i].attached.sas_address == sas_address) {
3430f9c34022SKashyap, Desai 			rphy = mptsas_get_rphy(&parent->phy_info[i]);
3431f9c34022SKashyap, Desai 			parent_dev = &rphy->dev;
34320c33b27dSChristoph Hellwig 		}
34330c33b27dSChristoph Hellwig 	}
34349a28f49aSChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
34350c33b27dSChristoph Hellwig 
3436547f9a21SEric Moore 	mptsas_setup_wide_ports(ioc, port_info);
3437547f9a21SEric Moore 	for (i = 0; i < port_info->num_phys; i++, ioc->sas_index++)
3438f9c34022SKashyap, Desai 		mptsas_probe_one_phy(parent_dev, &port_info->phy_info[i],
3439e6b2d76aSMoore, Eric 		    ioc->sas_index, 0);
34400c33b27dSChristoph Hellwig }
34410c33b27dSChristoph Hellwig 
3442e6b2d76aSMoore, Eric static void
mptsas_expander_event_add(MPT_ADAPTER * ioc,MpiEventDataSasExpanderStatusChange_t * expander_data)3443f9c34022SKashyap, Desai mptsas_expander_event_add(MPT_ADAPTER *ioc,
3444f9c34022SKashyap, Desai     MpiEventDataSasExpanderStatusChange_t *expander_data)
3445e6b2d76aSMoore, Eric {
3446f9c34022SKashyap, Desai 	struct mptsas_portinfo *port_info;
3447e6b2d76aSMoore, Eric 	int i;
3448f9c34022SKashyap, Desai 	__le64 sas_address;
3449f9c34022SKashyap, Desai 
3450f9c34022SKashyap, Desai 	port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
34514dec8004Szhouchuangao 	BUG_ON(!port_info);
3452f9c34022SKashyap, Desai 	port_info->num_phys = (expander_data->NumPhys) ?
3453f9c34022SKashyap, Desai 	    expander_data->NumPhys : 1;
3454f9c34022SKashyap, Desai 	port_info->phy_info = kcalloc(port_info->num_phys,
3455f9c34022SKashyap, Desai 	    sizeof(struct mptsas_phyinfo), GFP_KERNEL);
34564dec8004Szhouchuangao 	BUG_ON(!port_info->phy_info);
3457f9c34022SKashyap, Desai 	memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
3458f9c34022SKashyap, Desai 	for (i = 0; i < port_info->num_phys; i++) {
3459f9c34022SKashyap, Desai 		port_info->phy_info[i].portinfo = port_info;
3460f9c34022SKashyap, Desai 		port_info->phy_info[i].handle =
3461f9c34022SKashyap, Desai 		    le16_to_cpu(expander_data->DevHandle);
3462f9c34022SKashyap, Desai 		port_info->phy_info[i].identify.sas_address =
3463f9c34022SKashyap, Desai 		    le64_to_cpu(sas_address);
3464f9c34022SKashyap, Desai 		port_info->phy_info[i].identify.handle_parent =
3465f9c34022SKashyap, Desai 		    le16_to_cpu(expander_data->ParentDevHandle);
3466f9c34022SKashyap, Desai 	}
3467e6b2d76aSMoore, Eric 
3468e6b2d76aSMoore, Eric 	mutex_lock(&ioc->sas_topology_mutex);
3469f9c34022SKashyap, Desai 	list_add_tail(&port_info->list, &ioc->sas_topology);
3470f9c34022SKashyap, Desai 	mutex_unlock(&ioc->sas_topology_mutex);
3471e6b2d76aSMoore, Eric 
3472f9c34022SKashyap, Desai 	printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
3473f9c34022SKashyap, Desai 	    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
3474f9c34022SKashyap, Desai 	    (unsigned long long)sas_address);
3475f9c34022SKashyap, Desai 
3476f9c34022SKashyap, Desai 	mptsas_expander_refresh(ioc, port_info);
3477f9c34022SKashyap, Desai }
3478f9c34022SKashyap, Desai 
3479f9c34022SKashyap, Desai /**
3480f9c34022SKashyap, Desai  * mptsas_delete_expander_siblings - remove siblings attached to expander
3481f9c34022SKashyap, Desai  * @ioc: Pointer to MPT_ADAPTER structure
3482f9c34022SKashyap, Desai  * @parent: the parent port_info object
3483f9c34022SKashyap, Desai  * @expander: the expander port_info object
3484f9c34022SKashyap, Desai  **/
3485f9c34022SKashyap, Desai static void
mptsas_delete_expander_siblings(MPT_ADAPTER * ioc,struct mptsas_portinfo * parent,struct mptsas_portinfo * expander)3486f9c34022SKashyap, Desai mptsas_delete_expander_siblings(MPT_ADAPTER *ioc, struct mptsas_portinfo
3487f9c34022SKashyap, Desai     *parent, struct mptsas_portinfo *expander)
3488f9c34022SKashyap, Desai {
3489f9c34022SKashyap, Desai 	struct mptsas_phyinfo *phy_info;
3490f9c34022SKashyap, Desai 	struct mptsas_portinfo *port_info;
3491f9c34022SKashyap, Desai 	struct sas_rphy *rphy;
3492f9c34022SKashyap, Desai 	int i;
3493f9c34022SKashyap, Desai 
3494f9c34022SKashyap, Desai 	phy_info = expander->phy_info;
3495f9c34022SKashyap, Desai 	for (i = 0; i < expander->num_phys; i++, phy_info++) {
3496f9c34022SKashyap, Desai 		rphy = mptsas_get_rphy(phy_info);
3497f9c34022SKashyap, Desai 		if (!rphy)
3498e6b2d76aSMoore, Eric 			continue;
3499f9c34022SKashyap, Desai 		if (rphy->identify.device_type == SAS_END_DEVICE)
3500f9c34022SKashyap, Desai 			mptsas_del_end_device(ioc, phy_info);
3501f9c34022SKashyap, Desai 	}
3502e6b2d76aSMoore, Eric 
3503f9c34022SKashyap, Desai 	phy_info = expander->phy_info;
3504f9c34022SKashyap, Desai 	for (i = 0; i < expander->num_phys; i++, phy_info++) {
3505f9c34022SKashyap, Desai 		rphy = mptsas_get_rphy(phy_info);
3506f9c34022SKashyap, Desai 		if (!rphy)
3507f9c34022SKashyap, Desai 			continue;
3508f9c34022SKashyap, Desai 		if (rphy->identify.device_type ==
3509f9c34022SKashyap, Desai 		    MPI_SAS_DEVICE_INFO_EDGE_EXPANDER ||
3510f9c34022SKashyap, Desai 		    rphy->identify.device_type ==
3511f9c34022SKashyap, Desai 		    MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER) {
3512f9c34022SKashyap, Desai 			port_info = mptsas_find_portinfo_by_sas_address(ioc,
3513f9c34022SKashyap, Desai 			    rphy->identify.sas_address);
3514f9c34022SKashyap, Desai 			if (!port_info)
3515f9c34022SKashyap, Desai 				continue;
3516f9c34022SKashyap, Desai 			if (port_info == parent) /* backlink rphy */
3517f9c34022SKashyap, Desai 				continue;
3518f9c34022SKashyap, Desai 			/*
3519f9c34022SKashyap, Desai 			Delete this expander even if the expdevpage is exists
3520f9c34022SKashyap, Desai 			because the parent expander is already deleted
3521f9c34022SKashyap, Desai 			*/
3522f9c34022SKashyap, Desai 			mptsas_expander_delete(ioc, port_info, 1);
3523f9c34022SKashyap, Desai 		}
3524f9c34022SKashyap, Desai 	}
3525f9c34022SKashyap, Desai }
3526f9c34022SKashyap, Desai 
3527f9c34022SKashyap, Desai 
3528f9c34022SKashyap, Desai /**
3529f9c34022SKashyap, Desai  *	mptsas_expander_delete - remove this expander
3530f9c34022SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
3531f9c34022SKashyap, Desai  *	@port_info: expander port_info struct
3532f9c34022SKashyap, Desai  *	@force: Flag to forcefully delete the expander
3533f9c34022SKashyap, Desai  *
3534f9c34022SKashyap, Desai  **/
3535f9c34022SKashyap, Desai 
mptsas_expander_delete(MPT_ADAPTER * ioc,struct mptsas_portinfo * port_info,u8 force)3536f9c34022SKashyap, Desai static void mptsas_expander_delete(MPT_ADAPTER *ioc,
3537f9c34022SKashyap, Desai 		struct mptsas_portinfo *port_info, u8 force)
3538f9c34022SKashyap, Desai {
3539f9c34022SKashyap, Desai 
3540f9c34022SKashyap, Desai 	struct mptsas_portinfo *parent;
3541f9c34022SKashyap, Desai 	int		i;
3542f9c34022SKashyap, Desai 	u64		expander_sas_address;
3543f9c34022SKashyap, Desai 	struct mptsas_phyinfo *phy_info;
3544f9c34022SKashyap, Desai 	struct mptsas_portinfo buffer;
3545f9c34022SKashyap, Desai 	struct mptsas_portinfo_details *port_details;
3546f9c34022SKashyap, Desai 	struct sas_port *port;
3547f9c34022SKashyap, Desai 
3548f9c34022SKashyap, Desai 	if (!port_info)
3549f9c34022SKashyap, Desai 		return;
3550f9c34022SKashyap, Desai 
3551f9c34022SKashyap, Desai 	/* see if expander is still there before deleting */
3552f9c34022SKashyap, Desai 	mptsas_sas_expander_pg0(ioc, &buffer,
3553e6b2d76aSMoore, Eric 	    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
35542ecce492SEric Moore 	    MPI_SAS_EXPAND_PGAD_FORM_SHIFT),
3555f9c34022SKashyap, Desai 	    port_info->phy_info[0].identify.handle);
3556f9c34022SKashyap, Desai 
3557f9c34022SKashyap, Desai 	if (buffer.num_phys) {
3558f9c34022SKashyap, Desai 		kfree(buffer.phy_info);
3559f9c34022SKashyap, Desai 		if (!force)
3560f9c34022SKashyap, Desai 			return;
3561f9c34022SKashyap, Desai 	}
3562f9c34022SKashyap, Desai 
3563e6b2d76aSMoore, Eric 
3564e6b2d76aSMoore, Eric 	/*
3565e6b2d76aSMoore, Eric 	 * Obtain the port_info instance to the parent port
3566e6b2d76aSMoore, Eric 	 */
3567f9c34022SKashyap, Desai 	port_details = NULL;
3568547f9a21SEric Moore 	expander_sas_address =
3569547f9a21SEric Moore 	    port_info->phy_info[0].identify.sas_address;
3570f9c34022SKashyap, Desai 	parent = mptsas_find_portinfo_by_handle(ioc,
3571f9c34022SKashyap, Desai 	    port_info->phy_info[0].identify.handle_parent);
3572f9c34022SKashyap, Desai 	mptsas_delete_expander_siblings(ioc, parent, port_info);
3573f9c34022SKashyap, Desai 	if (!parent)
3574f9c34022SKashyap, Desai 		goto out;
3575547f9a21SEric Moore 
3576e6b2d76aSMoore, Eric 	/*
3577e6b2d76aSMoore, Eric 	 * Delete rphys in the parent that point
3578f9c34022SKashyap, Desai 	 * to this expander.
3579e6b2d76aSMoore, Eric 	 */
3580547f9a21SEric Moore 	phy_info = parent->phy_info;
3581f9c34022SKashyap, Desai 	port = NULL;
3582547f9a21SEric Moore 	for (i = 0; i < parent->num_phys; i++, phy_info++) {
3583f9c34022SKashyap, Desai 		if (!phy_info->phy)
3584e6b2d76aSMoore, Eric 			continue;
3585547f9a21SEric Moore 		if (phy_info->attached.sas_address !=
3586547f9a21SEric Moore 		    expander_sas_address)
3587547f9a21SEric Moore 			continue;
3588f9c34022SKashyap, Desai 		if (!port) {
3589f9c34022SKashyap, Desai 			port = mptsas_get_port(phy_info);
3590f9c34022SKashyap, Desai 			port_details = phy_info->port_details;
3591e6b2d76aSMoore, Eric 		}
3592f9c34022SKashyap, Desai 		dev_printk(KERN_DEBUG, &phy_info->phy->dev,
3593f9c34022SKashyap, Desai 		    MYIOC_s_FMT "delete phy %d, phy-obj (0x%p)\n", ioc->name,
3594f9c34022SKashyap, Desai 		    phy_info->phy_id, phy_info->phy);
3595f9c34022SKashyap, Desai 		sas_port_delete_phy(port, phy_info->phy);
3596f9c34022SKashyap, Desai 	}
3597f9c34022SKashyap, Desai 	if (port) {
3598f9c34022SKashyap, Desai 		dev_printk(KERN_DEBUG, &port->dev,
3599f9c34022SKashyap, Desai 		    MYIOC_s_FMT "delete port %d, sas_addr (0x%llx)\n",
3600f9c34022SKashyap, Desai 		    ioc->name, port->port_identifier,
3601f9c34022SKashyap, Desai 		    (unsigned long long)expander_sas_address);
3602f9c34022SKashyap, Desai 		sas_port_delete(port);
3603f9c34022SKashyap, Desai 		mptsas_port_delete(ioc, port_details);
3604f9c34022SKashyap, Desai 	}
3605f9c34022SKashyap, Desai  out:
3606547f9a21SEric Moore 
3607f9c34022SKashyap, Desai 	printk(MYIOC_s_INFO_FMT "delete expander: num_phys %d, "
3608f9c34022SKashyap, Desai 	    "sas_addr (0x%llx)\n",  ioc->name, port_info->num_phys,
3609f9c34022SKashyap, Desai 	    (unsigned long long)expander_sas_address);
3610547f9a21SEric Moore 
3611f9c34022SKashyap, Desai 	/*
3612f9c34022SKashyap, Desai 	 * free link
3613f9c34022SKashyap, Desai 	 */
3614e6b2d76aSMoore, Eric 	list_del(&port_info->list);
3615e6b2d76aSMoore, Eric 	kfree(port_info->phy_info);
3616e6b2d76aSMoore, Eric 	kfree(port_info);
3617e6b2d76aSMoore, Eric }
3618f9c34022SKashyap, Desai 
3619f9c34022SKashyap, Desai 
3620f9c34022SKashyap, Desai /**
3621f9c34022SKashyap, Desai  * mptsas_send_expander_event - expanders events
3622cdcda465SRandy Dunlap  * @fw_event: event data
3623f9c34022SKashyap, Desai  *
3624f9c34022SKashyap, Desai  *
3625f9c34022SKashyap, Desai  * This function handles adding, removing, and refreshing
3626f9c34022SKashyap, Desai  * device handles within the expander objects.
3627e6b2d76aSMoore, Eric  */
3628f9c34022SKashyap, Desai static void
mptsas_send_expander_event(struct fw_event_work * fw_event)3629f9c34022SKashyap, Desai mptsas_send_expander_event(struct fw_event_work *fw_event)
3630f9c34022SKashyap, Desai {
3631f9c34022SKashyap, Desai 	MPT_ADAPTER *ioc;
3632f9c34022SKashyap, Desai 	MpiEventDataSasExpanderStatusChange_t *expander_data;
3633f9c34022SKashyap, Desai 	struct mptsas_portinfo *port_info;
3634f9c34022SKashyap, Desai 	__le64 sas_address;
3635f9c34022SKashyap, Desai 	int i;
3636f9c34022SKashyap, Desai 
3637f9c34022SKashyap, Desai 	ioc = fw_event->ioc;
3638f9c34022SKashyap, Desai 	expander_data = (MpiEventDataSasExpanderStatusChange_t *)
3639f9c34022SKashyap, Desai 	    fw_event->event_data;
3640f9c34022SKashyap, Desai 	memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
3641f44fd181SKashyap, Desai 	sas_address = le64_to_cpu(sas_address);
3642f9c34022SKashyap, Desai 	port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
3643f9c34022SKashyap, Desai 
3644f9c34022SKashyap, Desai 	if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
3645f9c34022SKashyap, Desai 		if (port_info) {
3646f9c34022SKashyap, Desai 			for (i = 0; i < port_info->num_phys; i++) {
3647f9c34022SKashyap, Desai 				port_info->phy_info[i].portinfo = port_info;
3648f9c34022SKashyap, Desai 				port_info->phy_info[i].handle =
3649f9c34022SKashyap, Desai 				    le16_to_cpu(expander_data->DevHandle);
3650f9c34022SKashyap, Desai 				port_info->phy_info[i].identify.sas_address =
3651f9c34022SKashyap, Desai 				    le64_to_cpu(sas_address);
3652f9c34022SKashyap, Desai 				port_info->phy_info[i].identify.handle_parent =
3653f9c34022SKashyap, Desai 				    le16_to_cpu(expander_data->ParentDevHandle);
3654e6b2d76aSMoore, Eric 			}
3655f9c34022SKashyap, Desai 			mptsas_expander_refresh(ioc, port_info);
3656f9c34022SKashyap, Desai 		} else if (!port_info && expander_data->NumPhys)
3657f9c34022SKashyap, Desai 			mptsas_expander_event_add(ioc, expander_data);
3658f9c34022SKashyap, Desai 	} else if (expander_data->ReasonCode ==
3659f9c34022SKashyap, Desai 	    MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING)
3660f9c34022SKashyap, Desai 		mptsas_expander_delete(ioc, port_info, 0);
3661f9c34022SKashyap, Desai 
3662f9c34022SKashyap, Desai 	mptsas_free_fw_event(ioc, fw_event);
3663f9c34022SKashyap, Desai }
3664f9c34022SKashyap, Desai 
3665f9c34022SKashyap, Desai 
3666f9c34022SKashyap, Desai /**
3667cdcda465SRandy Dunlap  * mptsas_expander_add - adds a newly discovered expander
3668f9c34022SKashyap, Desai  * @ioc: Pointer to MPT_ADAPTER structure
3669cdcda465SRandy Dunlap  * @handle: device handle
3670f9c34022SKashyap, Desai  *
3671f9c34022SKashyap, Desai  */
36725767d25fSJoe Lawrence static struct mptsas_portinfo *
mptsas_expander_add(MPT_ADAPTER * ioc,u16 handle)3673f9c34022SKashyap, Desai mptsas_expander_add(MPT_ADAPTER *ioc, u16 handle)
3674f9c34022SKashyap, Desai {
3675f9c34022SKashyap, Desai 	struct mptsas_portinfo buffer, *port_info;
3676f9c34022SKashyap, Desai 	int i;
3677f9c34022SKashyap, Desai 
3678f9c34022SKashyap, Desai 	if ((mptsas_sas_expander_pg0(ioc, &buffer,
3679f9c34022SKashyap, Desai 	    (MPI_SAS_EXPAND_PGAD_FORM_HANDLE <<
3680f9c34022SKashyap, Desai 	    MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)))
3681f9c34022SKashyap, Desai 		return NULL;
3682f9c34022SKashyap, Desai 
3683e3af2e3bSChristophe JAILLET 	port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
3684f9c34022SKashyap, Desai 	if (!port_info) {
3685f9c34022SKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
3686f9c34022SKashyap, Desai 		"%s: exit at line=%d\n", ioc->name,
3687f9c34022SKashyap, Desai 		__func__, __LINE__));
3688f9c34022SKashyap, Desai 		return NULL;
3689f9c34022SKashyap, Desai 	}
3690f9c34022SKashyap, Desai 	port_info->num_phys = buffer.num_phys;
3691f9c34022SKashyap, Desai 	port_info->phy_info = buffer.phy_info;
3692f9c34022SKashyap, Desai 	for (i = 0; i < port_info->num_phys; i++)
3693f9c34022SKashyap, Desai 		port_info->phy_info[i].portinfo = port_info;
3694f9c34022SKashyap, Desai 	mutex_lock(&ioc->sas_topology_mutex);
3695f9c34022SKashyap, Desai 	list_add_tail(&port_info->list, &ioc->sas_topology);
3696e6b2d76aSMoore, Eric 	mutex_unlock(&ioc->sas_topology_mutex);
3697f9c34022SKashyap, Desai 	printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
3698f9c34022SKashyap, Desai 	    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
3699f9c34022SKashyap, Desai 	    (unsigned long long)buffer.phy_info[0].identify.sas_address);
3700f9c34022SKashyap, Desai 	mptsas_expander_refresh(ioc, port_info);
3701f9c34022SKashyap, Desai 	return port_info;
3702f9c34022SKashyap, Desai }
3703f9c34022SKashyap, Desai 
3704f9c34022SKashyap, Desai static void
mptsas_send_link_status_event(struct fw_event_work * fw_event)3705f9c34022SKashyap, Desai mptsas_send_link_status_event(struct fw_event_work *fw_event)
3706f9c34022SKashyap, Desai {
3707f9c34022SKashyap, Desai 	MPT_ADAPTER *ioc;
3708f9c34022SKashyap, Desai 	MpiEventDataSasPhyLinkStatus_t *link_data;
3709f9c34022SKashyap, Desai 	struct mptsas_portinfo *port_info;
3710f9c34022SKashyap, Desai 	struct mptsas_phyinfo *phy_info = NULL;
3711f9c34022SKashyap, Desai 	__le64 sas_address;
3712f9c34022SKashyap, Desai 	u8 phy_num;
3713f9c34022SKashyap, Desai 	u8 link_rate;
3714f9c34022SKashyap, Desai 
3715f9c34022SKashyap, Desai 	ioc = fw_event->ioc;
3716f9c34022SKashyap, Desai 	link_data = (MpiEventDataSasPhyLinkStatus_t *)fw_event->event_data;
3717f9c34022SKashyap, Desai 
3718f9c34022SKashyap, Desai 	memcpy(&sas_address, &link_data->SASAddress, sizeof(__le64));
3719f9c34022SKashyap, Desai 	sas_address = le64_to_cpu(sas_address);
3720f9c34022SKashyap, Desai 	link_rate = link_data->LinkRates >> 4;
3721f9c34022SKashyap, Desai 	phy_num = link_data->PhyNum;
3722f9c34022SKashyap, Desai 
3723f9c34022SKashyap, Desai 	port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
3724f9c34022SKashyap, Desai 	if (port_info) {
3725f9c34022SKashyap, Desai 		phy_info = &port_info->phy_info[phy_num];
3726f9c34022SKashyap, Desai 		if (phy_info)
3727f9c34022SKashyap, Desai 			phy_info->negotiated_link_rate = link_rate;
3728f9c34022SKashyap, Desai 	}
3729f9c34022SKashyap, Desai 
3730f9c34022SKashyap, Desai 	if (link_rate == MPI_SAS_IOUNIT0_RATE_1_5 ||
3731d75733d5SKashyap, Desai 	    link_rate == MPI_SAS_IOUNIT0_RATE_3_0 ||
3732d75733d5SKashyap, Desai 	    link_rate == MPI_SAS_IOUNIT0_RATE_6_0) {
3733f9c34022SKashyap, Desai 
3734eedf92b9SKashyap, Desai 		if (!port_info) {
3735eedf92b9SKashyap, Desai 			if (ioc->old_sas_discovery_protocal) {
3736eedf92b9SKashyap, Desai 				port_info = mptsas_expander_add(ioc,
3737eedf92b9SKashyap, Desai 					le16_to_cpu(link_data->DevHandle));
3738eedf92b9SKashyap, Desai 				if (port_info)
3739f9c34022SKashyap, Desai 					goto out;
3740eedf92b9SKashyap, Desai 			}
3741eedf92b9SKashyap, Desai 			goto out;
3742eedf92b9SKashyap, Desai 		}
3743f9c34022SKashyap, Desai 
3744f9c34022SKashyap, Desai 		if (port_info == ioc->hba_port_info)
3745f9c34022SKashyap, Desai 			mptsas_probe_hba_phys(ioc);
3746f9c34022SKashyap, Desai 		else
3747f9c34022SKashyap, Desai 			mptsas_expander_refresh(ioc, port_info);
3748f9c34022SKashyap, Desai 	} else if (phy_info && phy_info->phy) {
3749f9c34022SKashyap, Desai 		if (link_rate ==  MPI_SAS_IOUNIT0_RATE_PHY_DISABLED)
3750f9c34022SKashyap, Desai 			phy_info->phy->negotiated_linkrate =
3751f9c34022SKashyap, Desai 			    SAS_PHY_DISABLED;
3752f9c34022SKashyap, Desai 		else if (link_rate ==
3753f9c34022SKashyap, Desai 		    MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION)
3754f9c34022SKashyap, Desai 			phy_info->phy->negotiated_linkrate =
3755f9c34022SKashyap, Desai 			    SAS_LINK_RATE_FAILED;
3756c9de7dc4SKashyap, Desai 		else {
3757f9c34022SKashyap, Desai 			phy_info->phy->negotiated_linkrate =
3758f9c34022SKashyap, Desai 			    SAS_LINK_RATE_UNKNOWN;
3759c9de7dc4SKashyap, Desai 			if (ioc->device_missing_delay &&
3760c9de7dc4SKashyap, Desai 			    mptsas_is_end_device(&phy_info->attached)) {
3761c9de7dc4SKashyap, Desai 				struct scsi_device		*sdev;
3762c9de7dc4SKashyap, Desai 				VirtDevice			*vdevice;
3763c9de7dc4SKashyap, Desai 				u8	channel, id;
3764c9de7dc4SKashyap, Desai 				id = phy_info->attached.id;
3765c9de7dc4SKashyap, Desai 				channel = phy_info->attached.channel;
3766c9de7dc4SKashyap, Desai 				devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
3767c9de7dc4SKashyap, Desai 				"Link down for fw_id %d:fw_channel %d\n",
3768c9de7dc4SKashyap, Desai 				    ioc->name, phy_info->attached.id,
3769c9de7dc4SKashyap, Desai 				    phy_info->attached.channel));
3770c9de7dc4SKashyap, Desai 
3771c9de7dc4SKashyap, Desai 				shost_for_each_device(sdev, ioc->sh) {
3772c9de7dc4SKashyap, Desai 					vdevice = sdev->hostdata;
3773c9de7dc4SKashyap, Desai 					if ((vdevice == NULL) ||
3774c9de7dc4SKashyap, Desai 						(vdevice->vtarget == NULL))
3775c9de7dc4SKashyap, Desai 						continue;
3776c9de7dc4SKashyap, Desai 					if ((vdevice->vtarget->tflags &
3777c9de7dc4SKashyap, Desai 					    MPT_TARGET_FLAGS_RAID_COMPONENT ||
3778c9de7dc4SKashyap, Desai 					    vdevice->vtarget->raidVolume))
3779c9de7dc4SKashyap, Desai 						continue;
3780c9de7dc4SKashyap, Desai 					if (vdevice->vtarget->id == id &&
3781c9de7dc4SKashyap, Desai 						vdevice->vtarget->channel ==
3782c9de7dc4SKashyap, Desai 						channel)
3783c9de7dc4SKashyap, Desai 						devtprintk(ioc,
3784c9de7dc4SKashyap, Desai 						printk(MYIOC_s_DEBUG_FMT
3785c9de7dc4SKashyap, Desai 						"SDEV OUTSTANDING CMDS"
3786c9de7dc4SKashyap, Desai 						"%d\n", ioc->name,
37878278807aSMing Lei 						scsi_device_busy(sdev)));
3788c9de7dc4SKashyap, Desai 				}
3789c9de7dc4SKashyap, Desai 
3790c9de7dc4SKashyap, Desai 			}
3791c9de7dc4SKashyap, Desai 		}
3792f9c34022SKashyap, Desai 	}
3793f9c34022SKashyap, Desai  out:
3794f9c34022SKashyap, Desai 	mptsas_free_fw_event(ioc, fw_event);
3795f9c34022SKashyap, Desai }
3796f9c34022SKashyap, Desai 
3797eedf92b9SKashyap, Desai static void
mptsas_not_responding_devices(MPT_ADAPTER * ioc)3798eedf92b9SKashyap, Desai mptsas_not_responding_devices(MPT_ADAPTER *ioc)
3799eedf92b9SKashyap, Desai {
3800eedf92b9SKashyap, Desai 	struct mptsas_portinfo buffer, *port_info;
3801eedf92b9SKashyap, Desai 	struct mptsas_device_info	*sas_info;
3802eedf92b9SKashyap, Desai 	struct mptsas_devinfo sas_device;
3803eedf92b9SKashyap, Desai 	u32	handle;
3804eedf92b9SKashyap, Desai 	VirtTarget *vtarget = NULL;
3805eedf92b9SKashyap, Desai 	struct mptsas_phyinfo *phy_info;
3806eedf92b9SKashyap, Desai 	u8 found_expander;
3807eedf92b9SKashyap, Desai 	int retval, retry_count;
3808eedf92b9SKashyap, Desai 	unsigned long flags;
3809eedf92b9SKashyap, Desai 
3810eedf92b9SKashyap, Desai 	mpt_findImVolumes(ioc);
3811eedf92b9SKashyap, Desai 
3812eedf92b9SKashyap, Desai 	spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
3813eedf92b9SKashyap, Desai 	if (ioc->ioc_reset_in_progress) {
3814eedf92b9SKashyap, Desai 		dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT
3815eedf92b9SKashyap, Desai 		   "%s: exiting due to a parallel reset \n", ioc->name,
3816eedf92b9SKashyap, Desai 		    __func__));
3817eedf92b9SKashyap, Desai 		spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
3818eedf92b9SKashyap, Desai 		return;
3819eedf92b9SKashyap, Desai 	}
3820eedf92b9SKashyap, Desai 	spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
3821eedf92b9SKashyap, Desai 
3822eedf92b9SKashyap, Desai 	/* devices, logical volumes */
3823eedf92b9SKashyap, Desai 	mutex_lock(&ioc->sas_device_info_mutex);
3824eedf92b9SKashyap, Desai  redo_device_scan:
3825eedf92b9SKashyap, Desai 	list_for_each_entry(sas_info, &ioc->sas_device_info_list, list) {
382657e98513SKashyap, Desai 		if (sas_info->is_cached)
382757e98513SKashyap, Desai 			continue;
3828a7938b0bSKashyap, Desai 		if (!sas_info->is_logical_volume) {
3829eedf92b9SKashyap, Desai 			sas_device.handle = 0;
3830eedf92b9SKashyap, Desai 			retry_count = 0;
3831eedf92b9SKashyap, Desai retry_page:
3832eedf92b9SKashyap, Desai 			retval = mptsas_sas_device_pg0(ioc, &sas_device,
3833eedf92b9SKashyap, Desai 				(MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID
3834eedf92b9SKashyap, Desai 				<< MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
3835eedf92b9SKashyap, Desai 				(sas_info->fw.channel << 8) +
3836eedf92b9SKashyap, Desai 				sas_info->fw.id);
3837eedf92b9SKashyap, Desai 
3838eedf92b9SKashyap, Desai 			if (sas_device.handle)
3839eedf92b9SKashyap, Desai 				continue;
3840eedf92b9SKashyap, Desai 			if (retval == -EBUSY) {
3841eedf92b9SKashyap, Desai 				spin_lock_irqsave(&ioc->taskmgmt_lock, flags);
3842eedf92b9SKashyap, Desai 				if (ioc->ioc_reset_in_progress) {
3843eedf92b9SKashyap, Desai 					dfailprintk(ioc,
3844eedf92b9SKashyap, Desai 					printk(MYIOC_s_DEBUG_FMT
3845eedf92b9SKashyap, Desai 					"%s: exiting due to reset\n",
3846eedf92b9SKashyap, Desai 					ioc->name, __func__));
3847eedf92b9SKashyap, Desai 					spin_unlock_irqrestore
3848eedf92b9SKashyap, Desai 					(&ioc->taskmgmt_lock, flags);
3849a7938b0bSKashyap, Desai 					mutex_unlock(&ioc->
3850a7938b0bSKashyap, Desai 					sas_device_info_mutex);
3851eedf92b9SKashyap, Desai 					return;
3852eedf92b9SKashyap, Desai 				}
3853eedf92b9SKashyap, Desai 				spin_unlock_irqrestore(&ioc->taskmgmt_lock,
3854eedf92b9SKashyap, Desai 				flags);
3855eedf92b9SKashyap, Desai 			}
3856eedf92b9SKashyap, Desai 
3857eedf92b9SKashyap, Desai 			if (retval && (retval != -ENODEV)) {
3858eedf92b9SKashyap, Desai 				if (retry_count < 10) {
3859eedf92b9SKashyap, Desai 					retry_count++;
3860eedf92b9SKashyap, Desai 					goto retry_page;
3861eedf92b9SKashyap, Desai 				} else {
3862eedf92b9SKashyap, Desai 					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
3863eedf92b9SKashyap, Desai 					"%s: Config page retry exceeded retry "
3864eedf92b9SKashyap, Desai 					"count deleting device 0x%llx\n",
3865eedf92b9SKashyap, Desai 					ioc->name, __func__,
3866eedf92b9SKashyap, Desai 					sas_info->sas_address));
3867eedf92b9SKashyap, Desai 				}
3868eedf92b9SKashyap, Desai 			}
3869eedf92b9SKashyap, Desai 
3870eedf92b9SKashyap, Desai 			/* delete device */
3871eedf92b9SKashyap, Desai 			vtarget = mptsas_find_vtarget(ioc,
3872eedf92b9SKashyap, Desai 				sas_info->fw.channel, sas_info->fw.id);
3873a7938b0bSKashyap, Desai 
3874eedf92b9SKashyap, Desai 			if (vtarget)
3875eedf92b9SKashyap, Desai 				vtarget->deleted = 1;
3876a7938b0bSKashyap, Desai 
3877eedf92b9SKashyap, Desai 			phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
3878eedf92b9SKashyap, Desai 					sas_info->sas_address);
3879a7938b0bSKashyap, Desai 
3880eedf92b9SKashyap, Desai 			mptsas_del_end_device(ioc, phy_info);
3881eedf92b9SKashyap, Desai 			goto redo_device_scan;
3882a7938b0bSKashyap, Desai 		} else
3883a7938b0bSKashyap, Desai 			mptsas_volume_delete(ioc, sas_info->fw.id);
3884eedf92b9SKashyap, Desai 	}
3885129dd981SJiri Slaby 	mutex_unlock(&ioc->sas_device_info_mutex);
3886eedf92b9SKashyap, Desai 
3887eedf92b9SKashyap, Desai 	/* expanders */
3888eedf92b9SKashyap, Desai 	mutex_lock(&ioc->sas_topology_mutex);
3889eedf92b9SKashyap, Desai  redo_expander_scan:
3890eedf92b9SKashyap, Desai 	list_for_each_entry(port_info, &ioc->sas_topology, list) {
3891eedf92b9SKashyap, Desai 
38929f21316fSJoe Lawrence 		if (!(port_info->phy_info[0].identify.device_info &
38939f21316fSJoe Lawrence 		    MPI_SAS_DEVICE_INFO_SMP_TARGET))
3894eedf92b9SKashyap, Desai 			continue;
3895eedf92b9SKashyap, Desai 		found_expander = 0;
3896eedf92b9SKashyap, Desai 		handle = 0xFFFF;
3897eedf92b9SKashyap, Desai 		while (!mptsas_sas_expander_pg0(ioc, &buffer,
3898eedf92b9SKashyap, Desai 		    (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
3899eedf92b9SKashyap, Desai 		     MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle) &&
3900eedf92b9SKashyap, Desai 		    !found_expander) {
3901eedf92b9SKashyap, Desai 
3902eedf92b9SKashyap, Desai 			handle = buffer.phy_info[0].handle;
3903eedf92b9SKashyap, Desai 			if (buffer.phy_info[0].identify.sas_address ==
3904eedf92b9SKashyap, Desai 			    port_info->phy_info[0].identify.sas_address) {
3905eedf92b9SKashyap, Desai 				found_expander = 1;
3906eedf92b9SKashyap, Desai 			}
3907eedf92b9SKashyap, Desai 			kfree(buffer.phy_info);
3908eedf92b9SKashyap, Desai 		}
3909eedf92b9SKashyap, Desai 
3910eedf92b9SKashyap, Desai 		if (!found_expander) {
3911eedf92b9SKashyap, Desai 			mptsas_expander_delete(ioc, port_info, 0);
3912eedf92b9SKashyap, Desai 			goto redo_expander_scan;
3913eedf92b9SKashyap, Desai 		}
3914eedf92b9SKashyap, Desai 	}
3915129dd981SJiri Slaby 	mutex_unlock(&ioc->sas_topology_mutex);
3916eedf92b9SKashyap, Desai }
3917eedf92b9SKashyap, Desai 
3918f9c34022SKashyap, Desai /**
3919f9c34022SKashyap, Desai  *	mptsas_probe_expanders - adding expanders
3920f9c34022SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
3921f9c34022SKashyap, Desai  *
3922f9c34022SKashyap, Desai  **/
3923f9c34022SKashyap, Desai static void
mptsas_probe_expanders(MPT_ADAPTER * ioc)3924f9c34022SKashyap, Desai mptsas_probe_expanders(MPT_ADAPTER *ioc)
3925f9c34022SKashyap, Desai {
3926f9c34022SKashyap, Desai 	struct mptsas_portinfo buffer, *port_info;
3927f9c34022SKashyap, Desai 	u32 			handle;
3928f9c34022SKashyap, Desai 	int i;
3929f9c34022SKashyap, Desai 
3930f9c34022SKashyap, Desai 	handle = 0xFFFF;
3931f9c34022SKashyap, Desai 	while (!mptsas_sas_expander_pg0(ioc, &buffer,
3932f9c34022SKashyap, Desai 	    (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE <<
3933f9c34022SKashyap, Desai 	     MPI_SAS_EXPAND_PGAD_FORM_SHIFT), handle)) {
3934f9c34022SKashyap, Desai 
3935f9c34022SKashyap, Desai 		handle = buffer.phy_info[0].handle;
3936f9c34022SKashyap, Desai 		port_info = mptsas_find_portinfo_by_sas_address(ioc,
3937f9c34022SKashyap, Desai 		    buffer.phy_info[0].identify.sas_address);
3938f9c34022SKashyap, Desai 
3939f9c34022SKashyap, Desai 		if (port_info) {
3940f9c34022SKashyap, Desai 			/* refreshing handles */
3941f9c34022SKashyap, Desai 			for (i = 0; i < buffer.num_phys; i++) {
3942f9c34022SKashyap, Desai 				port_info->phy_info[i].handle = handle;
3943f9c34022SKashyap, Desai 				port_info->phy_info[i].identify.handle_parent =
3944f9c34022SKashyap, Desai 				    buffer.phy_info[0].identify.handle_parent;
3945f9c34022SKashyap, Desai 			}
3946f9c34022SKashyap, Desai 			mptsas_expander_refresh(ioc, port_info);
3947f9c34022SKashyap, Desai 			kfree(buffer.phy_info);
3948f9c34022SKashyap, Desai 			continue;
3949f9c34022SKashyap, Desai 		}
3950f9c34022SKashyap, Desai 
3951f9c34022SKashyap, Desai 		port_info = kzalloc(sizeof(struct mptsas_portinfo), GFP_KERNEL);
3952f9c34022SKashyap, Desai 		if (!port_info) {
3953f9c34022SKashyap, Desai 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
3954f9c34022SKashyap, Desai 			"%s: exit at line=%d\n", ioc->name,
3955f9c34022SKashyap, Desai 			__func__, __LINE__));
3956f9c34022SKashyap, Desai 			return;
3957f9c34022SKashyap, Desai 		}
3958f9c34022SKashyap, Desai 		port_info->num_phys = buffer.num_phys;
3959f9c34022SKashyap, Desai 		port_info->phy_info = buffer.phy_info;
3960f9c34022SKashyap, Desai 		for (i = 0; i < port_info->num_phys; i++)
3961f9c34022SKashyap, Desai 			port_info->phy_info[i].portinfo = port_info;
3962f9c34022SKashyap, Desai 		mutex_lock(&ioc->sas_topology_mutex);
3963f9c34022SKashyap, Desai 		list_add_tail(&port_info->list, &ioc->sas_topology);
3964f9c34022SKashyap, Desai 		mutex_unlock(&ioc->sas_topology_mutex);
3965f9c34022SKashyap, Desai 		printk(MYIOC_s_INFO_FMT "add expander: num_phys %d, "
3966f9c34022SKashyap, Desai 		    "sas_addr (0x%llx)\n", ioc->name, port_info->num_phys,
3967f9c34022SKashyap, Desai 	    (unsigned long long)buffer.phy_info[0].identify.sas_address);
3968f9c34022SKashyap, Desai 		mptsas_expander_refresh(ioc, port_info);
3969f9c34022SKashyap, Desai 	}
3970f9c34022SKashyap, Desai }
3971f9c34022SKashyap, Desai 
3972f9c34022SKashyap, Desai static void
mptsas_probe_devices(MPT_ADAPTER * ioc)3973f9c34022SKashyap, Desai mptsas_probe_devices(MPT_ADAPTER *ioc)
3974f9c34022SKashyap, Desai {
3975f9c34022SKashyap, Desai 	u16 handle;
3976f9c34022SKashyap, Desai 	struct mptsas_devinfo sas_device;
3977f9c34022SKashyap, Desai 	struct mptsas_phyinfo *phy_info;
3978f9c34022SKashyap, Desai 
3979f9c34022SKashyap, Desai 	handle = 0xFFFF;
3980f9c34022SKashyap, Desai 	while (!(mptsas_sas_device_pg0(ioc, &sas_device,
3981f9c34022SKashyap, Desai 	    MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
3982f9c34022SKashyap, Desai 
3983f9c34022SKashyap, Desai 		handle = sas_device.handle;
3984f9c34022SKashyap, Desai 
3985f9c34022SKashyap, Desai 		if ((sas_device.device_info &
3986f9c34022SKashyap, Desai 		     (MPI_SAS_DEVICE_INFO_SSP_TARGET |
3987f9c34022SKashyap, Desai 		      MPI_SAS_DEVICE_INFO_STP_TARGET |
3988f9c34022SKashyap, Desai 		      MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0)
3989f9c34022SKashyap, Desai 			continue;
3990f9c34022SKashyap, Desai 
399151106ab5SKashyap, Desai 		/* If there is no FW B_T mapping for this device then continue
399251106ab5SKashyap, Desai 		 * */
399351106ab5SKashyap, Desai 		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
399451106ab5SKashyap, Desai 			|| !(sas_device.flags &
399551106ab5SKashyap, Desai 			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
399651106ab5SKashyap, Desai 			continue;
399751106ab5SKashyap, Desai 
3998f9c34022SKashyap, Desai 		phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
3999f9c34022SKashyap, Desai 		if (!phy_info)
4000f9c34022SKashyap, Desai 			continue;
4001f9c34022SKashyap, Desai 
4002f9c34022SKashyap, Desai 		if (mptsas_get_rphy(phy_info))
4003f9c34022SKashyap, Desai 			continue;
4004f9c34022SKashyap, Desai 
4005f9c34022SKashyap, Desai 		mptsas_add_end_device(ioc, phy_info);
4006f9c34022SKashyap, Desai 	}
4007e6b2d76aSMoore, Eric }
4008e6b2d76aSMoore, Eric 
40092f187862SKashyap, Desai /**
4010cdcda465SRandy Dunlap  *	mptsas_scan_sas_topology - scans new SAS topology
4011cdcda465SRandy Dunlap  *	  (part of probe or rescan)
40122f187862SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
40132f187862SKashyap, Desai  *
40142f187862SKashyap, Desai  **/
40150c33b27dSChristoph Hellwig static void
mptsas_scan_sas_topology(MPT_ADAPTER * ioc)40160c33b27dSChristoph Hellwig mptsas_scan_sas_topology(MPT_ADAPTER *ioc)
40170c33b27dSChristoph Hellwig {
4018f9c34022SKashyap, Desai 	struct scsi_device *sdev;
4019f44e5461SMoore, Eric 	int i;
40200c33b27dSChristoph Hellwig 
4021e6b2d76aSMoore, Eric 	mptsas_probe_hba_phys(ioc);
4022f9c34022SKashyap, Desai 	mptsas_probe_expanders(ioc);
4023f9c34022SKashyap, Desai 	mptsas_probe_devices(ioc);
4024f9c34022SKashyap, Desai 
4025f44e5461SMoore, Eric 	/*
4026f44e5461SMoore, Eric 	  Reporting RAID volumes.
4027f44e5461SMoore, Eric 	*/
4028f9c34022SKashyap, Desai 	if (!ioc->ir_firmware || !ioc->raid_data.pIocPg2 ||
4029f9c34022SKashyap, Desai 	    !ioc->raid_data.pIocPg2->NumActiveVolumes)
4030f9c34022SKashyap, Desai 		return;
4031f44e5461SMoore, Eric 	for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
4032f9c34022SKashyap, Desai 		sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
4033f9c34022SKashyap, Desai 		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
4034f9c34022SKashyap, Desai 		if (sdev) {
4035f9c34022SKashyap, Desai 			scsi_device_put(sdev);
4036f9c34022SKashyap, Desai 			continue;
4037f9c34022SKashyap, Desai 		}
4038f9c34022SKashyap, Desai 		printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
4039f9c34022SKashyap, Desai 		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
4040f9c34022SKashyap, Desai 		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID);
4041e8bf3941SJames Bottomley 		scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
4042f44e5461SMoore, Eric 		    ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID, 0);
4043f44e5461SMoore, Eric 	}
4044e6b2d76aSMoore, Eric }
4045e6b2d76aSMoore, Eric 
404657e98513SKashyap, Desai 
404757e98513SKashyap, Desai static void
mptsas_handle_queue_full_event(struct fw_event_work * fw_event)404857e98513SKashyap, Desai mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
404957e98513SKashyap, Desai {
405057e98513SKashyap, Desai 	MPT_ADAPTER *ioc;
405157e98513SKashyap, Desai 	EventDataQueueFull_t *qfull_data;
405257e98513SKashyap, Desai 	struct mptsas_device_info *sas_info;
405357e98513SKashyap, Desai 	struct scsi_device	*sdev;
405457e98513SKashyap, Desai 	int depth;
405557e98513SKashyap, Desai 	int id = -1;
405657e98513SKashyap, Desai 	int channel = -1;
405757e98513SKashyap, Desai 	int fw_id, fw_channel;
405857e98513SKashyap, Desai 	u16 current_depth;
405957e98513SKashyap, Desai 
406057e98513SKashyap, Desai 
406157e98513SKashyap, Desai 	ioc = fw_event->ioc;
406257e98513SKashyap, Desai 	qfull_data = (EventDataQueueFull_t *)fw_event->event_data;
406357e98513SKashyap, Desai 	fw_id = qfull_data->TargetID;
406457e98513SKashyap, Desai 	fw_channel = qfull_data->Bus;
406557e98513SKashyap, Desai 	current_depth = le16_to_cpu(qfull_data->CurrentDepth);
406657e98513SKashyap, Desai 
406757e98513SKashyap, Desai 	/* if hidden raid component, look for the volume id */
406857e98513SKashyap, Desai 	mutex_lock(&ioc->sas_device_info_mutex);
406957e98513SKashyap, Desai 	if (mptscsih_is_phys_disk(ioc, fw_channel, fw_id)) {
407057e98513SKashyap, Desai 		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
407157e98513SKashyap, Desai 		    list) {
407257e98513SKashyap, Desai 			if (sas_info->is_cached ||
407357e98513SKashyap, Desai 			    sas_info->is_logical_volume)
407457e98513SKashyap, Desai 				continue;
407557e98513SKashyap, Desai 			if (sas_info->is_hidden_raid_component &&
407657e98513SKashyap, Desai 			    (sas_info->fw.channel == fw_channel &&
407757e98513SKashyap, Desai 			    sas_info->fw.id == fw_id)) {
407857e98513SKashyap, Desai 				id = sas_info->volume_id;
407957e98513SKashyap, Desai 				channel = MPTSAS_RAID_CHANNEL;
408057e98513SKashyap, Desai 				goto out;
408157e98513SKashyap, Desai 			}
408257e98513SKashyap, Desai 		}
408357e98513SKashyap, Desai 	} else {
408457e98513SKashyap, Desai 		list_for_each_entry(sas_info, &ioc->sas_device_info_list,
408557e98513SKashyap, Desai 		    list) {
408657e98513SKashyap, Desai 			if (sas_info->is_cached ||
408757e98513SKashyap, Desai 			    sas_info->is_hidden_raid_component ||
408857e98513SKashyap, Desai 			    sas_info->is_logical_volume)
408957e98513SKashyap, Desai 				continue;
409057e98513SKashyap, Desai 			if (sas_info->fw.channel == fw_channel &&
409157e98513SKashyap, Desai 			    sas_info->fw.id == fw_id) {
409257e98513SKashyap, Desai 				id = sas_info->os.id;
409357e98513SKashyap, Desai 				channel = sas_info->os.channel;
409457e98513SKashyap, Desai 				goto out;
409557e98513SKashyap, Desai 			}
409657e98513SKashyap, Desai 		}
409757e98513SKashyap, Desai 
409857e98513SKashyap, Desai 	}
409957e98513SKashyap, Desai 
410057e98513SKashyap, Desai  out:
410157e98513SKashyap, Desai 	mutex_unlock(&ioc->sas_device_info_mutex);
410257e98513SKashyap, Desai 
410357e98513SKashyap, Desai 	if (id != -1) {
410457e98513SKashyap, Desai 		shost_for_each_device(sdev, ioc->sh) {
410557e98513SKashyap, Desai 			if (sdev->id == id && sdev->channel == channel) {
410657e98513SKashyap, Desai 				if (current_depth > sdev->queue_depth) {
410757e98513SKashyap, Desai 					sdev_printk(KERN_INFO, sdev,
410857e98513SKashyap, Desai 					    "strange observation, the queue "
410957e98513SKashyap, Desai 					    "depth is (%d) meanwhile fw queue "
411057e98513SKashyap, Desai 					    "depth (%d)\n", sdev->queue_depth,
411157e98513SKashyap, Desai 					    current_depth);
411257e98513SKashyap, Desai 					continue;
411357e98513SKashyap, Desai 				}
411457e98513SKashyap, Desai 				depth = scsi_track_queue_full(sdev,
41152865c073STomas Henzl 					sdev->queue_depth - 1);
411657e98513SKashyap, Desai 				if (depth > 0)
411757e98513SKashyap, Desai 					sdev_printk(KERN_INFO, sdev,
411857e98513SKashyap, Desai 					"Queue depth reduced to (%d)\n",
411957e98513SKashyap, Desai 					   depth);
412057e98513SKashyap, Desai 				else if (depth < 0)
412157e98513SKashyap, Desai 					sdev_printk(KERN_INFO, sdev,
412257e98513SKashyap, Desai 					"Tagged Command Queueing is being "
412357e98513SKashyap, Desai 					"disabled\n");
412457e98513SKashyap, Desai 				else if (depth == 0)
41252865c073STomas Henzl 					sdev_printk(KERN_DEBUG, sdev,
412657e98513SKashyap, Desai 					"Queue depth not changed yet\n");
412757e98513SKashyap, Desai 			}
412857e98513SKashyap, Desai 		}
412957e98513SKashyap, Desai 	}
413057e98513SKashyap, Desai 
413157e98513SKashyap, Desai 	mptsas_free_fw_event(ioc, fw_event);
413257e98513SKashyap, Desai }
413357e98513SKashyap, Desai 
413457e98513SKashyap, Desai 
41359a28f49aSChristoph Hellwig static struct mptsas_phyinfo *
mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER * ioc,u64 sas_address)4136547f9a21SEric Moore mptsas_find_phyinfo_by_sas_address(MPT_ADAPTER *ioc, u64 sas_address)
41379a28f49aSChristoph Hellwig {
41389a28f49aSChristoph Hellwig 	struct mptsas_portinfo *port_info;
41399a28f49aSChristoph Hellwig 	struct mptsas_phyinfo *phy_info = NULL;
4140547f9a21SEric Moore 	int i;
41419a28f49aSChristoph Hellwig 
41429a28f49aSChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
41439a28f49aSChristoph Hellwig 	list_for_each_entry(port_info, &ioc->sas_topology, list) {
41449a28f49aSChristoph Hellwig 		for (i = 0; i < port_info->num_phys; i++) {
4145547f9a21SEric Moore 			if (!mptsas_is_end_device(
4146547f9a21SEric Moore 				&port_info->phy_info[i].attached))
4147547f9a21SEric Moore 				continue;
4148b506ade9SEric Moore 			if (port_info->phy_info[i].attached.sas_address
4149b506ade9SEric Moore 			    != sas_address)
4150b506ade9SEric Moore 				continue;
41519a28f49aSChristoph Hellwig 			phy_info = &port_info->phy_info[i];
41529a28f49aSChristoph Hellwig 			break;
41539a28f49aSChristoph Hellwig 		}
41549a28f49aSChristoph Hellwig 	}
41559a28f49aSChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
41569a28f49aSChristoph Hellwig 	return phy_info;
41579a28f49aSChristoph Hellwig }
41589a28f49aSChristoph Hellwig 
4159a7938b0bSKashyap, Desai /**
4160cdcda465SRandy Dunlap  *	mptsas_find_phyinfo_by_phys_disk_num - find phyinfo for the
4161cdcda465SRandy Dunlap  *	  specified @phys_disk_num
4162a7938b0bSKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
4163cdcda465SRandy Dunlap  *	@phys_disk_num: (hot plug) physical disk number (for RAID support)
4164cdcda465SRandy Dunlap  *	@channel: channel number
4165cdcda465SRandy Dunlap  *	@id: Logical Target ID
4166a7938b0bSKashyap, Desai  *
4167a7938b0bSKashyap, Desai  **/
4168b506ade9SEric Moore static struct mptsas_phyinfo *
mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER * ioc,u8 phys_disk_num,u8 channel,u8 id)4169a7938b0bSKashyap, Desai mptsas_find_phyinfo_by_phys_disk_num(MPT_ADAPTER *ioc, u8 phys_disk_num,
4170a7938b0bSKashyap, Desai 	u8 channel, u8 id)
4171b506ade9SEric Moore {
4172b506ade9SEric Moore 	struct mptsas_phyinfo *phy_info = NULL;
4173a7938b0bSKashyap, Desai 	struct mptsas_portinfo *port_info;
4174a7938b0bSKashyap, Desai 	RaidPhysDiskPage1_t *phys_disk = NULL;
4175a7938b0bSKashyap, Desai 	int num_paths;
4176a7938b0bSKashyap, Desai 	u64 sas_address = 0;
4177b506ade9SEric Moore 	int i;
4178b506ade9SEric Moore 
4179a7938b0bSKashyap, Desai 	phy_info = NULL;
4180a7938b0bSKashyap, Desai 	if (!ioc->raid_data.pIocPg3)
4181a7938b0bSKashyap, Desai 		return NULL;
4182a7938b0bSKashyap, Desai 	/* dual port support */
4183a7938b0bSKashyap, Desai 	num_paths = mpt_raid_phys_disk_get_num_paths(ioc, phys_disk_num);
4184a7938b0bSKashyap, Desai 	if (!num_paths)
4185a7938b0bSKashyap, Desai 		goto out;
4186a7938b0bSKashyap, Desai 	phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
4187a7938b0bSKashyap, Desai 	   (num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL);
4188a7938b0bSKashyap, Desai 	if (!phys_disk)
4189a7938b0bSKashyap, Desai 		goto out;
4190a7938b0bSKashyap, Desai 	mpt_raid_phys_disk_pg1(ioc, phys_disk_num, phys_disk);
4191a7938b0bSKashyap, Desai 	for (i = 0; i < num_paths; i++) {
4192a7938b0bSKashyap, Desai 		if ((phys_disk->Path[i].Flags & 1) != 0)
4193a7938b0bSKashyap, Desai 			/* entry no longer valid */
4194a7938b0bSKashyap, Desai 			continue;
4195a7938b0bSKashyap, Desai 		if ((id == phys_disk->Path[i].PhysDiskID) &&
4196a7938b0bSKashyap, Desai 		    (channel == phys_disk->Path[i].PhysDiskBus)) {
4197a7938b0bSKashyap, Desai 			memcpy(&sas_address, &phys_disk->Path[i].WWID,
4198a7938b0bSKashyap, Desai 				sizeof(u64));
4199a7938b0bSKashyap, Desai 			phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
4200a7938b0bSKashyap, Desai 					sas_address);
4201a7938b0bSKashyap, Desai 			goto out;
4202a7938b0bSKashyap, Desai 		}
4203a7938b0bSKashyap, Desai 	}
4204a7938b0bSKashyap, Desai 
4205a7938b0bSKashyap, Desai  out:
4206a7938b0bSKashyap, Desai 	kfree(phys_disk);
4207a7938b0bSKashyap, Desai 	if (phy_info)
4208a7938b0bSKashyap, Desai 		return phy_info;
4209a7938b0bSKashyap, Desai 
4210a7938b0bSKashyap, Desai 	/*
4211a7938b0bSKashyap, Desai 	 * Extra code to handle RAID0 case, where the sas_address is not updated
4212a7938b0bSKashyap, Desai 	 * in phys_disk_page_1 when hotswapped
4213a7938b0bSKashyap, Desai 	 */
4214b506ade9SEric Moore 	mutex_lock(&ioc->sas_topology_mutex);
4215b506ade9SEric Moore 	list_for_each_entry(port_info, &ioc->sas_topology, list) {
4216a7938b0bSKashyap, Desai 		for (i = 0; i < port_info->num_phys && !phy_info; i++) {
4217b506ade9SEric Moore 			if (!mptsas_is_end_device(
4218b506ade9SEric Moore 				&port_info->phy_info[i].attached))
4219b506ade9SEric Moore 				continue;
4220b506ade9SEric Moore 			if (port_info->phy_info[i].attached.phys_disk_num == ~0)
4221b506ade9SEric Moore 				continue;
4222a7938b0bSKashyap, Desai 			if ((port_info->phy_info[i].attached.phys_disk_num ==
4223a7938b0bSKashyap, Desai 			    phys_disk_num) &&
4224a7938b0bSKashyap, Desai 			    (port_info->phy_info[i].attached.id == id) &&
4225a7938b0bSKashyap, Desai 			    (port_info->phy_info[i].attached.channel ==
4226a7938b0bSKashyap, Desai 			     channel))
42279a28f49aSChristoph Hellwig 				phy_info = &port_info->phy_info[i];
42289a28f49aSChristoph Hellwig 		}
42299a28f49aSChristoph Hellwig 	}
42309a28f49aSChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
42319a28f49aSChristoph Hellwig 	return phy_info;
42329a28f49aSChristoph Hellwig }
42339a28f49aSChristoph Hellwig 
42349a28f49aSChristoph Hellwig static void
mptsas_reprobe_lun(struct scsi_device * sdev,void * data)4235f44e5461SMoore, Eric mptsas_reprobe_lun(struct scsi_device *sdev, void *data)
4236f44e5461SMoore, Eric {
4237f44e5461SMoore, Eric 	sdev->no_uld_attach = data ? 1 : 0;
4238*ac3c9fb6SZeng Heng 	WARN_ON(scsi_device_reprobe(sdev));
4239f44e5461SMoore, Eric }
4240f44e5461SMoore, Eric 
4241f44e5461SMoore, Eric static void
mptsas_reprobe_target(struct scsi_target * starget,int uld_attach)4242f44e5461SMoore, Eric mptsas_reprobe_target(struct scsi_target *starget, int uld_attach)
4243f44e5461SMoore, Eric {
4244f44e5461SMoore, Eric 	starget_for_each_device(starget, uld_attach ? (void *)1 : NULL,
4245f44e5461SMoore, Eric 			mptsas_reprobe_lun);
4246f44e5461SMoore, Eric }
4247f44e5461SMoore, Eric 
4248b506ade9SEric Moore static void
mptsas_adding_inactive_raid_components(MPT_ADAPTER * ioc,u8 channel,u8 id)4249b506ade9SEric Moore mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id)
4250b506ade9SEric Moore {
4251b506ade9SEric Moore 	CONFIGPARMS			cfg;
4252b506ade9SEric Moore 	ConfigPageHeader_t		hdr;
4253b506ade9SEric Moore 	dma_addr_t			dma_handle;
4254b506ade9SEric Moore 	pRaidVolumePage0_t		buffer = NULL;
4255b506ade9SEric Moore 	RaidPhysDiskPage0_t 		phys_disk;
4256b506ade9SEric Moore 	int				i;
42573eb0822cSKashyap, Desai 	struct mptsas_phyinfo	*phy_info;
42583eb0822cSKashyap, Desai 	struct mptsas_devinfo		sas_device;
4259b506ade9SEric Moore 
4260b506ade9SEric Moore 	memset(&cfg, 0 , sizeof(CONFIGPARMS));
4261b506ade9SEric Moore 	memset(&hdr, 0 , sizeof(ConfigPageHeader_t));
4262b506ade9SEric Moore 	hdr.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
4263b506ade9SEric Moore 	cfg.pageAddr = (channel << 8) + id;
4264b506ade9SEric Moore 	cfg.cfghdr.hdr = &hdr;
4265b506ade9SEric Moore 	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
4266568da769SKashyap, Desai 	cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
4267b506ade9SEric Moore 
4268b506ade9SEric Moore 	if (mpt_config(ioc, &cfg) != 0)
4269b506ade9SEric Moore 		goto out;
4270b506ade9SEric Moore 
4271b506ade9SEric Moore 	if (!hdr.PageLength)
4272b506ade9SEric Moore 		goto out;
4273b506ade9SEric Moore 
427476a334d7SChristophe JAILLET 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
427576a334d7SChristophe JAILLET 				    &dma_handle, GFP_KERNEL);
4276b506ade9SEric Moore 
4277b506ade9SEric Moore 	if (!buffer)
4278b506ade9SEric Moore 		goto out;
4279b506ade9SEric Moore 
4280b506ade9SEric Moore 	cfg.physAddr = dma_handle;
4281b506ade9SEric Moore 	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
4282b506ade9SEric Moore 
4283b506ade9SEric Moore 	if (mpt_config(ioc, &cfg) != 0)
4284b506ade9SEric Moore 		goto out;
4285b506ade9SEric Moore 
4286b506ade9SEric Moore 	if (!(buffer->VolumeStatus.Flags &
4287b506ade9SEric Moore 	    MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE))
4288b506ade9SEric Moore 		goto out;
4289b506ade9SEric Moore 
4290b506ade9SEric Moore 	if (!buffer->NumPhysDisks)
4291b506ade9SEric Moore 		goto out;
4292b506ade9SEric Moore 
4293b506ade9SEric Moore 	for (i = 0; i < buffer->NumPhysDisks; i++) {
4294b506ade9SEric Moore 
4295b506ade9SEric Moore 		if (mpt_raid_phys_disk_pg0(ioc,
4296b506ade9SEric Moore 		    buffer->PhysDisk[i].PhysDiskNum, &phys_disk) != 0)
4297b506ade9SEric Moore 			continue;
4298b506ade9SEric Moore 
42993eb0822cSKashyap, Desai 		if (mptsas_sas_device_pg0(ioc, &sas_device,
43003eb0822cSKashyap, Desai 		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
43013eb0822cSKashyap, Desai 		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
43023eb0822cSKashyap, Desai 			(phys_disk.PhysDiskBus << 8) +
43033eb0822cSKashyap, Desai 			phys_disk.PhysDiskID))
43043eb0822cSKashyap, Desai 			continue;
4305b506ade9SEric Moore 
430651106ab5SKashyap, Desai 		/* If there is no FW B_T mapping for this device then continue
430751106ab5SKashyap, Desai 		 * */
430851106ab5SKashyap, Desai 		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
430951106ab5SKashyap, Desai 			|| !(sas_device.flags &
431051106ab5SKashyap, Desai 			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
431151106ab5SKashyap, Desai 			continue;
431251106ab5SKashyap, Desai 
431351106ab5SKashyap, Desai 
43143eb0822cSKashyap, Desai 		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
43153eb0822cSKashyap, Desai 		    sas_device.sas_address);
43163eb0822cSKashyap, Desai 		mptsas_add_end_device(ioc, phy_info);
4317b506ade9SEric Moore 	}
4318b506ade9SEric Moore 
4319b506ade9SEric Moore  out:
4320b506ade9SEric Moore 	if (buffer)
4321b114dda6SChristophe JAILLET 		dma_free_coherent(&ioc->pcidev->dev, hdr.PageLength * 4,
4322b114dda6SChristophe JAILLET 				  buffer, dma_handle);
4323b506ade9SEric Moore }
4324e6b2d76aSMoore, Eric /*
4325e6b2d76aSMoore, Eric  * Work queue thread to handle SAS hotplug events
4326e6b2d76aSMoore, Eric  */
4327f44e5461SMoore, Eric static void
mptsas_hotplug_work(MPT_ADAPTER * ioc,struct fw_event_work * fw_event,struct mptsas_hotplug_event * hot_plug_info)43283eb0822cSKashyap, Desai mptsas_hotplug_work(MPT_ADAPTER *ioc, struct fw_event_work *fw_event,
43293eb0822cSKashyap, Desai     struct mptsas_hotplug_event *hot_plug_info)
43309a28f49aSChristoph Hellwig {
43319a28f49aSChristoph Hellwig 	struct mptsas_phyinfo *phy_info;
4332547f9a21SEric Moore 	struct scsi_target * starget;
4333c73787eeSMoore, Eric 	struct mptsas_devinfo sas_device;
4334f44e5461SMoore, Eric 	VirtTarget *vtarget;
43353eb0822cSKashyap, Desai 	int i;
4336cc7e9f5fSKashyap, Desai 	struct mptsas_portinfo *port_info;
4337547f9a21SEric Moore 
43383eb0822cSKashyap, Desai 	switch (hot_plug_info->event_type) {
43393eb0822cSKashyap, Desai 
43403eb0822cSKashyap, Desai 	case MPTSAS_ADD_PHYSDISK:
43413eb0822cSKashyap, Desai 
43423eb0822cSKashyap, Desai 		if (!ioc->raid_data.pIocPg2)
43433eb0822cSKashyap, Desai 			break;
43443eb0822cSKashyap, Desai 
43453eb0822cSKashyap, Desai 		for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++) {
43463eb0822cSKashyap, Desai 			if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID ==
43473eb0822cSKashyap, Desai 			    hot_plug_info->id) {
43483eb0822cSKashyap, Desai 				printk(MYIOC_s_WARN_FMT "firmware bug: unable "
4349c09a21d8SColin Ian King 				    "to add hidden disk - target_id matches "
43503eb0822cSKashyap, Desai 				    "volume_id\n", ioc->name);
43513eb0822cSKashyap, Desai 				mptsas_free_fw_event(ioc, fw_event);
43523eb0822cSKashyap, Desai 				return;
43533eb0822cSKashyap, Desai 			}
43543eb0822cSKashyap, Desai 		}
43553eb0822cSKashyap, Desai 		mpt_findImVolumes(ioc);
4356df561f66SGustavo A. R. Silva 		fallthrough;
43573eb0822cSKashyap, Desai 
43583eb0822cSKashyap, Desai 	case MPTSAS_ADD_DEVICE:
43593eb0822cSKashyap, Desai 		memset(&sas_device, 0, sizeof(struct mptsas_devinfo));
43603eb0822cSKashyap, Desai 		mptsas_sas_device_pg0(ioc, &sas_device,
43613eb0822cSKashyap, Desai 		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
43623eb0822cSKashyap, Desai 		    MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
43633eb0822cSKashyap, Desai 		    (hot_plug_info->channel << 8) +
43643eb0822cSKashyap, Desai 		    hot_plug_info->id);
43653eb0822cSKashyap, Desai 
436651106ab5SKashyap, Desai 		/* If there is no FW B_T mapping for this device then break
436751106ab5SKashyap, Desai 		 * */
436851106ab5SKashyap, Desai 		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
436951106ab5SKashyap, Desai 			|| !(sas_device.flags &
437051106ab5SKashyap, Desai 			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
437151106ab5SKashyap, Desai 			break;
437251106ab5SKashyap, Desai 
43733eb0822cSKashyap, Desai 		if (!sas_device.handle)
43743eb0822cSKashyap, Desai 			return;
43753eb0822cSKashyap, Desai 
43763eb0822cSKashyap, Desai 		phy_info = mptsas_refreshing_device_handles(ioc, &sas_device);
4377ee3e2d83SHannes Reinecke 		/* Device hot plug */
4378ee3e2d83SHannes Reinecke 		if (!phy_info) {
4379cc7e9f5fSKashyap, Desai 			devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
4380ee3e2d83SHannes Reinecke 				"%s %d HOT PLUG: "
4381cc7e9f5fSKashyap, Desai 				"parent handle of device %x\n", ioc->name,
4382cc7e9f5fSKashyap, Desai 				__func__, __LINE__, sas_device.handle_parent));
4383cc7e9f5fSKashyap, Desai 			port_info = mptsas_find_portinfo_by_handle(ioc,
4384cc7e9f5fSKashyap, Desai 				sas_device.handle_parent);
4385cc7e9f5fSKashyap, Desai 
4386cc7e9f5fSKashyap, Desai 			if (port_info == ioc->hba_port_info)
4387cc7e9f5fSKashyap, Desai 				mptsas_probe_hba_phys(ioc);
4388cc7e9f5fSKashyap, Desai 			else if (port_info)
4389cc7e9f5fSKashyap, Desai 				mptsas_expander_refresh(ioc, port_info);
4390cc7e9f5fSKashyap, Desai 			else {
4391cc7e9f5fSKashyap, Desai 				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
4392cc7e9f5fSKashyap, Desai 					"%s %d port info is NULL\n",
4393cc7e9f5fSKashyap, Desai 					ioc->name, __func__, __LINE__));
43943eb0822cSKashyap, Desai 				break;
4395cc7e9f5fSKashyap, Desai 			}
4396cc7e9f5fSKashyap, Desai 			phy_info = mptsas_refreshing_device_handles
4397cc7e9f5fSKashyap, Desai 				(ioc, &sas_device);
4398cc7e9f5fSKashyap, Desai 		}
4399cc7e9f5fSKashyap, Desai 
4400cc7e9f5fSKashyap, Desai 		if (!phy_info) {
4401cc7e9f5fSKashyap, Desai 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
4402cc7e9f5fSKashyap, Desai 				"%s %d phy info is NULL\n",
4403cc7e9f5fSKashyap, Desai 				ioc->name, __func__, __LINE__));
4404cc7e9f5fSKashyap, Desai 			break;
4405cc7e9f5fSKashyap, Desai 		}
44063eb0822cSKashyap, Desai 
44073eb0822cSKashyap, Desai 		if (mptsas_get_rphy(phy_info))
44083eb0822cSKashyap, Desai 			break;
44093eb0822cSKashyap, Desai 
44103eb0822cSKashyap, Desai 		mptsas_add_end_device(ioc, phy_info);
44113eb0822cSKashyap, Desai 		break;
44123eb0822cSKashyap, Desai 
44139a28f49aSChristoph Hellwig 	case MPTSAS_DEL_DEVICE:
44143eb0822cSKashyap, Desai 		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
44153eb0822cSKashyap, Desai 		    hot_plug_info->sas_address);
44163eb0822cSKashyap, Desai 		mptsas_del_end_device(ioc, phy_info);
44173eb0822cSKashyap, Desai 		break;
44189a28f49aSChristoph Hellwig 
44193eb0822cSKashyap, Desai 	case MPTSAS_DEL_PHYSDISK:
44203eb0822cSKashyap, Desai 
44213eb0822cSKashyap, Desai 		mpt_findImVolumes(ioc);
44223eb0822cSKashyap, Desai 
44233eb0822cSKashyap, Desai 		phy_info = mptsas_find_phyinfo_by_phys_disk_num(
4424a7938b0bSKashyap, Desai 				ioc, hot_plug_info->phys_disk_num,
4425a7938b0bSKashyap, Desai 				hot_plug_info->channel,
4426a7938b0bSKashyap, Desai 				hot_plug_info->id);
44273eb0822cSKashyap, Desai 		mptsas_del_end_device(ioc, phy_info);
44283eb0822cSKashyap, Desai 		break;
44293eb0822cSKashyap, Desai 
44303eb0822cSKashyap, Desai 	case MPTSAS_ADD_PHYSDISK_REPROBE:
44313eb0822cSKashyap, Desai 
4432b506ade9SEric Moore 		if (mptsas_sas_device_pg0(ioc, &sas_device,
4433b506ade9SEric Moore 		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
4434b506ade9SEric Moore 		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
44353eb0822cSKashyap, Desai 		    (hot_plug_info->channel << 8) + hot_plug_info->id)) {
4436d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
44373eb0822cSKashyap, Desai 			"%s: fw_id=%d exit at line=%d\n", ioc->name,
44383eb0822cSKashyap, Desai 				 __func__, hot_plug_info->id, __LINE__));
4439b506ade9SEric Moore 			break;
4440b506ade9SEric Moore 		}
44413eb0822cSKashyap, Desai 
444251106ab5SKashyap, Desai 		/* If there is no FW B_T mapping for this device then break
444351106ab5SKashyap, Desai 		 * */
444451106ab5SKashyap, Desai 		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
444551106ab5SKashyap, Desai 			|| !(sas_device.flags &
444651106ab5SKashyap, Desai 			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
444751106ab5SKashyap, Desai 			break;
444851106ab5SKashyap, Desai 
4449b506ade9SEric Moore 		phy_info = mptsas_find_phyinfo_by_sas_address(
4450b506ade9SEric Moore 		    ioc, sas_device.sas_address);
4451b506ade9SEric Moore 
4452b506ade9SEric Moore 		if (!phy_info) {
4453d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
44543eb0822cSKashyap, Desai 				"%s: fw_id=%d exit at line=%d\n", ioc->name,
44553eb0822cSKashyap, Desai 				 __func__, hot_plug_info->id, __LINE__));
4456547f9a21SEric Moore 			break;
4457547f9a21SEric Moore 		}
4458f44e5461SMoore, Eric 
4459547f9a21SEric Moore 		starget = mptsas_get_starget(phy_info);
44603eb0822cSKashyap, Desai 		if (!starget) {
44613eb0822cSKashyap, Desai 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
44623eb0822cSKashyap, Desai 				"%s: fw_id=%d exit at line=%d\n", ioc->name,
44633eb0822cSKashyap, Desai 				 __func__, hot_plug_info->id, __LINE__));
44643eb0822cSKashyap, Desai 			break;
44653eb0822cSKashyap, Desai 		}
4466547f9a21SEric Moore 
44673eb0822cSKashyap, Desai 		vtarget = starget->hostdata;
4468547f9a21SEric Moore 		if (!vtarget) {
4469d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
44703eb0822cSKashyap, Desai 				"%s: fw_id=%d exit at line=%d\n", ioc->name,
44713eb0822cSKashyap, Desai 				 __func__, hot_plug_info->id, __LINE__));
4472f44e5461SMoore, Eric 			break;
4473547f9a21SEric Moore 		}
4474547f9a21SEric Moore 
4475bd23e94cSMoore, Eric 		mpt_findImVolumes(ioc);
4476bd23e94cSMoore, Eric 
44773eb0822cSKashyap, Desai 		starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Hidding: "
44783eb0822cSKashyap, Desai 		    "fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
44793eb0822cSKashyap, Desai 		    ioc->name, hot_plug_info->channel, hot_plug_info->id,
44803eb0822cSKashyap, Desai 		    hot_plug_info->phys_disk_num, (unsigned long long)
44813eb0822cSKashyap, Desai 		    sas_device.sas_address);
44823eb0822cSKashyap, Desai 
44833eb0822cSKashyap, Desai 		vtarget->id = hot_plug_info->phys_disk_num;
44843eb0822cSKashyap, Desai 		vtarget->tflags |= MPT_TARGET_FLAGS_RAID_COMPONENT;
44853eb0822cSKashyap, Desai 		phy_info->attached.phys_disk_num = hot_plug_info->phys_disk_num;
44863eb0822cSKashyap, Desai 		mptsas_reprobe_target(starget, 1);
44873eb0822cSKashyap, Desai 		break;
44883eb0822cSKashyap, Desai 
44893eb0822cSKashyap, Desai 	case MPTSAS_DEL_PHYSDISK_REPROBE:
44903eb0822cSKashyap, Desai 
4491e3094447SChristoph Hellwig 		if (mptsas_sas_device_pg0(ioc, &sas_device,
4492c73787eeSMoore, Eric 		    (MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID <<
4493b506ade9SEric Moore 		     MPI_SAS_DEVICE_PGAD_FORM_SHIFT),
44943eb0822cSKashyap, Desai 			(hot_plug_info->channel << 8) + hot_plug_info->id)) {
4495d6ecdd63SPrakash, Sathya 				dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
44963eb0822cSKashyap, Desai 				    "%s: fw_id=%d exit at line=%d\n",
44973eb0822cSKashyap, Desai 				    ioc->name, __func__,
44983eb0822cSKashyap, Desai 				    hot_plug_info->id, __LINE__));
4499f44e5461SMoore, Eric 			break;
4500e6b2d76aSMoore, Eric 		}
4501e6b2d76aSMoore, Eric 
450251106ab5SKashyap, Desai 		/* If there is no FW B_T mapping for this device then break
450351106ab5SKashyap, Desai 		 * */
450451106ab5SKashyap, Desai 		if (!(sas_device.flags & MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)
450551106ab5SKashyap, Desai 			|| !(sas_device.flags &
450651106ab5SKashyap, Desai 			MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED))
450751106ab5SKashyap, Desai 			break;
450851106ab5SKashyap, Desai 
4509547f9a21SEric Moore 		phy_info = mptsas_find_phyinfo_by_sas_address(ioc,
4510547f9a21SEric Moore 				sas_device.sas_address);
45113eb0822cSKashyap, Desai 		if (!phy_info) {
4512d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
45133eb0822cSKashyap, Desai 			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
45143eb0822cSKashyap, Desai 			 __func__, hot_plug_info->id, __LINE__));
4515f44e5461SMoore, Eric 			break;
4516547f9a21SEric Moore 		}
4517547f9a21SEric Moore 
4518547f9a21SEric Moore 		starget = mptsas_get_starget(phy_info);
45193eb0822cSKashyap, Desai 		if (!starget) {
45203eb0822cSKashyap, Desai 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
45213eb0822cSKashyap, Desai 			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
45223eb0822cSKashyap, Desai 			 __func__, hot_plug_info->id, __LINE__));
45233eb0822cSKashyap, Desai 			break;
45243eb0822cSKashyap, Desai 		}
4525b506ade9SEric Moore 
4526547f9a21SEric Moore 		vtarget = starget->hostdata;
4527547f9a21SEric Moore 		if (!vtarget) {
4528d6ecdd63SPrakash, Sathya 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
45293eb0822cSKashyap, Desai 			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
45303eb0822cSKashyap, Desai 			 __func__, hot_plug_info->id, __LINE__));
4531547f9a21SEric Moore 			break;
4532547f9a21SEric Moore 		}
45333eb0822cSKashyap, Desai 
45343eb0822cSKashyap, Desai 		if (!(vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)) {
45353eb0822cSKashyap, Desai 			dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
45363eb0822cSKashyap, Desai 			    "%s: fw_id=%d exit at line=%d\n", ioc->name,
45373eb0822cSKashyap, Desai 			 __func__, hot_plug_info->id, __LINE__));
45383eb0822cSKashyap, Desai 			break;
45393eb0822cSKashyap, Desai 		}
45403eb0822cSKashyap, Desai 
45413eb0822cSKashyap, Desai 		mpt_findImVolumes(ioc);
45423eb0822cSKashyap, Desai 
45433eb0822cSKashyap, Desai 		starget_printk(KERN_INFO, starget, MYIOC_s_FMT "RAID Exposing:"
45443eb0822cSKashyap, Desai 		    " fw_channel=%d, fw_id=%d, physdsk %d, sas_addr 0x%llx\n",
45453eb0822cSKashyap, Desai 		    ioc->name, hot_plug_info->channel, hot_plug_info->id,
45463eb0822cSKashyap, Desai 		    hot_plug_info->phys_disk_num, (unsigned long long)
45473eb0822cSKashyap, Desai 		    sas_device.sas_address);
45483eb0822cSKashyap, Desai 
45493eb0822cSKashyap, Desai 		vtarget->tflags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT;
45503eb0822cSKashyap, Desai 		vtarget->id = hot_plug_info->id;
4551b506ade9SEric Moore 		phy_info->attached.phys_disk_num = ~0;
45523eb0822cSKashyap, Desai 		mptsas_reprobe_target(starget, 0);
45533eb0822cSKashyap, Desai 		mptsas_add_device_component_by_fw(ioc,
45543eb0822cSKashyap, Desai 		    hot_plug_info->channel, hot_plug_info->id);
45559a28f49aSChristoph Hellwig 		break;
45569a28f49aSChristoph Hellwig 
4557c73787eeSMoore, Eric 	case MPTSAS_ADD_RAID:
45583eb0822cSKashyap, Desai 
4559c73787eeSMoore, Eric 		mpt_findImVolumes(ioc);
45603eb0822cSKashyap, Desai 		printk(MYIOC_s_INFO_FMT "attaching raid volume, channel %d, "
45613eb0822cSKashyap, Desai 		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
45623eb0822cSKashyap, Desai 		    hot_plug_info->id);
45633eb0822cSKashyap, Desai 		scsi_add_device(ioc->sh, MPTSAS_RAID_CHANNEL,
45643eb0822cSKashyap, Desai 		    hot_plug_info->id, 0);
4565c73787eeSMoore, Eric 		break;
45663eb0822cSKashyap, Desai 
4567c73787eeSMoore, Eric 	case MPTSAS_DEL_RAID:
45683eb0822cSKashyap, Desai 
4569c73787eeSMoore, Eric 		mpt_findImVolumes(ioc);
45703eb0822cSKashyap, Desai 		printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
45713eb0822cSKashyap, Desai 		    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL,
45723eb0822cSKashyap, Desai 		    hot_plug_info->id);
45733eb0822cSKashyap, Desai 		scsi_remove_device(hot_plug_info->sdev);
45743eb0822cSKashyap, Desai 		scsi_device_put(hot_plug_info->sdev);
4575c73787eeSMoore, Eric 		break;
45763eb0822cSKashyap, Desai 
4577b506ade9SEric Moore 	case MPTSAS_ADD_INACTIVE_VOLUME:
45783eb0822cSKashyap, Desai 
45793eb0822cSKashyap, Desai 		mpt_findImVolumes(ioc);
4580b506ade9SEric Moore 		mptsas_adding_inactive_raid_components(ioc,
45813eb0822cSKashyap, Desai 		    hot_plug_info->channel, hot_plug_info->id);
4582b506ade9SEric Moore 		break;
45833eb0822cSKashyap, Desai 
4584bd23e94cSMoore, Eric 	default:
4585bd23e94cSMoore, Eric 		break;
45869a28f49aSChristoph Hellwig 	}
45879a28f49aSChristoph Hellwig 
45883eb0822cSKashyap, Desai 	mptsas_free_fw_event(ioc, fw_event);
45899a28f49aSChristoph Hellwig }
45909a28f49aSChristoph Hellwig 
45919a28f49aSChristoph Hellwig static void
mptsas_send_sas_event(struct fw_event_work * fw_event)45923eb0822cSKashyap, Desai mptsas_send_sas_event(struct fw_event_work *fw_event)
45939a28f49aSChristoph Hellwig {
45943eb0822cSKashyap, Desai 	MPT_ADAPTER *ioc;
45953eb0822cSKashyap, Desai 	struct mptsas_hotplug_event hot_plug_info;
45963eb0822cSKashyap, Desai 	EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data;
45973eb0822cSKashyap, Desai 	u32 device_info;
45983eb0822cSKashyap, Desai 	u64 sas_address;
45993eb0822cSKashyap, Desai 
46003eb0822cSKashyap, Desai 	ioc = fw_event->ioc;
46013eb0822cSKashyap, Desai 	sas_event_data = (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)
46023eb0822cSKashyap, Desai 	    fw_event->event_data;
46033eb0822cSKashyap, Desai 	device_info = le32_to_cpu(sas_event_data->DeviceInfo);
46049a28f49aSChristoph Hellwig 
46059a28f49aSChristoph Hellwig 	if ((device_info &
46069a28f49aSChristoph Hellwig 		(MPI_SAS_DEVICE_INFO_SSP_TARGET |
46079a28f49aSChristoph Hellwig 		MPI_SAS_DEVICE_INFO_STP_TARGET |
46083eb0822cSKashyap, Desai 		MPI_SAS_DEVICE_INFO_SATA_DEVICE)) == 0) {
46093eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
46109a28f49aSChristoph Hellwig 		return;
46113eb0822cSKashyap, Desai 	}
46123eb0822cSKashyap, Desai 
46133eb0822cSKashyap, Desai 	if (sas_event_data->ReasonCode ==
46143eb0822cSKashyap, Desai 		MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED) {
46153eb0822cSKashyap, Desai 		mptbase_sas_persist_operation(ioc,
46163eb0822cSKashyap, Desai 		MPI_SAS_OP_CLEAR_NOT_PRESENT);
46173eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
46183eb0822cSKashyap, Desai 		return;
46193eb0822cSKashyap, Desai 	}
46209a28f49aSChristoph Hellwig 
46214b766471SMoore, Eric 	switch (sas_event_data->ReasonCode) {
46224b766471SMoore, Eric 	case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING:
4623df9e062aSEric Moore 	case MPI_EVENT_SAS_DEV_STAT_RC_ADDED:
46243eb0822cSKashyap, Desai 		memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
46253eb0822cSKashyap, Desai 		hot_plug_info.handle = le16_to_cpu(sas_event_data->DevHandle);
46263eb0822cSKashyap, Desai 		hot_plug_info.channel = sas_event_data->Bus;
46273eb0822cSKashyap, Desai 		hot_plug_info.id = sas_event_data->TargetID;
46283eb0822cSKashyap, Desai 		hot_plug_info.phy_id = sas_event_data->PhyNum;
46294b766471SMoore, Eric 		memcpy(&sas_address, &sas_event_data->SASAddress,
46303eb0822cSKashyap, Desai 		    sizeof(u64));
46313eb0822cSKashyap, Desai 		hot_plug_info.sas_address = le64_to_cpu(sas_address);
46323eb0822cSKashyap, Desai 		hot_plug_info.device_info = device_info;
46334b766471SMoore, Eric 		if (sas_event_data->ReasonCode &
46344b766471SMoore, Eric 		    MPI_EVENT_SAS_DEV_STAT_RC_ADDED)
46353eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_ADD_DEVICE;
46369a28f49aSChristoph Hellwig 		else
46373eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_DEL_DEVICE;
46383eb0822cSKashyap, Desai 		mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
46394b766471SMoore, Eric 		break;
46403eb0822cSKashyap, Desai 
46414b766471SMoore, Eric 	case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED:
46423eb0822cSKashyap, Desai 		mptbase_sas_persist_operation(ioc,
46433eb0822cSKashyap, Desai 		    MPI_SAS_OP_CLEAR_NOT_PRESENT);
46443eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
46454b766471SMoore, Eric 		break;
46463eb0822cSKashyap, Desai 
46474b766471SMoore, Eric 	case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
46483eb0822cSKashyap, Desai 	/* TODO */
46494b766471SMoore, Eric 	case MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
46503eb0822cSKashyap, Desai 	/* TODO */
46514b766471SMoore, Eric 	default:
46523eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
46534b766471SMoore, Eric 		break;
46544b766471SMoore, Eric 	}
46559a28f49aSChristoph Hellwig }
46563eb0822cSKashyap, Desai 
4657c73787eeSMoore, Eric static void
mptsas_send_raid_event(struct fw_event_work * fw_event)46583eb0822cSKashyap, Desai mptsas_send_raid_event(struct fw_event_work *fw_event)
4659c73787eeSMoore, Eric {
46603eb0822cSKashyap, Desai 	MPT_ADAPTER *ioc;
46613eb0822cSKashyap, Desai 	EVENT_DATA_RAID *raid_event_data;
46623eb0822cSKashyap, Desai 	struct mptsas_hotplug_event hot_plug_info;
46633eb0822cSKashyap, Desai 	int status;
46643eb0822cSKashyap, Desai 	int state;
46653eb0822cSKashyap, Desai 	struct scsi_device *sdev = NULL;
46663eb0822cSKashyap, Desai 	VirtDevice *vdevice = NULL;
46673eb0822cSKashyap, Desai 	RaidPhysDiskPage0_t phys_disk;
4668c73787eeSMoore, Eric 
46693eb0822cSKashyap, Desai 	ioc = fw_event->ioc;
46703eb0822cSKashyap, Desai 	raid_event_data = (EVENT_DATA_RAID *)fw_event->event_data;
46713eb0822cSKashyap, Desai 	status = le32_to_cpu(raid_event_data->SettingsStatus);
46723eb0822cSKashyap, Desai 	state = (status >> 8) & 0xff;
4673c73787eeSMoore, Eric 
46743eb0822cSKashyap, Desai 	memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
46753eb0822cSKashyap, Desai 	hot_plug_info.id = raid_event_data->VolumeID;
46763eb0822cSKashyap, Desai 	hot_plug_info.channel = raid_event_data->VolumeBus;
46773eb0822cSKashyap, Desai 	hot_plug_info.phys_disk_num = raid_event_data->PhysDiskNum;
46783eb0822cSKashyap, Desai 
46793eb0822cSKashyap, Desai 	if (raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_DELETED ||
46803eb0822cSKashyap, Desai 	    raid_event_data->ReasonCode == MPI_EVENT_RAID_RC_VOLUME_CREATED ||
46813eb0822cSKashyap, Desai 	    raid_event_data->ReasonCode ==
46823eb0822cSKashyap, Desai 	    MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED) {
46833eb0822cSKashyap, Desai 		sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL,
46843eb0822cSKashyap, Desai 		    hot_plug_info.id, 0);
46853eb0822cSKashyap, Desai 		hot_plug_info.sdev = sdev;
46863eb0822cSKashyap, Desai 		if (sdev)
46873eb0822cSKashyap, Desai 			vdevice = sdev->hostdata;
4688c73787eeSMoore, Eric 	}
4689c73787eeSMoore, Eric 
46903eb0822cSKashyap, Desai 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
46913eb0822cSKashyap, Desai 	    "ReasonCode=%02x\n", ioc->name, __func__,
46923eb0822cSKashyap, Desai 	    raid_event_data->ReasonCode));
4693c73787eeSMoore, Eric 
4694c73787eeSMoore, Eric 	switch (raid_event_data->ReasonCode) {
4695c73787eeSMoore, Eric 	case MPI_EVENT_RAID_RC_PHYSDISK_DELETED:
46963eb0822cSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK_REPROBE;
4697c73787eeSMoore, Eric 		break;
4698c73787eeSMoore, Eric 	case MPI_EVENT_RAID_RC_PHYSDISK_CREATED:
46993eb0822cSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK_REPROBE;
4700c73787eeSMoore, Eric 		break;
4701bd23e94cSMoore, Eric 	case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED:
4702bd23e94cSMoore, Eric 		switch (state) {
4703bd23e94cSMoore, Eric 		case MPI_PD_STATE_ONLINE:
4704b506ade9SEric Moore 		case MPI_PD_STATE_NOT_COMPATIBLE:
47053eb0822cSKashyap, Desai 			mpt_raid_phys_disk_pg0(ioc,
47063eb0822cSKashyap, Desai 			    raid_event_data->PhysDiskNum, &phys_disk);
47073eb0822cSKashyap, Desai 			hot_plug_info.id = phys_disk.PhysDiskID;
47083eb0822cSKashyap, Desai 			hot_plug_info.channel = phys_disk.PhysDiskBus;
47093eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
4710bd23e94cSMoore, Eric 			break;
47113eb0822cSKashyap, Desai 		case MPI_PD_STATE_FAILED:
4712bd23e94cSMoore, Eric 		case MPI_PD_STATE_MISSING:
4713bd23e94cSMoore, Eric 		case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST:
4714bd23e94cSMoore, Eric 		case MPI_PD_STATE_FAILED_AT_HOST_REQUEST:
4715bd23e94cSMoore, Eric 		case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON:
47163eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
4717bd23e94cSMoore, Eric 			break;
4718bd23e94cSMoore, Eric 		default:
4719bd23e94cSMoore, Eric 			break;
4720bd23e94cSMoore, Eric 		}
4721bd23e94cSMoore, Eric 		break;
4722c73787eeSMoore, Eric 	case MPI_EVENT_RAID_RC_VOLUME_DELETED:
47233eb0822cSKashyap, Desai 		if (!sdev)
47243eb0822cSKashyap, Desai 			break;
47253eb0822cSKashyap, Desai 		vdevice->vtarget->deleted = 1; /* block IO */
47263eb0822cSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_DEL_RAID;
4727c73787eeSMoore, Eric 		break;
4728c73787eeSMoore, Eric 	case MPI_EVENT_RAID_RC_VOLUME_CREATED:
47293eb0822cSKashyap, Desai 		if (sdev) {
47303eb0822cSKashyap, Desai 			scsi_device_put(sdev);
47313eb0822cSKashyap, Desai 			break;
47323eb0822cSKashyap, Desai 		}
47333eb0822cSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_ADD_RAID;
4734c73787eeSMoore, Eric 		break;
4735c73787eeSMoore, Eric 	case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED:
47363eb0822cSKashyap, Desai 		if (!(status & MPI_RAIDVOL0_STATUS_FLAG_ENABLED)) {
47373eb0822cSKashyap, Desai 			if (!sdev)
47383eb0822cSKashyap, Desai 				break;
47393eb0822cSKashyap, Desai 			vdevice->vtarget->deleted = 1; /* block IO */
47403eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_DEL_RAID;
47413eb0822cSKashyap, Desai 			break;
47423eb0822cSKashyap, Desai 		}
4743bd23e94cSMoore, Eric 		switch (state) {
4744bd23e94cSMoore, Eric 		case MPI_RAIDVOL0_STATUS_STATE_FAILED:
4745bd23e94cSMoore, Eric 		case MPI_RAIDVOL0_STATUS_STATE_MISSING:
47463eb0822cSKashyap, Desai 			if (!sdev)
47473eb0822cSKashyap, Desai 				break;
47483eb0822cSKashyap, Desai 			vdevice->vtarget->deleted = 1; /* block IO */
47493eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_DEL_RAID;
4750bd23e94cSMoore, Eric 			break;
4751bd23e94cSMoore, Eric 		case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL:
4752bd23e94cSMoore, Eric 		case MPI_RAIDVOL0_STATUS_STATE_DEGRADED:
47533eb0822cSKashyap, Desai 			if (sdev) {
47543eb0822cSKashyap, Desai 				scsi_device_put(sdev);
47553eb0822cSKashyap, Desai 				break;
47563eb0822cSKashyap, Desai 			}
47573eb0822cSKashyap, Desai 			hot_plug_info.event_type = MPTSAS_ADD_RAID;
4758bd23e94cSMoore, Eric 			break;
4759bd23e94cSMoore, Eric 		default:
4760bd23e94cSMoore, Eric 			break;
4761bd23e94cSMoore, Eric 		}
4762c73787eeSMoore, Eric 		break;
4763c73787eeSMoore, Eric 	default:
4764c73787eeSMoore, Eric 		break;
4765c73787eeSMoore, Eric 	}
47663eb0822cSKashyap, Desai 
47673eb0822cSKashyap, Desai 	if (hot_plug_info.event_type != MPTSAS_IGNORE_EVENT)
47683eb0822cSKashyap, Desai 		mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
47693eb0822cSKashyap, Desai 	else
47703eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
4771c73787eeSMoore, Eric }
4772c73787eeSMoore, Eric 
4773db7051b2SKashyap, Desai /**
4774db7051b2SKashyap, Desai  *	mptsas_issue_tm - send mptsas internal tm request
4775db7051b2SKashyap, Desai  *	@ioc: Pointer to MPT_ADAPTER structure
4776db7051b2SKashyap, Desai  *	@type: Task Management type
4777db7051b2SKashyap, Desai  *	@channel: channel number for task management
4778db7051b2SKashyap, Desai  *	@id: Logical Target ID for reset (if appropriate)
4779db7051b2SKashyap, Desai  *	@lun: Logical unit for reset (if appropriate)
4780db7051b2SKashyap, Desai  *	@task_context: Context for the task to be aborted
4781db7051b2SKashyap, Desai  *	@timeout: timeout for task management control
4782cdcda465SRandy Dunlap  *	@issue_reset: set to 1 on return if reset is needed, else 0
4783db7051b2SKashyap, Desai  *
4784cdcda465SRandy Dunlap  *	Return: 0 on success or -1 on failure.
4785db7051b2SKashyap, Desai  *
4786db7051b2SKashyap, Desai  */
4787db7051b2SKashyap, Desai static int
mptsas_issue_tm(MPT_ADAPTER * ioc,u8 type,u8 channel,u8 id,u64 lun,int task_context,ulong timeout,u8 * issue_reset)4788db7051b2SKashyap, Desai mptsas_issue_tm(MPT_ADAPTER *ioc, u8 type, u8 channel, u8 id, u64 lun,
4789db7051b2SKashyap, Desai 	int task_context, ulong timeout, u8 *issue_reset)
4790db7051b2SKashyap, Desai {
4791db7051b2SKashyap, Desai 	MPT_FRAME_HDR	*mf;
4792db7051b2SKashyap, Desai 	SCSITaskMgmt_t	*pScsiTm;
4793db7051b2SKashyap, Desai 	int		 retval;
4794db7051b2SKashyap, Desai 	unsigned long	 timeleft;
4795db7051b2SKashyap, Desai 
4796db7051b2SKashyap, Desai 	*issue_reset = 0;
4797db7051b2SKashyap, Desai 	mf = mpt_get_msg_frame(mptsasDeviceResetCtx, ioc);
4798db7051b2SKashyap, Desai 	if (mf == NULL) {
4799db7051b2SKashyap, Desai 		retval = -1; /* return failure */
4800db7051b2SKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "TaskMgmt request: no "
4801db7051b2SKashyap, Desai 		    "msg frames!!\n", ioc->name));
4802db7051b2SKashyap, Desai 		goto out;
4803db7051b2SKashyap, Desai 	}
4804db7051b2SKashyap, Desai 
4805db7051b2SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request: mr = %p, "
4806db7051b2SKashyap, Desai 	    "task_type = 0x%02X,\n\t timeout = %ld, fw_channel = %d, "
4807db7051b2SKashyap, Desai 	    "fw_id = %d, lun = %lld,\n\t task_context = 0x%x\n", ioc->name, mf,
4808db7051b2SKashyap, Desai 	     type, timeout, channel, id, (unsigned long long)lun,
4809db7051b2SKashyap, Desai 	     task_context));
4810db7051b2SKashyap, Desai 
4811db7051b2SKashyap, Desai 	pScsiTm = (SCSITaskMgmt_t *) mf;
4812db7051b2SKashyap, Desai 	memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t));
4813db7051b2SKashyap, Desai 	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
4814db7051b2SKashyap, Desai 	pScsiTm->TaskType = type;
4815db7051b2SKashyap, Desai 	pScsiTm->MsgFlags = 0;
4816db7051b2SKashyap, Desai 	pScsiTm->TargetID = id;
4817db7051b2SKashyap, Desai 	pScsiTm->Bus = channel;
4818db7051b2SKashyap, Desai 	pScsiTm->ChainOffset = 0;
4819db7051b2SKashyap, Desai 	pScsiTm->Reserved = 0;
4820db7051b2SKashyap, Desai 	pScsiTm->Reserved1 = 0;
4821db7051b2SKashyap, Desai 	pScsiTm->TaskMsgContext = task_context;
4822db7051b2SKashyap, Desai 	int_to_scsilun(lun, (struct scsi_lun *)pScsiTm->LUN);
4823db7051b2SKashyap, Desai 
4824db7051b2SKashyap, Desai 	INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status)
4825db7051b2SKashyap, Desai 	CLEAR_MGMT_STATUS(ioc->internal_cmds.status)
4826db7051b2SKashyap, Desai 	retval = 0;
4827db7051b2SKashyap, Desai 	mpt_put_msg_frame_hi_pri(mptsasDeviceResetCtx, ioc, mf);
4828db7051b2SKashyap, Desai 
4829db7051b2SKashyap, Desai 	/* Now wait for the command to complete */
4830db7051b2SKashyap, Desai 	timeleft = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done,
4831db7051b2SKashyap, Desai 	    timeout*HZ);
4832db7051b2SKashyap, Desai 	if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) {
4833db7051b2SKashyap, Desai 		retval = -1; /* return failure */
4834db7051b2SKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_ERR_FMT
4835db7051b2SKashyap, Desai 		    "TaskMgmt request: TIMED OUT!(mr=%p)\n", ioc->name, mf));
4836db7051b2SKashyap, Desai 		mpt_free_msg_frame(ioc, mf);
4837db7051b2SKashyap, Desai 		if (ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_DID_IOCRESET)
4838db7051b2SKashyap, Desai 			goto out;
4839db7051b2SKashyap, Desai 		*issue_reset = 1;
4840db7051b2SKashyap, Desai 		goto out;
4841db7051b2SKashyap, Desai 	}
4842db7051b2SKashyap, Desai 
4843db7051b2SKashyap, Desai 	if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) {
4844db7051b2SKashyap, Desai 		retval = -1; /* return failure */
4845db7051b2SKashyap, Desai 		dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
4846db7051b2SKashyap, Desai 		    "TaskMgmt request: failed with no reply\n", ioc->name));
4847db7051b2SKashyap, Desai 		goto out;
4848db7051b2SKashyap, Desai 	}
4849db7051b2SKashyap, Desai 
4850db7051b2SKashyap, Desai  out:
4851db7051b2SKashyap, Desai 	CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status)
4852db7051b2SKashyap, Desai 	return retval;
4853db7051b2SKashyap, Desai }
4854db7051b2SKashyap, Desai 
4855db7051b2SKashyap, Desai /**
485694e989deSColin Ian King  *	mptsas_broadcast_primitive_work - Handle broadcast primitives
4857cdcda465SRandy Dunlap  *	@fw_event: work queue payload containing info describing the event
4858db7051b2SKashyap, Desai  *
4859cdcda465SRandy Dunlap  *	This will be handled in workqueue context.
4860db7051b2SKashyap, Desai  */
4861db7051b2SKashyap, Desai static void
mptsas_broadcast_primitive_work(struct fw_event_work * fw_event)486294e989deSColin Ian King mptsas_broadcast_primitive_work(struct fw_event_work *fw_event)
4863db7051b2SKashyap, Desai {
4864db7051b2SKashyap, Desai 	MPT_ADAPTER *ioc = fw_event->ioc;
4865db7051b2SKashyap, Desai 	MPT_FRAME_HDR	*mf;
4866db7051b2SKashyap, Desai 	VirtDevice	*vdevice;
4867db7051b2SKashyap, Desai 	int			ii;
4868db7051b2SKashyap, Desai 	struct scsi_cmnd	*sc;
4869db7051b2SKashyap, Desai 	SCSITaskMgmtReply_t	*pScsiTmReply;
4870db7051b2SKashyap, Desai 	u8			issue_reset;
4871db7051b2SKashyap, Desai 	int			task_context;
4872db7051b2SKashyap, Desai 	u8			channel, id;
4873db7051b2SKashyap, Desai 	int			 lun;
4874db7051b2SKashyap, Desai 	u32			 termination_count;
4875db7051b2SKashyap, Desai 	u32			 query_count;
4876db7051b2SKashyap, Desai 
4877db7051b2SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
4878db7051b2SKashyap, Desai 	    "%s - enter\n", ioc->name, __func__));
4879db7051b2SKashyap, Desai 
4880db7051b2SKashyap, Desai 	mutex_lock(&ioc->taskmgmt_cmds.mutex);
4881db7051b2SKashyap, Desai 	if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) {
4882db7051b2SKashyap, Desai 		mutex_unlock(&ioc->taskmgmt_cmds.mutex);
4883db7051b2SKashyap, Desai 		mptsas_requeue_fw_event(ioc, fw_event, 1000);
4884db7051b2SKashyap, Desai 		return;
4885db7051b2SKashyap, Desai 	}
4886db7051b2SKashyap, Desai 
4887db7051b2SKashyap, Desai 	issue_reset = 0;
4888db7051b2SKashyap, Desai 	termination_count = 0;
4889db7051b2SKashyap, Desai 	query_count = 0;
4890db7051b2SKashyap, Desai 	mpt_findImVolumes(ioc);
4891db7051b2SKashyap, Desai 	pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply;
4892db7051b2SKashyap, Desai 
4893db7051b2SKashyap, Desai 	for (ii = 0; ii < ioc->req_depth; ii++) {
4894db7051b2SKashyap, Desai 		if (ioc->fw_events_off)
4895db7051b2SKashyap, Desai 			goto out;
4896db7051b2SKashyap, Desai 		sc = mptscsih_get_scsi_lookup(ioc, ii);
4897db7051b2SKashyap, Desai 		if (!sc)
4898db7051b2SKashyap, Desai 			continue;
4899db7051b2SKashyap, Desai 		mf = MPT_INDEX_2_MFPTR(ioc, ii);
4900db7051b2SKashyap, Desai 		if (!mf)
4901db7051b2SKashyap, Desai 			continue;
4902db7051b2SKashyap, Desai 		task_context = mf->u.frame.hwhdr.msgctxu.MsgContext;
4903db7051b2SKashyap, Desai 		vdevice = sc->device->hostdata;
4904db7051b2SKashyap, Desai 		if (!vdevice || !vdevice->vtarget)
4905db7051b2SKashyap, Desai 			continue;
4906db7051b2SKashyap, Desai 		if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
4907db7051b2SKashyap, Desai 			continue; /* skip hidden raid components */
4908db7051b2SKashyap, Desai 		if (vdevice->vtarget->raidVolume)
4909db7051b2SKashyap, Desai 			continue; /* skip hidden raid components */
4910db7051b2SKashyap, Desai 		channel = vdevice->vtarget->channel;
4911db7051b2SKashyap, Desai 		id = vdevice->vtarget->id;
4912db7051b2SKashyap, Desai 		lun = vdevice->lun;
4913db7051b2SKashyap, Desai 		if (mptsas_issue_tm(ioc, MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK,
4914db7051b2SKashyap, Desai 		    channel, id, (u64)lun, task_context, 30, &issue_reset))
4915db7051b2SKashyap, Desai 			goto out;
4916db7051b2SKashyap, Desai 		query_count++;
4917db7051b2SKashyap, Desai 		termination_count +=
4918db7051b2SKashyap, Desai 		    le32_to_cpu(pScsiTmReply->TerminationCount);
4919db7051b2SKashyap, Desai 		if ((pScsiTmReply->IOCStatus == MPI_IOCSTATUS_SUCCESS) &&
4920db7051b2SKashyap, Desai 		    (pScsiTmReply->ResponseCode ==
4921db7051b2SKashyap, Desai 		    MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
4922db7051b2SKashyap, Desai 		    pScsiTmReply->ResponseCode ==
4923db7051b2SKashyap, Desai 		    MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))
4924db7051b2SKashyap, Desai 			continue;
4925db7051b2SKashyap, Desai 		if (mptsas_issue_tm(ioc,
4926db7051b2SKashyap, Desai 		    MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET,
4927db7051b2SKashyap, Desai 		    channel, id, (u64)lun, 0, 30, &issue_reset))
4928db7051b2SKashyap, Desai 			goto out;
4929db7051b2SKashyap, Desai 		termination_count +=
4930db7051b2SKashyap, Desai 		    le32_to_cpu(pScsiTmReply->TerminationCount);
4931db7051b2SKashyap, Desai 	}
4932db7051b2SKashyap, Desai 
4933db7051b2SKashyap, Desai  out:
4934db7051b2SKashyap, Desai 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
4935db7051b2SKashyap, Desai 	    "%s - exit, query_count = %d termination_count = %d\n",
4936db7051b2SKashyap, Desai 	    ioc->name, __func__, query_count, termination_count));
4937db7051b2SKashyap, Desai 
4938db7051b2SKashyap, Desai 	ioc->broadcast_aen_busy = 0;
4939db7051b2SKashyap, Desai 	mpt_clear_taskmgmt_in_progress_flag(ioc);
4940db7051b2SKashyap, Desai 	mutex_unlock(&ioc->taskmgmt_cmds.mutex);
4941db7051b2SKashyap, Desai 
4942db7051b2SKashyap, Desai 	if (issue_reset) {
494397009a29SKei Tokunaga 		printk(MYIOC_s_WARN_FMT
494497009a29SKei Tokunaga 		       "Issuing Reset from %s!! doorbell=0x%08x\n",
494597009a29SKei Tokunaga 		       ioc->name, __func__, mpt_GetIocState(ioc, 0));
4946d0f698c4SKashyap, Desai 		mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP);
4947db7051b2SKashyap, Desai 	}
4948db7051b2SKashyap, Desai 	mptsas_free_fw_event(ioc, fw_event);
4949db7051b2SKashyap, Desai }
4950db7051b2SKashyap, Desai 
4951b506ade9SEric Moore /*
4952b506ade9SEric Moore  * mptsas_send_ir2_event - handle exposing hidden disk when
4953b506ade9SEric Moore  * an inactive raid volume is added
4954b506ade9SEric Moore  *
4955b506ade9SEric Moore  * @ioc: Pointer to MPT_ADAPTER structure
4956b506ade9SEric Moore  * @ir2_data
4957b506ade9SEric Moore  *
4958b506ade9SEric Moore  */
4959b506ade9SEric Moore static void
mptsas_send_ir2_event(struct fw_event_work * fw_event)49603eb0822cSKashyap, Desai mptsas_send_ir2_event(struct fw_event_work *fw_event)
4961b506ade9SEric Moore {
49623eb0822cSKashyap, Desai 	MPT_ADAPTER	*ioc;
49633eb0822cSKashyap, Desai 	struct mptsas_hotplug_event hot_plug_info;
49643eb0822cSKashyap, Desai 	MPI_EVENT_DATA_IR2	*ir2_data;
49653eb0822cSKashyap, Desai 	u8 reasonCode;
4966a7938b0bSKashyap, Desai 	RaidPhysDiskPage0_t phys_disk;
4967b506ade9SEric Moore 
49683eb0822cSKashyap, Desai 	ioc = fw_event->ioc;
49693eb0822cSKashyap, Desai 	ir2_data = (MPI_EVENT_DATA_IR2 *)fw_event->event_data;
49703eb0822cSKashyap, Desai 	reasonCode = ir2_data->ReasonCode;
49713eb0822cSKashyap, Desai 
49723eb0822cSKashyap, Desai 	devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Entering %s: "
49733eb0822cSKashyap, Desai 	    "ReasonCode=%02x\n", ioc->name, __func__, reasonCode));
49743eb0822cSKashyap, Desai 
49753eb0822cSKashyap, Desai 	memset(&hot_plug_info, 0, sizeof(struct mptsas_hotplug_event));
49763eb0822cSKashyap, Desai 	hot_plug_info.id = ir2_data->TargetID;
49773eb0822cSKashyap, Desai 	hot_plug_info.channel = ir2_data->Bus;
49783eb0822cSKashyap, Desai 	switch (reasonCode) {
49793eb0822cSKashyap, Desai 	case MPI_EVENT_IR2_RC_FOREIGN_CFG_DETECTED:
49803eb0822cSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_ADD_INACTIVE_VOLUME;
49813eb0822cSKashyap, Desai 		break;
4982a7938b0bSKashyap, Desai 	case MPI_EVENT_IR2_RC_DUAL_PORT_REMOVED:
4983a7938b0bSKashyap, Desai 		hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
4984a7938b0bSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_DEL_PHYSDISK;
4985a7938b0bSKashyap, Desai 		break;
4986a7938b0bSKashyap, Desai 	case MPI_EVENT_IR2_RC_DUAL_PORT_ADDED:
4987a7938b0bSKashyap, Desai 		hot_plug_info.phys_disk_num = ir2_data->PhysDiskNum;
4988a7938b0bSKashyap, Desai 		mpt_raid_phys_disk_pg0(ioc,
4989a7938b0bSKashyap, Desai 		    ir2_data->PhysDiskNum, &phys_disk);
4990a7938b0bSKashyap, Desai 		hot_plug_info.id = phys_disk.PhysDiskID;
4991a7938b0bSKashyap, Desai 		hot_plug_info.event_type = MPTSAS_ADD_PHYSDISK;
4992a7938b0bSKashyap, Desai 		break;
49933eb0822cSKashyap, Desai 	default:
49943eb0822cSKashyap, Desai 		mptsas_free_fw_event(ioc, fw_event);
4995b506ade9SEric Moore 		return;
49963eb0822cSKashyap, Desai 	}
49973eb0822cSKashyap, Desai 	mptsas_hotplug_work(ioc, fw_event, &hot_plug_info);
49983eb0822cSKashyap, Desai }
4999e6b2d76aSMoore, Eric 
50009a28f49aSChristoph Hellwig static int
mptsas_event_process(MPT_ADAPTER * ioc,EventNotificationReply_t * reply)50019a28f49aSChristoph Hellwig mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
50029a28f49aSChristoph Hellwig {
50033eb0822cSKashyap, Desai 	u32 event = le32_to_cpu(reply->Event);
500432696198SJoe Lawrence 	int event_data_sz;
50053eb0822cSKashyap, Desai 	struct fw_event_work *fw_event;
50063eb0822cSKashyap, Desai 	unsigned long delay;
50079a28f49aSChristoph Hellwig 
5008ffb7fef3SKashyap, Desai 	if (ioc->bus_type != SAS)
5009ffb7fef3SKashyap, Desai 		return 0;
5010ffb7fef3SKashyap, Desai 
50113eb0822cSKashyap, Desai 	/* events turned off due to host reset or driver unloading */
50123eb0822cSKashyap, Desai 	if (ioc->fw_events_off)
50133eb0822cSKashyap, Desai 		return 0;
50149a28f49aSChristoph Hellwig 
50153eb0822cSKashyap, Desai 	delay = msecs_to_jiffies(1);
50169a28f49aSChristoph Hellwig 	switch (event) {
5017db7051b2SKashyap, Desai 	case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
5018db7051b2SKashyap, Desai 	{
5019db7051b2SKashyap, Desai 		EVENT_DATA_SAS_BROADCAST_PRIMITIVE *broadcast_event_data =
5020db7051b2SKashyap, Desai 		    (EVENT_DATA_SAS_BROADCAST_PRIMITIVE *)reply->Data;
5021db7051b2SKashyap, Desai 		if (broadcast_event_data->Primitive !=
5022db7051b2SKashyap, Desai 		    MPI_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
5023db7051b2SKashyap, Desai 			return 0;
5024db7051b2SKashyap, Desai 		if (ioc->broadcast_aen_busy)
5025db7051b2SKashyap, Desai 			return 0;
5026db7051b2SKashyap, Desai 		ioc->broadcast_aen_busy = 1;
5027db7051b2SKashyap, Desai 		break;
5028db7051b2SKashyap, Desai 	}
50299a28f49aSChristoph Hellwig 	case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE:
50303eb0822cSKashyap, Desai 	{
50313eb0822cSKashyap, Desai 		EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *sas_event_data =
50323eb0822cSKashyap, Desai 		    (EVENT_DATA_SAS_DEVICE_STATUS_CHANGE *)reply->Data;
5033c9de7dc4SKashyap, Desai 		u16	ioc_stat;
5034c9de7dc4SKashyap, Desai 		ioc_stat = le16_to_cpu(reply->IOCStatus);
50353eb0822cSKashyap, Desai 
50363eb0822cSKashyap, Desai 		if (sas_event_data->ReasonCode ==
50373eb0822cSKashyap, Desai 		    MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING) {
50383eb0822cSKashyap, Desai 			mptsas_target_reset_queue(ioc, sas_event_data);
50393eb0822cSKashyap, Desai 			return 0;
50403eb0822cSKashyap, Desai 		}
5041c9de7dc4SKashyap, Desai 		if (sas_event_data->ReasonCode ==
5042c9de7dc4SKashyap, Desai 			MPI_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
5043c9de7dc4SKashyap, Desai 			ioc->device_missing_delay &&
5044c9de7dc4SKashyap, Desai 			(ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)) {
5045c9de7dc4SKashyap, Desai 			VirtTarget *vtarget = NULL;
5046c9de7dc4SKashyap, Desai 			u8		id, channel;
5047c9de7dc4SKashyap, Desai 
5048c9de7dc4SKashyap, Desai 			id = sas_event_data->TargetID;
5049c9de7dc4SKashyap, Desai 			channel = sas_event_data->Bus;
5050c9de7dc4SKashyap, Desai 
5051c9de7dc4SKashyap, Desai 			vtarget = mptsas_find_vtarget(ioc, channel, id);
5052c9de7dc4SKashyap, Desai 			if (vtarget) {
5053c9de7dc4SKashyap, Desai 				devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
5054c9de7dc4SKashyap, Desai 				    "LogInfo (0x%x) available for "
5055c9de7dc4SKashyap, Desai 				   "INTERNAL_DEVICE_RESET"
5056c9de7dc4SKashyap, Desai 				   "fw_id %d fw_channel %d\n", ioc->name,
505772ef0e57SBorislav Petkov 				   le32_to_cpu(reply->IOCLogInfo),
505872ef0e57SBorislav Petkov 				   id, channel));
5059c9de7dc4SKashyap, Desai 				if (vtarget->raidVolume) {
5060c9de7dc4SKashyap, Desai 					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
5061c9de7dc4SKashyap, Desai 					"Skipping Raid Volume for inDMD\n",
5062c9de7dc4SKashyap, Desai 					ioc->name));
5063c9de7dc4SKashyap, Desai 				} else {
5064c9de7dc4SKashyap, Desai 					devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
5065c9de7dc4SKashyap, Desai 					"Setting device flag inDMD\n",
5066c9de7dc4SKashyap, Desai 					ioc->name));
5067c9de7dc4SKashyap, Desai 					vtarget->inDMD = 1;
5068c9de7dc4SKashyap, Desai 				}
5069c9de7dc4SKashyap, Desai 
5070c9de7dc4SKashyap, Desai 			}
5071c9de7dc4SKashyap, Desai 
5072c9de7dc4SKashyap, Desai 		}
5073c9de7dc4SKashyap, Desai 
5074c73787eeSMoore, Eric 		break;
50753eb0822cSKashyap, Desai 	}
5076f9c34022SKashyap, Desai 	case MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE:
5077f9c34022SKashyap, Desai 	{
5078f9c34022SKashyap, Desai 		MpiEventDataSasExpanderStatusChange_t *expander_data =
5079f9c34022SKashyap, Desai 		    (MpiEventDataSasExpanderStatusChange_t *)reply->Data;
5080f9c34022SKashyap, Desai 
5081eedf92b9SKashyap, Desai 		if (ioc->old_sas_discovery_protocal)
5082eedf92b9SKashyap, Desai 			return 0;
5083f9c34022SKashyap, Desai 
5084f9c34022SKashyap, Desai 		if (expander_data->ReasonCode ==
5085f9c34022SKashyap, Desai 		    MPI_EVENT_SAS_EXP_RC_NOT_RESPONDING &&
5086f9c34022SKashyap, Desai 		    ioc->device_missing_delay)
5087f9c34022SKashyap, Desai 			delay = HZ * ioc->device_missing_delay;
5088e6b2d76aSMoore, Eric 		break;
5089f9c34022SKashyap, Desai 	}
5090f9c34022SKashyap, Desai 	case MPI_EVENT_SAS_DISCOVERY:
5091f9c34022SKashyap, Desai 	{
5092f9c34022SKashyap, Desai 		u32 discovery_status;
5093f9c34022SKashyap, Desai 		EventDataSasDiscovery_t *discovery_data =
5094f9c34022SKashyap, Desai 		    (EventDataSasDiscovery_t *)reply->Data;
5095f9c34022SKashyap, Desai 
5096f9c34022SKashyap, Desai 		discovery_status = le32_to_cpu(discovery_data->DiscoveryStatus);
5097f9c34022SKashyap, Desai 		ioc->sas_discovery_quiesce_io = discovery_status ? 1 : 0;
5098eedf92b9SKashyap, Desai 		if (ioc->old_sas_discovery_protocal && !discovery_status)
5099eedf92b9SKashyap, Desai 			mptsas_queue_rescan(ioc);
5100f9c34022SKashyap, Desai 		return 0;
5101f9c34022SKashyap, Desai 	}
51023eb0822cSKashyap, Desai 	case MPI_EVENT_INTEGRATED_RAID:
51033eb0822cSKashyap, Desai 	case MPI_EVENT_PERSISTENT_TABLE_FULL:
5104b506ade9SEric Moore 	case MPI_EVENT_IR2:
51053eb0822cSKashyap, Desai 	case MPI_EVENT_SAS_PHY_LINK_STATUS:
51063eb0822cSKashyap, Desai 	case MPI_EVENT_QUEUE_FULL:
5107b506ade9SEric Moore 		break;
51089a28f49aSChristoph Hellwig 	default:
51093eb0822cSKashyap, Desai 		return 0;
51109a28f49aSChristoph Hellwig 	}
5111c73787eeSMoore, Eric 
51123eb0822cSKashyap, Desai 	event_data_sz = ((reply->MsgLength * 4) -
51133eb0822cSKashyap, Desai 	    offsetof(EventNotificationReply_t, Data));
511432696198SJoe Lawrence 	fw_event = kzalloc(sizeof(*fw_event) + event_data_sz, GFP_ATOMIC);
51153eb0822cSKashyap, Desai 	if (!fw_event) {
51163eb0822cSKashyap, Desai 		printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n", ioc->name,
51173eb0822cSKashyap, Desai 		 __func__, __LINE__);
51183eb0822cSKashyap, Desai 		return 0;
51193eb0822cSKashyap, Desai 	}
51203eb0822cSKashyap, Desai 	memcpy(fw_event->event_data, reply->Data, event_data_sz);
51213eb0822cSKashyap, Desai 	fw_event->event = event;
51223eb0822cSKashyap, Desai 	fw_event->ioc = ioc;
51233eb0822cSKashyap, Desai 	mptsas_add_fw_event(ioc, fw_event, delay);
51243eb0822cSKashyap, Desai 	return 0;
51259a28f49aSChristoph Hellwig }
51269a28f49aSChristoph Hellwig 
5127a7938b0bSKashyap, Desai /* Delete a volume when no longer listed in ioc pg2
5128a7938b0bSKashyap, Desai  */
mptsas_volume_delete(MPT_ADAPTER * ioc,u8 id)5129a7938b0bSKashyap, Desai static void mptsas_volume_delete(MPT_ADAPTER *ioc, u8 id)
5130a7938b0bSKashyap, Desai {
5131a7938b0bSKashyap, Desai 	struct scsi_device *sdev;
5132a7938b0bSKashyap, Desai 	int i;
5133a7938b0bSKashyap, Desai 
5134a7938b0bSKashyap, Desai 	sdev = scsi_device_lookup(ioc->sh, MPTSAS_RAID_CHANNEL, id, 0);
5135a7938b0bSKashyap, Desai 	if (!sdev)
5136a7938b0bSKashyap, Desai 		return;
5137a7938b0bSKashyap, Desai 	if (!ioc->raid_data.pIocPg2)
5138a7938b0bSKashyap, Desai 		goto out;
5139a7938b0bSKashyap, Desai 	if (!ioc->raid_data.pIocPg2->NumActiveVolumes)
5140a7938b0bSKashyap, Desai 		goto out;
5141a7938b0bSKashyap, Desai 	for (i = 0; i < ioc->raid_data.pIocPg2->NumActiveVolumes; i++)
5142a7938b0bSKashyap, Desai 		if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID == id)
5143a7938b0bSKashyap, Desai 			goto release_sdev;
5144a7938b0bSKashyap, Desai  out:
5145a7938b0bSKashyap, Desai 	printk(MYIOC_s_INFO_FMT "removing raid volume, channel %d, "
5146a7938b0bSKashyap, Desai 	    "id %d\n", ioc->name, MPTSAS_RAID_CHANNEL, id);
5147a7938b0bSKashyap, Desai 	scsi_remove_device(sdev);
5148a7938b0bSKashyap, Desai  release_sdev:
5149a7938b0bSKashyap, Desai 	scsi_device_put(sdev);
5150a7938b0bSKashyap, Desai }
5151a7938b0bSKashyap, Desai 
51520c33b27dSChristoph Hellwig static int
mptsas_probe(struct pci_dev * pdev,const struct pci_device_id * id)51530c33b27dSChristoph Hellwig mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
51540c33b27dSChristoph Hellwig {
51550c33b27dSChristoph Hellwig 	struct Scsi_Host	*sh;
51560c33b27dSChristoph Hellwig 	MPT_SCSI_HOST		*hd;
51570c33b27dSChristoph Hellwig 	MPT_ADAPTER 		*ioc;
51580c33b27dSChristoph Hellwig 	unsigned long		 flags;
51591ca00bb7SChristoph Hellwig 	int			 ii;
51600c33b27dSChristoph Hellwig 	int			 numSGE = 0;
51610c33b27dSChristoph Hellwig 	int			 scale;
51620c33b27dSChristoph Hellwig 	int			 ioc_cap;
51630c33b27dSChristoph Hellwig 	int			error=0;
51640c33b27dSChristoph Hellwig 	int			r;
51650c33b27dSChristoph Hellwig 
51660c33b27dSChristoph Hellwig 	r = mpt_attach(pdev,id);
51670c33b27dSChristoph Hellwig 	if (r)
51680c33b27dSChristoph Hellwig 		return r;
51690c33b27dSChristoph Hellwig 
51700c33b27dSChristoph Hellwig 	ioc = pci_get_drvdata(pdev);
51713eb0822cSKashyap, Desai 	mptsas_fw_event_off(ioc);
51720c33b27dSChristoph Hellwig 	ioc->DoneCtx = mptsasDoneCtx;
51730c33b27dSChristoph Hellwig 	ioc->TaskCtx = mptsasTaskCtx;
51740c33b27dSChristoph Hellwig 	ioc->InternalCtx = mptsasInternalCtx;
5175b68bf096SKashyap, Desai 	ioc->schedule_target_reset = &mptsas_schedule_target_reset;
5176e62cca19Skashyap.desai@lsi.com 	ioc->schedule_dead_ioc_flush_running_cmds =
5177e62cca19Skashyap.desai@lsi.com 				&mptscsih_flush_running_cmds;
51780c33b27dSChristoph Hellwig 	/*  Added sanity check on readiness of the MPT adapter.
51790c33b27dSChristoph Hellwig 	 */
51800c33b27dSChristoph Hellwig 	if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
51810c33b27dSChristoph Hellwig 		printk(MYIOC_s_WARN_FMT
51820c33b27dSChristoph Hellwig 		  "Skipping because it's not operational!\n",
51830c33b27dSChristoph Hellwig 		  ioc->name);
51847acec1e7SMoore, Eric Dean 		error = -ENODEV;
51857acec1e7SMoore, Eric Dean 		goto out_mptsas_probe;
51860c33b27dSChristoph Hellwig 	}
51870c33b27dSChristoph Hellwig 
51880c33b27dSChristoph Hellwig 	if (!ioc->active) {
51890c33b27dSChristoph Hellwig 		printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
51900c33b27dSChristoph Hellwig 		  ioc->name);
51917acec1e7SMoore, Eric Dean 		error = -ENODEV;
51927acec1e7SMoore, Eric Dean 		goto out_mptsas_probe;
51930c33b27dSChristoph Hellwig 	}
51940c33b27dSChristoph Hellwig 
51950c33b27dSChristoph Hellwig 	/*  Sanity check - ensure at least 1 port is INITIATOR capable
51960c33b27dSChristoph Hellwig 	 */
51970c33b27dSChristoph Hellwig 	ioc_cap = 0;
51980c33b27dSChristoph Hellwig 	for (ii = 0; ii < ioc->facts.NumberOfPorts; ii++) {
51990c33b27dSChristoph Hellwig 		if (ioc->pfacts[ii].ProtocolFlags &
52000c33b27dSChristoph Hellwig 				MPI_PORTFACTS_PROTOCOL_INITIATOR)
52010c33b27dSChristoph Hellwig 			ioc_cap++;
52020c33b27dSChristoph Hellwig 	}
52030c33b27dSChristoph Hellwig 
52040c33b27dSChristoph Hellwig 	if (!ioc_cap) {
52050c33b27dSChristoph Hellwig 		printk(MYIOC_s_WARN_FMT
52060c33b27dSChristoph Hellwig 			"Skipping ioc=%p because SCSI Initiator mode "
52070c33b27dSChristoph Hellwig 			"is NOT enabled!\n", ioc->name, ioc);
5208466544d8SMoore, Eric Dean 		return 0;
52090c33b27dSChristoph Hellwig 	}
52100c33b27dSChristoph Hellwig 
52110c33b27dSChristoph Hellwig 	sh = scsi_host_alloc(&mptsas_driver_template, sizeof(MPT_SCSI_HOST));
52120c33b27dSChristoph Hellwig 	if (!sh) {
52130c33b27dSChristoph Hellwig 		printk(MYIOC_s_WARN_FMT
52140c33b27dSChristoph Hellwig 			"Unable to register controller with SCSI subsystem\n",
52150c33b27dSChristoph Hellwig 			ioc->name);
52167acec1e7SMoore, Eric Dean 		error = -1;
52177acec1e7SMoore, Eric Dean 		goto out_mptsas_probe;
52180c33b27dSChristoph Hellwig         }
52190c33b27dSChristoph Hellwig 
52200c33b27dSChristoph Hellwig 	spin_lock_irqsave(&ioc->FreeQlock, flags);
52210c33b27dSChristoph Hellwig 
52220c33b27dSChristoph Hellwig 	/* Attach the SCSI Host to the IOC structure
52230c33b27dSChristoph Hellwig 	 */
52240c33b27dSChristoph Hellwig 	ioc->sh = sh;
52250c33b27dSChristoph Hellwig 
52260c33b27dSChristoph Hellwig 	sh->io_port = 0;
52270c33b27dSChristoph Hellwig 	sh->n_io_port = 0;
52280c33b27dSChristoph Hellwig 	sh->irq = 0;
52290c33b27dSChristoph Hellwig 
52300c33b27dSChristoph Hellwig 	/* set 16 byte cdb's */
52310c33b27dSChristoph Hellwig 	sh->max_cmd_len = 16;
523279a3ec1aSKashyap, Desai 	sh->can_queue = min_t(int, ioc->req_depth - 10, sh->can_queue);
523379a3ec1aSKashyap, Desai 	sh->max_id = -1;
5234793955f5SEric Moore 	sh->max_lun = max_lun;
52350c33b27dSChristoph Hellwig 	sh->transportt = mptsas_transport_template;
52360c33b27dSChristoph Hellwig 
52370c33b27dSChristoph Hellwig 	/* Required entry.
52380c33b27dSChristoph Hellwig 	 */
52390c33b27dSChristoph Hellwig 	sh->unique_id = ioc->id;
52400c33b27dSChristoph Hellwig 
52410c33b27dSChristoph Hellwig 	INIT_LIST_HEAD(&ioc->sas_topology);
52429a28f49aSChristoph Hellwig 	mutex_init(&ioc->sas_topology_mutex);
5243e6b2d76aSMoore, Eric 	mutex_init(&ioc->sas_discovery_mutex);
5244eeb846ceSChristoph Hellwig 	mutex_init(&ioc->sas_mgmt.mutex);
5245da4fa655SChristoph Hellwig 	init_completion(&ioc->sas_mgmt.done);
52460c33b27dSChristoph Hellwig 
52470c33b27dSChristoph Hellwig 	/* Verify that we won't exceed the maximum
52480c33b27dSChristoph Hellwig 	 * number of chain buffers
52490c33b27dSChristoph Hellwig 	 * We can optimize:  ZZ = req_sz/sizeof(SGE)
52500c33b27dSChristoph Hellwig 	 * For 32bit SGE's:
52510c33b27dSChristoph Hellwig 	 *  numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
52520c33b27dSChristoph Hellwig 	 *               + (req_sz - 64)/sizeof(SGE)
52530c33b27dSChristoph Hellwig 	 * A slightly different algorithm is required for
52540c33b27dSChristoph Hellwig 	 * 64bit SGEs.
52550c33b27dSChristoph Hellwig 	 */
525614d0f0b0SKashyap, Desai 	scale = ioc->req_sz/ioc->SGE_size;
525714d0f0b0SKashyap, Desai 	if (ioc->sg_addr_size == sizeof(u64)) {
52580c33b27dSChristoph Hellwig 		numSGE = (scale - 1) *
52590c33b27dSChristoph Hellwig 		  (ioc->facts.MaxChainDepth-1) + scale +
526014d0f0b0SKashyap, Desai 		  (ioc->req_sz - 60) / ioc->SGE_size;
52610c33b27dSChristoph Hellwig 	} else {
52620c33b27dSChristoph Hellwig 		numSGE = 1 + (scale - 1) *
52630c33b27dSChristoph Hellwig 		  (ioc->facts.MaxChainDepth-1) + scale +
526414d0f0b0SKashyap, Desai 		  (ioc->req_sz - 64) / ioc->SGE_size;
52650c33b27dSChristoph Hellwig 	}
52660c33b27dSChristoph Hellwig 
52670c33b27dSChristoph Hellwig 	if (numSGE < sh->sg_tablesize) {
52680c33b27dSChristoph Hellwig 		/* Reset this value */
5269d6ecdd63SPrakash, Sathya 		dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
52700c33b27dSChristoph Hellwig 		  "Resetting sg_tablesize to %d from %d\n",
52710c33b27dSChristoph Hellwig 		  ioc->name, numSGE, sh->sg_tablesize));
52720c33b27dSChristoph Hellwig 		sh->sg_tablesize = numSGE;
52730c33b27dSChristoph Hellwig 	}
52740c33b27dSChristoph Hellwig 
52753850b14eSkashyap.desai@lsi.com 	if (mpt_loadtime_max_sectors) {
52763850b14eSkashyap.desai@lsi.com 		if (mpt_loadtime_max_sectors < 64 ||
52773850b14eSkashyap.desai@lsi.com 			mpt_loadtime_max_sectors > 8192) {
52783850b14eSkashyap.desai@lsi.com 			printk(MYIOC_s_INFO_FMT "Invalid value passed for"
52793850b14eSkashyap.desai@lsi.com 				"mpt_loadtime_max_sectors %d."
52803850b14eSkashyap.desai@lsi.com 				"Range from 64 to 8192\n", ioc->name,
52813850b14eSkashyap.desai@lsi.com 				mpt_loadtime_max_sectors);
52823850b14eSkashyap.desai@lsi.com 		}
52833850b14eSkashyap.desai@lsi.com 		mpt_loadtime_max_sectors &=  0xFFFFFFFE;
52843850b14eSkashyap.desai@lsi.com 		dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
52853850b14eSkashyap.desai@lsi.com 			"Resetting max sector to %d from %d\n",
52863850b14eSkashyap.desai@lsi.com 		  ioc->name, mpt_loadtime_max_sectors, sh->max_sectors));
52873850b14eSkashyap.desai@lsi.com 		sh->max_sectors = mpt_loadtime_max_sectors;
52883850b14eSkashyap.desai@lsi.com 	}
52893850b14eSkashyap.desai@lsi.com 
5290e7eae9f6SEric Moore 	hd = shost_priv(sh);
52910c33b27dSChristoph Hellwig 	hd->ioc = ioc;
52920c33b27dSChristoph Hellwig 
52930c33b27dSChristoph Hellwig 	/* SCSI needs scsi_cmnd lookup table!
52940c33b27dSChristoph Hellwig 	 * (with size equal to req_depth*PtrSz!)
52950c33b27dSChristoph Hellwig 	 */
5296e8206381SEric Moore 	ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
5297e8206381SEric Moore 	if (!ioc->ScsiLookup) {
52980c33b27dSChristoph Hellwig 		error = -ENOMEM;
5299bc6e089aSEric Moore 		spin_unlock_irqrestore(&ioc->FreeQlock, flags);
53007acec1e7SMoore, Eric Dean 		goto out_mptsas_probe;
53010c33b27dSChristoph Hellwig 	}
5302e8206381SEric Moore 	spin_lock_init(&ioc->scsi_lookup_lock);
53030c33b27dSChristoph Hellwig 
5304d6ecdd63SPrakash, Sathya 	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
5305e8206381SEric Moore 		 ioc->name, ioc->ScsiLookup));
53060c33b27dSChristoph Hellwig 
53070c33b27dSChristoph Hellwig 	ioc->sas_data.ptClear = mpt_pt_clear;
53080c33b27dSChristoph Hellwig 
5309df9e062aSEric Moore 	hd->last_queue_full = 0;
5310df9e062aSEric Moore 	INIT_LIST_HEAD(&hd->target_reset_list);
53113eb0822cSKashyap, Desai 	INIT_LIST_HEAD(&ioc->sas_device_info_list);
53123eb0822cSKashyap, Desai 	mutex_init(&ioc->sas_device_info_mutex);
53133eb0822cSKashyap, Desai 
5314df9e062aSEric Moore 	spin_unlock_irqrestore(&ioc->FreeQlock, flags);
5315df9e062aSEric Moore 
53160c33b27dSChristoph Hellwig 	if (ioc->sas_data.ptClear==1) {
53170c33b27dSChristoph Hellwig 		mptbase_sas_persist_operation(
53180c33b27dSChristoph Hellwig 		    ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT);
53190c33b27dSChristoph Hellwig 	}
53200c33b27dSChristoph Hellwig 
53210c33b27dSChristoph Hellwig 	error = scsi_add_host(sh, &ioc->pcidev->dev);
53220c33b27dSChristoph Hellwig 	if (error) {
532329dd3609SEric Moore 		dprintk(ioc, printk(MYIOC_s_ERR_FMT
532429dd3609SEric Moore 		  "scsi_add_host failed\n", ioc->name));
53257acec1e7SMoore, Eric Dean 		goto out_mptsas_probe;
53260c33b27dSChristoph Hellwig 	}
53270c33b27dSChristoph Hellwig 
5328eedf92b9SKashyap, Desai 	/* older firmware doesn't support expander events */
5329eedf92b9SKashyap, Desai 	if ((ioc->facts.HeaderVersion >> 8) < 0xE)
5330eedf92b9SKashyap, Desai 		ioc->old_sas_discovery_protocal = 1;
53310c33b27dSChristoph Hellwig 	mptsas_scan_sas_topology(ioc);
53323eb0822cSKashyap, Desai 	mptsas_fw_event_on(ioc);
53330c33b27dSChristoph Hellwig 	return 0;
53340c33b27dSChristoph Hellwig 
53357acec1e7SMoore, Eric Dean  out_mptsas_probe:
53360c33b27dSChristoph Hellwig 
53370c33b27dSChristoph Hellwig 	mptscsih_remove(pdev);
53380c33b27dSChristoph Hellwig 	return error;
53390c33b27dSChristoph Hellwig }
53400c33b27dSChristoph Hellwig 
53415767d25fSJoe Lawrence static void
mptsas_shutdown(struct pci_dev * pdev)53427b5a65b9SKashyap, Desai mptsas_shutdown(struct pci_dev *pdev)
53437b5a65b9SKashyap, Desai {
53447b5a65b9SKashyap, Desai 	MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
53457b5a65b9SKashyap, Desai 
53463eb0822cSKashyap, Desai 	mptsas_fw_event_off(ioc);
53473eb0822cSKashyap, Desai 	mptsas_cleanup_fw_event_q(ioc);
53487b5a65b9SKashyap, Desai }
53497b5a65b9SKashyap, Desai 
mptsas_remove(struct pci_dev * pdev)535047b1ea75SGreg Kroah-Hartman static void mptsas_remove(struct pci_dev *pdev)
53510c33b27dSChristoph Hellwig {
53520c33b27dSChristoph Hellwig 	MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
53530c33b27dSChristoph Hellwig 	struct mptsas_portinfo *p, *n;
5354547f9a21SEric Moore 	int i;
53550c33b27dSChristoph Hellwig 
535648959f1eSKashyap, Desai 	if (!ioc->sh) {
535748959f1eSKashyap, Desai 		printk(MYIOC_s_INFO_FMT "IOC is in Target mode\n", ioc->name);
535848959f1eSKashyap, Desai 		mpt_detach(pdev);
535948959f1eSKashyap, Desai 		return;
536048959f1eSKashyap, Desai 	}
536148959f1eSKashyap, Desai 
53627b5a65b9SKashyap, Desai 	mptsas_shutdown(pdev);
53637b5a65b9SKashyap, Desai 
53643eb0822cSKashyap, Desai 	mptsas_del_device_components(ioc);
53653eb0822cSKashyap, Desai 
5366e6b2d76aSMoore, Eric 	ioc->sas_discovery_ignore_events = 1;
53670c33b27dSChristoph Hellwig 	sas_remove_host(ioc->sh);
53680c33b27dSChristoph Hellwig 
53699a28f49aSChristoph Hellwig 	mutex_lock(&ioc->sas_topology_mutex);
53700c33b27dSChristoph Hellwig 	list_for_each_entry_safe(p, n, &ioc->sas_topology, list) {
53710c33b27dSChristoph Hellwig 		list_del(&p->list);
5372547f9a21SEric Moore 		for (i = 0 ; i < p->num_phys ; i++)
5373d6ecdd63SPrakash, Sathya 			mptsas_port_delete(ioc, p->phy_info[i].port_details);
53743eb0822cSKashyap, Desai 
5375db9c9174SMoore, Eric 		kfree(p->phy_info);
53760c33b27dSChristoph Hellwig 		kfree(p);
53770c33b27dSChristoph Hellwig 	}
53789a28f49aSChristoph Hellwig 	mutex_unlock(&ioc->sas_topology_mutex);
5379f9c34022SKashyap, Desai 	ioc->hba_port_info = NULL;
53800c33b27dSChristoph Hellwig 	mptscsih_remove(pdev);
53810c33b27dSChristoph Hellwig }
53820c33b27dSChristoph Hellwig 
53830c33b27dSChristoph Hellwig static struct pci_device_id mptsas_pci_table[] = {
538487cf8986SEric Moore 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064,
53850c33b27dSChristoph Hellwig 		PCI_ANY_ID, PCI_ANY_ID },
538687cf8986SEric Moore 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068,
53870c33b27dSChristoph Hellwig 		PCI_ANY_ID, PCI_ANY_ID },
538887cf8986SEric Moore 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1064E,
53890c33b27dSChristoph Hellwig 		PCI_ANY_ID, PCI_ANY_ID },
539087cf8986SEric Moore 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068E,
53910c33b27dSChristoph Hellwig 		PCI_ANY_ID, PCI_ANY_ID },
539287cf8986SEric Moore 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1078,
53930c33b27dSChristoph Hellwig 		PCI_ANY_ID, PCI_ANY_ID },
539485d37226SChandrakala Chavva 	{ PCI_VENDOR_ID_LSI_LOGIC, MPI_MANUFACTPAGE_DEVID_SAS1068_820XELP,
539585d37226SChandrakala Chavva 		PCI_ANY_ID, PCI_ANY_ID },
53960c33b27dSChristoph Hellwig 	{0}	/* Terminating entry */
53970c33b27dSChristoph Hellwig };
53980c33b27dSChristoph Hellwig MODULE_DEVICE_TABLE(pci, mptsas_pci_table);
53990c33b27dSChristoph Hellwig 
54000c33b27dSChristoph Hellwig 
54010c33b27dSChristoph Hellwig static struct pci_driver mptsas_driver = {
54020c33b27dSChristoph Hellwig 	.name		= "mptsas",
54030c33b27dSChristoph Hellwig 	.id_table	= mptsas_pci_table,
54040c33b27dSChristoph Hellwig 	.probe		= mptsas_probe,
540547b1ea75SGreg Kroah-Hartman 	.remove		= mptsas_remove,
54067b5a65b9SKashyap, Desai 	.shutdown	= mptsas_shutdown,
54070c33b27dSChristoph Hellwig #ifdef CONFIG_PM
54080c33b27dSChristoph Hellwig 	.suspend	= mptscsih_suspend,
54090c33b27dSChristoph Hellwig 	.resume		= mptscsih_resume,
54100c33b27dSChristoph Hellwig #endif
54110c33b27dSChristoph Hellwig };
54120c33b27dSChristoph Hellwig 
54130c33b27dSChristoph Hellwig static int __init
mptsas_init(void)54140c33b27dSChristoph Hellwig mptsas_init(void)
54150c33b27dSChristoph Hellwig {
541657ce21bfSPrakash, Sathya 	int error;
541757ce21bfSPrakash, Sathya 
54180c33b27dSChristoph Hellwig 	show_mptmod_ver(my_NAME, my_VERSION);
54190c33b27dSChristoph Hellwig 
54200c33b27dSChristoph Hellwig 	mptsas_transport_template =
54210c33b27dSChristoph Hellwig 	    sas_attach_transport(&mptsas_transport_functions);
54220c33b27dSChristoph Hellwig 	if (!mptsas_transport_template)
54230c33b27dSChristoph Hellwig 		return -ENODEV;
54240c33b27dSChristoph Hellwig 
5425213aaca3SKashyap, Desai 	mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER,
5426213aaca3SKashyap, Desai 	    "mptscsih_io_done");
5427213aaca3SKashyap, Desai 	mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER,
5428213aaca3SKashyap, Desai 	    "mptscsih_taskmgmt_complete");
54290c33b27dSChristoph Hellwig 	mptsasInternalCtx =
5430213aaca3SKashyap, Desai 		mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER,
5431213aaca3SKashyap, Desai 		    "mptscsih_scandv_complete");
5432213aaca3SKashyap, Desai 	mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER,
5433213aaca3SKashyap, Desai 	    "mptsas_mgmt_done");
5434e7deff33SKashyap, Desai 	mptsasDeviceResetCtx =
5435213aaca3SKashyap, Desai 		mpt_register(mptsas_taskmgmt_complete, MPTSAS_DRIVER,
5436213aaca3SKashyap, Desai 		    "mptsas_taskmgmt_complete");
54370c33b27dSChristoph Hellwig 
5438d6ecdd63SPrakash, Sathya 	mpt_event_register(mptsasDoneCtx, mptsas_event_process);
5439d6ecdd63SPrakash, Sathya 	mpt_reset_register(mptsasDoneCtx, mptsas_ioc_reset);
54400c33b27dSChristoph Hellwig 
544157ce21bfSPrakash, Sathya 	error = pci_register_driver(&mptsas_driver);
544257ce21bfSPrakash, Sathya 	if (error)
544357ce21bfSPrakash, Sathya 		sas_release_transport(mptsas_transport_template);
544457ce21bfSPrakash, Sathya 
544557ce21bfSPrakash, Sathya 	return error;
54460c33b27dSChristoph Hellwig }
54470c33b27dSChristoph Hellwig 
54480c33b27dSChristoph Hellwig static void __exit
mptsas_exit(void)54490c33b27dSChristoph Hellwig mptsas_exit(void)
54500c33b27dSChristoph Hellwig {
54510c33b27dSChristoph Hellwig 	pci_unregister_driver(&mptsas_driver);
54520c33b27dSChristoph Hellwig 	sas_release_transport(mptsas_transport_template);
54530c33b27dSChristoph Hellwig 
54540c33b27dSChristoph Hellwig 	mpt_reset_deregister(mptsasDoneCtx);
54550c33b27dSChristoph Hellwig 	mpt_event_deregister(mptsasDoneCtx);
54560c33b27dSChristoph Hellwig 
5457da4fa655SChristoph Hellwig 	mpt_deregister(mptsasMgmtCtx);
54580c33b27dSChristoph Hellwig 	mpt_deregister(mptsasInternalCtx);
54590c33b27dSChristoph Hellwig 	mpt_deregister(mptsasTaskCtx);
54600c33b27dSChristoph Hellwig 	mpt_deregister(mptsasDoneCtx);
5461e7deff33SKashyap, Desai 	mpt_deregister(mptsasDeviceResetCtx);
54620c33b27dSChristoph Hellwig }
54630c33b27dSChristoph Hellwig 
54640c33b27dSChristoph Hellwig module_init(mptsas_init);
54650c33b27dSChristoph Hellwig module_exit(mptsas_exit);
5466