1fc7212fdSSreekanth Reddy // SPDX-License-Identifier: GPL-2.0-or-later
2fc7212fdSSreekanth Reddy /*
3fc7212fdSSreekanth Reddy  * Driver for Broadcom MPI3 Storage Controllers
4fc7212fdSSreekanth Reddy  *
5e74f2fbdSRanjan Kumar  * Copyright (C) 2017-2023 Broadcom Inc.
6fc7212fdSSreekanth Reddy  *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
7fc7212fdSSreekanth Reddy  *
8fc7212fdSSreekanth Reddy  */
9fc7212fdSSreekanth Reddy 
10fc7212fdSSreekanth Reddy #include "mpi3mr.h"
11fc7212fdSSreekanth Reddy 
12fc7212fdSSreekanth Reddy /**
132bd37e28SSreekanth Reddy  * mpi3mr_post_transport_req - Issue transport requests and wait
142bd37e28SSreekanth Reddy  * @mrioc: Adapter instance reference
152bd37e28SSreekanth Reddy  * @request: Properly populated MPI3 request
162bd37e28SSreekanth Reddy  * @request_sz: Size of the MPI3 request
172bd37e28SSreekanth Reddy  * @reply: Pointer to return MPI3 reply
182bd37e28SSreekanth Reddy  * @reply_sz: Size of the MPI3 reply buffer
192bd37e28SSreekanth Reddy  * @timeout: Timeout in seconds
202bd37e28SSreekanth Reddy  * @ioc_status: Pointer to return ioc status
212bd37e28SSreekanth Reddy  *
222bd37e28SSreekanth Reddy  * A generic function for posting MPI3 requests from the SAS
232bd37e28SSreekanth Reddy  * transport layer that uses transport command infrastructure.
242bd37e28SSreekanth Reddy  * This blocks for the completion of request for timeout seconds
252bd37e28SSreekanth Reddy  * and if the request times out this function faults the
262bd37e28SSreekanth Reddy  * controller with proper reason code.
272bd37e28SSreekanth Reddy  *
282bd37e28SSreekanth Reddy  * On successful completion of the request this function returns
292bd37e28SSreekanth Reddy  * appropriate ioc status from the firmware back to the caller.
302bd37e28SSreekanth Reddy  *
312bd37e28SSreekanth Reddy  * Return: 0 on success, non-zero on failure.
322bd37e28SSreekanth Reddy  */
mpi3mr_post_transport_req(struct mpi3mr_ioc * mrioc,void * request,u16 request_sz,void * reply,u16 reply_sz,int timeout,u16 * ioc_status)332bd37e28SSreekanth Reddy static int mpi3mr_post_transport_req(struct mpi3mr_ioc *mrioc, void *request,
342bd37e28SSreekanth Reddy 	u16 request_sz, void *reply, u16 reply_sz, int timeout,
352bd37e28SSreekanth Reddy 	u16 *ioc_status)
362bd37e28SSreekanth Reddy {
372bd37e28SSreekanth Reddy 	int retval = 0;
382bd37e28SSreekanth Reddy 
392bd37e28SSreekanth Reddy 	mutex_lock(&mrioc->transport_cmds.mutex);
402bd37e28SSreekanth Reddy 	if (mrioc->transport_cmds.state & MPI3MR_CMD_PENDING) {
412bd37e28SSreekanth Reddy 		retval = -1;
422bd37e28SSreekanth Reddy 		ioc_err(mrioc, "sending transport request failed due to command in use\n");
432bd37e28SSreekanth Reddy 		mutex_unlock(&mrioc->transport_cmds.mutex);
442bd37e28SSreekanth Reddy 		goto out;
452bd37e28SSreekanth Reddy 	}
462bd37e28SSreekanth Reddy 	mrioc->transport_cmds.state = MPI3MR_CMD_PENDING;
472bd37e28SSreekanth Reddy 	mrioc->transport_cmds.is_waiting = 1;
482bd37e28SSreekanth Reddy 	mrioc->transport_cmds.callback = NULL;
492bd37e28SSreekanth Reddy 	mrioc->transport_cmds.ioc_status = 0;
502bd37e28SSreekanth Reddy 	mrioc->transport_cmds.ioc_loginfo = 0;
512bd37e28SSreekanth Reddy 
522bd37e28SSreekanth Reddy 	init_completion(&mrioc->transport_cmds.done);
532bd37e28SSreekanth Reddy 	dprint_cfg_info(mrioc, "posting transport request\n");
542bd37e28SSreekanth Reddy 	if (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO)
552bd37e28SSreekanth Reddy 		dprint_dump(request, request_sz, "transport_req");
562bd37e28SSreekanth Reddy 	retval = mpi3mr_admin_request_post(mrioc, request, request_sz, 1);
572bd37e28SSreekanth Reddy 	if (retval) {
582bd37e28SSreekanth Reddy 		ioc_err(mrioc, "posting transport request failed\n");
592bd37e28SSreekanth Reddy 		goto out_unlock;
602bd37e28SSreekanth Reddy 	}
612bd37e28SSreekanth Reddy 	wait_for_completion_timeout(&mrioc->transport_cmds.done,
622bd37e28SSreekanth Reddy 	    (timeout * HZ));
632bd37e28SSreekanth Reddy 	if (!(mrioc->transport_cmds.state & MPI3MR_CMD_COMPLETE)) {
642bd37e28SSreekanth Reddy 		mpi3mr_check_rh_fault_ioc(mrioc,
652bd37e28SSreekanth Reddy 		    MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT);
662bd37e28SSreekanth Reddy 		ioc_err(mrioc, "transport request timed out\n");
672bd37e28SSreekanth Reddy 		retval = -1;
682bd37e28SSreekanth Reddy 		goto out_unlock;
692bd37e28SSreekanth Reddy 	}
702bd37e28SSreekanth Reddy 	*ioc_status = mrioc->transport_cmds.ioc_status &
712bd37e28SSreekanth Reddy 		MPI3_IOCSTATUS_STATUS_MASK;
722bd37e28SSreekanth Reddy 	if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
732bd37e28SSreekanth Reddy 		dprint_transport_err(mrioc,
742bd37e28SSreekanth Reddy 		    "transport request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
752bd37e28SSreekanth Reddy 		    *ioc_status, mrioc->transport_cmds.ioc_loginfo);
762bd37e28SSreekanth Reddy 
772bd37e28SSreekanth Reddy 	if ((reply) && (mrioc->transport_cmds.state & MPI3MR_CMD_REPLY_VALID))
782bd37e28SSreekanth Reddy 		memcpy((u8 *)reply, mrioc->transport_cmds.reply, reply_sz);
792bd37e28SSreekanth Reddy 
802bd37e28SSreekanth Reddy out_unlock:
812bd37e28SSreekanth Reddy 	mrioc->transport_cmds.state = MPI3MR_CMD_NOTUSED;
822bd37e28SSreekanth Reddy 	mutex_unlock(&mrioc->transport_cmds.mutex);
832bd37e28SSreekanth Reddy 
842bd37e28SSreekanth Reddy out:
852bd37e28SSreekanth Reddy 	return retval;
862bd37e28SSreekanth Reddy }
872bd37e28SSreekanth Reddy 
882bd37e28SSreekanth Reddy /* report manufacture request structure */
892bd37e28SSreekanth Reddy struct rep_manu_request {
902bd37e28SSreekanth Reddy 	u8 smp_frame_type;
912bd37e28SSreekanth Reddy 	u8 function;
922bd37e28SSreekanth Reddy 	u8 reserved;
932bd37e28SSreekanth Reddy 	u8 request_length;
942bd37e28SSreekanth Reddy };
952bd37e28SSreekanth Reddy 
962bd37e28SSreekanth Reddy /* report manufacture reply structure */
972bd37e28SSreekanth Reddy struct rep_manu_reply {
982bd37e28SSreekanth Reddy 	u8 smp_frame_type; /* 0x41 */
992bd37e28SSreekanth Reddy 	u8 function; /* 0x01 */
1002bd37e28SSreekanth Reddy 	u8 function_result;
1012bd37e28SSreekanth Reddy 	u8 response_length;
1022bd37e28SSreekanth Reddy 	u16 expander_change_count;
1032bd37e28SSreekanth Reddy 	u8 reserved0[2];
1042bd37e28SSreekanth Reddy 	u8 sas_format;
1052bd37e28SSreekanth Reddy 	u8 reserved2[3];
1062bd37e28SSreekanth Reddy 	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
1072bd37e28SSreekanth Reddy 	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
1082bd37e28SSreekanth Reddy 	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
1092bd37e28SSreekanth Reddy 	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
1102bd37e28SSreekanth Reddy 	u16 component_id;
1112bd37e28SSreekanth Reddy 	u8 component_revision_id;
1122bd37e28SSreekanth Reddy 	u8 reserved3;
1132bd37e28SSreekanth Reddy 	u8 vendor_specific[8];
1142bd37e28SSreekanth Reddy };
1152bd37e28SSreekanth Reddy 
1162bd37e28SSreekanth Reddy /**
1172bd37e28SSreekanth Reddy  * mpi3mr_report_manufacture - obtain SMP report_manufacture
1182bd37e28SSreekanth Reddy  * @mrioc: Adapter instance reference
1192bd37e28SSreekanth Reddy  * @sas_address: SAS address of the expander device
1202bd37e28SSreekanth Reddy  * @edev: SAS transport layer sas_expander_device object
1212bd37e28SSreekanth Reddy  * @port_id: ID of the HBA port
1222bd37e28SSreekanth Reddy  *
1232bd37e28SSreekanth Reddy  * Fills in the sas_expander_device with manufacturing info.
1242bd37e28SSreekanth Reddy  *
1252bd37e28SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
1262bd37e28SSreekanth Reddy  */
mpi3mr_report_manufacture(struct mpi3mr_ioc * mrioc,u64 sas_address,struct sas_expander_device * edev,u8 port_id)1272bd37e28SSreekanth Reddy static int mpi3mr_report_manufacture(struct mpi3mr_ioc *mrioc,
1282bd37e28SSreekanth Reddy 	u64 sas_address, struct sas_expander_device *edev, u8 port_id)
1292bd37e28SSreekanth Reddy {
1302bd37e28SSreekanth Reddy 	struct mpi3_smp_passthrough_request mpi_request;
1312bd37e28SSreekanth Reddy 	struct mpi3_smp_passthrough_reply mpi_reply;
1322bd37e28SSreekanth Reddy 	struct rep_manu_reply *manufacture_reply;
1332bd37e28SSreekanth Reddy 	struct rep_manu_request *manufacture_request;
1342bd37e28SSreekanth Reddy 	int rc = 0;
1352bd37e28SSreekanth Reddy 	void *psge;
1362bd37e28SSreekanth Reddy 	void *data_out = NULL;
1372bd37e28SSreekanth Reddy 	dma_addr_t data_out_dma;
1382bd37e28SSreekanth Reddy 	dma_addr_t data_in_dma;
1392bd37e28SSreekanth Reddy 	size_t data_in_sz;
1402bd37e28SSreekanth Reddy 	size_t data_out_sz;
1412bd37e28SSreekanth Reddy 	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
1422bd37e28SSreekanth Reddy 	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
1432bd37e28SSreekanth Reddy 	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
1442bd37e28SSreekanth Reddy 	u16 ioc_status;
145a113c02fSDan Carpenter 	u8 *tmp;
1462bd37e28SSreekanth Reddy 
1472bd37e28SSreekanth Reddy 	if (mrioc->reset_in_progress) {
1482bd37e28SSreekanth Reddy 		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
1492bd37e28SSreekanth Reddy 		return -EFAULT;
1502bd37e28SSreekanth Reddy 	}
1512bd37e28SSreekanth Reddy 
1522bd37e28SSreekanth Reddy 	data_out_sz = sizeof(struct rep_manu_request);
1532bd37e28SSreekanth Reddy 	data_in_sz = sizeof(struct rep_manu_reply);
1542bd37e28SSreekanth Reddy 	data_out = dma_alloc_coherent(&mrioc->pdev->dev,
1552bd37e28SSreekanth Reddy 	    data_out_sz + data_in_sz, &data_out_dma, GFP_KERNEL);
1562bd37e28SSreekanth Reddy 	if (!data_out) {
1572bd37e28SSreekanth Reddy 		rc = -ENOMEM;
1582bd37e28SSreekanth Reddy 		goto out;
1592bd37e28SSreekanth Reddy 	}
1602bd37e28SSreekanth Reddy 
1612bd37e28SSreekanth Reddy 	data_in_dma = data_out_dma + data_out_sz;
1622bd37e28SSreekanth Reddy 	manufacture_reply = data_out + data_out_sz;
1632bd37e28SSreekanth Reddy 
1642bd37e28SSreekanth Reddy 	manufacture_request = data_out;
1652bd37e28SSreekanth Reddy 	manufacture_request->smp_frame_type = 0x40;
1662bd37e28SSreekanth Reddy 	manufacture_request->function = 1;
1672bd37e28SSreekanth Reddy 	manufacture_request->reserved = 0;
1682bd37e28SSreekanth Reddy 	manufacture_request->request_length = 0;
1692bd37e28SSreekanth Reddy 
1702bd37e28SSreekanth Reddy 	memset(&mpi_request, 0, request_sz);
1712bd37e28SSreekanth Reddy 	memset(&mpi_reply, 0, reply_sz);
1722bd37e28SSreekanth Reddy 	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
1732bd37e28SSreekanth Reddy 	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
1742bd37e28SSreekanth Reddy 	mpi_request.io_unit_port = (u8) port_id;
1752bd37e28SSreekanth Reddy 	mpi_request.sas_address = cpu_to_le64(sas_address);
1762bd37e28SSreekanth Reddy 
1772bd37e28SSreekanth Reddy 	psge = &mpi_request.request_sge;
1782bd37e28SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
1792bd37e28SSreekanth Reddy 
1802bd37e28SSreekanth Reddy 	psge = &mpi_request.response_sge;
1812bd37e28SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
1822bd37e28SSreekanth Reddy 
1832bd37e28SSreekanth Reddy 	dprint_transport_info(mrioc,
1842bd37e28SSreekanth Reddy 	    "sending report manufacturer SMP request to sas_address(0x%016llx), port(%d)\n",
1852bd37e28SSreekanth Reddy 	    (unsigned long long)sas_address, port_id);
1862bd37e28SSreekanth Reddy 
187a113c02fSDan Carpenter 	rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
188a113c02fSDan Carpenter 				       &mpi_reply, reply_sz,
189a113c02fSDan Carpenter 				       MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
190a113c02fSDan Carpenter 	if (rc)
1912bd37e28SSreekanth Reddy 		goto out;
1922bd37e28SSreekanth Reddy 
1932bd37e28SSreekanth Reddy 	dprint_transport_info(mrioc,
1942bd37e28SSreekanth Reddy 	    "report manufacturer SMP request completed with ioc_status(0x%04x)\n",
1952bd37e28SSreekanth Reddy 	    ioc_status);
1962bd37e28SSreekanth Reddy 
197a113c02fSDan Carpenter 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
198a113c02fSDan Carpenter 		rc = -EINVAL;
199a113c02fSDan Carpenter 		goto out;
200a113c02fSDan Carpenter 	}
2012bd37e28SSreekanth Reddy 
2022bd37e28SSreekanth Reddy 	dprint_transport_info(mrioc,
2032bd37e28SSreekanth Reddy 	    "report manufacturer - reply data transfer size(%d)\n",
2042bd37e28SSreekanth Reddy 	    le16_to_cpu(mpi_reply.response_data_length));
2052bd37e28SSreekanth Reddy 
2062bd37e28SSreekanth Reddy 	if (le16_to_cpu(mpi_reply.response_data_length) !=
207a113c02fSDan Carpenter 	    sizeof(struct rep_manu_reply)) {
208a113c02fSDan Carpenter 		rc = -EINVAL;
2092bd37e28SSreekanth Reddy 		goto out;
210a113c02fSDan Carpenter 	}
2112bd37e28SSreekanth Reddy 
2122bd37e28SSreekanth Reddy 	strscpy(edev->vendor_id, manufacture_reply->vendor_id,
2132bd37e28SSreekanth Reddy 	     SAS_EXPANDER_VENDOR_ID_LEN);
2142bd37e28SSreekanth Reddy 	strscpy(edev->product_id, manufacture_reply->product_id,
2152bd37e28SSreekanth Reddy 	     SAS_EXPANDER_PRODUCT_ID_LEN);
2162bd37e28SSreekanth Reddy 	strscpy(edev->product_rev, manufacture_reply->product_rev,
2172bd37e28SSreekanth Reddy 	     SAS_EXPANDER_PRODUCT_REV_LEN);
2182bd37e28SSreekanth Reddy 	edev->level = manufacture_reply->sas_format & 1;
2192bd37e28SSreekanth Reddy 	if (edev->level) {
2202bd37e28SSreekanth Reddy 		strscpy(edev->component_vendor_id,
2212bd37e28SSreekanth Reddy 		    manufacture_reply->component_vendor_id,
2222bd37e28SSreekanth Reddy 		     SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
2232bd37e28SSreekanth Reddy 		tmp = (u8 *)&manufacture_reply->component_id;
2242bd37e28SSreekanth Reddy 		edev->component_id = tmp[0] << 8 | tmp[1];
2252bd37e28SSreekanth Reddy 		edev->component_revision_id =
2262bd37e28SSreekanth Reddy 		    manufacture_reply->component_revision_id;
2272bd37e28SSreekanth Reddy 	}
2282bd37e28SSreekanth Reddy 
2292bd37e28SSreekanth Reddy out:
2302bd37e28SSreekanth Reddy 	if (data_out)
2312bd37e28SSreekanth Reddy 		dma_free_coherent(&mrioc->pdev->dev, data_out_sz + data_in_sz,
2322bd37e28SSreekanth Reddy 		    data_out, data_out_dma);
2332bd37e28SSreekanth Reddy 
2342bd37e28SSreekanth Reddy 	return rc;
2352bd37e28SSreekanth Reddy }
2362bd37e28SSreekanth Reddy 
2372bd37e28SSreekanth Reddy /**
238125ad1e6SSreekanth Reddy  * __mpi3mr_expander_find_by_handle - expander search by handle
239125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
240125ad1e6SSreekanth Reddy  * @handle: Firmware device handle of the expander
241125ad1e6SSreekanth Reddy  *
242125ad1e6SSreekanth Reddy  * Context: The caller should acquire sas_node_lock
243125ad1e6SSreekanth Reddy  *
244125ad1e6SSreekanth Reddy  * This searches for expander device based on handle, then
245125ad1e6SSreekanth Reddy  * returns the sas_node object.
246125ad1e6SSreekanth Reddy  *
247125ad1e6SSreekanth Reddy  * Return: Expander sas_node object reference or NULL
248125ad1e6SSreekanth Reddy  */
__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc * mrioc,u16 handle)249e22bae30SSreekanth Reddy struct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc
250125ad1e6SSreekanth Reddy 	*mrioc, u16 handle)
251125ad1e6SSreekanth Reddy {
252125ad1e6SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander, *r;
253125ad1e6SSreekanth Reddy 
254125ad1e6SSreekanth Reddy 	r = NULL;
255125ad1e6SSreekanth Reddy 	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
256125ad1e6SSreekanth Reddy 		if (sas_expander->handle != handle)
257125ad1e6SSreekanth Reddy 			continue;
258125ad1e6SSreekanth Reddy 		r = sas_expander;
259125ad1e6SSreekanth Reddy 		goto out;
260125ad1e6SSreekanth Reddy 	}
261125ad1e6SSreekanth Reddy  out:
262125ad1e6SSreekanth Reddy 	return r;
263125ad1e6SSreekanth Reddy }
264125ad1e6SSreekanth Reddy 
265125ad1e6SSreekanth Reddy /**
266125ad1e6SSreekanth Reddy  * mpi3mr_is_expander_device - if device is an expander
267125ad1e6SSreekanth Reddy  * @device_info: Bitfield providing information about the device
268125ad1e6SSreekanth Reddy  *
269125ad1e6SSreekanth Reddy  * Return: 1 if the device is expander device, else 0.
270125ad1e6SSreekanth Reddy  */
mpi3mr_is_expander_device(u16 device_info)271125ad1e6SSreekanth Reddy u8 mpi3mr_is_expander_device(u16 device_info)
272125ad1e6SSreekanth Reddy {
273125ad1e6SSreekanth Reddy 	if ((device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) ==
274125ad1e6SSreekanth Reddy 	     MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER)
275125ad1e6SSreekanth Reddy 		return 1;
276125ad1e6SSreekanth Reddy 	else
277125ad1e6SSreekanth Reddy 		return 0;
278125ad1e6SSreekanth Reddy }
279125ad1e6SSreekanth Reddy 
280125ad1e6SSreekanth Reddy /**
281125ad1e6SSreekanth Reddy  * mpi3mr_get_sas_address - retrieve sas_address for handle
282125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
283125ad1e6SSreekanth Reddy  * @handle: Firmware device handle
284125ad1e6SSreekanth Reddy  * @sas_address: Address to hold sas address
285125ad1e6SSreekanth Reddy  *
286125ad1e6SSreekanth Reddy  * This function issues device page0 read for a given device
287125ad1e6SSreekanth Reddy  * handle and gets the SAS address and return it back
288125ad1e6SSreekanth Reddy  *
289125ad1e6SSreekanth Reddy  * Return: 0 for success, non-zero for failure
290125ad1e6SSreekanth Reddy  */
mpi3mr_get_sas_address(struct mpi3mr_ioc * mrioc,u16 handle,u64 * sas_address)291125ad1e6SSreekanth Reddy static int mpi3mr_get_sas_address(struct mpi3mr_ioc *mrioc, u16 handle,
292125ad1e6SSreekanth Reddy 	u64 *sas_address)
293125ad1e6SSreekanth Reddy {
294125ad1e6SSreekanth Reddy 	struct mpi3_device_page0 dev_pg0;
295125ad1e6SSreekanth Reddy 	u16 ioc_status;
296125ad1e6SSreekanth Reddy 	struct mpi3_device0_sas_sata_format *sasinf;
297125ad1e6SSreekanth Reddy 
298125ad1e6SSreekanth Reddy 	*sas_address = 0;
299125ad1e6SSreekanth Reddy 
300125ad1e6SSreekanth Reddy 	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
301125ad1e6SSreekanth Reddy 	    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
302125ad1e6SSreekanth Reddy 	    handle))) {
303125ad1e6SSreekanth Reddy 		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
304125ad1e6SSreekanth Reddy 		return -ENXIO;
305125ad1e6SSreekanth Reddy 	}
306125ad1e6SSreekanth Reddy 
307125ad1e6SSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
308125ad1e6SSreekanth Reddy 		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
309125ad1e6SSreekanth Reddy 		    handle, ioc_status, __FILE__, __LINE__, __func__);
310125ad1e6SSreekanth Reddy 		return -ENXIO;
311125ad1e6SSreekanth Reddy 	}
312125ad1e6SSreekanth Reddy 
313125ad1e6SSreekanth Reddy 	if (le16_to_cpu(dev_pg0.flags) &
314125ad1e6SSreekanth Reddy 	    MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE)
315125ad1e6SSreekanth Reddy 		*sas_address = mrioc->sas_hba.sas_address;
316125ad1e6SSreekanth Reddy 	else if (dev_pg0.device_form == MPI3_DEVICE_DEVFORM_SAS_SATA) {
317125ad1e6SSreekanth Reddy 		sasinf = &dev_pg0.device_specific.sas_sata_format;
318125ad1e6SSreekanth Reddy 		*sas_address = le64_to_cpu(sasinf->sas_address);
319125ad1e6SSreekanth Reddy 	} else {
320125ad1e6SSreekanth Reddy 		ioc_err(mrioc, "%s: device_form(%d) is not SAS_SATA\n",
321125ad1e6SSreekanth Reddy 		    __func__, dev_pg0.device_form);
322125ad1e6SSreekanth Reddy 		return -ENXIO;
323125ad1e6SSreekanth Reddy 	}
324125ad1e6SSreekanth Reddy 	return 0;
325125ad1e6SSreekanth Reddy }
326125ad1e6SSreekanth Reddy 
327125ad1e6SSreekanth Reddy /**
328125ad1e6SSreekanth Reddy  * __mpi3mr_get_tgtdev_by_addr - target device search
329125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
330125ad1e6SSreekanth Reddy  * @sas_address: SAS address of the device
331125ad1e6SSreekanth Reddy  * @hba_port: HBA port entry
332125ad1e6SSreekanth Reddy  *
333125ad1e6SSreekanth Reddy  * This searches for target device from sas address and hba port
334125ad1e6SSreekanth Reddy  * pointer then return mpi3mr_tgt_dev object.
335125ad1e6SSreekanth Reddy  *
336125ad1e6SSreekanth Reddy  * Return: Valid tget_dev or NULL
337125ad1e6SSreekanth Reddy  */
__mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)338125ad1e6SSreekanth Reddy static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
339125ad1e6SSreekanth Reddy 	u64 sas_address, struct mpi3mr_hba_port *hba_port)
340125ad1e6SSreekanth Reddy {
341125ad1e6SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev;
342125ad1e6SSreekanth Reddy 
343125ad1e6SSreekanth Reddy 	assert_spin_locked(&mrioc->tgtdev_lock);
344125ad1e6SSreekanth Reddy 
345125ad1e6SSreekanth Reddy 	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
346125ad1e6SSreekanth Reddy 		if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
347125ad1e6SSreekanth Reddy 		    (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
348125ad1e6SSreekanth Reddy 		    && (tgtdev->dev_spec.sas_sata_inf.hba_port == hba_port))
349125ad1e6SSreekanth Reddy 			goto found_device;
350125ad1e6SSreekanth Reddy 	return NULL;
351125ad1e6SSreekanth Reddy found_device:
352125ad1e6SSreekanth Reddy 	mpi3mr_tgtdev_get(tgtdev);
353125ad1e6SSreekanth Reddy 	return tgtdev;
354125ad1e6SSreekanth Reddy }
355125ad1e6SSreekanth Reddy 
356125ad1e6SSreekanth Reddy /**
357125ad1e6SSreekanth Reddy  * mpi3mr_get_tgtdev_by_addr - target device search
358125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
359125ad1e6SSreekanth Reddy  * @sas_address: SAS address of the device
360125ad1e6SSreekanth Reddy  * @hba_port: HBA port entry
361125ad1e6SSreekanth Reddy  *
362125ad1e6SSreekanth Reddy  * This searches for target device from sas address and hba port
363125ad1e6SSreekanth Reddy  * pointer then return mpi3mr_tgt_dev object.
364125ad1e6SSreekanth Reddy  *
365125ad1e6SSreekanth Reddy  * Context: This function will acquire tgtdev_lock and will
366125ad1e6SSreekanth Reddy  * release before returning the mpi3mr_tgt_dev object.
367125ad1e6SSreekanth Reddy  *
368125ad1e6SSreekanth Reddy  * Return: Valid tget_dev or NULL
369125ad1e6SSreekanth Reddy  */
mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)370125ad1e6SSreekanth Reddy static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
371125ad1e6SSreekanth Reddy 	u64 sas_address, struct mpi3mr_hba_port *hba_port)
372125ad1e6SSreekanth Reddy {
373125ad1e6SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev = NULL;
374125ad1e6SSreekanth Reddy 	unsigned long flags;
375125ad1e6SSreekanth Reddy 
376125ad1e6SSreekanth Reddy 	if (!hba_port)
377125ad1e6SSreekanth Reddy 		goto out;
378125ad1e6SSreekanth Reddy 
379125ad1e6SSreekanth Reddy 	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
380125ad1e6SSreekanth Reddy 	tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc, sas_address, hba_port);
381125ad1e6SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
382125ad1e6SSreekanth Reddy 
383125ad1e6SSreekanth Reddy out:
384125ad1e6SSreekanth Reddy 	return tgtdev;
385125ad1e6SSreekanth Reddy }
386125ad1e6SSreekanth Reddy 
387125ad1e6SSreekanth Reddy /**
388e22bae30SSreekanth Reddy  * mpi3mr_remove_device_by_sas_address - remove the device
389e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
390e22bae30SSreekanth Reddy  * @sas_address: SAS address of the device
391e22bae30SSreekanth Reddy  * @hba_port: HBA port entry
392e22bae30SSreekanth Reddy  *
393e22bae30SSreekanth Reddy  * This searches for target device using sas address and hba
394e22bae30SSreekanth Reddy  * port pointer then removes it from the OS.
395e22bae30SSreekanth Reddy  *
396e22bae30SSreekanth Reddy  * Return: None
397e22bae30SSreekanth Reddy  */
mpi3mr_remove_device_by_sas_address(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)398e22bae30SSreekanth Reddy static void mpi3mr_remove_device_by_sas_address(struct mpi3mr_ioc *mrioc,
399e22bae30SSreekanth Reddy 	u64 sas_address, struct mpi3mr_hba_port *hba_port)
400e22bae30SSreekanth Reddy {
401e22bae30SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev = NULL;
402e22bae30SSreekanth Reddy 	unsigned long flags;
403e22bae30SSreekanth Reddy 	u8 was_on_tgtdev_list = 0;
404e22bae30SSreekanth Reddy 
405e22bae30SSreekanth Reddy 	if (!hba_port)
406e22bae30SSreekanth Reddy 		return;
407e22bae30SSreekanth Reddy 
408e22bae30SSreekanth Reddy 	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
409e22bae30SSreekanth Reddy 	tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc,
410e22bae30SSreekanth Reddy 			 sas_address, hba_port);
411e22bae30SSreekanth Reddy 	if (tgtdev) {
412e22bae30SSreekanth Reddy 		if (!list_empty(&tgtdev->list)) {
413e22bae30SSreekanth Reddy 			list_del_init(&tgtdev->list);
414e22bae30SSreekanth Reddy 			was_on_tgtdev_list = 1;
415e22bae30SSreekanth Reddy 			mpi3mr_tgtdev_put(tgtdev);
416e22bae30SSreekanth Reddy 		}
417e22bae30SSreekanth Reddy 	}
418e22bae30SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
419e22bae30SSreekanth Reddy 	if (was_on_tgtdev_list) {
420e22bae30SSreekanth Reddy 		if (tgtdev->host_exposed)
421e22bae30SSreekanth Reddy 			mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
422e22bae30SSreekanth Reddy 		mpi3mr_tgtdev_put(tgtdev);
423e22bae30SSreekanth Reddy 	}
424e22bae30SSreekanth Reddy }
425e22bae30SSreekanth Reddy 
426e22bae30SSreekanth Reddy /**
427626665e9SSreekanth Reddy  * __mpi3mr_get_tgtdev_by_addr_and_rphy - target device search
428626665e9SSreekanth Reddy  * @mrioc: Adapter instance reference
429626665e9SSreekanth Reddy  * @sas_address: SAS address of the device
430626665e9SSreekanth Reddy  * @rphy: SAS transport layer rphy object
431626665e9SSreekanth Reddy  *
432626665e9SSreekanth Reddy  * This searches for target device from sas address and rphy
433626665e9SSreekanth Reddy  * pointer then return mpi3mr_tgt_dev object.
434626665e9SSreekanth Reddy  *
435626665e9SSreekanth Reddy  * Return: Valid tget_dev or NULL
436626665e9SSreekanth Reddy  */
__mpi3mr_get_tgtdev_by_addr_and_rphy(struct mpi3mr_ioc * mrioc,u64 sas_address,struct sas_rphy * rphy)437626665e9SSreekanth Reddy struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr_and_rphy(
438626665e9SSreekanth Reddy 	struct mpi3mr_ioc *mrioc, u64 sas_address, struct sas_rphy *rphy)
439626665e9SSreekanth Reddy {
440626665e9SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev;
441626665e9SSreekanth Reddy 
442626665e9SSreekanth Reddy 	assert_spin_locked(&mrioc->tgtdev_lock);
443626665e9SSreekanth Reddy 
444626665e9SSreekanth Reddy 	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
445626665e9SSreekanth Reddy 		if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
446626665e9SSreekanth Reddy 		    (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
447626665e9SSreekanth Reddy 		    && (tgtdev->dev_spec.sas_sata_inf.rphy == rphy))
448626665e9SSreekanth Reddy 			goto found_device;
449626665e9SSreekanth Reddy 	return NULL;
450626665e9SSreekanth Reddy found_device:
451626665e9SSreekanth Reddy 	mpi3mr_tgtdev_get(tgtdev);
452626665e9SSreekanth Reddy 	return tgtdev;
453626665e9SSreekanth Reddy }
454626665e9SSreekanth Reddy 
455626665e9SSreekanth Reddy /**
456125ad1e6SSreekanth Reddy  * mpi3mr_expander_find_by_sas_address - sas expander search
457125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
458125ad1e6SSreekanth Reddy  * @sas_address: SAS address of expander
459125ad1e6SSreekanth Reddy  * @hba_port: HBA port entry
460125ad1e6SSreekanth Reddy  *
461125ad1e6SSreekanth Reddy  * Return: A valid SAS expander node or NULL.
462125ad1e6SSreekanth Reddy  *
463125ad1e6SSreekanth Reddy  */
mpi3mr_expander_find_by_sas_address(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)464125ad1e6SSreekanth Reddy static struct mpi3mr_sas_node *mpi3mr_expander_find_by_sas_address(
465125ad1e6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc, u64 sas_address,
466125ad1e6SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port)
467125ad1e6SSreekanth Reddy {
468125ad1e6SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander, *r = NULL;
469125ad1e6SSreekanth Reddy 
470125ad1e6SSreekanth Reddy 	if (!hba_port)
471125ad1e6SSreekanth Reddy 		goto out;
472125ad1e6SSreekanth Reddy 
473125ad1e6SSreekanth Reddy 	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
474125ad1e6SSreekanth Reddy 		if ((sas_expander->sas_address != sas_address) ||
475125ad1e6SSreekanth Reddy 					 (sas_expander->hba_port != hba_port))
476125ad1e6SSreekanth Reddy 			continue;
477125ad1e6SSreekanth Reddy 		r = sas_expander;
478125ad1e6SSreekanth Reddy 		goto out;
479125ad1e6SSreekanth Reddy 	}
480125ad1e6SSreekanth Reddy out:
481125ad1e6SSreekanth Reddy 	return r;
482125ad1e6SSreekanth Reddy }
483125ad1e6SSreekanth Reddy 
484125ad1e6SSreekanth Reddy /**
485125ad1e6SSreekanth Reddy  * __mpi3mr_sas_node_find_by_sas_address - sas node search
486125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
487125ad1e6SSreekanth Reddy  * @sas_address: SAS address of expander or sas host
488125ad1e6SSreekanth Reddy  * @hba_port: HBA port entry
489125ad1e6SSreekanth Reddy  * Context: Caller should acquire mrioc->sas_node_lock.
490125ad1e6SSreekanth Reddy  *
491125ad1e6SSreekanth Reddy  * If the SAS address indicates the device is direct attached to
492125ad1e6SSreekanth Reddy  * the controller (controller's SAS address) then the SAS node
493125ad1e6SSreekanth Reddy  * associated with the controller is returned back else the SAS
494125ad1e6SSreekanth Reddy  * address and hba port are used to identify the exact expander
495125ad1e6SSreekanth Reddy  * and the associated sas_node object is returned. If there is
496125ad1e6SSreekanth Reddy  * no match NULL is returned.
497125ad1e6SSreekanth Reddy  *
498125ad1e6SSreekanth Reddy  * Return: A valid SAS node or NULL.
499125ad1e6SSreekanth Reddy  *
500125ad1e6SSreekanth Reddy  */
__mpi3mr_sas_node_find_by_sas_address(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)501125ad1e6SSreekanth Reddy static struct mpi3mr_sas_node *__mpi3mr_sas_node_find_by_sas_address(
502125ad1e6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc, u64 sas_address,
503125ad1e6SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port)
504125ad1e6SSreekanth Reddy {
505125ad1e6SSreekanth Reddy 
506125ad1e6SSreekanth Reddy 	if (mrioc->sas_hba.sas_address == sas_address)
507125ad1e6SSreekanth Reddy 		return &mrioc->sas_hba;
508125ad1e6SSreekanth Reddy 	return mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
509125ad1e6SSreekanth Reddy 	    hba_port);
510125ad1e6SSreekanth Reddy }
511125ad1e6SSreekanth Reddy 
512125ad1e6SSreekanth Reddy /**
513125ad1e6SSreekanth Reddy  * mpi3mr_parent_present - Is parent present for a phy
514125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
515125ad1e6SSreekanth Reddy  * @phy: SAS transport layer phy object
516125ad1e6SSreekanth Reddy  *
517125ad1e6SSreekanth Reddy  * Return: 0 if parent is present else non-zero
518125ad1e6SSreekanth Reddy  */
mpi3mr_parent_present(struct mpi3mr_ioc * mrioc,struct sas_phy * phy)519125ad1e6SSreekanth Reddy static int mpi3mr_parent_present(struct mpi3mr_ioc *mrioc, struct sas_phy *phy)
520125ad1e6SSreekanth Reddy {
521125ad1e6SSreekanth Reddy 	unsigned long flags;
522125ad1e6SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port = phy->hostdata;
523125ad1e6SSreekanth Reddy 
524125ad1e6SSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
525125ad1e6SSreekanth Reddy 	if (__mpi3mr_sas_node_find_by_sas_address(mrioc,
526125ad1e6SSreekanth Reddy 	    phy->identify.sas_address,
527125ad1e6SSreekanth Reddy 	    hba_port) == NULL) {
528125ad1e6SSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
529125ad1e6SSreekanth Reddy 		return -1;
530125ad1e6SSreekanth Reddy 	}
531125ad1e6SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
532125ad1e6SSreekanth Reddy 	return 0;
533125ad1e6SSreekanth Reddy }
534125ad1e6SSreekanth Reddy 
535125ad1e6SSreekanth Reddy /**
536fc7212fdSSreekanth Reddy  * mpi3mr_convert_phy_link_rate -
537fc7212fdSSreekanth Reddy  * @link_rate: link rate as defined in the MPI header
538fc7212fdSSreekanth Reddy  *
539fc7212fdSSreekanth Reddy  * Convert link_rate from mpi format into sas_transport layer
540fc7212fdSSreekanth Reddy  * form.
541fc7212fdSSreekanth Reddy  *
542fc7212fdSSreekanth Reddy  * Return: A valid SAS transport layer defined link rate
543fc7212fdSSreekanth Reddy  */
mpi3mr_convert_phy_link_rate(u8 link_rate)544fc7212fdSSreekanth Reddy static enum sas_linkrate mpi3mr_convert_phy_link_rate(u8 link_rate)
545fc7212fdSSreekanth Reddy {
546fc7212fdSSreekanth Reddy 	enum sas_linkrate rc;
547fc7212fdSSreekanth Reddy 
548fc7212fdSSreekanth Reddy 	switch (link_rate) {
549fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_1_5:
550fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_1_5_GBPS;
551fc7212fdSSreekanth Reddy 		break;
552fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_3_0:
553fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_3_0_GBPS;
554fc7212fdSSreekanth Reddy 		break;
555fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_6_0:
556fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_6_0_GBPS;
557fc7212fdSSreekanth Reddy 		break;
558fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_12_0:
559fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_12_0_GBPS;
560fc7212fdSSreekanth Reddy 		break;
561fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_22_5:
562fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_22_5_GBPS;
563fc7212fdSSreekanth Reddy 		break;
564fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED:
565fc7212fdSSreekanth Reddy 		rc = SAS_PHY_DISABLED;
566fc7212fdSSreekanth Reddy 		break;
567fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
568fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_FAILED;
569fc7212fdSSreekanth Reddy 		break;
570fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR:
571fc7212fdSSreekanth Reddy 		rc = SAS_SATA_PORT_SELECTOR;
572fc7212fdSSreekanth Reddy 		break;
573fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
574fc7212fdSSreekanth Reddy 		rc = SAS_PHY_RESET_IN_PROGRESS;
575fc7212fdSSreekanth Reddy 		break;
576fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
577fc7212fdSSreekanth Reddy 	case MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
578fc7212fdSSreekanth Reddy 	default:
579fc7212fdSSreekanth Reddy 		rc = SAS_LINK_RATE_UNKNOWN;
580fc7212fdSSreekanth Reddy 		break;
581fc7212fdSSreekanth Reddy 	}
582fc7212fdSSreekanth Reddy 	return rc;
583fc7212fdSSreekanth Reddy }
584fc7212fdSSreekanth Reddy 
585fc7212fdSSreekanth Reddy /**
586fc7212fdSSreekanth Reddy  * mpi3mr_delete_sas_phy - Remove a single phy from port
587fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
588fc7212fdSSreekanth Reddy  * @mr_sas_port: Internal Port object
589fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object
590fc7212fdSSreekanth Reddy  *
591fc7212fdSSreekanth Reddy  * Return: None.
592fc7212fdSSreekanth Reddy  */
mpi3mr_delete_sas_phy(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_port * mr_sas_port,struct mpi3mr_sas_phy * mr_sas_phy)593fc7212fdSSreekanth Reddy static void mpi3mr_delete_sas_phy(struct mpi3mr_ioc *mrioc,
594fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port,
595fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy)
596fc7212fdSSreekanth Reddy {
597fc7212fdSSreekanth Reddy 	u64 sas_address = mr_sas_port->remote_identify.sas_address;
598fc7212fdSSreekanth Reddy 
599fc7212fdSSreekanth Reddy 	dev_info(&mr_sas_phy->phy->dev,
600fc7212fdSSreekanth Reddy 	    "remove: sas_address(0x%016llx), phy(%d)\n",
601fc7212fdSSreekanth Reddy 	    (unsigned long long) sas_address, mr_sas_phy->phy_id);
602fc7212fdSSreekanth Reddy 
603fc7212fdSSreekanth Reddy 	list_del(&mr_sas_phy->port_siblings);
604fc7212fdSSreekanth Reddy 	mr_sas_port->num_phys--;
6052745ce0eSSreekanth Reddy 	mr_sas_port->phy_mask &= ~(1 << mr_sas_phy->phy_id);
6062745ce0eSSreekanth Reddy 	if (mr_sas_port->lowest_phy == mr_sas_phy->phy_id)
6072745ce0eSSreekanth Reddy 		mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
608fc7212fdSSreekanth Reddy 	sas_port_delete_phy(mr_sas_port->port, mr_sas_phy->phy);
609fc7212fdSSreekanth Reddy 	mr_sas_phy->phy_belongs_to_port = 0;
610fc7212fdSSreekanth Reddy }
611fc7212fdSSreekanth Reddy 
612fc7212fdSSreekanth Reddy /**
613fc7212fdSSreekanth Reddy  * mpi3mr_add_sas_phy - Adding a single phy to a port
614fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
615fc7212fdSSreekanth Reddy  * @mr_sas_port: Internal Port object
616fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object
617fc7212fdSSreekanth Reddy  *
618fc7212fdSSreekanth Reddy  * Return: None.
619fc7212fdSSreekanth Reddy  */
mpi3mr_add_sas_phy(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_port * mr_sas_port,struct mpi3mr_sas_phy * mr_sas_phy)620fc7212fdSSreekanth Reddy static void mpi3mr_add_sas_phy(struct mpi3mr_ioc *mrioc,
621fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port,
622fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy)
623fc7212fdSSreekanth Reddy {
624fc7212fdSSreekanth Reddy 	u64 sas_address = mr_sas_port->remote_identify.sas_address;
625fc7212fdSSreekanth Reddy 
626fc7212fdSSreekanth Reddy 	dev_info(&mr_sas_phy->phy->dev,
627fc7212fdSSreekanth Reddy 	    "add: sas_address(0x%016llx), phy(%d)\n", (unsigned long long)
628fc7212fdSSreekanth Reddy 	    sas_address, mr_sas_phy->phy_id);
629fc7212fdSSreekanth Reddy 
630fc7212fdSSreekanth Reddy 	list_add_tail(&mr_sas_phy->port_siblings, &mr_sas_port->phy_list);
631fc7212fdSSreekanth Reddy 	mr_sas_port->num_phys++;
6322745ce0eSSreekanth Reddy 	mr_sas_port->phy_mask |= (1 << mr_sas_phy->phy_id);
6332745ce0eSSreekanth Reddy 	if (mr_sas_phy->phy_id < mr_sas_port->lowest_phy)
6342745ce0eSSreekanth Reddy 		mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
635fc7212fdSSreekanth Reddy 	sas_port_add_phy(mr_sas_port->port, mr_sas_phy->phy);
636fc7212fdSSreekanth Reddy 	mr_sas_phy->phy_belongs_to_port = 1;
637fc7212fdSSreekanth Reddy }
638fc7212fdSSreekanth Reddy 
639fc7212fdSSreekanth Reddy /**
640fc7212fdSSreekanth Reddy  * mpi3mr_add_phy_to_an_existing_port - add phy to existing port
641fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
642fc7212fdSSreekanth Reddy  * @mr_sas_node: Internal sas node object (expander or host)
643fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object *
644fc7212fdSSreekanth Reddy  * @sas_address: SAS address of device/expander were phy needs
645fc7212fdSSreekanth Reddy  *             to be added to
646fc7212fdSSreekanth Reddy  * @hba_port: HBA port entry
647fc7212fdSSreekanth Reddy  *
648fc7212fdSSreekanth Reddy  * Return: None.
649fc7212fdSSreekanth Reddy  */
mpi3mr_add_phy_to_an_existing_port(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_node * mr_sas_node,struct mpi3mr_sas_phy * mr_sas_phy,u64 sas_address,struct mpi3mr_hba_port * hba_port)650fc7212fdSSreekanth Reddy static void mpi3mr_add_phy_to_an_existing_port(struct mpi3mr_ioc *mrioc,
651fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy,
652fc7212fdSSreekanth Reddy 	u64 sas_address, struct mpi3mr_hba_port *hba_port)
653fc7212fdSSreekanth Reddy {
654fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port;
655fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *srch_phy;
656fc7212fdSSreekanth Reddy 
657fc7212fdSSreekanth Reddy 	if (mr_sas_phy->phy_belongs_to_port == 1)
658fc7212fdSSreekanth Reddy 		return;
659fc7212fdSSreekanth Reddy 
660fc7212fdSSreekanth Reddy 	if (!hba_port)
661fc7212fdSSreekanth Reddy 		return;
662fc7212fdSSreekanth Reddy 
663fc7212fdSSreekanth Reddy 	list_for_each_entry(mr_sas_port, &mr_sas_node->sas_port_list,
664fc7212fdSSreekanth Reddy 	    port_list) {
665fc7212fdSSreekanth Reddy 		if (mr_sas_port->remote_identify.sas_address !=
666fc7212fdSSreekanth Reddy 		    sas_address)
667fc7212fdSSreekanth Reddy 			continue;
668fc7212fdSSreekanth Reddy 		if (mr_sas_port->hba_port != hba_port)
669fc7212fdSSreekanth Reddy 			continue;
670fc7212fdSSreekanth Reddy 		list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
671fc7212fdSSreekanth Reddy 		    port_siblings) {
672fc7212fdSSreekanth Reddy 			if (srch_phy == mr_sas_phy)
673fc7212fdSSreekanth Reddy 				return;
674fc7212fdSSreekanth Reddy 		}
675fc7212fdSSreekanth Reddy 		mpi3mr_add_sas_phy(mrioc, mr_sas_port, mr_sas_phy);
676fc7212fdSSreekanth Reddy 		return;
677fc7212fdSSreekanth Reddy 	}
678fc7212fdSSreekanth Reddy }
679fc7212fdSSreekanth Reddy 
680fc7212fdSSreekanth Reddy /**
681e22bae30SSreekanth Reddy  * mpi3mr_delete_sas_port - helper function to removing a port
682e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
683e22bae30SSreekanth Reddy  * @mr_sas_port: Internal Port object
684e22bae30SSreekanth Reddy  *
685e22bae30SSreekanth Reddy  * Return: None.
686e22bae30SSreekanth Reddy  */
mpi3mr_delete_sas_port(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_port * mr_sas_port)687e22bae30SSreekanth Reddy static void  mpi3mr_delete_sas_port(struct mpi3mr_ioc *mrioc,
688e22bae30SSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port)
689e22bae30SSreekanth Reddy {
690e22bae30SSreekanth Reddy 	u64 sas_address = mr_sas_port->remote_identify.sas_address;
691e22bae30SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port = mr_sas_port->hba_port;
692e22bae30SSreekanth Reddy 	enum sas_device_type device_type =
693e22bae30SSreekanth Reddy 	    mr_sas_port->remote_identify.device_type;
694e22bae30SSreekanth Reddy 
695e22bae30SSreekanth Reddy 	dev_info(&mr_sas_port->port->dev,
696e22bae30SSreekanth Reddy 	    "remove: sas_address(0x%016llx)\n",
697e22bae30SSreekanth Reddy 	    (unsigned long long) sas_address);
698e22bae30SSreekanth Reddy 
699e22bae30SSreekanth Reddy 	if (device_type == SAS_END_DEVICE)
700e22bae30SSreekanth Reddy 		mpi3mr_remove_device_by_sas_address(mrioc, sas_address,
701e22bae30SSreekanth Reddy 		    hba_port);
702e22bae30SSreekanth Reddy 
703e22bae30SSreekanth Reddy 	else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
704e22bae30SSreekanth Reddy 	    device_type == SAS_FANOUT_EXPANDER_DEVICE)
705e22bae30SSreekanth Reddy 		mpi3mr_expander_remove(mrioc, sas_address, hba_port);
706e22bae30SSreekanth Reddy }
707e22bae30SSreekanth Reddy 
708e22bae30SSreekanth Reddy /**
709fc7212fdSSreekanth Reddy  * mpi3mr_del_phy_from_an_existing_port - del phy from a port
710fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
711fc7212fdSSreekanth Reddy  * @mr_sas_node: Internal sas node object (expander or host)
712fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object
713fc7212fdSSreekanth Reddy  *
714fc7212fdSSreekanth Reddy  * Return: None.
715fc7212fdSSreekanth Reddy  */
mpi3mr_del_phy_from_an_existing_port(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_node * mr_sas_node,struct mpi3mr_sas_phy * mr_sas_phy)716fc7212fdSSreekanth Reddy static void mpi3mr_del_phy_from_an_existing_port(struct mpi3mr_ioc *mrioc,
717fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy)
718fc7212fdSSreekanth Reddy {
719fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port, *next;
720fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *srch_phy;
721fc7212fdSSreekanth Reddy 
722fc7212fdSSreekanth Reddy 	if (mr_sas_phy->phy_belongs_to_port == 0)
723fc7212fdSSreekanth Reddy 		return;
724fc7212fdSSreekanth Reddy 
725fc7212fdSSreekanth Reddy 	list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
726fc7212fdSSreekanth Reddy 	    port_list) {
727fc7212fdSSreekanth Reddy 		list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
728fc7212fdSSreekanth Reddy 		    port_siblings) {
729fc7212fdSSreekanth Reddy 			if (srch_phy != mr_sas_phy)
730fc7212fdSSreekanth Reddy 				continue;
731e22bae30SSreekanth Reddy 			if ((mr_sas_port->num_phys == 1) &&
732e22bae30SSreekanth Reddy 			    !mrioc->reset_in_progress)
733e22bae30SSreekanth Reddy 				mpi3mr_delete_sas_port(mrioc, mr_sas_port);
734e22bae30SSreekanth Reddy 			else
735fc7212fdSSreekanth Reddy 				mpi3mr_delete_sas_phy(mrioc, mr_sas_port,
736fc7212fdSSreekanth Reddy 				    mr_sas_phy);
737fc7212fdSSreekanth Reddy 			return;
738fc7212fdSSreekanth Reddy 		}
739fc7212fdSSreekanth Reddy 	}
740fc7212fdSSreekanth Reddy }
741fc7212fdSSreekanth Reddy 
742fc7212fdSSreekanth Reddy /**
743fc7212fdSSreekanth Reddy  * mpi3mr_sas_port_sanity_check - sanity check while adding port
744fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
745fc7212fdSSreekanth Reddy  * @mr_sas_node: Internal sas node object (expander or host)
746fc7212fdSSreekanth Reddy  * @sas_address: SAS address of device/expander
747fc7212fdSSreekanth Reddy  * @hba_port: HBA port entry
748fc7212fdSSreekanth Reddy  *
749fc7212fdSSreekanth Reddy  * Verifies whether the Phys attached to a device with the given
750fc7212fdSSreekanth Reddy  * SAS address already belongs to an existing sas port if so
751fc7212fdSSreekanth Reddy  * will remove those phys from the sas port
752fc7212fdSSreekanth Reddy  *
753fc7212fdSSreekanth Reddy  * Return: None.
754fc7212fdSSreekanth Reddy  */
mpi3mr_sas_port_sanity_check(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_node * mr_sas_node,u64 sas_address,struct mpi3mr_hba_port * hba_port)755fc7212fdSSreekanth Reddy static void mpi3mr_sas_port_sanity_check(struct mpi3mr_ioc *mrioc,
756fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node, u64 sas_address,
757fc7212fdSSreekanth Reddy 	struct mpi3mr_hba_port *hba_port)
758fc7212fdSSreekanth Reddy {
759fc7212fdSSreekanth Reddy 	int i;
760fc7212fdSSreekanth Reddy 
761fc7212fdSSreekanth Reddy 	for (i = 0; i < mr_sas_node->num_phys; i++) {
762fc7212fdSSreekanth Reddy 		if ((mr_sas_node->phy[i].remote_identify.sas_address !=
763fc7212fdSSreekanth Reddy 		    sas_address) || (mr_sas_node->phy[i].hba_port != hba_port))
764fc7212fdSSreekanth Reddy 			continue;
765fc7212fdSSreekanth Reddy 		if (mr_sas_node->phy[i].phy_belongs_to_port == 1)
766fc7212fdSSreekanth Reddy 			mpi3mr_del_phy_from_an_existing_port(mrioc,
767fc7212fdSSreekanth Reddy 			    mr_sas_node, &mr_sas_node->phy[i]);
768fc7212fdSSreekanth Reddy 	}
769fc7212fdSSreekanth Reddy }
770fc7212fdSSreekanth Reddy 
771fc7212fdSSreekanth Reddy /**
772fc7212fdSSreekanth Reddy  * mpi3mr_set_identify - set identify for phys and end devices
773fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
774fc7212fdSSreekanth Reddy  * @handle: Firmware device handle
775fc7212fdSSreekanth Reddy  * @identify: SAS transport layer's identify info
776fc7212fdSSreekanth Reddy  *
777fc7212fdSSreekanth Reddy  * Populates sas identify info for a specific device.
778fc7212fdSSreekanth Reddy  *
779fc7212fdSSreekanth Reddy  * Return: 0 for success, non-zero for failure.
780fc7212fdSSreekanth Reddy  */
mpi3mr_set_identify(struct mpi3mr_ioc * mrioc,u16 handle,struct sas_identify * identify)781fc7212fdSSreekanth Reddy static int mpi3mr_set_identify(struct mpi3mr_ioc *mrioc, u16 handle,
782fc7212fdSSreekanth Reddy 	struct sas_identify *identify)
783fc7212fdSSreekanth Reddy {
784fc7212fdSSreekanth Reddy 
785fc7212fdSSreekanth Reddy 	struct mpi3_device_page0 device_pg0;
786fc7212fdSSreekanth Reddy 	struct mpi3_device0_sas_sata_format *sasinf;
787fc7212fdSSreekanth Reddy 	u16 device_info;
788fc7212fdSSreekanth Reddy 	u16 ioc_status;
789fc7212fdSSreekanth Reddy 
790fc7212fdSSreekanth Reddy 	if (mrioc->reset_in_progress) {
791fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
792fc7212fdSSreekanth Reddy 		return -EFAULT;
793fc7212fdSSreekanth Reddy 	}
794fc7212fdSSreekanth Reddy 
795fc7212fdSSreekanth Reddy 	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &device_pg0,
796fc7212fdSSreekanth Reddy 	    sizeof(device_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE, handle))) {
797fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
798fc7212fdSSreekanth Reddy 		return -ENXIO;
799fc7212fdSSreekanth Reddy 	}
800fc7212fdSSreekanth Reddy 
801fc7212fdSSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
802fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
803fc7212fdSSreekanth Reddy 		    handle, ioc_status, __FILE__, __LINE__, __func__);
804fc7212fdSSreekanth Reddy 		return -EIO;
805fc7212fdSSreekanth Reddy 	}
806fc7212fdSSreekanth Reddy 
807fc7212fdSSreekanth Reddy 	memset(identify, 0, sizeof(struct sas_identify));
808fc7212fdSSreekanth Reddy 	sasinf = &device_pg0.device_specific.sas_sata_format;
809fc7212fdSSreekanth Reddy 	device_info = le16_to_cpu(sasinf->device_info);
810fc7212fdSSreekanth Reddy 
811fc7212fdSSreekanth Reddy 	/* sas_address */
812fc7212fdSSreekanth Reddy 	identify->sas_address = le64_to_cpu(sasinf->sas_address);
813fc7212fdSSreekanth Reddy 
814fc7212fdSSreekanth Reddy 	/* phy number of the parent device this device is linked to */
815fc7212fdSSreekanth Reddy 	identify->phy_identifier = sasinf->phy_num;
816fc7212fdSSreekanth Reddy 
817fc7212fdSSreekanth Reddy 	/* device_type */
818fc7212fdSSreekanth Reddy 	switch (device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) {
819fc7212fdSSreekanth Reddy 	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE:
820fc7212fdSSreekanth Reddy 		identify->device_type = SAS_PHY_UNUSED;
821fc7212fdSSreekanth Reddy 		break;
822fc7212fdSSreekanth Reddy 	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE:
823fc7212fdSSreekanth Reddy 		identify->device_type = SAS_END_DEVICE;
824fc7212fdSSreekanth Reddy 		break;
825fc7212fdSSreekanth Reddy 	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER:
826fc7212fdSSreekanth Reddy 		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
827fc7212fdSSreekanth Reddy 		break;
828fc7212fdSSreekanth Reddy 	}
829fc7212fdSSreekanth Reddy 
830fc7212fdSSreekanth Reddy 	/* initiator_port_protocols */
831fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_SSP_INITIATOR)
832fc7212fdSSreekanth Reddy 		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
833fc7212fdSSreekanth Reddy 	/* MPI3.0 doesn't have define for SATA INIT so setting both here*/
834fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_STP_INITIATOR)
835fc7212fdSSreekanth Reddy 		identify->initiator_port_protocols |= (SAS_PROTOCOL_STP |
836fc7212fdSSreekanth Reddy 		    SAS_PROTOCOL_SATA);
837fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_SMP_INITIATOR)
838fc7212fdSSreekanth Reddy 		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
839fc7212fdSSreekanth Reddy 
840fc7212fdSSreekanth Reddy 	/* target_port_protocols */
841fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_SSP_TARGET)
842fc7212fdSSreekanth Reddy 		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
843fc7212fdSSreekanth Reddy 	/* MPI3.0 doesn't have define for STP Target so setting both here*/
844fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET)
845fc7212fdSSreekanth Reddy 		identify->target_port_protocols |= (SAS_PROTOCOL_STP |
846fc7212fdSSreekanth Reddy 		    SAS_PROTOCOL_SATA);
847fc7212fdSSreekanth Reddy 	if (device_info & MPI3_SAS_DEVICE_INFO_SMP_TARGET)
848fc7212fdSSreekanth Reddy 		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
849fc7212fdSSreekanth Reddy 	return 0;
850fc7212fdSSreekanth Reddy }
851fc7212fdSSreekanth Reddy 
852fc7212fdSSreekanth Reddy /**
853fc7212fdSSreekanth Reddy  * mpi3mr_add_host_phy - report sas_host phy to SAS transport
854fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
855fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object
856fc7212fdSSreekanth Reddy  * @phy_pg0: SAS phy page 0
857fc7212fdSSreekanth Reddy  * @parent_dev: Prent device class object
858fc7212fdSSreekanth Reddy  *
859fc7212fdSSreekanth Reddy  * Return: 0 for success, non-zero for failure.
860fc7212fdSSreekanth Reddy  */
mpi3mr_add_host_phy(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_phy * mr_sas_phy,struct mpi3_sas_phy_page0 phy_pg0,struct device * parent_dev)861fc7212fdSSreekanth Reddy static int mpi3mr_add_host_phy(struct mpi3mr_ioc *mrioc,
862fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy, struct mpi3_sas_phy_page0 phy_pg0,
863fc7212fdSSreekanth Reddy 	struct device *parent_dev)
864fc7212fdSSreekanth Reddy {
865fc7212fdSSreekanth Reddy 	struct sas_phy *phy;
866fc7212fdSSreekanth Reddy 	int phy_index = mr_sas_phy->phy_id;
867fc7212fdSSreekanth Reddy 
868fc7212fdSSreekanth Reddy 
869fc7212fdSSreekanth Reddy 	INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
870fc7212fdSSreekanth Reddy 	phy = sas_phy_alloc(parent_dev, phy_index);
871fc7212fdSSreekanth Reddy 	if (!phy) {
872fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
873fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
874fc7212fdSSreekanth Reddy 		return -1;
875fc7212fdSSreekanth Reddy 	}
876fc7212fdSSreekanth Reddy 	if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
877fc7212fdSSreekanth Reddy 	    &mr_sas_phy->identify))) {
878fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
879fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
880fc7212fdSSreekanth Reddy 		sas_phy_free(phy);
881fc7212fdSSreekanth Reddy 		return -1;
882fc7212fdSSreekanth Reddy 	}
883fc7212fdSSreekanth Reddy 	phy->identify = mr_sas_phy->identify;
884fc7212fdSSreekanth Reddy 	mr_sas_phy->attached_handle = le16_to_cpu(phy_pg0.attached_dev_handle);
885fc7212fdSSreekanth Reddy 	if (mr_sas_phy->attached_handle)
886fc7212fdSSreekanth Reddy 		mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
887fc7212fdSSreekanth Reddy 		    &mr_sas_phy->remote_identify);
888fc7212fdSSreekanth Reddy 	phy->identify.phy_identifier = mr_sas_phy->phy_id;
889fc7212fdSSreekanth Reddy 	phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
890fc7212fdSSreekanth Reddy 	    (phy_pg0.negotiated_link_rate &
891fc7212fdSSreekanth Reddy 	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
892fc7212fdSSreekanth Reddy 	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
893fc7212fdSSreekanth Reddy 	phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
894fc7212fdSSreekanth Reddy 	    phy_pg0.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
895fc7212fdSSreekanth Reddy 	phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
896fc7212fdSSreekanth Reddy 	    phy_pg0.hw_link_rate >> 4);
897fc7212fdSSreekanth Reddy 	phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
898fc7212fdSSreekanth Reddy 	    phy_pg0.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
899fc7212fdSSreekanth Reddy 	phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
900fc7212fdSSreekanth Reddy 	    phy_pg0.programmed_link_rate >> 4);
901fc7212fdSSreekanth Reddy 	phy->hostdata = mr_sas_phy->hba_port;
902fc7212fdSSreekanth Reddy 
903fc7212fdSSreekanth Reddy 	if ((sas_phy_add(phy))) {
904fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
905fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
906fc7212fdSSreekanth Reddy 		sas_phy_free(phy);
907fc7212fdSSreekanth Reddy 		return -1;
908fc7212fdSSreekanth Reddy 	}
909fc7212fdSSreekanth Reddy 	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
910fc7212fdSSreekanth Reddy 		dev_info(&phy->dev,
911fc7212fdSSreekanth Reddy 		    "add: handle(0x%04x), sas_address(0x%016llx)\n"
912fc7212fdSSreekanth Reddy 		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
913fc7212fdSSreekanth Reddy 		    mr_sas_phy->handle, (unsigned long long)
914fc7212fdSSreekanth Reddy 		    mr_sas_phy->identify.sas_address,
915fc7212fdSSreekanth Reddy 		    mr_sas_phy->attached_handle,
916fc7212fdSSreekanth Reddy 		    (unsigned long long)
917fc7212fdSSreekanth Reddy 		    mr_sas_phy->remote_identify.sas_address);
918fc7212fdSSreekanth Reddy 	mr_sas_phy->phy = phy;
919fc7212fdSSreekanth Reddy 	return 0;
920fc7212fdSSreekanth Reddy }
921fc7212fdSSreekanth Reddy 
922fc7212fdSSreekanth Reddy /**
923fc7212fdSSreekanth Reddy  * mpi3mr_add_expander_phy - report expander phy to transport
924fc7212fdSSreekanth Reddy  * @mrioc: Adapter instance reference
925fc7212fdSSreekanth Reddy  * @mr_sas_phy: Internal Phy object
926fc7212fdSSreekanth Reddy  * @expander_pg1: SAS Expander page 1
927fc7212fdSSreekanth Reddy  * @parent_dev: Parent device class object
928fc7212fdSSreekanth Reddy  *
929fc7212fdSSreekanth Reddy  * Return: 0 for success, non-zero for failure.
930fc7212fdSSreekanth Reddy  */
mpi3mr_add_expander_phy(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_phy * mr_sas_phy,struct mpi3_sas_expander_page1 expander_pg1,struct device * parent_dev)931fc7212fdSSreekanth Reddy static int mpi3mr_add_expander_phy(struct mpi3mr_ioc *mrioc,
932fc7212fdSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy,
933fc7212fdSSreekanth Reddy 	struct mpi3_sas_expander_page1 expander_pg1,
934fc7212fdSSreekanth Reddy 	struct device *parent_dev)
935fc7212fdSSreekanth Reddy {
936fc7212fdSSreekanth Reddy 	struct sas_phy *phy;
937fc7212fdSSreekanth Reddy 	int phy_index = mr_sas_phy->phy_id;
938fc7212fdSSreekanth Reddy 
939fc7212fdSSreekanth Reddy 	INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
940fc7212fdSSreekanth Reddy 	phy = sas_phy_alloc(parent_dev, phy_index);
941fc7212fdSSreekanth Reddy 	if (!phy) {
942fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
943fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
944fc7212fdSSreekanth Reddy 		return -1;
945fc7212fdSSreekanth Reddy 	}
946fc7212fdSSreekanth Reddy 	if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
947fc7212fdSSreekanth Reddy 	    &mr_sas_phy->identify))) {
948fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
949fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
950fc7212fdSSreekanth Reddy 		sas_phy_free(phy);
951fc7212fdSSreekanth Reddy 		return -1;
952fc7212fdSSreekanth Reddy 	}
953fc7212fdSSreekanth Reddy 	phy->identify = mr_sas_phy->identify;
954fc7212fdSSreekanth Reddy 	mr_sas_phy->attached_handle =
955fc7212fdSSreekanth Reddy 	    le16_to_cpu(expander_pg1.attached_dev_handle);
956fc7212fdSSreekanth Reddy 	if (mr_sas_phy->attached_handle)
957fc7212fdSSreekanth Reddy 		mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
958fc7212fdSSreekanth Reddy 		    &mr_sas_phy->remote_identify);
959fc7212fdSSreekanth Reddy 	phy->identify.phy_identifier = mr_sas_phy->phy_id;
960fc7212fdSSreekanth Reddy 	phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
961fc7212fdSSreekanth Reddy 	    (expander_pg1.negotiated_link_rate &
962fc7212fdSSreekanth Reddy 	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
963fc7212fdSSreekanth Reddy 	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
964fc7212fdSSreekanth Reddy 	phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
965fc7212fdSSreekanth Reddy 	    expander_pg1.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
966fc7212fdSSreekanth Reddy 	phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
967fc7212fdSSreekanth Reddy 	    expander_pg1.hw_link_rate >> 4);
968fc7212fdSSreekanth Reddy 	phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
969fc7212fdSSreekanth Reddy 	    expander_pg1.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
970fc7212fdSSreekanth Reddy 	phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
971fc7212fdSSreekanth Reddy 	    expander_pg1.programmed_link_rate >> 4);
972fc7212fdSSreekanth Reddy 	phy->hostdata = mr_sas_phy->hba_port;
973fc7212fdSSreekanth Reddy 
974fc7212fdSSreekanth Reddy 	if ((sas_phy_add(phy))) {
975fc7212fdSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
976fc7212fdSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
977fc7212fdSSreekanth Reddy 		sas_phy_free(phy);
978fc7212fdSSreekanth Reddy 		return -1;
979fc7212fdSSreekanth Reddy 	}
980fc7212fdSSreekanth Reddy 	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
981fc7212fdSSreekanth Reddy 		dev_info(&phy->dev,
982fc7212fdSSreekanth Reddy 		    "add: handle(0x%04x), sas_address(0x%016llx)\n"
983fc7212fdSSreekanth Reddy 		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
984fc7212fdSSreekanth Reddy 		    mr_sas_phy->handle, (unsigned long long)
985fc7212fdSSreekanth Reddy 		    mr_sas_phy->identify.sas_address,
986fc7212fdSSreekanth Reddy 		    mr_sas_phy->attached_handle,
987fc7212fdSSreekanth Reddy 		    (unsigned long long)
988fc7212fdSSreekanth Reddy 		    mr_sas_phy->remote_identify.sas_address);
989fc7212fdSSreekanth Reddy 	mr_sas_phy->phy = phy;
990fc7212fdSSreekanth Reddy 	return 0;
991fc7212fdSSreekanth Reddy }
992125ad1e6SSreekanth Reddy 
993125ad1e6SSreekanth Reddy /**
994125ad1e6SSreekanth Reddy  * mpi3mr_alloc_hba_port - alloc hba port object
995125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
996125ad1e6SSreekanth Reddy  * @port_id: Port number
997125ad1e6SSreekanth Reddy  *
998125ad1e6SSreekanth Reddy  * Alloc memory for hba port object.
999125ad1e6SSreekanth Reddy  */
1000125ad1e6SSreekanth Reddy static struct mpi3mr_hba_port *
mpi3mr_alloc_hba_port(struct mpi3mr_ioc * mrioc,u16 port_id)1001125ad1e6SSreekanth Reddy mpi3mr_alloc_hba_port(struct mpi3mr_ioc *mrioc, u16 port_id)
1002125ad1e6SSreekanth Reddy {
1003125ad1e6SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port;
1004125ad1e6SSreekanth Reddy 
1005125ad1e6SSreekanth Reddy 	hba_port = kzalloc(sizeof(struct mpi3mr_hba_port),
1006125ad1e6SSreekanth Reddy 	    GFP_KERNEL);
1007125ad1e6SSreekanth Reddy 	if (!hba_port)
1008125ad1e6SSreekanth Reddy 		return NULL;
1009125ad1e6SSreekanth Reddy 	hba_port->port_id = port_id;
1010125ad1e6SSreekanth Reddy 	ioc_info(mrioc, "hba_port entry: %p, port: %d is added to hba_port list\n",
1011125ad1e6SSreekanth Reddy 	    hba_port, hba_port->port_id);
1012125ad1e6SSreekanth Reddy 	list_add_tail(&hba_port->list, &mrioc->hba_port_table_list);
1013125ad1e6SSreekanth Reddy 	return hba_port;
1014125ad1e6SSreekanth Reddy }
1015125ad1e6SSreekanth Reddy 
1016125ad1e6SSreekanth Reddy /**
1017125ad1e6SSreekanth Reddy  * mpi3mr_get_hba_port_by_id - find hba port by id
1018125ad1e6SSreekanth Reddy  * @mrioc: Adapter instance reference
1019125ad1e6SSreekanth Reddy  * @port_id - Port ID to search
1020125ad1e6SSreekanth Reddy  *
1021125ad1e6SSreekanth Reddy  * Return: mpi3mr_hba_port reference for the matched port
1022125ad1e6SSreekanth Reddy  */
1023125ad1e6SSreekanth Reddy 
mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc * mrioc,u8 port_id)1024125ad1e6SSreekanth Reddy struct mpi3mr_hba_port *mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc *mrioc,
1025125ad1e6SSreekanth Reddy 	u8 port_id)
1026125ad1e6SSreekanth Reddy {
1027125ad1e6SSreekanth Reddy 	struct mpi3mr_hba_port *port, *port_next;
1028125ad1e6SSreekanth Reddy 
1029125ad1e6SSreekanth Reddy 	list_for_each_entry_safe(port, port_next,
1030125ad1e6SSreekanth Reddy 	    &mrioc->hba_port_table_list, list) {
1031125ad1e6SSreekanth Reddy 		if (port->port_id != port_id)
1032125ad1e6SSreekanth Reddy 			continue;
1033125ad1e6SSreekanth Reddy 		if (port->flags & MPI3MR_HBA_PORT_FLAG_DIRTY)
1034125ad1e6SSreekanth Reddy 			continue;
1035125ad1e6SSreekanth Reddy 		return port;
1036125ad1e6SSreekanth Reddy 	}
1037125ad1e6SSreekanth Reddy 
1038125ad1e6SSreekanth Reddy 	return NULL;
1039125ad1e6SSreekanth Reddy }
104042fc9feeSSreekanth Reddy 
104142fc9feeSSreekanth Reddy /**
104242fc9feeSSreekanth Reddy  * mpi3mr_update_links - refreshing SAS phy link changes
104342fc9feeSSreekanth Reddy  * @mrioc: Adapter instance reference
104442fc9feeSSreekanth Reddy  * @sas_address_parent: SAS address of parent expander or host
104542fc9feeSSreekanth Reddy  * @handle: Firmware device handle of attached device
104642fc9feeSSreekanth Reddy  * @phy_number: Phy number
104742fc9feeSSreekanth Reddy  * @link_rate: New link rate
104842fc9feeSSreekanth Reddy  * @hba_port: HBA port entry
104942fc9feeSSreekanth Reddy  *
105042fc9feeSSreekanth Reddy  * Return: None.
105142fc9feeSSreekanth Reddy  */
mpi3mr_update_links(struct mpi3mr_ioc * mrioc,u64 sas_address_parent,u16 handle,u8 phy_number,u8 link_rate,struct mpi3mr_hba_port * hba_port)105242fc9feeSSreekanth Reddy void mpi3mr_update_links(struct mpi3mr_ioc *mrioc,
105342fc9feeSSreekanth Reddy 	u64 sas_address_parent, u16 handle, u8 phy_number, u8 link_rate,
105442fc9feeSSreekanth Reddy 	struct mpi3mr_hba_port *hba_port)
105542fc9feeSSreekanth Reddy {
105642fc9feeSSreekanth Reddy 	unsigned long flags;
105742fc9feeSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node;
105842fc9feeSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy;
105942fc9feeSSreekanth Reddy 
106042fc9feeSSreekanth Reddy 	if (mrioc->reset_in_progress)
106142fc9feeSSreekanth Reddy 		return;
106242fc9feeSSreekanth Reddy 
106342fc9feeSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
106442fc9feeSSreekanth Reddy 	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
106542fc9feeSSreekanth Reddy 	    sas_address_parent, hba_port);
106642fc9feeSSreekanth Reddy 	if (!mr_sas_node) {
106742fc9feeSSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
106842fc9feeSSreekanth Reddy 		return;
106942fc9feeSSreekanth Reddy 	}
107042fc9feeSSreekanth Reddy 
107142fc9feeSSreekanth Reddy 	mr_sas_phy = &mr_sas_node->phy[phy_number];
107242fc9feeSSreekanth Reddy 	mr_sas_phy->attached_handle = handle;
107342fc9feeSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
107442fc9feeSSreekanth Reddy 	if (handle && (link_rate >= MPI3_SAS_NEG_LINK_RATE_1_5)) {
107542fc9feeSSreekanth Reddy 		mpi3mr_set_identify(mrioc, handle,
107642fc9feeSSreekanth Reddy 		    &mr_sas_phy->remote_identify);
107742fc9feeSSreekanth Reddy 		mpi3mr_add_phy_to_an_existing_port(mrioc, mr_sas_node,
107842fc9feeSSreekanth Reddy 		    mr_sas_phy, mr_sas_phy->remote_identify.sas_address,
107942fc9feeSSreekanth Reddy 		    hba_port);
108042fc9feeSSreekanth Reddy 	} else
108142fc9feeSSreekanth Reddy 		memset(&mr_sas_phy->remote_identify, 0, sizeof(struct
108242fc9feeSSreekanth Reddy 		    sas_identify));
108342fc9feeSSreekanth Reddy 
108442fc9feeSSreekanth Reddy 	if (mr_sas_phy->phy)
108542fc9feeSSreekanth Reddy 		mr_sas_phy->phy->negotiated_linkrate =
108642fc9feeSSreekanth Reddy 		    mpi3mr_convert_phy_link_rate(link_rate);
108742fc9feeSSreekanth Reddy 
108842fc9feeSSreekanth Reddy 	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
108942fc9feeSSreekanth Reddy 		dev_info(&mr_sas_phy->phy->dev,
109042fc9feeSSreekanth Reddy 		    "refresh: parent sas_address(0x%016llx),\n"
109142fc9feeSSreekanth Reddy 		    "\tlink_rate(0x%02x), phy(%d)\n"
109242fc9feeSSreekanth Reddy 		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
109342fc9feeSSreekanth Reddy 		    (unsigned long long)sas_address_parent,
109442fc9feeSSreekanth Reddy 		    link_rate, phy_number, handle, (unsigned long long)
109542fc9feeSSreekanth Reddy 		    mr_sas_phy->remote_identify.sas_address);
109642fc9feeSSreekanth Reddy }
109742fc9feeSSreekanth Reddy 
109842fc9feeSSreekanth Reddy /**
109942fc9feeSSreekanth Reddy  * mpi3mr_sas_host_refresh - refreshing sas host object contents
110042fc9feeSSreekanth Reddy  * @mrioc: Adapter instance reference
110142fc9feeSSreekanth Reddy  *
110242fc9feeSSreekanth Reddy  * This function refreshes the controllers phy information and
110342fc9feeSSreekanth Reddy  * updates the SAS transport layer with updated information,
110442fc9feeSSreekanth Reddy  * this is executed for each device addition or device info
110542fc9feeSSreekanth Reddy  * change events
110642fc9feeSSreekanth Reddy  *
110742fc9feeSSreekanth Reddy  * Return: None.
110842fc9feeSSreekanth Reddy  */
mpi3mr_sas_host_refresh(struct mpi3mr_ioc * mrioc)110942fc9feeSSreekanth Reddy void mpi3mr_sas_host_refresh(struct mpi3mr_ioc *mrioc)
111042fc9feeSSreekanth Reddy {
111142fc9feeSSreekanth Reddy 	int i;
111242fc9feeSSreekanth Reddy 	u8 link_rate;
111342fc9feeSSreekanth Reddy 	u16 sz, port_id, attached_handle;
111442fc9feeSSreekanth Reddy 	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
111542fc9feeSSreekanth Reddy 
111642fc9feeSSreekanth Reddy 	dprint_transport_info(mrioc,
111742fc9feeSSreekanth Reddy 	    "updating handles for sas_host(0x%016llx)\n",
111842fc9feeSSreekanth Reddy 	    (unsigned long long)mrioc->sas_hba.sas_address);
111942fc9feeSSreekanth Reddy 
112042fc9feeSSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
112142fc9feeSSreekanth Reddy 	    (mrioc->sas_hba.num_phys *
112242fc9feeSSreekanth Reddy 	     sizeof(struct mpi3_sas_io_unit0_phy_data));
112342fc9feeSSreekanth Reddy 	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
112442fc9feeSSreekanth Reddy 	if (!sas_io_unit_pg0)
112542fc9feeSSreekanth Reddy 		return;
112642fc9feeSSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
112742fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
112842fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
112942fc9feeSSreekanth Reddy 		goto out;
113042fc9feeSSreekanth Reddy 	}
113142fc9feeSSreekanth Reddy 
113242fc9feeSSreekanth Reddy 	mrioc->sas_hba.handle = 0;
113342fc9feeSSreekanth Reddy 	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
113442fc9feeSSreekanth Reddy 		if (sas_io_unit_pg0->phy_data[i].phy_flags &
113542fc9feeSSreekanth Reddy 		    (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
113642fc9feeSSreekanth Reddy 		     MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
113742fc9feeSSreekanth Reddy 			continue;
113842fc9feeSSreekanth Reddy 		link_rate =
113942fc9feeSSreekanth Reddy 		    sas_io_unit_pg0->phy_data[i].negotiated_link_rate >> 4;
114042fc9feeSSreekanth Reddy 		if (!mrioc->sas_hba.handle)
114142fc9feeSSreekanth Reddy 			mrioc->sas_hba.handle = le16_to_cpu(
114242fc9feeSSreekanth Reddy 			    sas_io_unit_pg0->phy_data[i].controller_dev_handle);
114342fc9feeSSreekanth Reddy 		port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
114442fc9feeSSreekanth Reddy 		if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
114542fc9feeSSreekanth Reddy 			if (!mpi3mr_alloc_hba_port(mrioc, port_id))
114642fc9feeSSreekanth Reddy 				goto out;
114742fc9feeSSreekanth Reddy 
114842fc9feeSSreekanth Reddy 		mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
114942fc9feeSSreekanth Reddy 		attached_handle = le16_to_cpu(
115042fc9feeSSreekanth Reddy 		    sas_io_unit_pg0->phy_data[i].attached_dev_handle);
115142fc9feeSSreekanth Reddy 		if (attached_handle && link_rate < MPI3_SAS_NEG_LINK_RATE_1_5)
115242fc9feeSSreekanth Reddy 			link_rate = MPI3_SAS_NEG_LINK_RATE_1_5;
115342fc9feeSSreekanth Reddy 		mrioc->sas_hba.phy[i].hba_port =
115442fc9feeSSreekanth Reddy 			mpi3mr_get_hba_port_by_id(mrioc, port_id);
115542fc9feeSSreekanth Reddy 		mpi3mr_update_links(mrioc, mrioc->sas_hba.sas_address,
115642fc9feeSSreekanth Reddy 		    attached_handle, i, link_rate,
115742fc9feeSSreekanth Reddy 		    mrioc->sas_hba.phy[i].hba_port);
115842fc9feeSSreekanth Reddy 	}
115942fc9feeSSreekanth Reddy  out:
116042fc9feeSSreekanth Reddy 	kfree(sas_io_unit_pg0);
116142fc9feeSSreekanth Reddy }
116242fc9feeSSreekanth Reddy 
116342fc9feeSSreekanth Reddy /**
116442fc9feeSSreekanth Reddy  * mpi3mr_sas_host_add - create sas host object
116542fc9feeSSreekanth Reddy  * @mrioc: Adapter instance reference
116642fc9feeSSreekanth Reddy  *
116742fc9feeSSreekanth Reddy  * This function creates the controllers phy information and
116842fc9feeSSreekanth Reddy  * updates the SAS transport layer with updated information,
116942fc9feeSSreekanth Reddy  * this is executed for first device addition or device info
117042fc9feeSSreekanth Reddy  * change event.
117142fc9feeSSreekanth Reddy  *
117242fc9feeSSreekanth Reddy  * Return: None.
117342fc9feeSSreekanth Reddy  */
mpi3mr_sas_host_add(struct mpi3mr_ioc * mrioc)117442fc9feeSSreekanth Reddy void mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
117542fc9feeSSreekanth Reddy {
117642fc9feeSSreekanth Reddy 	int i;
117742fc9feeSSreekanth Reddy 	u16 sz, num_phys = 1, port_id, ioc_status;
117842fc9feeSSreekanth Reddy 	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
117942fc9feeSSreekanth Reddy 	struct mpi3_sas_phy_page0 phy_pg0;
118042fc9feeSSreekanth Reddy 	struct mpi3_device_page0 dev_pg0;
118142fc9feeSSreekanth Reddy 	struct mpi3_enclosure_page0 encl_pg0;
118242fc9feeSSreekanth Reddy 	struct mpi3_device0_sas_sata_format *sasinf;
118342fc9feeSSreekanth Reddy 
118442fc9feeSSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
118542fc9feeSSreekanth Reddy 	    (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
118642fc9feeSSreekanth Reddy 	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
118742fc9feeSSreekanth Reddy 	if (!sas_io_unit_pg0)
118842fc9feeSSreekanth Reddy 		return;
118942fc9feeSSreekanth Reddy 
119042fc9feeSSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
119142fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
119242fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
119342fc9feeSSreekanth Reddy 		goto out;
119442fc9feeSSreekanth Reddy 	}
119542fc9feeSSreekanth Reddy 	num_phys = sas_io_unit_pg0->num_phys;
119642fc9feeSSreekanth Reddy 	kfree(sas_io_unit_pg0);
119742fc9feeSSreekanth Reddy 
119842fc9feeSSreekanth Reddy 	mrioc->sas_hba.host_node = 1;
119942fc9feeSSreekanth Reddy 	INIT_LIST_HEAD(&mrioc->sas_hba.sas_port_list);
120042fc9feeSSreekanth Reddy 	mrioc->sas_hba.parent_dev = &mrioc->shost->shost_gendev;
120142fc9feeSSreekanth Reddy 	mrioc->sas_hba.phy = kcalloc(num_phys,
120242fc9feeSSreekanth Reddy 	    sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
120342fc9feeSSreekanth Reddy 	if (!mrioc->sas_hba.phy)
120442fc9feeSSreekanth Reddy 		return;
120542fc9feeSSreekanth Reddy 
120642fc9feeSSreekanth Reddy 	mrioc->sas_hba.num_phys = num_phys;
120742fc9feeSSreekanth Reddy 
120842fc9feeSSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
120942fc9feeSSreekanth Reddy 	    (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
121042fc9feeSSreekanth Reddy 	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
121142fc9feeSSreekanth Reddy 	if (!sas_io_unit_pg0)
121242fc9feeSSreekanth Reddy 		return;
121342fc9feeSSreekanth Reddy 
121442fc9feeSSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
121542fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
121642fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
121742fc9feeSSreekanth Reddy 		goto out;
121842fc9feeSSreekanth Reddy 	}
121942fc9feeSSreekanth Reddy 
122042fc9feeSSreekanth Reddy 	mrioc->sas_hba.handle = 0;
122142fc9feeSSreekanth Reddy 	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
122242fc9feeSSreekanth Reddy 		if (sas_io_unit_pg0->phy_data[i].phy_flags &
122342fc9feeSSreekanth Reddy 		    (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
122442fc9feeSSreekanth Reddy 		    MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
122542fc9feeSSreekanth Reddy 			continue;
122642fc9feeSSreekanth Reddy 		if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
122742fc9feeSSreekanth Reddy 		    sizeof(struct mpi3_sas_phy_page0),
122842fc9feeSSreekanth Reddy 		    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, i)) {
122942fc9feeSSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
123042fc9feeSSreekanth Reddy 			    __FILE__, __LINE__, __func__);
123142fc9feeSSreekanth Reddy 			goto out;
123242fc9feeSSreekanth Reddy 		}
123342fc9feeSSreekanth Reddy 		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
123442fc9feeSSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
123542fc9feeSSreekanth Reddy 			    __FILE__, __LINE__, __func__);
123642fc9feeSSreekanth Reddy 			goto out;
123742fc9feeSSreekanth Reddy 		}
123842fc9feeSSreekanth Reddy 
123942fc9feeSSreekanth Reddy 		if (!mrioc->sas_hba.handle)
124042fc9feeSSreekanth Reddy 			mrioc->sas_hba.handle = le16_to_cpu(
124142fc9feeSSreekanth Reddy 			    sas_io_unit_pg0->phy_data[i].controller_dev_handle);
124242fc9feeSSreekanth Reddy 		port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
124342fc9feeSSreekanth Reddy 
124442fc9feeSSreekanth Reddy 		if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
124542fc9feeSSreekanth Reddy 			if (!mpi3mr_alloc_hba_port(mrioc, port_id))
124642fc9feeSSreekanth Reddy 				goto out;
124742fc9feeSSreekanth Reddy 
124842fc9feeSSreekanth Reddy 		mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
124942fc9feeSSreekanth Reddy 		mrioc->sas_hba.phy[i].phy_id = i;
125042fc9feeSSreekanth Reddy 		mrioc->sas_hba.phy[i].hba_port =
125142fc9feeSSreekanth Reddy 		    mpi3mr_get_hba_port_by_id(mrioc, port_id);
125242fc9feeSSreekanth Reddy 		mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i],
125342fc9feeSSreekanth Reddy 		    phy_pg0, mrioc->sas_hba.parent_dev);
125442fc9feeSSreekanth Reddy 	}
125542fc9feeSSreekanth Reddy 	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
125642fc9feeSSreekanth Reddy 	    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
125742fc9feeSSreekanth Reddy 	    mrioc->sas_hba.handle))) {
125842fc9feeSSreekanth Reddy 		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
125942fc9feeSSreekanth Reddy 		goto out;
126042fc9feeSSreekanth Reddy 	}
126142fc9feeSSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
126242fc9feeSSreekanth Reddy 		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
126342fc9feeSSreekanth Reddy 		    mrioc->sas_hba.handle, ioc_status, __FILE__, __LINE__,
126442fc9feeSSreekanth Reddy 		    __func__);
126542fc9feeSSreekanth Reddy 		goto out;
126642fc9feeSSreekanth Reddy 	}
126742fc9feeSSreekanth Reddy 	mrioc->sas_hba.enclosure_handle =
126842fc9feeSSreekanth Reddy 	    le16_to_cpu(dev_pg0.enclosure_handle);
126942fc9feeSSreekanth Reddy 	sasinf = &dev_pg0.device_specific.sas_sata_format;
127042fc9feeSSreekanth Reddy 	mrioc->sas_hba.sas_address =
127142fc9feeSSreekanth Reddy 	    le64_to_cpu(sasinf->sas_address);
127242fc9feeSSreekanth Reddy 	ioc_info(mrioc,
127342fc9feeSSreekanth Reddy 	    "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
127442fc9feeSSreekanth Reddy 	    mrioc->sas_hba.handle,
127542fc9feeSSreekanth Reddy 	    (unsigned long long) mrioc->sas_hba.sas_address,
127642fc9feeSSreekanth Reddy 	    mrioc->sas_hba.num_phys);
127742fc9feeSSreekanth Reddy 
127842fc9feeSSreekanth Reddy 	if (mrioc->sas_hba.enclosure_handle) {
127942fc9feeSSreekanth Reddy 		if (!(mpi3mr_cfg_get_enclosure_pg0(mrioc, &ioc_status,
1280ae7d45f5STomas Henzl 		    &encl_pg0, sizeof(encl_pg0),
128142fc9feeSSreekanth Reddy 		    MPI3_ENCLOS_PGAD_FORM_HANDLE,
128242fc9feeSSreekanth Reddy 		    mrioc->sas_hba.enclosure_handle)) &&
128342fc9feeSSreekanth Reddy 		    (ioc_status == MPI3_IOCSTATUS_SUCCESS))
128442fc9feeSSreekanth Reddy 			mrioc->sas_hba.enclosure_logical_id =
128542fc9feeSSreekanth Reddy 				le64_to_cpu(encl_pg0.enclosure_logical_id);
128642fc9feeSSreekanth Reddy 	}
128742fc9feeSSreekanth Reddy 
128842fc9feeSSreekanth Reddy out:
128942fc9feeSSreekanth Reddy 	kfree(sas_io_unit_pg0);
129042fc9feeSSreekanth Reddy }
129142fc9feeSSreekanth Reddy 
129242fc9feeSSreekanth Reddy /**
129342fc9feeSSreekanth Reddy  * mpi3mr_sas_port_add - Expose the SAS device to the SAS TL
129442fc9feeSSreekanth Reddy  * @mrioc: Adapter instance reference
129542fc9feeSSreekanth Reddy  * @handle: Firmware device handle of the attached device
129642fc9feeSSreekanth Reddy  * @sas_address_parent: sas address of parent expander or host
129742fc9feeSSreekanth Reddy  * @hba_port: HBA port entry
129842fc9feeSSreekanth Reddy  *
129942fc9feeSSreekanth Reddy  * This function creates a new sas port object for the given end
130042fc9feeSSreekanth Reddy  * device matching sas address and hba_port and adds it to the
130142fc9feeSSreekanth Reddy  * sas_node's sas_port_list and expose the attached sas device
130242fc9feeSSreekanth Reddy  * to the SAS transport layer through sas_rphy_add.
130342fc9feeSSreekanth Reddy  *
130442fc9feeSSreekanth Reddy  * Returns a valid mpi3mr_sas_port reference or NULL.
130542fc9feeSSreekanth Reddy  */
mpi3mr_sas_port_add(struct mpi3mr_ioc * mrioc,u16 handle,u64 sas_address_parent,struct mpi3mr_hba_port * hba_port)130642fc9feeSSreekanth Reddy static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
130742fc9feeSSreekanth Reddy 	u16 handle, u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
130842fc9feeSSreekanth Reddy {
130942fc9feeSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy, *next;
131042fc9feeSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port;
131142fc9feeSSreekanth Reddy 	unsigned long flags;
131242fc9feeSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node;
131342fc9feeSSreekanth Reddy 	struct sas_rphy *rphy;
131442fc9feeSSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev = NULL;
131542fc9feeSSreekanth Reddy 	int i;
131642fc9feeSSreekanth Reddy 	struct sas_port *port;
131742fc9feeSSreekanth Reddy 
131842fc9feeSSreekanth Reddy 	if (!hba_port) {
131942fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
132042fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
132142fc9feeSSreekanth Reddy 		return NULL;
132242fc9feeSSreekanth Reddy 	}
132342fc9feeSSreekanth Reddy 
132442fc9feeSSreekanth Reddy 	mr_sas_port = kzalloc(sizeof(struct mpi3mr_sas_port), GFP_KERNEL);
132542fc9feeSSreekanth Reddy 	if (!mr_sas_port)
132642fc9feeSSreekanth Reddy 		return NULL;
132742fc9feeSSreekanth Reddy 
132842fc9feeSSreekanth Reddy 	INIT_LIST_HEAD(&mr_sas_port->port_list);
132942fc9feeSSreekanth Reddy 	INIT_LIST_HEAD(&mr_sas_port->phy_list);
133042fc9feeSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
133142fc9feeSSreekanth Reddy 	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
133242fc9feeSSreekanth Reddy 	    sas_address_parent, hba_port);
133342fc9feeSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
133442fc9feeSSreekanth Reddy 
133542fc9feeSSreekanth Reddy 	if (!mr_sas_node) {
133642fc9feeSSreekanth Reddy 		ioc_err(mrioc, "%s:could not find parent sas_address(0x%016llx)!\n",
133742fc9feeSSreekanth Reddy 		    __func__, (unsigned long long)sas_address_parent);
133842fc9feeSSreekanth Reddy 		goto out_fail;
133942fc9feeSSreekanth Reddy 	}
134042fc9feeSSreekanth Reddy 
134142fc9feeSSreekanth Reddy 	if ((mpi3mr_set_identify(mrioc, handle,
134242fc9feeSSreekanth Reddy 	    &mr_sas_port->remote_identify))) {
134342fc9feeSSreekanth Reddy 		ioc_err(mrioc,  "failure at %s:%d/%s()!\n",
134442fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
134542fc9feeSSreekanth Reddy 		goto out_fail;
134642fc9feeSSreekanth Reddy 	}
134742fc9feeSSreekanth Reddy 
134842fc9feeSSreekanth Reddy 	if (mr_sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
134942fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
135042fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
135142fc9feeSSreekanth Reddy 		goto out_fail;
135242fc9feeSSreekanth Reddy 	}
135342fc9feeSSreekanth Reddy 
135442fc9feeSSreekanth Reddy 	mr_sas_port->hba_port = hba_port;
135542fc9feeSSreekanth Reddy 	mpi3mr_sas_port_sanity_check(mrioc, mr_sas_node,
135642fc9feeSSreekanth Reddy 	    mr_sas_port->remote_identify.sas_address, hba_port);
135742fc9feeSSreekanth Reddy 
135842fc9feeSSreekanth Reddy 	for (i = 0; i < mr_sas_node->num_phys; i++) {
135942fc9feeSSreekanth Reddy 		if ((mr_sas_node->phy[i].remote_identify.sas_address !=
136042fc9feeSSreekanth Reddy 		    mr_sas_port->remote_identify.sas_address) ||
136142fc9feeSSreekanth Reddy 		    (mr_sas_node->phy[i].hba_port != hba_port))
136242fc9feeSSreekanth Reddy 			continue;
136342fc9feeSSreekanth Reddy 		list_add_tail(&mr_sas_node->phy[i].port_siblings,
136442fc9feeSSreekanth Reddy 		    &mr_sas_port->phy_list);
136542fc9feeSSreekanth Reddy 		mr_sas_port->num_phys++;
13662745ce0eSSreekanth Reddy 		mr_sas_port->phy_mask |= (1 << i);
136742fc9feeSSreekanth Reddy 	}
136842fc9feeSSreekanth Reddy 
136942fc9feeSSreekanth Reddy 	if (!mr_sas_port->num_phys) {
137042fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
137142fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
137242fc9feeSSreekanth Reddy 		goto out_fail;
137342fc9feeSSreekanth Reddy 	}
137442fc9feeSSreekanth Reddy 
13752745ce0eSSreekanth Reddy 	mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
13762745ce0eSSreekanth Reddy 
137742fc9feeSSreekanth Reddy 	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
137842fc9feeSSreekanth Reddy 		tgtdev = mpi3mr_get_tgtdev_by_addr(mrioc,
137942fc9feeSSreekanth Reddy 		    mr_sas_port->remote_identify.sas_address,
138042fc9feeSSreekanth Reddy 		    mr_sas_port->hba_port);
138142fc9feeSSreekanth Reddy 
138242fc9feeSSreekanth Reddy 		if (!tgtdev) {
138342fc9feeSSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
138442fc9feeSSreekanth Reddy 			    __FILE__, __LINE__, __func__);
138542fc9feeSSreekanth Reddy 			goto out_fail;
138642fc9feeSSreekanth Reddy 		}
138742fc9feeSSreekanth Reddy 		tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 1;
138842fc9feeSSreekanth Reddy 	}
138942fc9feeSSreekanth Reddy 
139042fc9feeSSreekanth Reddy 	if (!mr_sas_node->parent_dev) {
139142fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
139242fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
139342fc9feeSSreekanth Reddy 		goto out_fail;
139442fc9feeSSreekanth Reddy 	}
139542fc9feeSSreekanth Reddy 
139642fc9feeSSreekanth Reddy 	port = sas_port_alloc_num(mr_sas_node->parent_dev);
139742fc9feeSSreekanth Reddy 	if ((sas_port_add(port))) {
139842fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
139942fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
140042fc9feeSSreekanth Reddy 		goto out_fail;
140142fc9feeSSreekanth Reddy 	}
140242fc9feeSSreekanth Reddy 
140342fc9feeSSreekanth Reddy 	list_for_each_entry(mr_sas_phy, &mr_sas_port->phy_list,
140442fc9feeSSreekanth Reddy 	    port_siblings) {
140542fc9feeSSreekanth Reddy 		if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
140642fc9feeSSreekanth Reddy 			dev_info(&port->dev,
140742fc9feeSSreekanth Reddy 			    "add: handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
140842fc9feeSSreekanth Reddy 			    handle, (unsigned long long)
140942fc9feeSSreekanth Reddy 			    mr_sas_port->remote_identify.sas_address,
141042fc9feeSSreekanth Reddy 			    mr_sas_phy->phy_id);
141142fc9feeSSreekanth Reddy 		sas_port_add_phy(port, mr_sas_phy->phy);
141242fc9feeSSreekanth Reddy 		mr_sas_phy->phy_belongs_to_port = 1;
141342fc9feeSSreekanth Reddy 		mr_sas_phy->hba_port = hba_port;
141442fc9feeSSreekanth Reddy 	}
141542fc9feeSSreekanth Reddy 
141642fc9feeSSreekanth Reddy 	mr_sas_port->port = port;
141742fc9feeSSreekanth Reddy 	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
141842fc9feeSSreekanth Reddy 		rphy = sas_end_device_alloc(port);
141942fc9feeSSreekanth Reddy 		tgtdev->dev_spec.sas_sata_inf.rphy = rphy;
142042fc9feeSSreekanth Reddy 	} else {
142142fc9feeSSreekanth Reddy 		rphy = sas_expander_alloc(port,
142242fc9feeSSreekanth Reddy 		    mr_sas_port->remote_identify.device_type);
142342fc9feeSSreekanth Reddy 	}
142442fc9feeSSreekanth Reddy 	rphy->identify = mr_sas_port->remote_identify;
142542fc9feeSSreekanth Reddy 
142642fc9feeSSreekanth Reddy 	if (mrioc->current_event)
142742fc9feeSSreekanth Reddy 		mrioc->current_event->pending_at_sml = 1;
142842fc9feeSSreekanth Reddy 
142942fc9feeSSreekanth Reddy 	if ((sas_rphy_add(rphy))) {
143042fc9feeSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
143142fc9feeSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
143242fc9feeSSreekanth Reddy 	}
143342fc9feeSSreekanth Reddy 	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
143442fc9feeSSreekanth Reddy 		tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0;
143542fc9feeSSreekanth Reddy 		tgtdev->dev_spec.sas_sata_inf.sas_transport_attached = 1;
143642fc9feeSSreekanth Reddy 		mpi3mr_tgtdev_put(tgtdev);
143742fc9feeSSreekanth Reddy 	}
143842fc9feeSSreekanth Reddy 
143942fc9feeSSreekanth Reddy 	dev_info(&rphy->dev,
144042fc9feeSSreekanth Reddy 	    "%s: added: handle(0x%04x), sas_address(0x%016llx)\n",
144142fc9feeSSreekanth Reddy 	    __func__, handle, (unsigned long long)
144242fc9feeSSreekanth Reddy 	    mr_sas_port->remote_identify.sas_address);
144342fc9feeSSreekanth Reddy 
144442fc9feeSSreekanth Reddy 	mr_sas_port->rphy = rphy;
144542fc9feeSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
144642fc9feeSSreekanth Reddy 	list_add_tail(&mr_sas_port->port_list, &mr_sas_node->sas_port_list);
144742fc9feeSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
144842fc9feeSSreekanth Reddy 
144942fc9feeSSreekanth Reddy 	if (mrioc->current_event) {
145042fc9feeSSreekanth Reddy 		mrioc->current_event->pending_at_sml = 0;
145142fc9feeSSreekanth Reddy 		if (mrioc->current_event->discard)
145242fc9feeSSreekanth Reddy 			mpi3mr_print_device_event_notice(mrioc, true);
145342fc9feeSSreekanth Reddy 	}
145442fc9feeSSreekanth Reddy 
14552bd37e28SSreekanth Reddy 	/* fill in report manufacture */
14562bd37e28SSreekanth Reddy 	if (mr_sas_port->remote_identify.device_type ==
14572bd37e28SSreekanth Reddy 	    SAS_EDGE_EXPANDER_DEVICE ||
14582bd37e28SSreekanth Reddy 	    mr_sas_port->remote_identify.device_type ==
14592bd37e28SSreekanth Reddy 	    SAS_FANOUT_EXPANDER_DEVICE)
14602bd37e28SSreekanth Reddy 		mpi3mr_report_manufacture(mrioc,
14612bd37e28SSreekanth Reddy 		    mr_sas_port->remote_identify.sas_address,
14622bd37e28SSreekanth Reddy 		    rphy_to_expander_device(rphy), hba_port->port_id);
14632bd37e28SSreekanth Reddy 
146442fc9feeSSreekanth Reddy 	return mr_sas_port;
146542fc9feeSSreekanth Reddy 
146642fc9feeSSreekanth Reddy  out_fail:
146742fc9feeSSreekanth Reddy 	list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list,
146842fc9feeSSreekanth Reddy 	    port_siblings)
146942fc9feeSSreekanth Reddy 		list_del(&mr_sas_phy->port_siblings);
147042fc9feeSSreekanth Reddy 	kfree(mr_sas_port);
147142fc9feeSSreekanth Reddy 	return NULL;
147242fc9feeSSreekanth Reddy }
147342fc9feeSSreekanth Reddy 
147442fc9feeSSreekanth Reddy /**
147542fc9feeSSreekanth Reddy  * mpi3mr_sas_port_remove - remove port from the list
147642fc9feeSSreekanth Reddy  * @mrioc: Adapter instance reference
147742fc9feeSSreekanth Reddy  * @sas_address: SAS address of attached device
147842fc9feeSSreekanth Reddy  * @sas_address_parent: SAS address of parent expander or host
147942fc9feeSSreekanth Reddy  * @hba_port: HBA port entry
148042fc9feeSSreekanth Reddy  *
148142fc9feeSSreekanth Reddy  * Removing object and freeing associated memory from the
148242fc9feeSSreekanth Reddy  * sas_port_list.
148342fc9feeSSreekanth Reddy  *
148442fc9feeSSreekanth Reddy  * Return: None
148542fc9feeSSreekanth Reddy  */
mpi3mr_sas_port_remove(struct mpi3mr_ioc * mrioc,u64 sas_address,u64 sas_address_parent,struct mpi3mr_hba_port * hba_port)148642fc9feeSSreekanth Reddy static void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
148742fc9feeSSreekanth Reddy 	u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
148842fc9feeSSreekanth Reddy {
148942fc9feeSSreekanth Reddy 	int i;
149042fc9feeSSreekanth Reddy 	unsigned long flags;
149142fc9feeSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port, *next;
149242fc9feeSSreekanth Reddy 	struct mpi3mr_sas_node *mr_sas_node;
149342fc9feeSSreekanth Reddy 	u8 found = 0;
149442fc9feeSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy, *next_phy;
149542fc9feeSSreekanth Reddy 	struct mpi3mr_hba_port *srch_port, *hba_port_next = NULL;
149642fc9feeSSreekanth Reddy 
149742fc9feeSSreekanth Reddy 	if (!hba_port)
149842fc9feeSSreekanth Reddy 		return;
149942fc9feeSSreekanth Reddy 
150042fc9feeSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
150142fc9feeSSreekanth Reddy 	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
150242fc9feeSSreekanth Reddy 	    sas_address_parent, hba_port);
150342fc9feeSSreekanth Reddy 	if (!mr_sas_node) {
150442fc9feeSSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
150542fc9feeSSreekanth Reddy 		return;
150642fc9feeSSreekanth Reddy 	}
150742fc9feeSSreekanth Reddy 	list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
150842fc9feeSSreekanth Reddy 	    port_list) {
150942fc9feeSSreekanth Reddy 		if (mr_sas_port->remote_identify.sas_address != sas_address)
151042fc9feeSSreekanth Reddy 			continue;
151142fc9feeSSreekanth Reddy 		if (mr_sas_port->hba_port != hba_port)
151242fc9feeSSreekanth Reddy 			continue;
151342fc9feeSSreekanth Reddy 		found = 1;
151442fc9feeSSreekanth Reddy 		list_del(&mr_sas_port->port_list);
151542fc9feeSSreekanth Reddy 		goto out;
151642fc9feeSSreekanth Reddy 	}
151742fc9feeSSreekanth Reddy 
151842fc9feeSSreekanth Reddy  out:
151942fc9feeSSreekanth Reddy 	if (!found) {
152042fc9feeSSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
152142fc9feeSSreekanth Reddy 		return;
152242fc9feeSSreekanth Reddy 	}
152342fc9feeSSreekanth Reddy 
152442fc9feeSSreekanth Reddy 	if (mr_sas_node->host_node) {
152542fc9feeSSreekanth Reddy 		list_for_each_entry_safe(srch_port, hba_port_next,
152642fc9feeSSreekanth Reddy 		    &mrioc->hba_port_table_list, list) {
152742fc9feeSSreekanth Reddy 			if (srch_port != hba_port)
152842fc9feeSSreekanth Reddy 				continue;
152942fc9feeSSreekanth Reddy 			ioc_info(mrioc,
153042fc9feeSSreekanth Reddy 			    "removing hba_port entry: %p port: %d from hba_port list\n",
153142fc9feeSSreekanth Reddy 			    srch_port, srch_port->port_id);
153242fc9feeSSreekanth Reddy 			list_del(&hba_port->list);
153342fc9feeSSreekanth Reddy 			kfree(hba_port);
153442fc9feeSSreekanth Reddy 			break;
153542fc9feeSSreekanth Reddy 		}
153642fc9feeSSreekanth Reddy 	}
153742fc9feeSSreekanth Reddy 
153842fc9feeSSreekanth Reddy 	for (i = 0; i < mr_sas_node->num_phys; i++) {
153942fc9feeSSreekanth Reddy 		if (mr_sas_node->phy[i].remote_identify.sas_address ==
154042fc9feeSSreekanth Reddy 		    sas_address)
154142fc9feeSSreekanth Reddy 			memset(&mr_sas_node->phy[i].remote_identify, 0,
154242fc9feeSSreekanth Reddy 			    sizeof(struct sas_identify));
154342fc9feeSSreekanth Reddy 	}
154442fc9feeSSreekanth Reddy 
154542fc9feeSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
154642fc9feeSSreekanth Reddy 
154742fc9feeSSreekanth Reddy 	if (mrioc->current_event)
154842fc9feeSSreekanth Reddy 		mrioc->current_event->pending_at_sml = 1;
154942fc9feeSSreekanth Reddy 
155042fc9feeSSreekanth Reddy 	list_for_each_entry_safe(mr_sas_phy, next_phy,
155142fc9feeSSreekanth Reddy 	    &mr_sas_port->phy_list, port_siblings) {
15525b06a716SRanjan Kumar 		if ((!mrioc->stop_drv_processing) &&
15535b06a716SRanjan Kumar 		    (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
155442fc9feeSSreekanth Reddy 			dev_info(&mr_sas_port->port->dev,
155542fc9feeSSreekanth Reddy 			    "remove: sas_address(0x%016llx), phy(%d)\n",
155642fc9feeSSreekanth Reddy 			    (unsigned long long)
155742fc9feeSSreekanth Reddy 			    mr_sas_port->remote_identify.sas_address,
155842fc9feeSSreekanth Reddy 			    mr_sas_phy->phy_id);
155942fc9feeSSreekanth Reddy 		mr_sas_phy->phy_belongs_to_port = 0;
156042fc9feeSSreekanth Reddy 		if (!mrioc->stop_drv_processing)
156142fc9feeSSreekanth Reddy 			sas_port_delete_phy(mr_sas_port->port,
156242fc9feeSSreekanth Reddy 			    mr_sas_phy->phy);
156342fc9feeSSreekanth Reddy 		list_del(&mr_sas_phy->port_siblings);
156442fc9feeSSreekanth Reddy 	}
156542fc9feeSSreekanth Reddy 	if (!mrioc->stop_drv_processing)
156642fc9feeSSreekanth Reddy 		sas_port_delete(mr_sas_port->port);
156742fc9feeSSreekanth Reddy 	ioc_info(mrioc, "%s: removed sas_address(0x%016llx)\n",
156842fc9feeSSreekanth Reddy 	    __func__, (unsigned long long)sas_address);
156942fc9feeSSreekanth Reddy 
157042fc9feeSSreekanth Reddy 	if (mrioc->current_event) {
157142fc9feeSSreekanth Reddy 		mrioc->current_event->pending_at_sml = 0;
157242fc9feeSSreekanth Reddy 		if (mrioc->current_event->discard)
157342fc9feeSSreekanth Reddy 			mpi3mr_print_device_event_notice(mrioc, false);
157442fc9feeSSreekanth Reddy 	}
157542fc9feeSSreekanth Reddy 
157642fc9feeSSreekanth Reddy 	kfree(mr_sas_port);
157742fc9feeSSreekanth Reddy }
1578e22bae30SSreekanth Reddy 
1579e22bae30SSreekanth Reddy /**
15802745ce0eSSreekanth Reddy  * struct host_port - host port details
15812745ce0eSSreekanth Reddy  * @sas_address: SAS Address of the attached device
15822745ce0eSSreekanth Reddy  * @phy_mask: phy mask of host port
15832745ce0eSSreekanth Reddy  * @handle: Device Handle of attached device
15842745ce0eSSreekanth Reddy  * @iounit_port_id: port ID
15852745ce0eSSreekanth Reddy  * @used: host port is already matched with sas port from sas_port_list
15862745ce0eSSreekanth Reddy  * @lowest_phy: lowest phy ID of host port
15872745ce0eSSreekanth Reddy  */
15882745ce0eSSreekanth Reddy struct host_port {
15892745ce0eSSreekanth Reddy 	u64	sas_address;
15902745ce0eSSreekanth Reddy 	u32	phy_mask;
15912745ce0eSSreekanth Reddy 	u16	handle;
15922745ce0eSSreekanth Reddy 	u8	iounit_port_id;
15932745ce0eSSreekanth Reddy 	u8	used;
15942745ce0eSSreekanth Reddy 	u8	lowest_phy;
15952745ce0eSSreekanth Reddy };
15962745ce0eSSreekanth Reddy 
15972745ce0eSSreekanth Reddy /**
15982745ce0eSSreekanth Reddy  * mpi3mr_update_mr_sas_port - update sas port objects during reset
15992745ce0eSSreekanth Reddy  * @mrioc: Adapter instance reference
16002745ce0eSSreekanth Reddy  * @h_port: host_port object
16012745ce0eSSreekanth Reddy  * @mr_sas_port: sas_port objects which needs to be updated
16022745ce0eSSreekanth Reddy  *
16032745ce0eSSreekanth Reddy  * Update the port ID of sas port object. Also add the phys if new phys got
16042745ce0eSSreekanth Reddy  * added to current sas port and remove the phys if some phys are moved
16052745ce0eSSreekanth Reddy  * out of the current sas port.
16062745ce0eSSreekanth Reddy  *
16072745ce0eSSreekanth Reddy  * Return: Nothing.
16082745ce0eSSreekanth Reddy  */
16092745ce0eSSreekanth Reddy static void
mpi3mr_update_mr_sas_port(struct mpi3mr_ioc * mrioc,struct host_port * h_port,struct mpi3mr_sas_port * mr_sas_port)16102745ce0eSSreekanth Reddy mpi3mr_update_mr_sas_port(struct mpi3mr_ioc *mrioc, struct host_port *h_port,
16112745ce0eSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port)
16122745ce0eSSreekanth Reddy {
16132745ce0eSSreekanth Reddy 	struct mpi3mr_sas_phy *mr_sas_phy;
16142745ce0eSSreekanth Reddy 	u32 phy_mask_xor;
16152745ce0eSSreekanth Reddy 	u64 phys_to_be_added, phys_to_be_removed;
16162745ce0eSSreekanth Reddy 	int i;
16172745ce0eSSreekanth Reddy 
16182745ce0eSSreekanth Reddy 	h_port->used = 1;
16192745ce0eSSreekanth Reddy 	mr_sas_port->marked_responding = 1;
16202745ce0eSSreekanth Reddy 
16212745ce0eSSreekanth Reddy 	dev_info(&mr_sas_port->port->dev,
16222745ce0eSSreekanth Reddy 	    "sas_address(0x%016llx), old: port_id %d phy_mask 0x%x, new: port_id %d phy_mask:0x%x\n",
16232745ce0eSSreekanth Reddy 	    mr_sas_port->remote_identify.sas_address,
16242745ce0eSSreekanth Reddy 	    mr_sas_port->hba_port->port_id, mr_sas_port->phy_mask,
16252745ce0eSSreekanth Reddy 	    h_port->iounit_port_id, h_port->phy_mask);
16262745ce0eSSreekanth Reddy 
16272745ce0eSSreekanth Reddy 	mr_sas_port->hba_port->port_id = h_port->iounit_port_id;
16282745ce0eSSreekanth Reddy 	mr_sas_port->hba_port->flags &= ~MPI3MR_HBA_PORT_FLAG_DIRTY;
16292745ce0eSSreekanth Reddy 
16302745ce0eSSreekanth Reddy 	/* Get the newly added phys bit map & removed phys bit map */
16312745ce0eSSreekanth Reddy 	phy_mask_xor = mr_sas_port->phy_mask ^ h_port->phy_mask;
16322745ce0eSSreekanth Reddy 	phys_to_be_added = h_port->phy_mask & phy_mask_xor;
16332745ce0eSSreekanth Reddy 	phys_to_be_removed = mr_sas_port->phy_mask & phy_mask_xor;
16342745ce0eSSreekanth Reddy 
16352745ce0eSSreekanth Reddy 	/*
16362745ce0eSSreekanth Reddy 	 * Register these new phys to current mr_sas_port's port.
16372745ce0eSSreekanth Reddy 	 * if these phys are previously registered with another port
16382745ce0eSSreekanth Reddy 	 * then delete these phys from that port first.
16392745ce0eSSreekanth Reddy 	 */
16402745ce0eSSreekanth Reddy 	for_each_set_bit(i, (ulong *) &phys_to_be_added, BITS_PER_TYPE(u32)) {
16412745ce0eSSreekanth Reddy 		mr_sas_phy = &mrioc->sas_hba.phy[i];
16422745ce0eSSreekanth Reddy 		if (mr_sas_phy->phy_belongs_to_port)
16432745ce0eSSreekanth Reddy 			mpi3mr_del_phy_from_an_existing_port(mrioc,
16442745ce0eSSreekanth Reddy 			    &mrioc->sas_hba, mr_sas_phy);
16452745ce0eSSreekanth Reddy 		mpi3mr_add_phy_to_an_existing_port(mrioc,
16462745ce0eSSreekanth Reddy 		    &mrioc->sas_hba, mr_sas_phy,
16472745ce0eSSreekanth Reddy 		    mr_sas_port->remote_identify.sas_address,
16482745ce0eSSreekanth Reddy 		    mr_sas_port->hba_port);
16492745ce0eSSreekanth Reddy 	}
16502745ce0eSSreekanth Reddy 
16512745ce0eSSreekanth Reddy 	/* Delete the phys which are not part of current mr_sas_port's port. */
16522745ce0eSSreekanth Reddy 	for_each_set_bit(i, (ulong *) &phys_to_be_removed, BITS_PER_TYPE(u32)) {
16532745ce0eSSreekanth Reddy 		mr_sas_phy = &mrioc->sas_hba.phy[i];
16542745ce0eSSreekanth Reddy 		if (mr_sas_phy->phy_belongs_to_port)
16552745ce0eSSreekanth Reddy 			mpi3mr_del_phy_from_an_existing_port(mrioc,
16562745ce0eSSreekanth Reddy 			    &mrioc->sas_hba, mr_sas_phy);
16572745ce0eSSreekanth Reddy 	}
16582745ce0eSSreekanth Reddy }
16592745ce0eSSreekanth Reddy 
16602745ce0eSSreekanth Reddy /**
16612745ce0eSSreekanth Reddy  * mpi3mr_refresh_sas_ports - update host's sas ports during reset
16622745ce0eSSreekanth Reddy  * @mrioc: Adapter instance reference
16632745ce0eSSreekanth Reddy  *
16642745ce0eSSreekanth Reddy  * Update the host's sas ports during reset by checking whether
16652745ce0eSSreekanth Reddy  * sas ports are still intact or not. Add/remove phys if any hba
16662745ce0eSSreekanth Reddy  * phys are (moved in)/(moved out) of sas port. Also update
16672745ce0eSSreekanth Reddy  * io_unit_port if it got changed during reset.
16682745ce0eSSreekanth Reddy  *
16692745ce0eSSreekanth Reddy  * Return: Nothing.
16702745ce0eSSreekanth Reddy  */
16712745ce0eSSreekanth Reddy void
mpi3mr_refresh_sas_ports(struct mpi3mr_ioc * mrioc)16722745ce0eSSreekanth Reddy mpi3mr_refresh_sas_ports(struct mpi3mr_ioc *mrioc)
16732745ce0eSSreekanth Reddy {
16742745ce0eSSreekanth Reddy 	struct host_port h_port[32];
16752745ce0eSSreekanth Reddy 	int i, j, found, host_port_count = 0, port_idx;
16762745ce0eSSreekanth Reddy 	u16 sz, attached_handle, ioc_status;
16772745ce0eSSreekanth Reddy 	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
16782745ce0eSSreekanth Reddy 	struct mpi3_device_page0 dev_pg0;
16792745ce0eSSreekanth Reddy 	struct mpi3_device0_sas_sata_format *sasinf;
16802745ce0eSSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port;
16812745ce0eSSreekanth Reddy 
16822745ce0eSSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
16832745ce0eSSreekanth Reddy 		(mrioc->sas_hba.num_phys *
16842745ce0eSSreekanth Reddy 		 sizeof(struct mpi3_sas_io_unit0_phy_data));
16852745ce0eSSreekanth Reddy 	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
16862745ce0eSSreekanth Reddy 	if (!sas_io_unit_pg0)
16872745ce0eSSreekanth Reddy 		return;
16882745ce0eSSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
16892745ce0eSSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
16902745ce0eSSreekanth Reddy 		    __FILE__, __LINE__, __func__);
16912745ce0eSSreekanth Reddy 		goto out;
16922745ce0eSSreekanth Reddy 	}
16932745ce0eSSreekanth Reddy 
16942745ce0eSSreekanth Reddy 	/* Create a new expander port table */
16952745ce0eSSreekanth Reddy 	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
16962745ce0eSSreekanth Reddy 		attached_handle = le16_to_cpu(
16972745ce0eSSreekanth Reddy 		    sas_io_unit_pg0->phy_data[i].attached_dev_handle);
16982745ce0eSSreekanth Reddy 		if (!attached_handle)
16992745ce0eSSreekanth Reddy 			continue;
17002745ce0eSSreekanth Reddy 		found = 0;
17012745ce0eSSreekanth Reddy 		for (j = 0; j < host_port_count; j++) {
17022745ce0eSSreekanth Reddy 			if (h_port[j].handle == attached_handle) {
17032745ce0eSSreekanth Reddy 				h_port[j].phy_mask |= (1 << i);
17042745ce0eSSreekanth Reddy 				found = 1;
17052745ce0eSSreekanth Reddy 				break;
17062745ce0eSSreekanth Reddy 			}
17072745ce0eSSreekanth Reddy 		}
17082745ce0eSSreekanth Reddy 		if (found)
17092745ce0eSSreekanth Reddy 			continue;
17102745ce0eSSreekanth Reddy 		if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
17112745ce0eSSreekanth Reddy 		    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
17122745ce0eSSreekanth Reddy 		    attached_handle))) {
17132745ce0eSSreekanth Reddy 			dprint_reset(mrioc,
17142745ce0eSSreekanth Reddy 			    "failed to read dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
17152745ce0eSSreekanth Reddy 			    attached_handle, __FILE__, __LINE__, __func__);
17162745ce0eSSreekanth Reddy 			continue;
17172745ce0eSSreekanth Reddy 		}
17182745ce0eSSreekanth Reddy 		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
17192745ce0eSSreekanth Reddy 			dprint_reset(mrioc,
17202745ce0eSSreekanth Reddy 			    "ioc_status(0x%x) while reading dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
17212745ce0eSSreekanth Reddy 			    ioc_status, attached_handle,
17222745ce0eSSreekanth Reddy 			    __FILE__, __LINE__, __func__);
17232745ce0eSSreekanth Reddy 			continue;
17242745ce0eSSreekanth Reddy 		}
17252745ce0eSSreekanth Reddy 		sasinf = &dev_pg0.device_specific.sas_sata_format;
17262745ce0eSSreekanth Reddy 
17272745ce0eSSreekanth Reddy 		port_idx = host_port_count;
17282745ce0eSSreekanth Reddy 		h_port[port_idx].sas_address = le64_to_cpu(sasinf->sas_address);
17292745ce0eSSreekanth Reddy 		h_port[port_idx].handle = attached_handle;
17302745ce0eSSreekanth Reddy 		h_port[port_idx].phy_mask = (1 << i);
17312745ce0eSSreekanth Reddy 		h_port[port_idx].iounit_port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
17322745ce0eSSreekanth Reddy 		h_port[port_idx].lowest_phy = sasinf->phy_num;
17332745ce0eSSreekanth Reddy 		h_port[port_idx].used = 0;
17342745ce0eSSreekanth Reddy 		host_port_count++;
17352745ce0eSSreekanth Reddy 	}
17362745ce0eSSreekanth Reddy 
17372745ce0eSSreekanth Reddy 	if (!host_port_count)
17382745ce0eSSreekanth Reddy 		goto out;
17392745ce0eSSreekanth Reddy 
17402745ce0eSSreekanth Reddy 	if (mrioc->logging_level & MPI3_DEBUG_RESET) {
17412745ce0eSSreekanth Reddy 		ioc_info(mrioc, "Host port details before reset\n");
17422745ce0eSSreekanth Reddy 		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
17432745ce0eSSreekanth Reddy 		    port_list) {
17442745ce0eSSreekanth Reddy 			ioc_info(mrioc,
17452745ce0eSSreekanth Reddy 			    "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%x), lowest phy id:%d\n",
17462745ce0eSSreekanth Reddy 			    mr_sas_port->hba_port->port_id,
17472745ce0eSSreekanth Reddy 			    mr_sas_port->remote_identify.sas_address,
17482745ce0eSSreekanth Reddy 			    mr_sas_port->phy_mask, mr_sas_port->lowest_phy);
17492745ce0eSSreekanth Reddy 		}
17502745ce0eSSreekanth Reddy 		mr_sas_port = NULL;
17512745ce0eSSreekanth Reddy 		ioc_info(mrioc, "Host port details after reset\n");
17522745ce0eSSreekanth Reddy 		for (i = 0; i < host_port_count; i++) {
17532745ce0eSSreekanth Reddy 			ioc_info(mrioc,
17542745ce0eSSreekanth Reddy 			    "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%x), lowest phy id:%d\n",
17552745ce0eSSreekanth Reddy 			    h_port[i].iounit_port_id, h_port[i].sas_address,
17562745ce0eSSreekanth Reddy 			    h_port[i].phy_mask, h_port[i].lowest_phy);
17572745ce0eSSreekanth Reddy 		}
17582745ce0eSSreekanth Reddy 	}
17592745ce0eSSreekanth Reddy 
17602745ce0eSSreekanth Reddy 	/* mark all host sas port entries as dirty */
17612745ce0eSSreekanth Reddy 	list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
17622745ce0eSSreekanth Reddy 	    port_list) {
17632745ce0eSSreekanth Reddy 		mr_sas_port->marked_responding = 0;
17642745ce0eSSreekanth Reddy 		mr_sas_port->hba_port->flags |= MPI3MR_HBA_PORT_FLAG_DIRTY;
17652745ce0eSSreekanth Reddy 	}
17662745ce0eSSreekanth Reddy 
17672745ce0eSSreekanth Reddy 	/* First check for matching lowest phy */
17682745ce0eSSreekanth Reddy 	for (i = 0; i < host_port_count; i++) {
17692745ce0eSSreekanth Reddy 		mr_sas_port = NULL;
17702745ce0eSSreekanth Reddy 		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
17712745ce0eSSreekanth Reddy 		    port_list) {
17722745ce0eSSreekanth Reddy 			if (mr_sas_port->marked_responding)
17732745ce0eSSreekanth Reddy 				continue;
17742745ce0eSSreekanth Reddy 			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
17752745ce0eSSreekanth Reddy 				continue;
17762745ce0eSSreekanth Reddy 			if (h_port[i].lowest_phy == mr_sas_port->lowest_phy) {
17772745ce0eSSreekanth Reddy 				mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
17782745ce0eSSreekanth Reddy 				break;
17792745ce0eSSreekanth Reddy 			}
17802745ce0eSSreekanth Reddy 		}
17812745ce0eSSreekanth Reddy 	}
17822745ce0eSSreekanth Reddy 
17832745ce0eSSreekanth Reddy 	/* In case if lowest phy is got enabled or disabled during reset */
17842745ce0eSSreekanth Reddy 	for (i = 0; i < host_port_count; i++) {
17852745ce0eSSreekanth Reddy 		if (h_port[i].used)
17862745ce0eSSreekanth Reddy 			continue;
17872745ce0eSSreekanth Reddy 		mr_sas_port = NULL;
17882745ce0eSSreekanth Reddy 		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
17892745ce0eSSreekanth Reddy 		    port_list) {
17902745ce0eSSreekanth Reddy 			if (mr_sas_port->marked_responding)
17912745ce0eSSreekanth Reddy 				continue;
17922745ce0eSSreekanth Reddy 			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
17932745ce0eSSreekanth Reddy 				continue;
17942745ce0eSSreekanth Reddy 			if (h_port[i].phy_mask & mr_sas_port->phy_mask) {
17952745ce0eSSreekanth Reddy 				mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
17962745ce0eSSreekanth Reddy 				break;
17972745ce0eSSreekanth Reddy 			}
17982745ce0eSSreekanth Reddy 		}
17992745ce0eSSreekanth Reddy 	}
18002745ce0eSSreekanth Reddy 
18012745ce0eSSreekanth Reddy 	/* In case if expander cable is removed & connected to another HBA port during reset */
18022745ce0eSSreekanth Reddy 	for (i = 0; i < host_port_count; i++) {
18032745ce0eSSreekanth Reddy 		if (h_port[i].used)
18042745ce0eSSreekanth Reddy 			continue;
18052745ce0eSSreekanth Reddy 		mr_sas_port = NULL;
18062745ce0eSSreekanth Reddy 		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
18072745ce0eSSreekanth Reddy 		    port_list) {
18082745ce0eSSreekanth Reddy 			if (mr_sas_port->marked_responding)
18092745ce0eSSreekanth Reddy 				continue;
18102745ce0eSSreekanth Reddy 			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
18112745ce0eSSreekanth Reddy 				continue;
18122745ce0eSSreekanth Reddy 			mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
18132745ce0eSSreekanth Reddy 			break;
18142745ce0eSSreekanth Reddy 		}
18152745ce0eSSreekanth Reddy 	}
18162745ce0eSSreekanth Reddy out:
18172745ce0eSSreekanth Reddy 	kfree(sas_io_unit_pg0);
18182745ce0eSSreekanth Reddy }
18192745ce0eSSreekanth Reddy 
18202745ce0eSSreekanth Reddy /**
18212745ce0eSSreekanth Reddy  * mpi3mr_refresh_expanders - Refresh expander device exposure
18222745ce0eSSreekanth Reddy  * @mrioc: Adapter instance reference
18232745ce0eSSreekanth Reddy  *
18242745ce0eSSreekanth Reddy  * This is executed post controller reset to identify any
18252745ce0eSSreekanth Reddy  * missing expander devices during reset and remove from the upper layers
18262745ce0eSSreekanth Reddy  * or expose any newly detected expander device to the upper layers.
18272745ce0eSSreekanth Reddy  *
18282745ce0eSSreekanth Reddy  * Return: Nothing.
18292745ce0eSSreekanth Reddy  */
18302745ce0eSSreekanth Reddy void
mpi3mr_refresh_expanders(struct mpi3mr_ioc * mrioc)18312745ce0eSSreekanth Reddy mpi3mr_refresh_expanders(struct mpi3mr_ioc *mrioc)
18322745ce0eSSreekanth Reddy {
18332745ce0eSSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander, *sas_expander_next;
18342745ce0eSSreekanth Reddy 	struct mpi3_sas_expander_page0 expander_pg0;
18352745ce0eSSreekanth Reddy 	u16 ioc_status, handle;
18362745ce0eSSreekanth Reddy 	u64 sas_address;
18372745ce0eSSreekanth Reddy 	int i;
18382745ce0eSSreekanth Reddy 	unsigned long flags;
18392745ce0eSSreekanth Reddy 	struct mpi3mr_hba_port *hba_port;
18402745ce0eSSreekanth Reddy 
18412745ce0eSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
18422745ce0eSSreekanth Reddy 	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
18432745ce0eSSreekanth Reddy 		sas_expander->non_responding = 1;
18442745ce0eSSreekanth Reddy 	}
18452745ce0eSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
18462745ce0eSSreekanth Reddy 
18472745ce0eSSreekanth Reddy 	sas_expander = NULL;
18482745ce0eSSreekanth Reddy 
18492745ce0eSSreekanth Reddy 	handle = 0xffff;
18502745ce0eSSreekanth Reddy 
18512745ce0eSSreekanth Reddy 	/* Search for responding expander devices and add them if they are newly got added */
18522745ce0eSSreekanth Reddy 	while (true) {
18532745ce0eSSreekanth Reddy 		if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
18542745ce0eSSreekanth Reddy 		    sizeof(struct mpi3_sas_expander_page0),
18552745ce0eSSreekanth Reddy 		    MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
18562745ce0eSSreekanth Reddy 			dprint_reset(mrioc,
18572745ce0eSSreekanth Reddy 			    "failed to read exp pg0 for handle(0x%04x) at %s:%d/%s()!\n",
18582745ce0eSSreekanth Reddy 			    handle, __FILE__, __LINE__, __func__);
18592745ce0eSSreekanth Reddy 			break;
18602745ce0eSSreekanth Reddy 		}
18612745ce0eSSreekanth Reddy 
18622745ce0eSSreekanth Reddy 		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
18632745ce0eSSreekanth Reddy 			dprint_reset(mrioc,
18642745ce0eSSreekanth Reddy 			   "ioc_status(0x%x) while reading exp pg0 for handle:(0x%04x), %s:%d/%s()!\n",
18652745ce0eSSreekanth Reddy 			   ioc_status, handle, __FILE__, __LINE__, __func__);
18662745ce0eSSreekanth Reddy 			break;
18672745ce0eSSreekanth Reddy 		}
18682745ce0eSSreekanth Reddy 
18692745ce0eSSreekanth Reddy 		handle = le16_to_cpu(expander_pg0.dev_handle);
18702745ce0eSSreekanth Reddy 		sas_address = le64_to_cpu(expander_pg0.sas_address);
18712745ce0eSSreekanth Reddy 		hba_port = mpi3mr_get_hba_port_by_id(mrioc, expander_pg0.io_unit_port);
18722745ce0eSSreekanth Reddy 
18732745ce0eSSreekanth Reddy 		if (!hba_port) {
18742745ce0eSSreekanth Reddy 			mpi3mr_sas_host_refresh(mrioc);
18752745ce0eSSreekanth Reddy 			mpi3mr_expander_add(mrioc, handle);
18762745ce0eSSreekanth Reddy 			continue;
18772745ce0eSSreekanth Reddy 		}
18782745ce0eSSreekanth Reddy 
18792745ce0eSSreekanth Reddy 		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
18802745ce0eSSreekanth Reddy 		sas_expander =
18812745ce0eSSreekanth Reddy 		    mpi3mr_expander_find_by_sas_address(mrioc,
18822745ce0eSSreekanth Reddy 		    sas_address, hba_port);
18832745ce0eSSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
18842745ce0eSSreekanth Reddy 
18852745ce0eSSreekanth Reddy 		if (!sas_expander) {
18862745ce0eSSreekanth Reddy 			mpi3mr_sas_host_refresh(mrioc);
18872745ce0eSSreekanth Reddy 			mpi3mr_expander_add(mrioc, handle);
18882745ce0eSSreekanth Reddy 			continue;
18892745ce0eSSreekanth Reddy 		}
18902745ce0eSSreekanth Reddy 
18912745ce0eSSreekanth Reddy 		sas_expander->non_responding = 0;
18922745ce0eSSreekanth Reddy 		if (sas_expander->handle == handle)
18932745ce0eSSreekanth Reddy 			continue;
18942745ce0eSSreekanth Reddy 
18952745ce0eSSreekanth Reddy 		sas_expander->handle = handle;
18962745ce0eSSreekanth Reddy 		for (i = 0 ; i < sas_expander->num_phys ; i++)
18972745ce0eSSreekanth Reddy 			sas_expander->phy[i].handle = handle;
18982745ce0eSSreekanth Reddy 	}
18992745ce0eSSreekanth Reddy 
19002745ce0eSSreekanth Reddy 	/*
19012745ce0eSSreekanth Reddy 	 * Delete non responding expander devices and the corresponding
19022745ce0eSSreekanth Reddy 	 * hba_port if the non responding expander device's parent device
19032745ce0eSSreekanth Reddy 	 * is a host node.
19042745ce0eSSreekanth Reddy 	 */
19052745ce0eSSreekanth Reddy 	sas_expander = NULL;
19062745ce0eSSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
19072745ce0eSSreekanth Reddy 	list_for_each_entry_safe_reverse(sas_expander, sas_expander_next,
19082745ce0eSSreekanth Reddy 	    &mrioc->sas_expander_list, list) {
19092745ce0eSSreekanth Reddy 		if (sas_expander->non_responding) {
19102745ce0eSSreekanth Reddy 			spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
19112745ce0eSSreekanth Reddy 			mpi3mr_expander_node_remove(mrioc, sas_expander);
19122745ce0eSSreekanth Reddy 			spin_lock_irqsave(&mrioc->sas_node_lock, flags);
19132745ce0eSSreekanth Reddy 		}
19142745ce0eSSreekanth Reddy 	}
19152745ce0eSSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
19162745ce0eSSreekanth Reddy }
19172745ce0eSSreekanth Reddy 
19182745ce0eSSreekanth Reddy /**
1919e22bae30SSreekanth Reddy  * mpi3mr_expander_node_add - insert an expander to the list.
1920e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
1921e22bae30SSreekanth Reddy  * @sas_expander: Expander sas node
1922e22bae30SSreekanth Reddy  * Context: This function will acquire sas_node_lock.
1923e22bae30SSreekanth Reddy  *
1924e22bae30SSreekanth Reddy  * Adding new object to the ioc->sas_expander_list.
1925e22bae30SSreekanth Reddy  *
1926e22bae30SSreekanth Reddy  * Return: None.
1927e22bae30SSreekanth Reddy  */
mpi3mr_expander_node_add(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_node * sas_expander)1928e22bae30SSreekanth Reddy static void mpi3mr_expander_node_add(struct mpi3mr_ioc *mrioc,
1929e22bae30SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander)
1930e22bae30SSreekanth Reddy {
1931e22bae30SSreekanth Reddy 	unsigned long flags;
1932e22bae30SSreekanth Reddy 
1933e22bae30SSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
1934e22bae30SSreekanth Reddy 	list_add_tail(&sas_expander->list, &mrioc->sas_expander_list);
1935e22bae30SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
1936e22bae30SSreekanth Reddy }
1937e22bae30SSreekanth Reddy 
1938e22bae30SSreekanth Reddy /**
1939e22bae30SSreekanth Reddy  * mpi3mr_expander_add -  Create expander object
1940e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
1941e22bae30SSreekanth Reddy  * @handle: Expander firmware device handle
1942e22bae30SSreekanth Reddy  *
1943e22bae30SSreekanth Reddy  * This function creating expander object, stored in
1944e22bae30SSreekanth Reddy  * sas_expander_list and expose it to the SAS transport
1945e22bae30SSreekanth Reddy  * layer.
1946e22bae30SSreekanth Reddy  *
1947e22bae30SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
1948e22bae30SSreekanth Reddy  */
mpi3mr_expander_add(struct mpi3mr_ioc * mrioc,u16 handle)1949e22bae30SSreekanth Reddy int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle)
1950e22bae30SSreekanth Reddy {
1951e22bae30SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander;
1952e22bae30SSreekanth Reddy 	struct mpi3mr_enclosure_node *enclosure_dev;
1953e22bae30SSreekanth Reddy 	struct mpi3_sas_expander_page0 expander_pg0;
1954e22bae30SSreekanth Reddy 	struct mpi3_sas_expander_page1 expander_pg1;
1955e22bae30SSreekanth Reddy 	u16 ioc_status, parent_handle, temp_handle;
1956e22bae30SSreekanth Reddy 	u64 sas_address, sas_address_parent = 0;
1957e22bae30SSreekanth Reddy 	int i;
1958e22bae30SSreekanth Reddy 	unsigned long flags;
1959e22bae30SSreekanth Reddy 	u8 port_id, link_rate;
1960e22bae30SSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port = NULL;
1961e22bae30SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port;
1962e22bae30SSreekanth Reddy 	u32 phynum_handle;
1963e22bae30SSreekanth Reddy 	int rc = 0;
1964e22bae30SSreekanth Reddy 
1965e22bae30SSreekanth Reddy 	if (!handle)
1966e22bae30SSreekanth Reddy 		return -1;
1967e22bae30SSreekanth Reddy 
1968e22bae30SSreekanth Reddy 	if (mrioc->reset_in_progress)
1969e22bae30SSreekanth Reddy 		return -1;
1970e22bae30SSreekanth Reddy 
1971e22bae30SSreekanth Reddy 	if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
1972e22bae30SSreekanth Reddy 	    sizeof(expander_pg0), MPI3_SAS_EXPAND_PGAD_FORM_HANDLE, handle))) {
1973e22bae30SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
1974e22bae30SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
1975e22bae30SSreekanth Reddy 		return -1;
1976e22bae30SSreekanth Reddy 	}
1977e22bae30SSreekanth Reddy 
1978e22bae30SSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
1979e22bae30SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
1980e22bae30SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
1981e22bae30SSreekanth Reddy 		return -1;
1982e22bae30SSreekanth Reddy 	}
1983e22bae30SSreekanth Reddy 
1984e22bae30SSreekanth Reddy 	parent_handle = le16_to_cpu(expander_pg0.parent_dev_handle);
1985e22bae30SSreekanth Reddy 	if (mpi3mr_get_sas_address(mrioc, parent_handle, &sas_address_parent)
1986e22bae30SSreekanth Reddy 	    != 0) {
1987e22bae30SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
1988e22bae30SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
1989e22bae30SSreekanth Reddy 		return -1;
1990e22bae30SSreekanth Reddy 	}
1991e22bae30SSreekanth Reddy 
1992e22bae30SSreekanth Reddy 	port_id = expander_pg0.io_unit_port;
1993e22bae30SSreekanth Reddy 	hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
1994e22bae30SSreekanth Reddy 	if (!hba_port) {
1995e22bae30SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
1996e22bae30SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
1997e22bae30SSreekanth Reddy 		return -1;
1998e22bae30SSreekanth Reddy 	}
1999e22bae30SSreekanth Reddy 
2000e22bae30SSreekanth Reddy 	if (sas_address_parent != mrioc->sas_hba.sas_address) {
2001e22bae30SSreekanth Reddy 		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
2002e22bae30SSreekanth Reddy 		sas_expander =
2003e22bae30SSreekanth Reddy 		   mpi3mr_expander_find_by_sas_address(mrioc,
2004e22bae30SSreekanth Reddy 		    sas_address_parent, hba_port);
2005e22bae30SSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
2006e22bae30SSreekanth Reddy 		if (!sas_expander) {
2007e22bae30SSreekanth Reddy 			rc = mpi3mr_expander_add(mrioc, parent_handle);
2008e22bae30SSreekanth Reddy 			if (rc != 0)
2009e22bae30SSreekanth Reddy 				return rc;
2010e22bae30SSreekanth Reddy 		} else {
2011e22bae30SSreekanth Reddy 			/*
2012e22bae30SSreekanth Reddy 			 * When there is a parent expander present, update it's
2013e22bae30SSreekanth Reddy 			 * phys where child expander is connected with the link
2014e22bae30SSreekanth Reddy 			 * speed, attached dev handle and sas address.
2015e22bae30SSreekanth Reddy 			 */
2016e22bae30SSreekanth Reddy 			for (i = 0 ; i < sas_expander->num_phys ; i++) {
2017e22bae30SSreekanth Reddy 				phynum_handle =
2018e22bae30SSreekanth Reddy 				    (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
2019e22bae30SSreekanth Reddy 				    parent_handle;
2020e22bae30SSreekanth Reddy 				if (mpi3mr_cfg_get_sas_exp_pg1(mrioc,
2021e22bae30SSreekanth Reddy 				    &ioc_status, &expander_pg1,
2022e22bae30SSreekanth Reddy 				    sizeof(expander_pg1),
2023e22bae30SSreekanth Reddy 				    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
2024e22bae30SSreekanth Reddy 				    phynum_handle)) {
2025e22bae30SSreekanth Reddy 					ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2026e22bae30SSreekanth Reddy 					    __FILE__, __LINE__, __func__);
2027e22bae30SSreekanth Reddy 					rc = -1;
2028e22bae30SSreekanth Reddy 					return rc;
2029e22bae30SSreekanth Reddy 				}
2030e22bae30SSreekanth Reddy 				if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
2031e22bae30SSreekanth Reddy 					ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2032e22bae30SSreekanth Reddy 					    __FILE__, __LINE__, __func__);
2033e22bae30SSreekanth Reddy 					rc = -1;
2034e22bae30SSreekanth Reddy 					return rc;
2035e22bae30SSreekanth Reddy 				}
2036e22bae30SSreekanth Reddy 				temp_handle = le16_to_cpu(
2037e22bae30SSreekanth Reddy 				    expander_pg1.attached_dev_handle);
2038e22bae30SSreekanth Reddy 				if (temp_handle != handle)
2039e22bae30SSreekanth Reddy 					continue;
2040e22bae30SSreekanth Reddy 				link_rate = (expander_pg1.negotiated_link_rate &
2041e22bae30SSreekanth Reddy 				    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
2042e22bae30SSreekanth Reddy 				    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
2043e22bae30SSreekanth Reddy 				mpi3mr_update_links(mrioc, sas_address_parent,
2044e22bae30SSreekanth Reddy 				    handle, i, link_rate, hba_port);
2045e22bae30SSreekanth Reddy 			}
2046e22bae30SSreekanth Reddy 		}
2047e22bae30SSreekanth Reddy 	}
2048e22bae30SSreekanth Reddy 
2049e22bae30SSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
2050e22bae30SSreekanth Reddy 	sas_address = le64_to_cpu(expander_pg0.sas_address);
2051e22bae30SSreekanth Reddy 	sas_expander = mpi3mr_expander_find_by_sas_address(mrioc,
2052e22bae30SSreekanth Reddy 	    sas_address, hba_port);
2053e22bae30SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
2054e22bae30SSreekanth Reddy 
2055e22bae30SSreekanth Reddy 	if (sas_expander)
2056e22bae30SSreekanth Reddy 		return 0;
2057e22bae30SSreekanth Reddy 
2058e22bae30SSreekanth Reddy 	sas_expander = kzalloc(sizeof(struct mpi3mr_sas_node),
2059e22bae30SSreekanth Reddy 	    GFP_KERNEL);
2060e22bae30SSreekanth Reddy 	if (!sas_expander)
2061*2a954832SHarshit Mogalapalli 		return -ENOMEM;
2062e22bae30SSreekanth Reddy 
2063e22bae30SSreekanth Reddy 	sas_expander->handle = handle;
2064e22bae30SSreekanth Reddy 	sas_expander->num_phys = expander_pg0.num_phys;
2065e22bae30SSreekanth Reddy 	sas_expander->sas_address_parent = sas_address_parent;
2066e22bae30SSreekanth Reddy 	sas_expander->sas_address = sas_address;
2067e22bae30SSreekanth Reddy 	sas_expander->hba_port = hba_port;
2068e22bae30SSreekanth Reddy 
2069e22bae30SSreekanth Reddy 	ioc_info(mrioc,
2070e22bae30SSreekanth Reddy 	    "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
2071e22bae30SSreekanth Reddy 	    handle, parent_handle, (unsigned long long)
2072e22bae30SSreekanth Reddy 	    sas_expander->sas_address, sas_expander->num_phys);
2073e22bae30SSreekanth Reddy 
2074e22bae30SSreekanth Reddy 	if (!sas_expander->num_phys) {
2075e22bae30SSreekanth Reddy 		rc = -1;
2076e22bae30SSreekanth Reddy 		goto out_fail;
2077e22bae30SSreekanth Reddy 	}
2078e22bae30SSreekanth Reddy 	sas_expander->phy = kcalloc(sas_expander->num_phys,
2079e22bae30SSreekanth Reddy 	    sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
2080e22bae30SSreekanth Reddy 	if (!sas_expander->phy) {
2081e22bae30SSreekanth Reddy 		rc = -1;
2082e22bae30SSreekanth Reddy 		goto out_fail;
2083e22bae30SSreekanth Reddy 	}
2084e22bae30SSreekanth Reddy 
2085e22bae30SSreekanth Reddy 	INIT_LIST_HEAD(&sas_expander->sas_port_list);
2086e22bae30SSreekanth Reddy 	mr_sas_port = mpi3mr_sas_port_add(mrioc, handle, sas_address_parent,
2087e22bae30SSreekanth Reddy 	    sas_expander->hba_port);
2088e22bae30SSreekanth Reddy 	if (!mr_sas_port) {
2089e22bae30SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2090e22bae30SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
2091e22bae30SSreekanth Reddy 		rc = -1;
2092e22bae30SSreekanth Reddy 		goto out_fail;
2093e22bae30SSreekanth Reddy 	}
2094e22bae30SSreekanth Reddy 	sas_expander->parent_dev = &mr_sas_port->rphy->dev;
2095e22bae30SSreekanth Reddy 	sas_expander->rphy = mr_sas_port->rphy;
2096e22bae30SSreekanth Reddy 
2097e22bae30SSreekanth Reddy 	for (i = 0 ; i < sas_expander->num_phys ; i++) {
2098e22bae30SSreekanth Reddy 		phynum_handle = (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
2099e22bae30SSreekanth Reddy 		    handle;
2100e22bae30SSreekanth Reddy 		if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
2101e22bae30SSreekanth Reddy 		    &expander_pg1, sizeof(expander_pg1),
2102e22bae30SSreekanth Reddy 		    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
2103e22bae30SSreekanth Reddy 		    phynum_handle)) {
2104e22bae30SSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2105e22bae30SSreekanth Reddy 			    __FILE__, __LINE__, __func__);
2106e22bae30SSreekanth Reddy 			rc = -1;
2107e22bae30SSreekanth Reddy 			goto out_fail;
2108e22bae30SSreekanth Reddy 		}
2109e22bae30SSreekanth Reddy 		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
2110e22bae30SSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2111e22bae30SSreekanth Reddy 			    __FILE__, __LINE__, __func__);
2112e22bae30SSreekanth Reddy 			rc = -1;
2113e22bae30SSreekanth Reddy 			goto out_fail;
2114e22bae30SSreekanth Reddy 		}
2115e22bae30SSreekanth Reddy 
2116e22bae30SSreekanth Reddy 		sas_expander->phy[i].handle = handle;
2117e22bae30SSreekanth Reddy 		sas_expander->phy[i].phy_id = i;
2118e22bae30SSreekanth Reddy 		sas_expander->phy[i].hba_port = hba_port;
2119e22bae30SSreekanth Reddy 
2120e22bae30SSreekanth Reddy 		if ((mpi3mr_add_expander_phy(mrioc, &sas_expander->phy[i],
2121e22bae30SSreekanth Reddy 		    expander_pg1, sas_expander->parent_dev))) {
2122e22bae30SSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2123e22bae30SSreekanth Reddy 			    __FILE__, __LINE__, __func__);
2124e22bae30SSreekanth Reddy 			rc = -1;
2125e22bae30SSreekanth Reddy 			goto out_fail;
2126e22bae30SSreekanth Reddy 		}
2127e22bae30SSreekanth Reddy 	}
2128e22bae30SSreekanth Reddy 
2129e22bae30SSreekanth Reddy 	if (sas_expander->enclosure_handle) {
2130e22bae30SSreekanth Reddy 		enclosure_dev =
2131e22bae30SSreekanth Reddy 			mpi3mr_enclosure_find_by_handle(mrioc,
2132e22bae30SSreekanth Reddy 						sas_expander->enclosure_handle);
2133e22bae30SSreekanth Reddy 		if (enclosure_dev)
2134e22bae30SSreekanth Reddy 			sas_expander->enclosure_logical_id = le64_to_cpu(
2135e22bae30SSreekanth Reddy 			    enclosure_dev->pg0.enclosure_logical_id);
2136e22bae30SSreekanth Reddy 	}
2137e22bae30SSreekanth Reddy 
2138e22bae30SSreekanth Reddy 	mpi3mr_expander_node_add(mrioc, sas_expander);
2139e22bae30SSreekanth Reddy 	return 0;
2140e22bae30SSreekanth Reddy 
2141e22bae30SSreekanth Reddy out_fail:
2142e22bae30SSreekanth Reddy 
2143e22bae30SSreekanth Reddy 	if (mr_sas_port)
2144e22bae30SSreekanth Reddy 		mpi3mr_sas_port_remove(mrioc,
2145e22bae30SSreekanth Reddy 		    sas_expander->sas_address,
2146e22bae30SSreekanth Reddy 		    sas_address_parent, sas_expander->hba_port);
2147e22bae30SSreekanth Reddy 	kfree(sas_expander->phy);
2148e22bae30SSreekanth Reddy 	kfree(sas_expander);
2149e22bae30SSreekanth Reddy 	return rc;
2150e22bae30SSreekanth Reddy }
2151e22bae30SSreekanth Reddy 
2152e22bae30SSreekanth Reddy /**
2153e22bae30SSreekanth Reddy  * mpi3mr_expander_node_remove - recursive removal of expander.
2154e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
2155e22bae30SSreekanth Reddy  * @sas_expander: Expander device object
2156e22bae30SSreekanth Reddy  *
2157e22bae30SSreekanth Reddy  * Removes expander object and freeing associated memory from
2158e22bae30SSreekanth Reddy  * the sas_expander_list and removes the same from SAS TL, if
2159e22bae30SSreekanth Reddy  * one of the attached device is an expander then it recursively
2160e22bae30SSreekanth Reddy  * removes the expander device too.
2161e22bae30SSreekanth Reddy  *
2162e22bae30SSreekanth Reddy  * Return nothing.
2163e22bae30SSreekanth Reddy  */
mpi3mr_expander_node_remove(struct mpi3mr_ioc * mrioc,struct mpi3mr_sas_node * sas_expander)2164ce756daaSTomas Henzl void mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc,
2165e22bae30SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander)
2166e22bae30SSreekanth Reddy {
2167e22bae30SSreekanth Reddy 	struct mpi3mr_sas_port *mr_sas_port, *next;
2168e22bae30SSreekanth Reddy 	unsigned long flags;
2169e22bae30SSreekanth Reddy 	u8 port_id;
2170e22bae30SSreekanth Reddy 
2171e22bae30SSreekanth Reddy 	/* remove sibling ports attached to this expander */
2172e22bae30SSreekanth Reddy 	list_for_each_entry_safe(mr_sas_port, next,
2173e22bae30SSreekanth Reddy 	   &sas_expander->sas_port_list, port_list) {
2174e22bae30SSreekanth Reddy 		if (mrioc->reset_in_progress)
2175e22bae30SSreekanth Reddy 			return;
2176e22bae30SSreekanth Reddy 		if (mr_sas_port->remote_identify.device_type ==
2177e22bae30SSreekanth Reddy 		    SAS_END_DEVICE)
2178e22bae30SSreekanth Reddy 			mpi3mr_remove_device_by_sas_address(mrioc,
2179e22bae30SSreekanth Reddy 			    mr_sas_port->remote_identify.sas_address,
2180e22bae30SSreekanth Reddy 			    mr_sas_port->hba_port);
2181e22bae30SSreekanth Reddy 		else if (mr_sas_port->remote_identify.device_type ==
2182e22bae30SSreekanth Reddy 		    SAS_EDGE_EXPANDER_DEVICE ||
2183e22bae30SSreekanth Reddy 		    mr_sas_port->remote_identify.device_type ==
2184e22bae30SSreekanth Reddy 		    SAS_FANOUT_EXPANDER_DEVICE)
2185e22bae30SSreekanth Reddy 			mpi3mr_expander_remove(mrioc,
2186e22bae30SSreekanth Reddy 			    mr_sas_port->remote_identify.sas_address,
2187e22bae30SSreekanth Reddy 			    mr_sas_port->hba_port);
2188e22bae30SSreekanth Reddy 	}
2189e22bae30SSreekanth Reddy 
2190e22bae30SSreekanth Reddy 	port_id = sas_expander->hba_port->port_id;
2191e22bae30SSreekanth Reddy 	mpi3mr_sas_port_remove(mrioc, sas_expander->sas_address,
2192e22bae30SSreekanth Reddy 	    sas_expander->sas_address_parent, sas_expander->hba_port);
2193e22bae30SSreekanth Reddy 
2194e22bae30SSreekanth Reddy 	ioc_info(mrioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n",
2195e22bae30SSreekanth Reddy 	    sas_expander->handle, (unsigned long long)
2196e22bae30SSreekanth Reddy 	    sas_expander->sas_address, port_id);
2197e22bae30SSreekanth Reddy 
2198e22bae30SSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
2199e22bae30SSreekanth Reddy 	list_del(&sas_expander->list);
2200e22bae30SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
2201e22bae30SSreekanth Reddy 
2202e22bae30SSreekanth Reddy 	kfree(sas_expander->phy);
2203e22bae30SSreekanth Reddy 	kfree(sas_expander);
2204e22bae30SSreekanth Reddy }
2205e22bae30SSreekanth Reddy 
2206e22bae30SSreekanth Reddy /**
2207e22bae30SSreekanth Reddy  * mpi3mr_expander_remove - Remove expander object
2208e22bae30SSreekanth Reddy  * @mrioc: Adapter instance reference
2209e22bae30SSreekanth Reddy  * @sas_address: Remove expander sas_address
2210e22bae30SSreekanth Reddy  * @hba_port: HBA port reference
2211e22bae30SSreekanth Reddy  *
2212e22bae30SSreekanth Reddy  * This function remove expander object, stored in
2213e22bae30SSreekanth Reddy  * mrioc->sas_expander_list and removes it from the SAS TL by
2214e22bae30SSreekanth Reddy  * calling mpi3mr_expander_node_remove().
2215e22bae30SSreekanth Reddy  *
2216e22bae30SSreekanth Reddy  * Return: None
2217e22bae30SSreekanth Reddy  */
mpi3mr_expander_remove(struct mpi3mr_ioc * mrioc,u64 sas_address,struct mpi3mr_hba_port * hba_port)2218e22bae30SSreekanth Reddy void mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
2219e22bae30SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port)
2220e22bae30SSreekanth Reddy {
2221e22bae30SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander;
2222e22bae30SSreekanth Reddy 	unsigned long flags;
2223e22bae30SSreekanth Reddy 
2224e22bae30SSreekanth Reddy 	if (mrioc->reset_in_progress)
2225e22bae30SSreekanth Reddy 		return;
2226e22bae30SSreekanth Reddy 
2227e22bae30SSreekanth Reddy 	if (!hba_port)
2228e22bae30SSreekanth Reddy 		return;
2229e22bae30SSreekanth Reddy 
2230e22bae30SSreekanth Reddy 	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
2231e22bae30SSreekanth Reddy 	sas_expander = mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
2232e22bae30SSreekanth Reddy 	    hba_port);
2233e22bae30SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
2234e22bae30SSreekanth Reddy 	if (sas_expander)
2235e22bae30SSreekanth Reddy 		mpi3mr_expander_node_remove(mrioc, sas_expander);
2236e22bae30SSreekanth Reddy 
2237e22bae30SSreekanth Reddy }
22387f56c791SSreekanth Reddy 
22397f56c791SSreekanth Reddy /**
22407f56c791SSreekanth Reddy  * mpi3mr_get_sas_negotiated_logical_linkrate - get linkrate
22417f56c791SSreekanth Reddy  * @mrioc: Adapter instance reference
22427f56c791SSreekanth Reddy  * @tgtdev: Target device
22437f56c791SSreekanth Reddy  *
22447f56c791SSreekanth Reddy  * This function identifies whether the target device is
22457f56c791SSreekanth Reddy  * attached directly or through expander and issues sas phy
22467f56c791SSreekanth Reddy  * page0 or expander phy page1 and gets the link rate, if there
22477f56c791SSreekanth Reddy  * is any failure in reading the pages then this returns link
22487f56c791SSreekanth Reddy  * rate of 1.5.
22497f56c791SSreekanth Reddy  *
22507f56c791SSreekanth Reddy  * Return: logical link rate.
22517f56c791SSreekanth Reddy  */
mpi3mr_get_sas_negotiated_logical_linkrate(struct mpi3mr_ioc * mrioc,struct mpi3mr_tgt_dev * tgtdev)22527f56c791SSreekanth Reddy static u8 mpi3mr_get_sas_negotiated_logical_linkrate(struct mpi3mr_ioc *mrioc,
22537f56c791SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev)
22547f56c791SSreekanth Reddy {
22557f56c791SSreekanth Reddy 	u8 link_rate = MPI3_SAS_NEG_LINK_RATE_1_5, phy_number;
22567f56c791SSreekanth Reddy 	struct mpi3_sas_expander_page1 expander_pg1;
22577f56c791SSreekanth Reddy 	struct mpi3_sas_phy_page0 phy_pg0;
22587f56c791SSreekanth Reddy 	u32 phynum_handle;
22597f56c791SSreekanth Reddy 	u16 ioc_status;
22607f56c791SSreekanth Reddy 
22617f56c791SSreekanth Reddy 	phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
22627f56c791SSreekanth Reddy 	if (!(tgtdev->devpg0_flag & MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED)) {
22637f56c791SSreekanth Reddy 		phynum_handle = ((phy_number<<MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT)
22647f56c791SSreekanth Reddy 				 | tgtdev->parent_handle);
22657f56c791SSreekanth Reddy 		if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
22667f56c791SSreekanth Reddy 		    &expander_pg1, sizeof(expander_pg1),
22677f56c791SSreekanth Reddy 		    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
22687f56c791SSreekanth Reddy 		    phynum_handle)) {
22697f56c791SSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
22707f56c791SSreekanth Reddy 			    __FILE__, __LINE__, __func__);
22717f56c791SSreekanth Reddy 			goto out;
22727f56c791SSreekanth Reddy 		}
22737f56c791SSreekanth Reddy 		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
22747f56c791SSreekanth Reddy 			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
22757f56c791SSreekanth Reddy 			    __FILE__, __LINE__, __func__);
22767f56c791SSreekanth Reddy 			goto out;
22777f56c791SSreekanth Reddy 		}
22787f56c791SSreekanth Reddy 		link_rate = (expander_pg1.negotiated_link_rate &
22797f56c791SSreekanth Reddy 			     MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
22807f56c791SSreekanth Reddy 			MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
22817f56c791SSreekanth Reddy 		goto out;
22827f56c791SSreekanth Reddy 	}
22837f56c791SSreekanth Reddy 	if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
22847f56c791SSreekanth Reddy 	    sizeof(struct mpi3_sas_phy_page0),
22857f56c791SSreekanth Reddy 	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy_number)) {
22867f56c791SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
22877f56c791SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
22887f56c791SSreekanth Reddy 		goto out;
22897f56c791SSreekanth Reddy 	}
22907f56c791SSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
22917f56c791SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
22927f56c791SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
22937f56c791SSreekanth Reddy 		goto out;
22947f56c791SSreekanth Reddy 	}
22957f56c791SSreekanth Reddy 	link_rate = (phy_pg0.negotiated_link_rate &
22967f56c791SSreekanth Reddy 		     MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
22977f56c791SSreekanth Reddy 		MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
22987f56c791SSreekanth Reddy out:
22997f56c791SSreekanth Reddy 	return link_rate;
23007f56c791SSreekanth Reddy }
23017f56c791SSreekanth Reddy 
23027f56c791SSreekanth Reddy /**
23037f56c791SSreekanth Reddy  * mpi3mr_report_tgtdev_to_sas_transport - expose dev to SAS TL
23047f56c791SSreekanth Reddy  * @mrioc: Adapter instance reference
23057f56c791SSreekanth Reddy  * @tgtdev: Target device
23067f56c791SSreekanth Reddy  *
23077f56c791SSreekanth Reddy  * This function exposes the target device after
23087f56c791SSreekanth Reddy  * preparing host_phy, setting up link rate etc.
23097f56c791SSreekanth Reddy  *
23107f56c791SSreekanth Reddy  * Return: 0 on success, non-zero for failure.
23117f56c791SSreekanth Reddy  */
mpi3mr_report_tgtdev_to_sas_transport(struct mpi3mr_ioc * mrioc,struct mpi3mr_tgt_dev * tgtdev)23127f56c791SSreekanth Reddy int mpi3mr_report_tgtdev_to_sas_transport(struct mpi3mr_ioc *mrioc,
23137f56c791SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev)
23147f56c791SSreekanth Reddy {
23157f56c791SSreekanth Reddy 	int retval = 0;
23167f56c791SSreekanth Reddy 	u8 link_rate, parent_phy_number;
23177f56c791SSreekanth Reddy 	u64 sas_address_parent, sas_address;
23187f56c791SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port;
23197f56c791SSreekanth Reddy 	u8 port_id;
23207f56c791SSreekanth Reddy 
23217f56c791SSreekanth Reddy 	if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
23227f56c791SSreekanth Reddy 	    !mrioc->sas_transport_enabled)
23237f56c791SSreekanth Reddy 		return -1;
23247f56c791SSreekanth Reddy 
23257f56c791SSreekanth Reddy 	sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
23267f56c791SSreekanth Reddy 	if (!mrioc->sas_hba.num_phys)
23277f56c791SSreekanth Reddy 		mpi3mr_sas_host_add(mrioc);
23287f56c791SSreekanth Reddy 	else
23297f56c791SSreekanth Reddy 		mpi3mr_sas_host_refresh(mrioc);
23307f56c791SSreekanth Reddy 
23317f56c791SSreekanth Reddy 	if (mpi3mr_get_sas_address(mrioc, tgtdev->parent_handle,
23327f56c791SSreekanth Reddy 	    &sas_address_parent) != 0) {
23337f56c791SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
23347f56c791SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
23357f56c791SSreekanth Reddy 		return -1;
23367f56c791SSreekanth Reddy 	}
23377f56c791SSreekanth Reddy 	tgtdev->dev_spec.sas_sata_inf.sas_address_parent = sas_address_parent;
23387f56c791SSreekanth Reddy 
23397f56c791SSreekanth Reddy 	parent_phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
23407f56c791SSreekanth Reddy 	port_id = tgtdev->io_unit_port;
23417f56c791SSreekanth Reddy 
23427f56c791SSreekanth Reddy 	hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
23437f56c791SSreekanth Reddy 	if (!hba_port) {
23447f56c791SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
23457f56c791SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
23467f56c791SSreekanth Reddy 		return -1;
23477f56c791SSreekanth Reddy 	}
23487f56c791SSreekanth Reddy 	tgtdev->dev_spec.sas_sata_inf.hba_port = hba_port;
23497f56c791SSreekanth Reddy 
23507f56c791SSreekanth Reddy 	link_rate = mpi3mr_get_sas_negotiated_logical_linkrate(mrioc, tgtdev);
23517f56c791SSreekanth Reddy 
23527f56c791SSreekanth Reddy 	mpi3mr_update_links(mrioc, sas_address_parent, tgtdev->dev_handle,
23537f56c791SSreekanth Reddy 	    parent_phy_number, link_rate, hba_port);
23547f56c791SSreekanth Reddy 
23557f56c791SSreekanth Reddy 	tgtdev->host_exposed = 1;
23567f56c791SSreekanth Reddy 	if (!mpi3mr_sas_port_add(mrioc, tgtdev->dev_handle,
23577f56c791SSreekanth Reddy 	    sas_address_parent, hba_port)) {
23587f56c791SSreekanth Reddy 		retval = -1;
23598e451839SRanjan Kumar 		} else if ((!tgtdev->starget) && (!mrioc->is_driver_loading)) {
23607f56c791SSreekanth Reddy 			mpi3mr_sas_port_remove(mrioc, sas_address,
23617f56c791SSreekanth Reddy 			    sas_address_parent, hba_port);
23627f56c791SSreekanth Reddy 		retval = -1;
23637f56c791SSreekanth Reddy 	}
23648e451839SRanjan Kumar 	if (retval) {
23658e451839SRanjan Kumar 		tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
23668e451839SRanjan Kumar 		tgtdev->host_exposed = 0;
23678e451839SRanjan Kumar 	}
23687f56c791SSreekanth Reddy 	return retval;
23697f56c791SSreekanth Reddy }
23707f56c791SSreekanth Reddy 
23717f56c791SSreekanth Reddy /**
23727f56c791SSreekanth Reddy  * mpi3mr_remove_tgtdev_from_sas_transport - remove from SAS TL
23737f56c791SSreekanth Reddy  * @mrioc: Adapter instance reference
23747f56c791SSreekanth Reddy  * @tgtdev: Target device
23757f56c791SSreekanth Reddy  *
23767f56c791SSreekanth Reddy  * This function removes the target device
23777f56c791SSreekanth Reddy  *
23787f56c791SSreekanth Reddy  * Return: None.
23797f56c791SSreekanth Reddy  */
mpi3mr_remove_tgtdev_from_sas_transport(struct mpi3mr_ioc * mrioc,struct mpi3mr_tgt_dev * tgtdev)23807f56c791SSreekanth Reddy void mpi3mr_remove_tgtdev_from_sas_transport(struct mpi3mr_ioc *mrioc,
23817f56c791SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev)
23827f56c791SSreekanth Reddy {
23837f56c791SSreekanth Reddy 	u64 sas_address_parent, sas_address;
23847f56c791SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port;
23857f56c791SSreekanth Reddy 
23867f56c791SSreekanth Reddy 	if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
23877f56c791SSreekanth Reddy 	    !mrioc->sas_transport_enabled)
23887f56c791SSreekanth Reddy 		return;
23897f56c791SSreekanth Reddy 
23907f56c791SSreekanth Reddy 	hba_port = tgtdev->dev_spec.sas_sata_inf.hba_port;
23917f56c791SSreekanth Reddy 	sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
23927f56c791SSreekanth Reddy 	sas_address_parent = tgtdev->dev_spec.sas_sata_inf.sas_address_parent;
23937f56c791SSreekanth Reddy 	mpi3mr_sas_port_remove(mrioc, sas_address, sas_address_parent,
23947f56c791SSreekanth Reddy 	    hba_port);
23957f56c791SSreekanth Reddy 	tgtdev->host_exposed = 0;
23968e451839SRanjan Kumar 	tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
23977f56c791SSreekanth Reddy }
2398176d4aa6SSreekanth Reddy 
2399176d4aa6SSreekanth Reddy /**
2400176d4aa6SSreekanth Reddy  * mpi3mr_get_port_id_by_sas_phy -  Get port ID of the given phy
2401176d4aa6SSreekanth Reddy  * @phy: SAS transport layer phy object
2402176d4aa6SSreekanth Reddy  *
2403176d4aa6SSreekanth Reddy  * Return: Port number for valid ID else 0xFFFF
2404176d4aa6SSreekanth Reddy  */
mpi3mr_get_port_id_by_sas_phy(struct sas_phy * phy)2405176d4aa6SSreekanth Reddy static inline u8 mpi3mr_get_port_id_by_sas_phy(struct sas_phy *phy)
2406176d4aa6SSreekanth Reddy {
2407176d4aa6SSreekanth Reddy 	u8 port_id = 0xFF;
2408176d4aa6SSreekanth Reddy 	struct mpi3mr_hba_port *hba_port = phy->hostdata;
2409176d4aa6SSreekanth Reddy 
2410176d4aa6SSreekanth Reddy 	if (hba_port)
2411176d4aa6SSreekanth Reddy 		port_id = hba_port->port_id;
2412176d4aa6SSreekanth Reddy 
2413176d4aa6SSreekanth Reddy 	return port_id;
2414176d4aa6SSreekanth Reddy }
2415176d4aa6SSreekanth Reddy 
2416176d4aa6SSreekanth Reddy /**
2417176d4aa6SSreekanth Reddy  * mpi3mr_get_port_id_by_rphy - Get Port number from SAS rphy
2418176d4aa6SSreekanth Reddy  *
2419176d4aa6SSreekanth Reddy  * @mrioc: Adapter instance reference
2420176d4aa6SSreekanth Reddy  * @rphy: SAS transport layer remote phy object
2421176d4aa6SSreekanth Reddy  *
2422176d4aa6SSreekanth Reddy  * Retrieves HBA port number in which the device pointed by the
2423176d4aa6SSreekanth Reddy  * rphy object is attached with.
2424176d4aa6SSreekanth Reddy  *
2425176d4aa6SSreekanth Reddy  * Return: Valid port number on success else OxFFFF.
2426176d4aa6SSreekanth Reddy  */
mpi3mr_get_port_id_by_rphy(struct mpi3mr_ioc * mrioc,struct sas_rphy * rphy)2427176d4aa6SSreekanth Reddy static u8 mpi3mr_get_port_id_by_rphy(struct mpi3mr_ioc *mrioc, struct sas_rphy *rphy)
2428176d4aa6SSreekanth Reddy {
2429176d4aa6SSreekanth Reddy 	struct mpi3mr_sas_node *sas_expander;
2430176d4aa6SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev;
2431176d4aa6SSreekanth Reddy 	unsigned long flags;
2432176d4aa6SSreekanth Reddy 	u8 port_id = 0xFF;
2433176d4aa6SSreekanth Reddy 
2434176d4aa6SSreekanth Reddy 	if (!rphy)
2435176d4aa6SSreekanth Reddy 		return port_id;
2436176d4aa6SSreekanth Reddy 
2437176d4aa6SSreekanth Reddy 	if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
2438176d4aa6SSreekanth Reddy 	    rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) {
2439176d4aa6SSreekanth Reddy 		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
2440176d4aa6SSreekanth Reddy 		list_for_each_entry(sas_expander, &mrioc->sas_expander_list,
2441176d4aa6SSreekanth Reddy 		    list) {
2442176d4aa6SSreekanth Reddy 			if (sas_expander->rphy == rphy) {
2443176d4aa6SSreekanth Reddy 				port_id = sas_expander->hba_port->port_id;
2444176d4aa6SSreekanth Reddy 				break;
2445176d4aa6SSreekanth Reddy 			}
2446176d4aa6SSreekanth Reddy 		}
2447176d4aa6SSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
2448176d4aa6SSreekanth Reddy 	} else if (rphy->identify.device_type == SAS_END_DEVICE) {
2449176d4aa6SSreekanth Reddy 		spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
2450176d4aa6SSreekanth Reddy 
2451176d4aa6SSreekanth Reddy 		tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
2452176d4aa6SSreekanth Reddy 			    rphy->identify.sas_address, rphy);
24538e451839SRanjan Kumar 		if (tgtdev && tgtdev->dev_spec.sas_sata_inf.hba_port) {
2454176d4aa6SSreekanth Reddy 			port_id =
2455176d4aa6SSreekanth Reddy 				tgtdev->dev_spec.sas_sata_inf.hba_port->port_id;
2456176d4aa6SSreekanth Reddy 			mpi3mr_tgtdev_put(tgtdev);
2457176d4aa6SSreekanth Reddy 		}
2458176d4aa6SSreekanth Reddy 		spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
2459176d4aa6SSreekanth Reddy 	}
2460176d4aa6SSreekanth Reddy 	return port_id;
2461176d4aa6SSreekanth Reddy }
2462176d4aa6SSreekanth Reddy 
phy_to_mrioc(struct sas_phy * phy)2463176d4aa6SSreekanth Reddy static inline struct mpi3mr_ioc *phy_to_mrioc(struct sas_phy *phy)
2464176d4aa6SSreekanth Reddy {
2465176d4aa6SSreekanth Reddy 	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
2466176d4aa6SSreekanth Reddy 
2467176d4aa6SSreekanth Reddy 	return shost_priv(shost);
2468176d4aa6SSreekanth Reddy }
2469176d4aa6SSreekanth Reddy 
rphy_to_mrioc(struct sas_rphy * rphy)2470176d4aa6SSreekanth Reddy static inline struct mpi3mr_ioc *rphy_to_mrioc(struct sas_rphy *rphy)
2471176d4aa6SSreekanth Reddy {
2472176d4aa6SSreekanth Reddy 	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
2473176d4aa6SSreekanth Reddy 
2474176d4aa6SSreekanth Reddy 	return shost_priv(shost);
2475176d4aa6SSreekanth Reddy }
2476176d4aa6SSreekanth Reddy 
2477176d4aa6SSreekanth Reddy /* report phy error log structure */
2478176d4aa6SSreekanth Reddy struct phy_error_log_request {
2479176d4aa6SSreekanth Reddy 	u8 smp_frame_type; /* 0x40 */
2480176d4aa6SSreekanth Reddy 	u8 function; /* 0x11 */
2481176d4aa6SSreekanth Reddy 	u8 allocated_response_length;
2482176d4aa6SSreekanth Reddy 	u8 request_length; /* 02 */
2483176d4aa6SSreekanth Reddy 	u8 reserved_1[5];
2484176d4aa6SSreekanth Reddy 	u8 phy_identifier;
2485176d4aa6SSreekanth Reddy 	u8 reserved_2[2];
2486176d4aa6SSreekanth Reddy };
2487176d4aa6SSreekanth Reddy 
2488176d4aa6SSreekanth Reddy /* report phy error log reply structure */
2489176d4aa6SSreekanth Reddy struct phy_error_log_reply {
2490176d4aa6SSreekanth Reddy 	u8 smp_frame_type; /* 0x41 */
2491176d4aa6SSreekanth Reddy 	u8 function; /* 0x11 */
2492176d4aa6SSreekanth Reddy 	u8 function_result;
2493176d4aa6SSreekanth Reddy 	u8 response_length;
2494176d4aa6SSreekanth Reddy 	__be16 expander_change_count;
2495176d4aa6SSreekanth Reddy 	u8 reserved_1[3];
2496176d4aa6SSreekanth Reddy 	u8 phy_identifier;
2497176d4aa6SSreekanth Reddy 	u8 reserved_2[2];
2498176d4aa6SSreekanth Reddy 	__be32 invalid_dword;
2499176d4aa6SSreekanth Reddy 	__be32 running_disparity_error;
2500176d4aa6SSreekanth Reddy 	__be32 loss_of_dword_sync;
2501176d4aa6SSreekanth Reddy 	__be32 phy_reset_problem;
2502176d4aa6SSreekanth Reddy };
2503176d4aa6SSreekanth Reddy 
2504176d4aa6SSreekanth Reddy 
2505176d4aa6SSreekanth Reddy /**
2506176d4aa6SSreekanth Reddy  * mpi3mr_get_expander_phy_error_log - return expander counters:
2507176d4aa6SSreekanth Reddy  * @mrioc: Adapter instance reference
2508176d4aa6SSreekanth Reddy  * @phy: The SAS transport layer phy object
2509176d4aa6SSreekanth Reddy  *
2510176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
2511176d4aa6SSreekanth Reddy  *
2512176d4aa6SSreekanth Reddy  */
mpi3mr_get_expander_phy_error_log(struct mpi3mr_ioc * mrioc,struct sas_phy * phy)2513176d4aa6SSreekanth Reddy static int mpi3mr_get_expander_phy_error_log(struct mpi3mr_ioc *mrioc,
2514176d4aa6SSreekanth Reddy 	struct sas_phy *phy)
2515176d4aa6SSreekanth Reddy {
2516176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_request mpi_request;
2517176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_reply mpi_reply;
2518176d4aa6SSreekanth Reddy 	struct phy_error_log_request *phy_error_log_request;
2519176d4aa6SSreekanth Reddy 	struct phy_error_log_reply *phy_error_log_reply;
2520176d4aa6SSreekanth Reddy 	int rc;
2521176d4aa6SSreekanth Reddy 	void *psge;
2522176d4aa6SSreekanth Reddy 	void *data_out = NULL;
2523176d4aa6SSreekanth Reddy 	dma_addr_t data_out_dma, data_in_dma;
2524176d4aa6SSreekanth Reddy 	u32 data_out_sz, data_in_sz, sz;
2525176d4aa6SSreekanth Reddy 	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
2526176d4aa6SSreekanth Reddy 	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
2527176d4aa6SSreekanth Reddy 	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
2528176d4aa6SSreekanth Reddy 	u16 ioc_status;
2529176d4aa6SSreekanth Reddy 
2530176d4aa6SSreekanth Reddy 	if (mrioc->reset_in_progress) {
2531176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
2532176d4aa6SSreekanth Reddy 		return -EFAULT;
2533176d4aa6SSreekanth Reddy 	}
2534176d4aa6SSreekanth Reddy 
2535176d4aa6SSreekanth Reddy 	data_out_sz = sizeof(struct phy_error_log_request);
2536176d4aa6SSreekanth Reddy 	data_in_sz = sizeof(struct phy_error_log_reply);
2537176d4aa6SSreekanth Reddy 	sz = data_out_sz + data_in_sz;
2538176d4aa6SSreekanth Reddy 	data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
2539176d4aa6SSreekanth Reddy 	    GFP_KERNEL);
2540176d4aa6SSreekanth Reddy 	if (!data_out) {
2541176d4aa6SSreekanth Reddy 		rc = -ENOMEM;
2542176d4aa6SSreekanth Reddy 		goto out;
2543176d4aa6SSreekanth Reddy 	}
2544176d4aa6SSreekanth Reddy 
2545176d4aa6SSreekanth Reddy 	data_in_dma = data_out_dma + data_out_sz;
2546176d4aa6SSreekanth Reddy 	phy_error_log_reply = data_out + data_out_sz;
2547176d4aa6SSreekanth Reddy 
2548176d4aa6SSreekanth Reddy 	rc = -EINVAL;
2549176d4aa6SSreekanth Reddy 	memset(data_out, 0, sz);
2550176d4aa6SSreekanth Reddy 	phy_error_log_request = data_out;
2551176d4aa6SSreekanth Reddy 	phy_error_log_request->smp_frame_type = 0x40;
2552176d4aa6SSreekanth Reddy 	phy_error_log_request->function = 0x11;
2553176d4aa6SSreekanth Reddy 	phy_error_log_request->request_length = 2;
2554176d4aa6SSreekanth Reddy 	phy_error_log_request->allocated_response_length = 0;
2555176d4aa6SSreekanth Reddy 	phy_error_log_request->phy_identifier = phy->number;
2556176d4aa6SSreekanth Reddy 
2557176d4aa6SSreekanth Reddy 	memset(&mpi_request, 0, request_sz);
2558176d4aa6SSreekanth Reddy 	memset(&mpi_reply, 0, reply_sz);
2559176d4aa6SSreekanth Reddy 	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
2560176d4aa6SSreekanth Reddy 	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
2561176d4aa6SSreekanth Reddy 	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
2562176d4aa6SSreekanth Reddy 	mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
2563176d4aa6SSreekanth Reddy 
2564176d4aa6SSreekanth Reddy 	psge = &mpi_request.request_sge;
2565176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
2566176d4aa6SSreekanth Reddy 
2567176d4aa6SSreekanth Reddy 	psge = &mpi_request.response_sge;
2568176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
2569176d4aa6SSreekanth Reddy 
2570176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2571176d4aa6SSreekanth Reddy 	    "sending phy error log SMP request to sas_address(0x%016llx), phy_id(%d)\n",
2572176d4aa6SSreekanth Reddy 	    (unsigned long long)phy->identify.sas_address, phy->number);
2573176d4aa6SSreekanth Reddy 
2574176d4aa6SSreekanth Reddy 	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
2575176d4aa6SSreekanth Reddy 	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
2576176d4aa6SSreekanth Reddy 		goto out;
2577176d4aa6SSreekanth Reddy 
2578176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2579176d4aa6SSreekanth Reddy 	    "phy error log SMP request completed with ioc_status(0x%04x)\n",
2580176d4aa6SSreekanth Reddy 	    ioc_status);
2581176d4aa6SSreekanth Reddy 
2582176d4aa6SSreekanth Reddy 	if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
2583176d4aa6SSreekanth Reddy 		dprint_transport_info(mrioc,
2584176d4aa6SSreekanth Reddy 		    "phy error log - reply data transfer size(%d)\n",
2585176d4aa6SSreekanth Reddy 		    le16_to_cpu(mpi_reply.response_data_length));
2586176d4aa6SSreekanth Reddy 
2587176d4aa6SSreekanth Reddy 		if (le16_to_cpu(mpi_reply.response_data_length) !=
2588176d4aa6SSreekanth Reddy 		    sizeof(struct phy_error_log_reply))
2589176d4aa6SSreekanth Reddy 			goto out;
2590176d4aa6SSreekanth Reddy 
2591176d4aa6SSreekanth Reddy 		dprint_transport_info(mrioc,
2592176d4aa6SSreekanth Reddy 		    "phy error log - function_result(%d)\n",
2593176d4aa6SSreekanth Reddy 		    phy_error_log_reply->function_result);
2594176d4aa6SSreekanth Reddy 
2595176d4aa6SSreekanth Reddy 		phy->invalid_dword_count =
2596176d4aa6SSreekanth Reddy 		    be32_to_cpu(phy_error_log_reply->invalid_dword);
2597176d4aa6SSreekanth Reddy 		phy->running_disparity_error_count =
2598176d4aa6SSreekanth Reddy 		    be32_to_cpu(phy_error_log_reply->running_disparity_error);
2599176d4aa6SSreekanth Reddy 		phy->loss_of_dword_sync_count =
2600176d4aa6SSreekanth Reddy 		    be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
2601176d4aa6SSreekanth Reddy 		phy->phy_reset_problem_count =
2602176d4aa6SSreekanth Reddy 		    be32_to_cpu(phy_error_log_reply->phy_reset_problem);
2603176d4aa6SSreekanth Reddy 		rc = 0;
2604176d4aa6SSreekanth Reddy 	}
2605176d4aa6SSreekanth Reddy 
2606176d4aa6SSreekanth Reddy out:
2607176d4aa6SSreekanth Reddy 	if (data_out)
2608176d4aa6SSreekanth Reddy 		dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
2609176d4aa6SSreekanth Reddy 		    data_out_dma);
2610176d4aa6SSreekanth Reddy 
2611176d4aa6SSreekanth Reddy 	return rc;
2612176d4aa6SSreekanth Reddy }
2613176d4aa6SSreekanth Reddy 
2614176d4aa6SSreekanth Reddy /**
2615176d4aa6SSreekanth Reddy  * mpi3mr_transport_get_linkerrors - return phy error counters
2616176d4aa6SSreekanth Reddy  * @phy: The SAS transport layer phy object
2617176d4aa6SSreekanth Reddy  *
2618176d4aa6SSreekanth Reddy  * This function retrieves the phy error log information of the
2619176d4aa6SSreekanth Reddy  * HBA or expander for which the phy belongs to
2620176d4aa6SSreekanth Reddy  *
2621176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
2622176d4aa6SSreekanth Reddy  */
mpi3mr_transport_get_linkerrors(struct sas_phy * phy)2623176d4aa6SSreekanth Reddy static int mpi3mr_transport_get_linkerrors(struct sas_phy *phy)
2624176d4aa6SSreekanth Reddy {
2625176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
2626176d4aa6SSreekanth Reddy 	struct mpi3_sas_phy_page1 phy_pg1;
2627176d4aa6SSreekanth Reddy 	int rc = 0;
2628176d4aa6SSreekanth Reddy 	u16 ioc_status;
2629176d4aa6SSreekanth Reddy 
2630176d4aa6SSreekanth Reddy 	rc = mpi3mr_parent_present(mrioc, phy);
2631176d4aa6SSreekanth Reddy 	if (rc)
2632176d4aa6SSreekanth Reddy 		return rc;
2633176d4aa6SSreekanth Reddy 
2634176d4aa6SSreekanth Reddy 	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
2635176d4aa6SSreekanth Reddy 		return mpi3mr_get_expander_phy_error_log(mrioc, phy);
2636176d4aa6SSreekanth Reddy 
2637176d4aa6SSreekanth Reddy 	memset(&phy_pg1, 0, sizeof(struct mpi3_sas_phy_page1));
2638176d4aa6SSreekanth Reddy 	/* get hba phy error logs */
2639176d4aa6SSreekanth Reddy 	if ((mpi3mr_cfg_get_sas_phy_pg1(mrioc, &ioc_status, &phy_pg1,
2640176d4aa6SSreekanth Reddy 	    sizeof(struct mpi3_sas_phy_page1),
2641176d4aa6SSreekanth Reddy 	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number))) {
2642176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2643176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
2644176d4aa6SSreekanth Reddy 		return -ENXIO;
2645176d4aa6SSreekanth Reddy 	}
2646176d4aa6SSreekanth Reddy 
2647176d4aa6SSreekanth Reddy 	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
2648176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2649176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
2650176d4aa6SSreekanth Reddy 		return -ENXIO;
2651176d4aa6SSreekanth Reddy 	}
2652176d4aa6SSreekanth Reddy 	phy->invalid_dword_count = le32_to_cpu(phy_pg1.invalid_dword_count);
2653176d4aa6SSreekanth Reddy 	phy->running_disparity_error_count =
2654176d4aa6SSreekanth Reddy 		le32_to_cpu(phy_pg1.running_disparity_error_count);
2655176d4aa6SSreekanth Reddy 	phy->loss_of_dword_sync_count =
2656176d4aa6SSreekanth Reddy 		le32_to_cpu(phy_pg1.loss_dword_synch_count);
2657176d4aa6SSreekanth Reddy 	phy->phy_reset_problem_count =
2658176d4aa6SSreekanth Reddy 		le32_to_cpu(phy_pg1.phy_reset_problem_count);
2659176d4aa6SSreekanth Reddy 	return 0;
2660176d4aa6SSreekanth Reddy }
2661176d4aa6SSreekanth Reddy 
2662176d4aa6SSreekanth Reddy /**
2663176d4aa6SSreekanth Reddy  * mpi3mr_transport_get_enclosure_identifier - Get Enclosure ID
2664176d4aa6SSreekanth Reddy  * @rphy: The SAS transport layer remote phy object
2665176d4aa6SSreekanth Reddy  * @identifier: Enclosure identifier to be returned
2666176d4aa6SSreekanth Reddy  *
2667176d4aa6SSreekanth Reddy  * Returns the enclosure id for the device pointed by the remote
2668176d4aa6SSreekanth Reddy  * phy object.
2669176d4aa6SSreekanth Reddy  *
2670176d4aa6SSreekanth Reddy  * Return: 0 on success or -ENXIO
2671176d4aa6SSreekanth Reddy  */
2672176d4aa6SSreekanth Reddy static int
mpi3mr_transport_get_enclosure_identifier(struct sas_rphy * rphy,u64 * identifier)2673176d4aa6SSreekanth Reddy mpi3mr_transport_get_enclosure_identifier(struct sas_rphy *rphy,
2674176d4aa6SSreekanth Reddy 	u64 *identifier)
2675176d4aa6SSreekanth Reddy {
2676176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
2677176d4aa6SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev = NULL;
2678176d4aa6SSreekanth Reddy 	unsigned long flags;
2679176d4aa6SSreekanth Reddy 	int rc;
2680176d4aa6SSreekanth Reddy 
2681176d4aa6SSreekanth Reddy 	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
2682176d4aa6SSreekanth Reddy 	tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
2683176d4aa6SSreekanth Reddy 	    rphy->identify.sas_address, rphy);
2684176d4aa6SSreekanth Reddy 	if (tgtdev) {
2685176d4aa6SSreekanth Reddy 		*identifier =
2686176d4aa6SSreekanth Reddy 			tgtdev->enclosure_logical_id;
2687176d4aa6SSreekanth Reddy 		rc = 0;
2688176d4aa6SSreekanth Reddy 		mpi3mr_tgtdev_put(tgtdev);
2689176d4aa6SSreekanth Reddy 	} else {
2690176d4aa6SSreekanth Reddy 		*identifier = 0;
2691176d4aa6SSreekanth Reddy 		rc = -ENXIO;
2692176d4aa6SSreekanth Reddy 	}
2693176d4aa6SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
2694176d4aa6SSreekanth Reddy 
2695176d4aa6SSreekanth Reddy 	return rc;
2696176d4aa6SSreekanth Reddy }
2697176d4aa6SSreekanth Reddy 
2698176d4aa6SSreekanth Reddy /**
2699176d4aa6SSreekanth Reddy  * mpi3mr_transport_get_bay_identifier - Get bay ID
2700176d4aa6SSreekanth Reddy  * @rphy: The SAS transport layer remote phy object
2701176d4aa6SSreekanth Reddy  *
2702176d4aa6SSreekanth Reddy  * Returns the slot id for the device pointed by the remote phy
2703176d4aa6SSreekanth Reddy  * object.
2704176d4aa6SSreekanth Reddy  *
2705176d4aa6SSreekanth Reddy  * Return: Valid slot ID on success or -ENXIO
2706176d4aa6SSreekanth Reddy  */
2707176d4aa6SSreekanth Reddy static int
mpi3mr_transport_get_bay_identifier(struct sas_rphy * rphy)2708176d4aa6SSreekanth Reddy mpi3mr_transport_get_bay_identifier(struct sas_rphy *rphy)
2709176d4aa6SSreekanth Reddy {
2710176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
2711176d4aa6SSreekanth Reddy 	struct mpi3mr_tgt_dev *tgtdev = NULL;
2712176d4aa6SSreekanth Reddy 	unsigned long flags;
2713176d4aa6SSreekanth Reddy 	int rc;
2714176d4aa6SSreekanth Reddy 
2715176d4aa6SSreekanth Reddy 	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
2716176d4aa6SSreekanth Reddy 	tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
2717176d4aa6SSreekanth Reddy 	    rphy->identify.sas_address, rphy);
2718176d4aa6SSreekanth Reddy 	if (tgtdev) {
2719176d4aa6SSreekanth Reddy 		rc = tgtdev->slot;
2720176d4aa6SSreekanth Reddy 		mpi3mr_tgtdev_put(tgtdev);
2721176d4aa6SSreekanth Reddy 	} else
2722176d4aa6SSreekanth Reddy 		rc = -ENXIO;
2723176d4aa6SSreekanth Reddy 	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
2724176d4aa6SSreekanth Reddy 
2725176d4aa6SSreekanth Reddy 	return rc;
2726176d4aa6SSreekanth Reddy }
2727176d4aa6SSreekanth Reddy 
2728176d4aa6SSreekanth Reddy /* phy control request structure */
2729176d4aa6SSreekanth Reddy struct phy_control_request {
2730176d4aa6SSreekanth Reddy 	u8 smp_frame_type; /* 0x40 */
2731176d4aa6SSreekanth Reddy 	u8 function; /* 0x91 */
2732176d4aa6SSreekanth Reddy 	u8 allocated_response_length;
2733176d4aa6SSreekanth Reddy 	u8 request_length; /* 0x09 */
2734176d4aa6SSreekanth Reddy 	u16 expander_change_count;
2735176d4aa6SSreekanth Reddy 	u8 reserved_1[3];
2736176d4aa6SSreekanth Reddy 	u8 phy_identifier;
2737176d4aa6SSreekanth Reddy 	u8 phy_operation;
2738176d4aa6SSreekanth Reddy 	u8 reserved_2[13];
2739176d4aa6SSreekanth Reddy 	u64 attached_device_name;
2740176d4aa6SSreekanth Reddy 	u8 programmed_min_physical_link_rate;
2741176d4aa6SSreekanth Reddy 	u8 programmed_max_physical_link_rate;
2742176d4aa6SSreekanth Reddy 	u8 reserved_3[6];
2743176d4aa6SSreekanth Reddy };
2744176d4aa6SSreekanth Reddy 
2745176d4aa6SSreekanth Reddy /* phy control reply structure */
2746176d4aa6SSreekanth Reddy struct phy_control_reply {
2747176d4aa6SSreekanth Reddy 	u8 smp_frame_type; /* 0x41 */
2748176d4aa6SSreekanth Reddy 	u8 function; /* 0x11 */
2749176d4aa6SSreekanth Reddy 	u8 function_result;
2750176d4aa6SSreekanth Reddy 	u8 response_length;
2751176d4aa6SSreekanth Reddy };
2752176d4aa6SSreekanth Reddy 
2753176d4aa6SSreekanth Reddy #define SMP_PHY_CONTROL_LINK_RESET	(0x01)
2754176d4aa6SSreekanth Reddy #define SMP_PHY_CONTROL_HARD_RESET	(0x02)
2755176d4aa6SSreekanth Reddy #define SMP_PHY_CONTROL_DISABLE		(0x03)
2756176d4aa6SSreekanth Reddy 
2757176d4aa6SSreekanth Reddy /**
2758176d4aa6SSreekanth Reddy  * mpi3mr_expander_phy_control - expander phy control
2759176d4aa6SSreekanth Reddy  * @mrioc: Adapter instance reference
2760176d4aa6SSreekanth Reddy  * @phy: The SAS transport layer phy object
2761176d4aa6SSreekanth Reddy  * @phy_operation: The phy operation to be executed
2762176d4aa6SSreekanth Reddy  *
2763176d4aa6SSreekanth Reddy  * Issues SMP passthru phy control request to execute a specific
2764176d4aa6SSreekanth Reddy  * phy operation for a given expander device.
2765176d4aa6SSreekanth Reddy  *
2766176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
2767176d4aa6SSreekanth Reddy  */
2768176d4aa6SSreekanth Reddy static int
mpi3mr_expander_phy_control(struct mpi3mr_ioc * mrioc,struct sas_phy * phy,u8 phy_operation)2769176d4aa6SSreekanth Reddy mpi3mr_expander_phy_control(struct mpi3mr_ioc *mrioc,
2770176d4aa6SSreekanth Reddy 	struct sas_phy *phy, u8 phy_operation)
2771176d4aa6SSreekanth Reddy {
2772176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_request mpi_request;
2773176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_reply mpi_reply;
2774176d4aa6SSreekanth Reddy 	struct phy_control_request *phy_control_request;
2775176d4aa6SSreekanth Reddy 	struct phy_control_reply *phy_control_reply;
2776176d4aa6SSreekanth Reddy 	int rc;
2777176d4aa6SSreekanth Reddy 	void *psge;
2778176d4aa6SSreekanth Reddy 	void *data_out = NULL;
2779176d4aa6SSreekanth Reddy 	dma_addr_t data_out_dma;
2780176d4aa6SSreekanth Reddy 	dma_addr_t data_in_dma;
2781176d4aa6SSreekanth Reddy 	size_t data_in_sz;
2782176d4aa6SSreekanth Reddy 	size_t data_out_sz;
2783176d4aa6SSreekanth Reddy 	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
2784176d4aa6SSreekanth Reddy 	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
2785176d4aa6SSreekanth Reddy 	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
2786176d4aa6SSreekanth Reddy 	u16 ioc_status;
2787176d4aa6SSreekanth Reddy 	u16 sz;
2788176d4aa6SSreekanth Reddy 
2789176d4aa6SSreekanth Reddy 	if (mrioc->reset_in_progress) {
2790176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
2791176d4aa6SSreekanth Reddy 		return -EFAULT;
2792176d4aa6SSreekanth Reddy 	}
2793176d4aa6SSreekanth Reddy 
2794176d4aa6SSreekanth Reddy 	data_out_sz = sizeof(struct phy_control_request);
2795176d4aa6SSreekanth Reddy 	data_in_sz = sizeof(struct phy_control_reply);
2796176d4aa6SSreekanth Reddy 	sz = data_out_sz + data_in_sz;
2797176d4aa6SSreekanth Reddy 	data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
2798176d4aa6SSreekanth Reddy 	    GFP_KERNEL);
2799176d4aa6SSreekanth Reddy 	if (!data_out) {
2800176d4aa6SSreekanth Reddy 		rc = -ENOMEM;
2801176d4aa6SSreekanth Reddy 		goto out;
2802176d4aa6SSreekanth Reddy 	}
2803176d4aa6SSreekanth Reddy 
2804176d4aa6SSreekanth Reddy 	data_in_dma = data_out_dma + data_out_sz;
2805176d4aa6SSreekanth Reddy 	phy_control_reply = data_out + data_out_sz;
2806176d4aa6SSreekanth Reddy 
2807176d4aa6SSreekanth Reddy 	rc = -EINVAL;
2808176d4aa6SSreekanth Reddy 	memset(data_out, 0, sz);
2809176d4aa6SSreekanth Reddy 
2810176d4aa6SSreekanth Reddy 	phy_control_request = data_out;
2811176d4aa6SSreekanth Reddy 	phy_control_request->smp_frame_type = 0x40;
2812176d4aa6SSreekanth Reddy 	phy_control_request->function = 0x91;
2813176d4aa6SSreekanth Reddy 	phy_control_request->request_length = 9;
2814176d4aa6SSreekanth Reddy 	phy_control_request->allocated_response_length = 0;
2815176d4aa6SSreekanth Reddy 	phy_control_request->phy_identifier = phy->number;
2816176d4aa6SSreekanth Reddy 	phy_control_request->phy_operation = phy_operation;
2817176d4aa6SSreekanth Reddy 	phy_control_request->programmed_min_physical_link_rate =
2818176d4aa6SSreekanth Reddy 	    phy->minimum_linkrate << 4;
2819176d4aa6SSreekanth Reddy 	phy_control_request->programmed_max_physical_link_rate =
2820176d4aa6SSreekanth Reddy 	    phy->maximum_linkrate << 4;
2821176d4aa6SSreekanth Reddy 
2822176d4aa6SSreekanth Reddy 	memset(&mpi_request, 0, request_sz);
2823176d4aa6SSreekanth Reddy 	memset(&mpi_reply, 0, reply_sz);
2824176d4aa6SSreekanth Reddy 	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
2825176d4aa6SSreekanth Reddy 	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
2826176d4aa6SSreekanth Reddy 	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
2827176d4aa6SSreekanth Reddy 	mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
2828176d4aa6SSreekanth Reddy 
2829176d4aa6SSreekanth Reddy 	psge = &mpi_request.request_sge;
2830176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
2831176d4aa6SSreekanth Reddy 
2832176d4aa6SSreekanth Reddy 	psge = &mpi_request.response_sge;
2833176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
2834176d4aa6SSreekanth Reddy 
2835176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2836176d4aa6SSreekanth Reddy 	    "sending phy control SMP request to sas_address(0x%016llx), phy_id(%d) opcode(%d)\n",
2837176d4aa6SSreekanth Reddy 	    (unsigned long long)phy->identify.sas_address, phy->number,
2838176d4aa6SSreekanth Reddy 	    phy_operation);
2839176d4aa6SSreekanth Reddy 
2840176d4aa6SSreekanth Reddy 	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
2841176d4aa6SSreekanth Reddy 	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
2842176d4aa6SSreekanth Reddy 		goto out;
2843176d4aa6SSreekanth Reddy 
2844176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2845176d4aa6SSreekanth Reddy 	    "phy control SMP request completed with ioc_status(0x%04x)\n",
2846176d4aa6SSreekanth Reddy 	    ioc_status);
2847176d4aa6SSreekanth Reddy 
2848176d4aa6SSreekanth Reddy 	if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
2849176d4aa6SSreekanth Reddy 		dprint_transport_info(mrioc,
2850176d4aa6SSreekanth Reddy 		    "phy control - reply data transfer size(%d)\n",
2851176d4aa6SSreekanth Reddy 		    le16_to_cpu(mpi_reply.response_data_length));
2852176d4aa6SSreekanth Reddy 
2853176d4aa6SSreekanth Reddy 		if (le16_to_cpu(mpi_reply.response_data_length) !=
2854176d4aa6SSreekanth Reddy 		    sizeof(struct phy_control_reply))
2855176d4aa6SSreekanth Reddy 			goto out;
2856176d4aa6SSreekanth Reddy 		dprint_transport_info(mrioc,
2857176d4aa6SSreekanth Reddy 		    "phy control - function_result(%d)\n",
2858176d4aa6SSreekanth Reddy 		    phy_control_reply->function_result);
2859176d4aa6SSreekanth Reddy 		rc = 0;
2860176d4aa6SSreekanth Reddy 	}
2861176d4aa6SSreekanth Reddy  out:
2862176d4aa6SSreekanth Reddy 	if (data_out)
2863176d4aa6SSreekanth Reddy 		dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
2864176d4aa6SSreekanth Reddy 		    data_out_dma);
2865176d4aa6SSreekanth Reddy 
2866176d4aa6SSreekanth Reddy 	return rc;
2867176d4aa6SSreekanth Reddy }
2868176d4aa6SSreekanth Reddy 
2869176d4aa6SSreekanth Reddy /**
2870176d4aa6SSreekanth Reddy  * mpi3mr_transport_phy_reset - Reset a given phy
2871176d4aa6SSreekanth Reddy  * @phy: The SAS transport layer phy object
2872176d4aa6SSreekanth Reddy  * @hard_reset: Flag to indicate the type of reset
2873176d4aa6SSreekanth Reddy  *
2874176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
2875176d4aa6SSreekanth Reddy  */
2876176d4aa6SSreekanth Reddy static int
mpi3mr_transport_phy_reset(struct sas_phy * phy,int hard_reset)2877176d4aa6SSreekanth Reddy mpi3mr_transport_phy_reset(struct sas_phy *phy, int hard_reset)
2878176d4aa6SSreekanth Reddy {
2879176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
2880176d4aa6SSreekanth Reddy 	struct mpi3_iounit_control_request mpi_request;
2881176d4aa6SSreekanth Reddy 	struct mpi3_iounit_control_reply mpi_reply;
2882176d4aa6SSreekanth Reddy 	u16 request_sz = sizeof(struct mpi3_iounit_control_request);
2883176d4aa6SSreekanth Reddy 	u16 reply_sz = sizeof(struct mpi3_iounit_control_reply);
2884176d4aa6SSreekanth Reddy 	int rc = 0;
2885176d4aa6SSreekanth Reddy 	u16 ioc_status;
2886176d4aa6SSreekanth Reddy 
2887176d4aa6SSreekanth Reddy 	rc = mpi3mr_parent_present(mrioc, phy);
2888176d4aa6SSreekanth Reddy 	if (rc)
2889176d4aa6SSreekanth Reddy 		return rc;
2890176d4aa6SSreekanth Reddy 
2891176d4aa6SSreekanth Reddy 	/* handle expander phys */
2892176d4aa6SSreekanth Reddy 	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
2893176d4aa6SSreekanth Reddy 		return mpi3mr_expander_phy_control(mrioc, phy,
2894176d4aa6SSreekanth Reddy 		    (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
2895176d4aa6SSreekanth Reddy 		    SMP_PHY_CONTROL_LINK_RESET);
2896176d4aa6SSreekanth Reddy 
2897176d4aa6SSreekanth Reddy 	/* handle hba phys */
2898176d4aa6SSreekanth Reddy 	memset(&mpi_request, 0, request_sz);
2899176d4aa6SSreekanth Reddy 	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
2900176d4aa6SSreekanth Reddy 	mpi_request.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
2901176d4aa6SSreekanth Reddy 	mpi_request.operation = MPI3_CTRL_OP_SAS_PHY_CONTROL;
2902176d4aa6SSreekanth Reddy 	mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_ACTION_INDEX] =
2903176d4aa6SSreekanth Reddy 		(hard_reset ? MPI3_CTRL_ACTION_HARD_RESET :
2904176d4aa6SSreekanth Reddy 		 MPI3_CTRL_ACTION_LINK_RESET);
2905176d4aa6SSreekanth Reddy 	mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_PHY_INDEX] =
2906176d4aa6SSreekanth Reddy 		phy->number;
2907176d4aa6SSreekanth Reddy 
2908176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2909176d4aa6SSreekanth Reddy 	    "sending phy reset request to sas_address(0x%016llx), phy_id(%d) hard_reset(%d)\n",
2910176d4aa6SSreekanth Reddy 	    (unsigned long long)phy->identify.sas_address, phy->number,
2911176d4aa6SSreekanth Reddy 	    hard_reset);
2912176d4aa6SSreekanth Reddy 
2913176d4aa6SSreekanth Reddy 	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
2914176d4aa6SSreekanth Reddy 	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status)) {
2915176d4aa6SSreekanth Reddy 		rc = -EAGAIN;
2916176d4aa6SSreekanth Reddy 		goto out;
2917176d4aa6SSreekanth Reddy 	}
2918176d4aa6SSreekanth Reddy 
2919176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
2920176d4aa6SSreekanth Reddy 	    "phy reset request completed with ioc_status(0x%04x)\n",
2921176d4aa6SSreekanth Reddy 	    ioc_status);
2922176d4aa6SSreekanth Reddy out:
2923176d4aa6SSreekanth Reddy 	return rc;
2924176d4aa6SSreekanth Reddy }
2925176d4aa6SSreekanth Reddy 
2926176d4aa6SSreekanth Reddy /**
2927176d4aa6SSreekanth Reddy  * mpi3mr_transport_phy_enable - enable/disable phys
2928176d4aa6SSreekanth Reddy  * @phy: The SAS transport layer phy object
2929176d4aa6SSreekanth Reddy  * @enable: flag to enable/disable, enable phy when true
2930176d4aa6SSreekanth Reddy  *
2931176d4aa6SSreekanth Reddy  * This function enables/disables a given by executing required
2932176d4aa6SSreekanth Reddy  * configuration page changes or expander phy control command
2933176d4aa6SSreekanth Reddy  *
2934176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
2935176d4aa6SSreekanth Reddy  */
2936176d4aa6SSreekanth Reddy static int
mpi3mr_transport_phy_enable(struct sas_phy * phy,int enable)2937176d4aa6SSreekanth Reddy mpi3mr_transport_phy_enable(struct sas_phy *phy, int enable)
2938176d4aa6SSreekanth Reddy {
2939176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
2940176d4aa6SSreekanth Reddy 	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
2941176d4aa6SSreekanth Reddy 	struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
2942176d4aa6SSreekanth Reddy 	u16 sz;
2943176d4aa6SSreekanth Reddy 	int rc = 0;
2944176d4aa6SSreekanth Reddy 	int i, discovery_active;
2945176d4aa6SSreekanth Reddy 
2946176d4aa6SSreekanth Reddy 	rc = mpi3mr_parent_present(mrioc, phy);
2947176d4aa6SSreekanth Reddy 	if (rc)
2948176d4aa6SSreekanth Reddy 		return rc;
2949176d4aa6SSreekanth Reddy 
2950176d4aa6SSreekanth Reddy 	/* handle expander phys */
2951176d4aa6SSreekanth Reddy 	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
2952176d4aa6SSreekanth Reddy 		return mpi3mr_expander_phy_control(mrioc, phy,
2953176d4aa6SSreekanth Reddy 		    (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
2954176d4aa6SSreekanth Reddy 		    SMP_PHY_CONTROL_DISABLE);
2955176d4aa6SSreekanth Reddy 
2956176d4aa6SSreekanth Reddy 	/* handle hba phys */
2957176d4aa6SSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
2958176d4aa6SSreekanth Reddy 		(mrioc->sas_hba.num_phys *
2959176d4aa6SSreekanth Reddy 		 sizeof(struct mpi3_sas_io_unit0_phy_data));
2960176d4aa6SSreekanth Reddy 	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
2961176d4aa6SSreekanth Reddy 	if (!sas_io_unit_pg0) {
2962176d4aa6SSreekanth Reddy 		rc = -ENOMEM;
2963176d4aa6SSreekanth Reddy 		goto out;
2964176d4aa6SSreekanth Reddy 	}
2965176d4aa6SSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
2966176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2967176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
2968176d4aa6SSreekanth Reddy 		rc = -ENXIO;
2969176d4aa6SSreekanth Reddy 		goto out;
2970176d4aa6SSreekanth Reddy 	}
2971176d4aa6SSreekanth Reddy 
2972176d4aa6SSreekanth Reddy 	/* unable to enable/disable phys when discovery is active */
2973176d4aa6SSreekanth Reddy 	for (i = 0, discovery_active = 0; i < mrioc->sas_hba.num_phys ; i++) {
2974176d4aa6SSreekanth Reddy 		if (sas_io_unit_pg0->phy_data[i].port_flags &
2975176d4aa6SSreekanth Reddy 		    MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS) {
2976176d4aa6SSreekanth Reddy 			ioc_err(mrioc,
2977176d4aa6SSreekanth Reddy 			    "discovery is active on port = %d, phy = %d\n"
2978176d4aa6SSreekanth Reddy 			    "\tunable to enable/disable phys, try again later!\n",
2979176d4aa6SSreekanth Reddy 			    sas_io_unit_pg0->phy_data[i].io_unit_port, i);
2980176d4aa6SSreekanth Reddy 			discovery_active = 1;
2981176d4aa6SSreekanth Reddy 		}
2982176d4aa6SSreekanth Reddy 	}
2983176d4aa6SSreekanth Reddy 
2984176d4aa6SSreekanth Reddy 	if (discovery_active) {
2985176d4aa6SSreekanth Reddy 		rc = -EAGAIN;
2986176d4aa6SSreekanth Reddy 		goto out;
2987176d4aa6SSreekanth Reddy 	}
2988176d4aa6SSreekanth Reddy 
2989176d4aa6SSreekanth Reddy 	if ((sas_io_unit_pg0->phy_data[phy->number].phy_flags &
2990176d4aa6SSreekanth Reddy 	     (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
2991176d4aa6SSreekanth Reddy 	      MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))) {
2992176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
2993176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
2994176d4aa6SSreekanth Reddy 		rc = -ENXIO;
2995176d4aa6SSreekanth Reddy 		goto out;
2996176d4aa6SSreekanth Reddy 	}
2997176d4aa6SSreekanth Reddy 
2998176d4aa6SSreekanth Reddy 	/* read sas_iounit page 1 */
2999176d4aa6SSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
3000176d4aa6SSreekanth Reddy 		(mrioc->sas_hba.num_phys *
3001176d4aa6SSreekanth Reddy 		 sizeof(struct mpi3_sas_io_unit1_phy_data));
3002176d4aa6SSreekanth Reddy 	sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
3003176d4aa6SSreekanth Reddy 	if (!sas_io_unit_pg1) {
3004176d4aa6SSreekanth Reddy 		rc = -ENOMEM;
3005176d4aa6SSreekanth Reddy 		goto out;
3006176d4aa6SSreekanth Reddy 	}
3007176d4aa6SSreekanth Reddy 
3008176d4aa6SSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
3009176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
3010176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
3011176d4aa6SSreekanth Reddy 		rc = -ENXIO;
3012176d4aa6SSreekanth Reddy 		goto out;
3013176d4aa6SSreekanth Reddy 	}
3014176d4aa6SSreekanth Reddy 
3015176d4aa6SSreekanth Reddy 	if (enable)
3016176d4aa6SSreekanth Reddy 		sas_io_unit_pg1->phy_data[phy->number].phy_flags
3017176d4aa6SSreekanth Reddy 		    &= ~MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
3018176d4aa6SSreekanth Reddy 	else
3019176d4aa6SSreekanth Reddy 		sas_io_unit_pg1->phy_data[phy->number].phy_flags
3020176d4aa6SSreekanth Reddy 		    |= MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
3021176d4aa6SSreekanth Reddy 
3022176d4aa6SSreekanth Reddy 	mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz);
3023176d4aa6SSreekanth Reddy 
3024176d4aa6SSreekanth Reddy 	/* link reset */
3025176d4aa6SSreekanth Reddy 	if (enable)
3026176d4aa6SSreekanth Reddy 		mpi3mr_transport_phy_reset(phy, 0);
3027176d4aa6SSreekanth Reddy 
3028176d4aa6SSreekanth Reddy  out:
3029176d4aa6SSreekanth Reddy 	kfree(sas_io_unit_pg1);
3030176d4aa6SSreekanth Reddy 	kfree(sas_io_unit_pg0);
3031176d4aa6SSreekanth Reddy 	return rc;
3032176d4aa6SSreekanth Reddy }
3033176d4aa6SSreekanth Reddy 
3034176d4aa6SSreekanth Reddy /**
3035176d4aa6SSreekanth Reddy  * mpi3mr_transport_phy_speed - set phy min/max speed
3036176d4aa6SSreekanth Reddy  * @phy: The SAS transport later phy object
3037176d4aa6SSreekanth Reddy  * @rates: Rates defined as in sas_phy_linkrates
3038176d4aa6SSreekanth Reddy  *
3039176d4aa6SSreekanth Reddy  * This function sets the link rates given in the rates
3040176d4aa6SSreekanth Reddy  * argument to the given phy by executing required configuration
3041176d4aa6SSreekanth Reddy  * page changes or expander phy control command
3042176d4aa6SSreekanth Reddy  *
3043176d4aa6SSreekanth Reddy  * Return: 0 for success, non-zero for failure.
3044176d4aa6SSreekanth Reddy  */
3045176d4aa6SSreekanth Reddy static int
mpi3mr_transport_phy_speed(struct sas_phy * phy,struct sas_phy_linkrates * rates)3046176d4aa6SSreekanth Reddy mpi3mr_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
3047176d4aa6SSreekanth Reddy {
3048176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
3049176d4aa6SSreekanth Reddy 	struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
3050176d4aa6SSreekanth Reddy 	struct mpi3_sas_phy_page0 phy_pg0;
3051176d4aa6SSreekanth Reddy 	u16 sz, ioc_status;
3052176d4aa6SSreekanth Reddy 	int rc = 0;
3053176d4aa6SSreekanth Reddy 
3054176d4aa6SSreekanth Reddy 	rc = mpi3mr_parent_present(mrioc, phy);
3055176d4aa6SSreekanth Reddy 	if (rc)
3056176d4aa6SSreekanth Reddy 		return rc;
3057176d4aa6SSreekanth Reddy 
3058176d4aa6SSreekanth Reddy 	if (!rates->minimum_linkrate)
3059176d4aa6SSreekanth Reddy 		rates->minimum_linkrate = phy->minimum_linkrate;
3060176d4aa6SSreekanth Reddy 	else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
3061176d4aa6SSreekanth Reddy 		rates->minimum_linkrate = phy->minimum_linkrate_hw;
3062176d4aa6SSreekanth Reddy 
3063176d4aa6SSreekanth Reddy 	if (!rates->maximum_linkrate)
3064176d4aa6SSreekanth Reddy 		rates->maximum_linkrate = phy->maximum_linkrate;
3065176d4aa6SSreekanth Reddy 	else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
3066176d4aa6SSreekanth Reddy 		rates->maximum_linkrate = phy->maximum_linkrate_hw;
3067176d4aa6SSreekanth Reddy 
3068176d4aa6SSreekanth Reddy 	/* handle expander phys */
3069176d4aa6SSreekanth Reddy 	if (phy->identify.sas_address != mrioc->sas_hba.sas_address) {
3070176d4aa6SSreekanth Reddy 		phy->minimum_linkrate = rates->minimum_linkrate;
3071176d4aa6SSreekanth Reddy 		phy->maximum_linkrate = rates->maximum_linkrate;
3072176d4aa6SSreekanth Reddy 		return mpi3mr_expander_phy_control(mrioc, phy,
3073176d4aa6SSreekanth Reddy 		    SMP_PHY_CONTROL_LINK_RESET);
3074176d4aa6SSreekanth Reddy 	}
3075176d4aa6SSreekanth Reddy 
3076176d4aa6SSreekanth Reddy 	/* handle hba phys */
3077176d4aa6SSreekanth Reddy 	sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
3078176d4aa6SSreekanth Reddy 		(mrioc->sas_hba.num_phys *
3079176d4aa6SSreekanth Reddy 		 sizeof(struct mpi3_sas_io_unit1_phy_data));
3080176d4aa6SSreekanth Reddy 	sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
3081176d4aa6SSreekanth Reddy 	if (!sas_io_unit_pg1) {
3082176d4aa6SSreekanth Reddy 		rc = -ENOMEM;
3083176d4aa6SSreekanth Reddy 		goto out;
3084176d4aa6SSreekanth Reddy 	}
3085176d4aa6SSreekanth Reddy 
3086176d4aa6SSreekanth Reddy 	if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
3087176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
3088176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
3089176d4aa6SSreekanth Reddy 		rc = -ENXIO;
3090176d4aa6SSreekanth Reddy 		goto out;
3091176d4aa6SSreekanth Reddy 	}
3092176d4aa6SSreekanth Reddy 
3093176d4aa6SSreekanth Reddy 	sas_io_unit_pg1->phy_data[phy->number].max_min_link_rate =
3094176d4aa6SSreekanth Reddy 		(rates->minimum_linkrate + (rates->maximum_linkrate << 4));
3095176d4aa6SSreekanth Reddy 
3096176d4aa6SSreekanth Reddy 	if (mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
3097176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
3098176d4aa6SSreekanth Reddy 		    __FILE__, __LINE__, __func__);
3099176d4aa6SSreekanth Reddy 		rc = -ENXIO;
3100176d4aa6SSreekanth Reddy 		goto out;
3101176d4aa6SSreekanth Reddy 	}
3102176d4aa6SSreekanth Reddy 
3103176d4aa6SSreekanth Reddy 	/* link reset */
3104176d4aa6SSreekanth Reddy 	mpi3mr_transport_phy_reset(phy, 0);
3105176d4aa6SSreekanth Reddy 
3106176d4aa6SSreekanth Reddy 	/* read phy page 0, then update the rates in the sas transport phy */
3107176d4aa6SSreekanth Reddy 	if (!mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
3108176d4aa6SSreekanth Reddy 	    sizeof(struct mpi3_sas_phy_page0),
3109176d4aa6SSreekanth Reddy 	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number) &&
3110176d4aa6SSreekanth Reddy 	    (ioc_status == MPI3_IOCSTATUS_SUCCESS)) {
3111176d4aa6SSreekanth Reddy 		phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
3112176d4aa6SSreekanth Reddy 		    phy_pg0.programmed_link_rate &
3113176d4aa6SSreekanth Reddy 		    MPI3_SAS_PRATE_MIN_RATE_MASK);
3114176d4aa6SSreekanth Reddy 		phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
3115176d4aa6SSreekanth Reddy 		    phy_pg0.programmed_link_rate >> 4);
3116176d4aa6SSreekanth Reddy 		phy->negotiated_linkrate =
3117176d4aa6SSreekanth Reddy 			mpi3mr_convert_phy_link_rate(
3118176d4aa6SSreekanth Reddy 			    (phy_pg0.negotiated_link_rate &
3119176d4aa6SSreekanth Reddy 			    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK)
3120176d4aa6SSreekanth Reddy 			    >> MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
3121176d4aa6SSreekanth Reddy 	}
3122176d4aa6SSreekanth Reddy 
3123176d4aa6SSreekanth Reddy out:
3124176d4aa6SSreekanth Reddy 	kfree(sas_io_unit_pg1);
3125176d4aa6SSreekanth Reddy 	return rc;
3126176d4aa6SSreekanth Reddy }
3127176d4aa6SSreekanth Reddy 
3128176d4aa6SSreekanth Reddy /**
3129176d4aa6SSreekanth Reddy  * mpi3mr_map_smp_buffer - map BSG dma buffer
3130176d4aa6SSreekanth Reddy  * @dev: Generic device reference
3131176d4aa6SSreekanth Reddy  * @buf: BSG buffer pointer
3132176d4aa6SSreekanth Reddy  * @dma_addr: Physical address holder
3133176d4aa6SSreekanth Reddy  * @dma_len: Mapped DMA buffer length.
3134176d4aa6SSreekanth Reddy  * @p: Virtual address holder
3135176d4aa6SSreekanth Reddy  *
3136176d4aa6SSreekanth Reddy  * This function maps the DMAable buffer
3137176d4aa6SSreekanth Reddy  *
3138176d4aa6SSreekanth Reddy  * Return: 0 on success, non-zero on failure
3139176d4aa6SSreekanth Reddy  */
3140176d4aa6SSreekanth Reddy static int
mpi3mr_map_smp_buffer(struct device * dev,struct bsg_buffer * buf,dma_addr_t * dma_addr,size_t * dma_len,void ** p)3141176d4aa6SSreekanth Reddy mpi3mr_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
3142176d4aa6SSreekanth Reddy 		dma_addr_t *dma_addr, size_t *dma_len, void **p)
3143176d4aa6SSreekanth Reddy {
3144176d4aa6SSreekanth Reddy 	/* Check if the request is split across multiple segments */
3145176d4aa6SSreekanth Reddy 	if (buf->sg_cnt > 1) {
3146176d4aa6SSreekanth Reddy 		*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
3147176d4aa6SSreekanth Reddy 				GFP_KERNEL);
3148176d4aa6SSreekanth Reddy 		if (!*p)
3149176d4aa6SSreekanth Reddy 			return -ENOMEM;
3150176d4aa6SSreekanth Reddy 		*dma_len = buf->payload_len;
3151176d4aa6SSreekanth Reddy 	} else {
3152176d4aa6SSreekanth Reddy 		if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
3153176d4aa6SSreekanth Reddy 			return -ENOMEM;
3154176d4aa6SSreekanth Reddy 		*dma_addr = sg_dma_address(buf->sg_list);
3155176d4aa6SSreekanth Reddy 		*dma_len = sg_dma_len(buf->sg_list);
3156176d4aa6SSreekanth Reddy 		*p = NULL;
3157176d4aa6SSreekanth Reddy 	}
3158176d4aa6SSreekanth Reddy 
3159176d4aa6SSreekanth Reddy 	return 0;
3160176d4aa6SSreekanth Reddy }
3161176d4aa6SSreekanth Reddy 
3162176d4aa6SSreekanth Reddy /**
3163176d4aa6SSreekanth Reddy  * mpi3mr_unmap_smp_buffer - unmap BSG dma buffer
3164176d4aa6SSreekanth Reddy  * @dev: Generic device reference
3165176d4aa6SSreekanth Reddy  * @buf: BSG buffer pointer
3166176d4aa6SSreekanth Reddy  * @dma_addr: Physical address to be unmapped
3167176d4aa6SSreekanth Reddy  * @p: Virtual address
3168176d4aa6SSreekanth Reddy  *
3169176d4aa6SSreekanth Reddy  * This function unmaps the DMAable buffer
3170176d4aa6SSreekanth Reddy  */
3171176d4aa6SSreekanth Reddy static void
mpi3mr_unmap_smp_buffer(struct device * dev,struct bsg_buffer * buf,dma_addr_t dma_addr,void * p)3172176d4aa6SSreekanth Reddy mpi3mr_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
3173176d4aa6SSreekanth Reddy 		dma_addr_t dma_addr, void *p)
3174176d4aa6SSreekanth Reddy {
3175176d4aa6SSreekanth Reddy 	if (p)
3176176d4aa6SSreekanth Reddy 		dma_free_coherent(dev, buf->payload_len, p, dma_addr);
3177176d4aa6SSreekanth Reddy 	else
3178176d4aa6SSreekanth Reddy 		dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
3179176d4aa6SSreekanth Reddy }
3180176d4aa6SSreekanth Reddy 
3181176d4aa6SSreekanth Reddy /**
3182176d4aa6SSreekanth Reddy  * mpi3mr_transport_smp_handler - handler for smp passthru
3183176d4aa6SSreekanth Reddy  * @job: BSG job reference
3184176d4aa6SSreekanth Reddy  * @shost: SCSI host object reference
3185176d4aa6SSreekanth Reddy  * @rphy: SAS transport rphy object pointing the expander
3186176d4aa6SSreekanth Reddy  *
3187176d4aa6SSreekanth Reddy  * This is used primarily by smp utils for sending the SMP
3188176d4aa6SSreekanth Reddy  * commands to the expanders attached to the controller
3189176d4aa6SSreekanth Reddy  */
3190176d4aa6SSreekanth Reddy static void
mpi3mr_transport_smp_handler(struct bsg_job * job,struct Scsi_Host * shost,struct sas_rphy * rphy)3191176d4aa6SSreekanth Reddy mpi3mr_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
3192176d4aa6SSreekanth Reddy 	struct sas_rphy *rphy)
3193176d4aa6SSreekanth Reddy {
3194176d4aa6SSreekanth Reddy 	struct mpi3mr_ioc *mrioc = shost_priv(shost);
3195176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_request mpi_request;
3196176d4aa6SSreekanth Reddy 	struct mpi3_smp_passthrough_reply mpi_reply;
3197176d4aa6SSreekanth Reddy 	int rc;
3198176d4aa6SSreekanth Reddy 	void *psge;
3199176d4aa6SSreekanth Reddy 	dma_addr_t dma_addr_in;
3200176d4aa6SSreekanth Reddy 	dma_addr_t dma_addr_out;
3201176d4aa6SSreekanth Reddy 	void *addr_in = NULL;
3202176d4aa6SSreekanth Reddy 	void *addr_out = NULL;
3203176d4aa6SSreekanth Reddy 	size_t dma_len_in;
3204176d4aa6SSreekanth Reddy 	size_t dma_len_out;
3205176d4aa6SSreekanth Reddy 	unsigned int reslen = 0;
3206176d4aa6SSreekanth Reddy 	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
3207176d4aa6SSreekanth Reddy 	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
3208176d4aa6SSreekanth Reddy 	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
3209176d4aa6SSreekanth Reddy 	u16 ioc_status;
3210176d4aa6SSreekanth Reddy 
3211176d4aa6SSreekanth Reddy 	if (mrioc->reset_in_progress) {
3212176d4aa6SSreekanth Reddy 		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
3213176d4aa6SSreekanth Reddy 		rc = -EFAULT;
3214176d4aa6SSreekanth Reddy 		goto out;
3215176d4aa6SSreekanth Reddy 	}
3216176d4aa6SSreekanth Reddy 
3217176d4aa6SSreekanth Reddy 	rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
3218176d4aa6SSreekanth Reddy 	    &dma_addr_out, &dma_len_out, &addr_out);
3219176d4aa6SSreekanth Reddy 	if (rc)
3220176d4aa6SSreekanth Reddy 		goto out;
3221176d4aa6SSreekanth Reddy 
3222176d4aa6SSreekanth Reddy 	if (addr_out)
3223176d4aa6SSreekanth Reddy 		sg_copy_to_buffer(job->request_payload.sg_list,
3224176d4aa6SSreekanth Reddy 		    job->request_payload.sg_cnt, addr_out,
3225176d4aa6SSreekanth Reddy 		    job->request_payload.payload_len);
3226176d4aa6SSreekanth Reddy 
3227176d4aa6SSreekanth Reddy 	rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
3228176d4aa6SSreekanth Reddy 			&dma_addr_in, &dma_len_in, &addr_in);
3229176d4aa6SSreekanth Reddy 	if (rc)
3230176d4aa6SSreekanth Reddy 		goto unmap_out;
3231176d4aa6SSreekanth Reddy 
3232176d4aa6SSreekanth Reddy 	memset(&mpi_request, 0, request_sz);
3233176d4aa6SSreekanth Reddy 	memset(&mpi_reply, 0, reply_sz);
3234176d4aa6SSreekanth Reddy 	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
3235176d4aa6SSreekanth Reddy 	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
3236176d4aa6SSreekanth Reddy 	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_rphy(mrioc, rphy);
3237176d4aa6SSreekanth Reddy 	mpi_request.sas_address = ((rphy) ?
3238176d4aa6SSreekanth Reddy 	    cpu_to_le64(rphy->identify.sas_address) :
3239176d4aa6SSreekanth Reddy 	    cpu_to_le64(mrioc->sas_hba.sas_address));
3240176d4aa6SSreekanth Reddy 	psge = &mpi_request.request_sge;
3241176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, dma_len_out - 4, dma_addr_out);
3242176d4aa6SSreekanth Reddy 
3243176d4aa6SSreekanth Reddy 	psge = &mpi_request.response_sge;
3244176d4aa6SSreekanth Reddy 	mpi3mr_add_sg_single(psge, sgl_flags, dma_len_in - 4, dma_addr_in);
3245176d4aa6SSreekanth Reddy 
3246176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc, "sending SMP request\n");
3247176d4aa6SSreekanth Reddy 
32485ba207e5SDan Carpenter 	rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
32495ba207e5SDan Carpenter 				       &mpi_reply, reply_sz,
32505ba207e5SDan Carpenter 				       MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
32515ba207e5SDan Carpenter 	if (rc)
3252176d4aa6SSreekanth Reddy 		goto unmap_in;
3253176d4aa6SSreekanth Reddy 
3254176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
3255176d4aa6SSreekanth Reddy 	    "SMP request completed with ioc_status(0x%04x)\n", ioc_status);
3256176d4aa6SSreekanth Reddy 
3257176d4aa6SSreekanth Reddy 	dprint_transport_info(mrioc,
3258176d4aa6SSreekanth Reddy 		    "SMP request - reply data transfer size(%d)\n",
3259176d4aa6SSreekanth Reddy 		    le16_to_cpu(mpi_reply.response_data_length));
3260176d4aa6SSreekanth Reddy 
3261176d4aa6SSreekanth Reddy 	memcpy(job->reply, &mpi_reply, reply_sz);
3262176d4aa6SSreekanth Reddy 	job->reply_len = reply_sz;
3263176d4aa6SSreekanth Reddy 	reslen = le16_to_cpu(mpi_reply.response_data_length);
3264176d4aa6SSreekanth Reddy 
3265176d4aa6SSreekanth Reddy 	if (addr_in)
3266176d4aa6SSreekanth Reddy 		sg_copy_from_buffer(job->reply_payload.sg_list,
3267176d4aa6SSreekanth Reddy 				job->reply_payload.sg_cnt, addr_in,
3268176d4aa6SSreekanth Reddy 				job->reply_payload.payload_len);
3269176d4aa6SSreekanth Reddy 
3270176d4aa6SSreekanth Reddy 	rc = 0;
3271176d4aa6SSreekanth Reddy unmap_in:
3272176d4aa6SSreekanth Reddy 	mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
3273176d4aa6SSreekanth Reddy 			dma_addr_in, addr_in);
3274176d4aa6SSreekanth Reddy unmap_out:
3275176d4aa6SSreekanth Reddy 	mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
3276176d4aa6SSreekanth Reddy 			dma_addr_out, addr_out);
3277176d4aa6SSreekanth Reddy out:
3278176d4aa6SSreekanth Reddy 	bsg_job_done(job, rc, reslen);
3279176d4aa6SSreekanth Reddy }
3280176d4aa6SSreekanth Reddy 
3281176d4aa6SSreekanth Reddy struct sas_function_template mpi3mr_transport_functions = {
3282176d4aa6SSreekanth Reddy 	.get_linkerrors		= mpi3mr_transport_get_linkerrors,
3283176d4aa6SSreekanth Reddy 	.get_enclosure_identifier = mpi3mr_transport_get_enclosure_identifier,
3284176d4aa6SSreekanth Reddy 	.get_bay_identifier	= mpi3mr_transport_get_bay_identifier,
3285176d4aa6SSreekanth Reddy 	.phy_reset		= mpi3mr_transport_phy_reset,
3286176d4aa6SSreekanth Reddy 	.phy_enable		= mpi3mr_transport_phy_enable,
3287176d4aa6SSreekanth Reddy 	.set_phy_speed		= mpi3mr_transport_phy_speed,
3288176d4aa6SSreekanth Reddy 	.smp_handler		= mpi3mr_transport_smp_handler,
3289176d4aa6SSreekanth Reddy };
3290176d4aa6SSreekanth Reddy 
3291176d4aa6SSreekanth Reddy struct scsi_transport_template *mpi3mr_transport_template;
3292