162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Broadcom MPI3 Storage Controllers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017-2023 Broadcom Inc.
662306a36Sopenharmony_ci *  (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "mpi3mr.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * mpi3mr_post_transport_req - Issue transport requests and wait
1462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
1562306a36Sopenharmony_ci * @request: Properly populated MPI3 request
1662306a36Sopenharmony_ci * @request_sz: Size of the MPI3 request
1762306a36Sopenharmony_ci * @reply: Pointer to return MPI3 reply
1862306a36Sopenharmony_ci * @reply_sz: Size of the MPI3 reply buffer
1962306a36Sopenharmony_ci * @timeout: Timeout in seconds
2062306a36Sopenharmony_ci * @ioc_status: Pointer to return ioc status
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * A generic function for posting MPI3 requests from the SAS
2362306a36Sopenharmony_ci * transport layer that uses transport command infrastructure.
2462306a36Sopenharmony_ci * This blocks for the completion of request for timeout seconds
2562306a36Sopenharmony_ci * and if the request times out this function faults the
2662306a36Sopenharmony_ci * controller with proper reason code.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * On successful completion of the request this function returns
2962306a36Sopenharmony_ci * appropriate ioc status from the firmware back to the caller.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * Return: 0 on success, non-zero on failure.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic int mpi3mr_post_transport_req(struct mpi3mr_ioc *mrioc, void *request,
3462306a36Sopenharmony_ci	u16 request_sz, void *reply, u16 reply_sz, int timeout,
3562306a36Sopenharmony_ci	u16 *ioc_status)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	int retval = 0;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	mutex_lock(&mrioc->transport_cmds.mutex);
4062306a36Sopenharmony_ci	if (mrioc->transport_cmds.state & MPI3MR_CMD_PENDING) {
4162306a36Sopenharmony_ci		retval = -1;
4262306a36Sopenharmony_ci		ioc_err(mrioc, "sending transport request failed due to command in use\n");
4362306a36Sopenharmony_ci		mutex_unlock(&mrioc->transport_cmds.mutex);
4462306a36Sopenharmony_ci		goto out;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci	mrioc->transport_cmds.state = MPI3MR_CMD_PENDING;
4762306a36Sopenharmony_ci	mrioc->transport_cmds.is_waiting = 1;
4862306a36Sopenharmony_ci	mrioc->transport_cmds.callback = NULL;
4962306a36Sopenharmony_ci	mrioc->transport_cmds.ioc_status = 0;
5062306a36Sopenharmony_ci	mrioc->transport_cmds.ioc_loginfo = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	init_completion(&mrioc->transport_cmds.done);
5362306a36Sopenharmony_ci	dprint_cfg_info(mrioc, "posting transport request\n");
5462306a36Sopenharmony_ci	if (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO)
5562306a36Sopenharmony_ci		dprint_dump(request, request_sz, "transport_req");
5662306a36Sopenharmony_ci	retval = mpi3mr_admin_request_post(mrioc, request, request_sz, 1);
5762306a36Sopenharmony_ci	if (retval) {
5862306a36Sopenharmony_ci		ioc_err(mrioc, "posting transport request failed\n");
5962306a36Sopenharmony_ci		goto out_unlock;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	wait_for_completion_timeout(&mrioc->transport_cmds.done,
6262306a36Sopenharmony_ci	    (timeout * HZ));
6362306a36Sopenharmony_ci	if (!(mrioc->transport_cmds.state & MPI3MR_CMD_COMPLETE)) {
6462306a36Sopenharmony_ci		mpi3mr_check_rh_fault_ioc(mrioc,
6562306a36Sopenharmony_ci		    MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT);
6662306a36Sopenharmony_ci		ioc_err(mrioc, "transport request timed out\n");
6762306a36Sopenharmony_ci		retval = -1;
6862306a36Sopenharmony_ci		goto out_unlock;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	*ioc_status = mrioc->transport_cmds.ioc_status &
7162306a36Sopenharmony_ci		MPI3_IOCSTATUS_STATUS_MASK;
7262306a36Sopenharmony_ci	if ((*ioc_status) != MPI3_IOCSTATUS_SUCCESS)
7362306a36Sopenharmony_ci		dprint_transport_err(mrioc,
7462306a36Sopenharmony_ci		    "transport request returned with ioc_status(0x%04x), log_info(0x%08x)\n",
7562306a36Sopenharmony_ci		    *ioc_status, mrioc->transport_cmds.ioc_loginfo);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if ((reply) && (mrioc->transport_cmds.state & MPI3MR_CMD_REPLY_VALID))
7862306a36Sopenharmony_ci		memcpy((u8 *)reply, mrioc->transport_cmds.reply, reply_sz);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciout_unlock:
8162306a36Sopenharmony_ci	mrioc->transport_cmds.state = MPI3MR_CMD_NOTUSED;
8262306a36Sopenharmony_ci	mutex_unlock(&mrioc->transport_cmds.mutex);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciout:
8562306a36Sopenharmony_ci	return retval;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* report manufacture request structure */
8962306a36Sopenharmony_cistruct rep_manu_request {
9062306a36Sopenharmony_ci	u8 smp_frame_type;
9162306a36Sopenharmony_ci	u8 function;
9262306a36Sopenharmony_ci	u8 reserved;
9362306a36Sopenharmony_ci	u8 request_length;
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* report manufacture reply structure */
9762306a36Sopenharmony_cistruct rep_manu_reply {
9862306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
9962306a36Sopenharmony_ci	u8 function; /* 0x01 */
10062306a36Sopenharmony_ci	u8 function_result;
10162306a36Sopenharmony_ci	u8 response_length;
10262306a36Sopenharmony_ci	u16 expander_change_count;
10362306a36Sopenharmony_ci	u8 reserved0[2];
10462306a36Sopenharmony_ci	u8 sas_format;
10562306a36Sopenharmony_ci	u8 reserved2[3];
10662306a36Sopenharmony_ci	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
10762306a36Sopenharmony_ci	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
10862306a36Sopenharmony_ci	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
10962306a36Sopenharmony_ci	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
11062306a36Sopenharmony_ci	u16 component_id;
11162306a36Sopenharmony_ci	u8 component_revision_id;
11262306a36Sopenharmony_ci	u8 reserved3;
11362306a36Sopenharmony_ci	u8 vendor_specific[8];
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * mpi3mr_report_manufacture - obtain SMP report_manufacture
11862306a36Sopenharmony_ci * @mrioc: Adapter instance reference
11962306a36Sopenharmony_ci * @sas_address: SAS address of the expander device
12062306a36Sopenharmony_ci * @edev: SAS transport layer sas_expander_device object
12162306a36Sopenharmony_ci * @port_id: ID of the HBA port
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * Fills in the sas_expander_device with manufacturing info.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_cistatic int mpi3mr_report_manufacture(struct mpi3mr_ioc *mrioc,
12862306a36Sopenharmony_ci	u64 sas_address, struct sas_expander_device *edev, u8 port_id)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct mpi3_smp_passthrough_request mpi_request;
13162306a36Sopenharmony_ci	struct mpi3_smp_passthrough_reply mpi_reply;
13262306a36Sopenharmony_ci	struct rep_manu_reply *manufacture_reply;
13362306a36Sopenharmony_ci	struct rep_manu_request *manufacture_request;
13462306a36Sopenharmony_ci	int rc = 0;
13562306a36Sopenharmony_ci	void *psge;
13662306a36Sopenharmony_ci	void *data_out = NULL;
13762306a36Sopenharmony_ci	dma_addr_t data_out_dma;
13862306a36Sopenharmony_ci	dma_addr_t data_in_dma;
13962306a36Sopenharmony_ci	size_t data_in_sz;
14062306a36Sopenharmony_ci	size_t data_out_sz;
14162306a36Sopenharmony_ci	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
14262306a36Sopenharmony_ci	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
14362306a36Sopenharmony_ci	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
14462306a36Sopenharmony_ci	u16 ioc_status;
14562306a36Sopenharmony_ci	u8 *tmp;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (mrioc->reset_in_progress) {
14862306a36Sopenharmony_ci		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
14962306a36Sopenharmony_ci		return -EFAULT;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	data_out_sz = sizeof(struct rep_manu_request);
15362306a36Sopenharmony_ci	data_in_sz = sizeof(struct rep_manu_reply);
15462306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&mrioc->pdev->dev,
15562306a36Sopenharmony_ci	    data_out_sz + data_in_sz, &data_out_dma, GFP_KERNEL);
15662306a36Sopenharmony_ci	if (!data_out) {
15762306a36Sopenharmony_ci		rc = -ENOMEM;
15862306a36Sopenharmony_ci		goto out;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	data_in_dma = data_out_dma + data_out_sz;
16262306a36Sopenharmony_ci	manufacture_reply = data_out + data_out_sz;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	manufacture_request = data_out;
16562306a36Sopenharmony_ci	manufacture_request->smp_frame_type = 0x40;
16662306a36Sopenharmony_ci	manufacture_request->function = 1;
16762306a36Sopenharmony_ci	manufacture_request->reserved = 0;
16862306a36Sopenharmony_ci	manufacture_request->request_length = 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	memset(&mpi_request, 0, request_sz);
17162306a36Sopenharmony_ci	memset(&mpi_reply, 0, reply_sz);
17262306a36Sopenharmony_ci	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
17362306a36Sopenharmony_ci	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
17462306a36Sopenharmony_ci	mpi_request.io_unit_port = (u8) port_id;
17562306a36Sopenharmony_ci	mpi_request.sas_address = cpu_to_le64(sas_address);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	psge = &mpi_request.request_sge;
17862306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	psge = &mpi_request.response_sge;
18162306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	dprint_transport_info(mrioc,
18462306a36Sopenharmony_ci	    "sending report manufacturer SMP request to sas_address(0x%016llx), port(%d)\n",
18562306a36Sopenharmony_ci	    (unsigned long long)sas_address, port_id);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
18862306a36Sopenharmony_ci				       &mpi_reply, reply_sz,
18962306a36Sopenharmony_ci				       MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
19062306a36Sopenharmony_ci	if (rc)
19162306a36Sopenharmony_ci		goto out;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	dprint_transport_info(mrioc,
19462306a36Sopenharmony_ci	    "report manufacturer SMP request completed with ioc_status(0x%04x)\n",
19562306a36Sopenharmony_ci	    ioc_status);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
19862306a36Sopenharmony_ci		rc = -EINVAL;
19962306a36Sopenharmony_ci		goto out;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dprint_transport_info(mrioc,
20362306a36Sopenharmony_ci	    "report manufacturer - reply data transfer size(%d)\n",
20462306a36Sopenharmony_ci	    le16_to_cpu(mpi_reply.response_data_length));
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (le16_to_cpu(mpi_reply.response_data_length) !=
20762306a36Sopenharmony_ci	    sizeof(struct rep_manu_reply)) {
20862306a36Sopenharmony_ci		rc = -EINVAL;
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	strscpy(edev->vendor_id, manufacture_reply->vendor_id,
21362306a36Sopenharmony_ci	     SAS_EXPANDER_VENDOR_ID_LEN);
21462306a36Sopenharmony_ci	strscpy(edev->product_id, manufacture_reply->product_id,
21562306a36Sopenharmony_ci	     SAS_EXPANDER_PRODUCT_ID_LEN);
21662306a36Sopenharmony_ci	strscpy(edev->product_rev, manufacture_reply->product_rev,
21762306a36Sopenharmony_ci	     SAS_EXPANDER_PRODUCT_REV_LEN);
21862306a36Sopenharmony_ci	edev->level = manufacture_reply->sas_format & 1;
21962306a36Sopenharmony_ci	if (edev->level) {
22062306a36Sopenharmony_ci		strscpy(edev->component_vendor_id,
22162306a36Sopenharmony_ci		    manufacture_reply->component_vendor_id,
22262306a36Sopenharmony_ci		     SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
22362306a36Sopenharmony_ci		tmp = (u8 *)&manufacture_reply->component_id;
22462306a36Sopenharmony_ci		edev->component_id = tmp[0] << 8 | tmp[1];
22562306a36Sopenharmony_ci		edev->component_revision_id =
22662306a36Sopenharmony_ci		    manufacture_reply->component_revision_id;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciout:
23062306a36Sopenharmony_ci	if (data_out)
23162306a36Sopenharmony_ci		dma_free_coherent(&mrioc->pdev->dev, data_out_sz + data_in_sz,
23262306a36Sopenharmony_ci		    data_out, data_out_dma);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return rc;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/**
23862306a36Sopenharmony_ci * __mpi3mr_expander_find_by_handle - expander search by handle
23962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
24062306a36Sopenharmony_ci * @handle: Firmware device handle of the expander
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * Context: The caller should acquire sas_node_lock
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * This searches for expander device based on handle, then
24562306a36Sopenharmony_ci * returns the sas_node object.
24662306a36Sopenharmony_ci *
24762306a36Sopenharmony_ci * Return: Expander sas_node object reference or NULL
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_cistruct mpi3mr_sas_node *__mpi3mr_expander_find_by_handle(struct mpi3mr_ioc
25062306a36Sopenharmony_ci	*mrioc, u16 handle)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander, *r;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	r = NULL;
25562306a36Sopenharmony_ci	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
25662306a36Sopenharmony_ci		if (sas_expander->handle != handle)
25762306a36Sopenharmony_ci			continue;
25862306a36Sopenharmony_ci		r = sas_expander;
25962306a36Sopenharmony_ci		goto out;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci out:
26262306a36Sopenharmony_ci	return r;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * mpi3mr_is_expander_device - if device is an expander
26762306a36Sopenharmony_ci * @device_info: Bitfield providing information about the device
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * Return: 1 if the device is expander device, else 0.
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_ciu8 mpi3mr_is_expander_device(u16 device_info)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	if ((device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) ==
27462306a36Sopenharmony_ci	     MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER)
27562306a36Sopenharmony_ci		return 1;
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * mpi3mr_get_sas_address - retrieve sas_address for handle
28262306a36Sopenharmony_ci * @mrioc: Adapter instance reference
28362306a36Sopenharmony_ci * @handle: Firmware device handle
28462306a36Sopenharmony_ci * @sas_address: Address to hold sas address
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * This function issues device page0 read for a given device
28762306a36Sopenharmony_ci * handle and gets the SAS address and return it back
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic int mpi3mr_get_sas_address(struct mpi3mr_ioc *mrioc, u16 handle,
29262306a36Sopenharmony_ci	u64 *sas_address)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct mpi3_device_page0 dev_pg0;
29562306a36Sopenharmony_ci	u16 ioc_status;
29662306a36Sopenharmony_ci	struct mpi3_device0_sas_sata_format *sasinf;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	*sas_address = 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
30162306a36Sopenharmony_ci	    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
30262306a36Sopenharmony_ci	    handle))) {
30362306a36Sopenharmony_ci		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
30462306a36Sopenharmony_ci		return -ENXIO;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
30862306a36Sopenharmony_ci		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
30962306a36Sopenharmony_ci		    handle, ioc_status, __FILE__, __LINE__, __func__);
31062306a36Sopenharmony_ci		return -ENXIO;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (le16_to_cpu(dev_pg0.flags) &
31462306a36Sopenharmony_ci	    MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE)
31562306a36Sopenharmony_ci		*sas_address = mrioc->sas_hba.sas_address;
31662306a36Sopenharmony_ci	else if (dev_pg0.device_form == MPI3_DEVICE_DEVFORM_SAS_SATA) {
31762306a36Sopenharmony_ci		sasinf = &dev_pg0.device_specific.sas_sata_format;
31862306a36Sopenharmony_ci		*sas_address = le64_to_cpu(sasinf->sas_address);
31962306a36Sopenharmony_ci	} else {
32062306a36Sopenharmony_ci		ioc_err(mrioc, "%s: device_form(%d) is not SAS_SATA\n",
32162306a36Sopenharmony_ci		    __func__, dev_pg0.device_form);
32262306a36Sopenharmony_ci		return -ENXIO;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/**
32862306a36Sopenharmony_ci * __mpi3mr_get_tgtdev_by_addr - target device search
32962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
33062306a36Sopenharmony_ci * @sas_address: SAS address of the device
33162306a36Sopenharmony_ci * @hba_port: HBA port entry
33262306a36Sopenharmony_ci *
33362306a36Sopenharmony_ci * This searches for target device from sas address and hba port
33462306a36Sopenharmony_ci * pointer then return mpi3mr_tgt_dev object.
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * Return: Valid tget_dev or NULL
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_cistatic struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
33962306a36Sopenharmony_ci	u64 sas_address, struct mpi3mr_hba_port *hba_port)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	assert_spin_locked(&mrioc->tgtdev_lock);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
34662306a36Sopenharmony_ci		if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
34762306a36Sopenharmony_ci		    (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
34862306a36Sopenharmony_ci		    && (tgtdev->dev_spec.sas_sata_inf.hba_port == hba_port))
34962306a36Sopenharmony_ci			goto found_device;
35062306a36Sopenharmony_ci	return NULL;
35162306a36Sopenharmony_cifound_device:
35262306a36Sopenharmony_ci	mpi3mr_tgtdev_get(tgtdev);
35362306a36Sopenharmony_ci	return tgtdev;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/**
35762306a36Sopenharmony_ci * mpi3mr_get_tgtdev_by_addr - target device search
35862306a36Sopenharmony_ci * @mrioc: Adapter instance reference
35962306a36Sopenharmony_ci * @sas_address: SAS address of the device
36062306a36Sopenharmony_ci * @hba_port: HBA port entry
36162306a36Sopenharmony_ci *
36262306a36Sopenharmony_ci * This searches for target device from sas address and hba port
36362306a36Sopenharmony_ci * pointer then return mpi3mr_tgt_dev object.
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Context: This function will acquire tgtdev_lock and will
36662306a36Sopenharmony_ci * release before returning the mpi3mr_tgt_dev object.
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * Return: Valid tget_dev or NULL
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_cistatic struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_addr(struct mpi3mr_ioc *mrioc,
37162306a36Sopenharmony_ci	u64 sas_address, struct mpi3mr_hba_port *hba_port)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev = NULL;
37462306a36Sopenharmony_ci	unsigned long flags;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (!hba_port)
37762306a36Sopenharmony_ci		goto out;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
38062306a36Sopenharmony_ci	tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc, sas_address, hba_port);
38162306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ciout:
38462306a36Sopenharmony_ci	return tgtdev;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * mpi3mr_remove_device_by_sas_address - remove the device
38962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
39062306a36Sopenharmony_ci * @sas_address: SAS address of the device
39162306a36Sopenharmony_ci * @hba_port: HBA port entry
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci * This searches for target device using sas address and hba
39462306a36Sopenharmony_ci * port pointer then removes it from the OS.
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * Return: None
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic void mpi3mr_remove_device_by_sas_address(struct mpi3mr_ioc *mrioc,
39962306a36Sopenharmony_ci	u64 sas_address, struct mpi3mr_hba_port *hba_port)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev = NULL;
40262306a36Sopenharmony_ci	unsigned long flags;
40362306a36Sopenharmony_ci	u8 was_on_tgtdev_list = 0;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!hba_port)
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
40962306a36Sopenharmony_ci	tgtdev = __mpi3mr_get_tgtdev_by_addr(mrioc,
41062306a36Sopenharmony_ci			 sas_address, hba_port);
41162306a36Sopenharmony_ci	if (tgtdev) {
41262306a36Sopenharmony_ci		if (!list_empty(&tgtdev->list)) {
41362306a36Sopenharmony_ci			list_del_init(&tgtdev->list);
41462306a36Sopenharmony_ci			was_on_tgtdev_list = 1;
41562306a36Sopenharmony_ci			mpi3mr_tgtdev_put(tgtdev);
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
41962306a36Sopenharmony_ci	if (was_on_tgtdev_list) {
42062306a36Sopenharmony_ci		if (tgtdev->host_exposed)
42162306a36Sopenharmony_ci			mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
42262306a36Sopenharmony_ci		mpi3mr_tgtdev_put(tgtdev);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/**
42762306a36Sopenharmony_ci * __mpi3mr_get_tgtdev_by_addr_and_rphy - target device search
42862306a36Sopenharmony_ci * @mrioc: Adapter instance reference
42962306a36Sopenharmony_ci * @sas_address: SAS address of the device
43062306a36Sopenharmony_ci * @rphy: SAS transport layer rphy object
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * This searches for target device from sas address and rphy
43362306a36Sopenharmony_ci * pointer then return mpi3mr_tgt_dev object.
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * Return: Valid tget_dev or NULL
43662306a36Sopenharmony_ci */
43762306a36Sopenharmony_cistruct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_addr_and_rphy(
43862306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc, u64 sas_address, struct sas_rphy *rphy)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	assert_spin_locked(&mrioc->tgtdev_lock);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
44562306a36Sopenharmony_ci		if ((tgtdev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA) &&
44662306a36Sopenharmony_ci		    (tgtdev->dev_spec.sas_sata_inf.sas_address == sas_address)
44762306a36Sopenharmony_ci		    && (tgtdev->dev_spec.sas_sata_inf.rphy == rphy))
44862306a36Sopenharmony_ci			goto found_device;
44962306a36Sopenharmony_ci	return NULL;
45062306a36Sopenharmony_cifound_device:
45162306a36Sopenharmony_ci	mpi3mr_tgtdev_get(tgtdev);
45262306a36Sopenharmony_ci	return tgtdev;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/**
45662306a36Sopenharmony_ci * mpi3mr_expander_find_by_sas_address - sas expander search
45762306a36Sopenharmony_ci * @mrioc: Adapter instance reference
45862306a36Sopenharmony_ci * @sas_address: SAS address of expander
45962306a36Sopenharmony_ci * @hba_port: HBA port entry
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * Return: A valid SAS expander node or NULL.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic struct mpi3mr_sas_node *mpi3mr_expander_find_by_sas_address(
46562306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc, u64 sas_address,
46662306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander, *r = NULL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (!hba_port)
47162306a36Sopenharmony_ci		goto out;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
47462306a36Sopenharmony_ci		if ((sas_expander->sas_address != sas_address) ||
47562306a36Sopenharmony_ci					 (sas_expander->hba_port != hba_port))
47662306a36Sopenharmony_ci			continue;
47762306a36Sopenharmony_ci		r = sas_expander;
47862306a36Sopenharmony_ci		goto out;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ciout:
48162306a36Sopenharmony_ci	return r;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/**
48562306a36Sopenharmony_ci * __mpi3mr_sas_node_find_by_sas_address - sas node search
48662306a36Sopenharmony_ci * @mrioc: Adapter instance reference
48762306a36Sopenharmony_ci * @sas_address: SAS address of expander or sas host
48862306a36Sopenharmony_ci * @hba_port: HBA port entry
48962306a36Sopenharmony_ci * Context: Caller should acquire mrioc->sas_node_lock.
49062306a36Sopenharmony_ci *
49162306a36Sopenharmony_ci * If the SAS address indicates the device is direct attached to
49262306a36Sopenharmony_ci * the controller (controller's SAS address) then the SAS node
49362306a36Sopenharmony_ci * associated with the controller is returned back else the SAS
49462306a36Sopenharmony_ci * address and hba port are used to identify the exact expander
49562306a36Sopenharmony_ci * and the associated sas_node object is returned. If there is
49662306a36Sopenharmony_ci * no match NULL is returned.
49762306a36Sopenharmony_ci *
49862306a36Sopenharmony_ci * Return: A valid SAS node or NULL.
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic struct mpi3mr_sas_node *__mpi3mr_sas_node_find_by_sas_address(
50262306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc, u64 sas_address,
50362306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (mrioc->sas_hba.sas_address == sas_address)
50762306a36Sopenharmony_ci		return &mrioc->sas_hba;
50862306a36Sopenharmony_ci	return mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
50962306a36Sopenharmony_ci	    hba_port);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/**
51362306a36Sopenharmony_ci * mpi3mr_parent_present - Is parent present for a phy
51462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
51562306a36Sopenharmony_ci * @phy: SAS transport layer phy object
51662306a36Sopenharmony_ci *
51762306a36Sopenharmony_ci * Return: 0 if parent is present else non-zero
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_cistatic int mpi3mr_parent_present(struct mpi3mr_ioc *mrioc, struct sas_phy *phy)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	unsigned long flags;
52262306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port = phy->hostdata;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
52562306a36Sopenharmony_ci	if (__mpi3mr_sas_node_find_by_sas_address(mrioc,
52662306a36Sopenharmony_ci	    phy->identify.sas_address,
52762306a36Sopenharmony_ci	    hba_port) == NULL) {
52862306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
52962306a36Sopenharmony_ci		return -1;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/**
53662306a36Sopenharmony_ci * mpi3mr_convert_phy_link_rate -
53762306a36Sopenharmony_ci * @link_rate: link rate as defined in the MPI header
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * Convert link_rate from mpi format into sas_transport layer
54062306a36Sopenharmony_ci * form.
54162306a36Sopenharmony_ci *
54262306a36Sopenharmony_ci * Return: A valid SAS transport layer defined link rate
54362306a36Sopenharmony_ci */
54462306a36Sopenharmony_cistatic enum sas_linkrate mpi3mr_convert_phy_link_rate(u8 link_rate)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	enum sas_linkrate rc;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	switch (link_rate) {
54962306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_1_5:
55062306a36Sopenharmony_ci		rc = SAS_LINK_RATE_1_5_GBPS;
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_3_0:
55362306a36Sopenharmony_ci		rc = SAS_LINK_RATE_3_0_GBPS;
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_6_0:
55662306a36Sopenharmony_ci		rc = SAS_LINK_RATE_6_0_GBPS;
55762306a36Sopenharmony_ci		break;
55862306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_12_0:
55962306a36Sopenharmony_ci		rc = SAS_LINK_RATE_12_0_GBPS;
56062306a36Sopenharmony_ci		break;
56162306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_22_5:
56262306a36Sopenharmony_ci		rc = SAS_LINK_RATE_22_5_GBPS;
56362306a36Sopenharmony_ci		break;
56462306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED:
56562306a36Sopenharmony_ci		rc = SAS_PHY_DISABLED;
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
56862306a36Sopenharmony_ci		rc = SAS_LINK_RATE_FAILED;
56962306a36Sopenharmony_ci		break;
57062306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR:
57162306a36Sopenharmony_ci		rc = SAS_SATA_PORT_SELECTOR;
57262306a36Sopenharmony_ci		break;
57362306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
57462306a36Sopenharmony_ci		rc = SAS_PHY_RESET_IN_PROGRESS;
57562306a36Sopenharmony_ci		break;
57662306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
57762306a36Sopenharmony_ci	case MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
57862306a36Sopenharmony_ci	default:
57962306a36Sopenharmony_ci		rc = SAS_LINK_RATE_UNKNOWN;
58062306a36Sopenharmony_ci		break;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	return rc;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * mpi3mr_delete_sas_phy - Remove a single phy from port
58762306a36Sopenharmony_ci * @mrioc: Adapter instance reference
58862306a36Sopenharmony_ci * @mr_sas_port: Internal Port object
58962306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci * Return: None.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic void mpi3mr_delete_sas_phy(struct mpi3mr_ioc *mrioc,
59462306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port,
59562306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	u64 sas_address = mr_sas_port->remote_identify.sas_address;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	dev_info(&mr_sas_phy->phy->dev,
60062306a36Sopenharmony_ci	    "remove: sas_address(0x%016llx), phy(%d)\n",
60162306a36Sopenharmony_ci	    (unsigned long long) sas_address, mr_sas_phy->phy_id);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	list_del(&mr_sas_phy->port_siblings);
60462306a36Sopenharmony_ci	mr_sas_port->num_phys--;
60562306a36Sopenharmony_ci	mr_sas_port->phy_mask &= ~(1 << mr_sas_phy->phy_id);
60662306a36Sopenharmony_ci	if (mr_sas_port->lowest_phy == mr_sas_phy->phy_id)
60762306a36Sopenharmony_ci		mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
60862306a36Sopenharmony_ci	sas_port_delete_phy(mr_sas_port->port, mr_sas_phy->phy);
60962306a36Sopenharmony_ci	mr_sas_phy->phy_belongs_to_port = 0;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/**
61362306a36Sopenharmony_ci * mpi3mr_add_sas_phy - Adding a single phy to a port
61462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
61562306a36Sopenharmony_ci * @mr_sas_port: Internal Port object
61662306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object
61762306a36Sopenharmony_ci *
61862306a36Sopenharmony_ci * Return: None.
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_cistatic void mpi3mr_add_sas_phy(struct mpi3mr_ioc *mrioc,
62162306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port,
62262306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	u64 sas_address = mr_sas_port->remote_identify.sas_address;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	dev_info(&mr_sas_phy->phy->dev,
62762306a36Sopenharmony_ci	    "add: sas_address(0x%016llx), phy(%d)\n", (unsigned long long)
62862306a36Sopenharmony_ci	    sas_address, mr_sas_phy->phy_id);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	list_add_tail(&mr_sas_phy->port_siblings, &mr_sas_port->phy_list);
63162306a36Sopenharmony_ci	mr_sas_port->num_phys++;
63262306a36Sopenharmony_ci	mr_sas_port->phy_mask |= (1 << mr_sas_phy->phy_id);
63362306a36Sopenharmony_ci	if (mr_sas_phy->phy_id < mr_sas_port->lowest_phy)
63462306a36Sopenharmony_ci		mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
63562306a36Sopenharmony_ci	sas_port_add_phy(mr_sas_port->port, mr_sas_phy->phy);
63662306a36Sopenharmony_ci	mr_sas_phy->phy_belongs_to_port = 1;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * mpi3mr_add_phy_to_an_existing_port - add phy to existing port
64162306a36Sopenharmony_ci * @mrioc: Adapter instance reference
64262306a36Sopenharmony_ci * @mr_sas_node: Internal sas node object (expander or host)
64362306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object *
64462306a36Sopenharmony_ci * @sas_address: SAS address of device/expander were phy needs
64562306a36Sopenharmony_ci *             to be added to
64662306a36Sopenharmony_ci * @hba_port: HBA port entry
64762306a36Sopenharmony_ci *
64862306a36Sopenharmony_ci * Return: None.
64962306a36Sopenharmony_ci */
65062306a36Sopenharmony_cistatic void mpi3mr_add_phy_to_an_existing_port(struct mpi3mr_ioc *mrioc,
65162306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy,
65262306a36Sopenharmony_ci	u64 sas_address, struct mpi3mr_hba_port *hba_port)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port;
65562306a36Sopenharmony_ci	struct mpi3mr_sas_phy *srch_phy;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (mr_sas_phy->phy_belongs_to_port == 1)
65862306a36Sopenharmony_ci		return;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (!hba_port)
66162306a36Sopenharmony_ci		return;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	list_for_each_entry(mr_sas_port, &mr_sas_node->sas_port_list,
66462306a36Sopenharmony_ci	    port_list) {
66562306a36Sopenharmony_ci		if (mr_sas_port->remote_identify.sas_address !=
66662306a36Sopenharmony_ci		    sas_address)
66762306a36Sopenharmony_ci			continue;
66862306a36Sopenharmony_ci		if (mr_sas_port->hba_port != hba_port)
66962306a36Sopenharmony_ci			continue;
67062306a36Sopenharmony_ci		list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
67162306a36Sopenharmony_ci		    port_siblings) {
67262306a36Sopenharmony_ci			if (srch_phy == mr_sas_phy)
67362306a36Sopenharmony_ci				return;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci		mpi3mr_add_sas_phy(mrioc, mr_sas_port, mr_sas_phy);
67662306a36Sopenharmony_ci		return;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci/**
68162306a36Sopenharmony_ci * mpi3mr_delete_sas_port - helper function to removing a port
68262306a36Sopenharmony_ci * @mrioc: Adapter instance reference
68362306a36Sopenharmony_ci * @mr_sas_port: Internal Port object
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * Return: None.
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_cistatic void  mpi3mr_delete_sas_port(struct mpi3mr_ioc *mrioc,
68862306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	u64 sas_address = mr_sas_port->remote_identify.sas_address;
69162306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port = mr_sas_port->hba_port;
69262306a36Sopenharmony_ci	enum sas_device_type device_type =
69362306a36Sopenharmony_ci	    mr_sas_port->remote_identify.device_type;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	dev_info(&mr_sas_port->port->dev,
69662306a36Sopenharmony_ci	    "remove: sas_address(0x%016llx)\n",
69762306a36Sopenharmony_ci	    (unsigned long long) sas_address);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (device_type == SAS_END_DEVICE)
70062306a36Sopenharmony_ci		mpi3mr_remove_device_by_sas_address(mrioc, sas_address,
70162306a36Sopenharmony_ci		    hba_port);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
70462306a36Sopenharmony_ci	    device_type == SAS_FANOUT_EXPANDER_DEVICE)
70562306a36Sopenharmony_ci		mpi3mr_expander_remove(mrioc, sas_address, hba_port);
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci/**
70962306a36Sopenharmony_ci * mpi3mr_del_phy_from_an_existing_port - del phy from a port
71062306a36Sopenharmony_ci * @mrioc: Adapter instance reference
71162306a36Sopenharmony_ci * @mr_sas_node: Internal sas node object (expander or host)
71262306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object
71362306a36Sopenharmony_ci *
71462306a36Sopenharmony_ci * Return: None.
71562306a36Sopenharmony_ci */
71662306a36Sopenharmony_cistatic void mpi3mr_del_phy_from_an_existing_port(struct mpi3mr_ioc *mrioc,
71762306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node, struct mpi3mr_sas_phy *mr_sas_phy)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port, *next;
72062306a36Sopenharmony_ci	struct mpi3mr_sas_phy *srch_phy;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (mr_sas_phy->phy_belongs_to_port == 0)
72362306a36Sopenharmony_ci		return;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
72662306a36Sopenharmony_ci	    port_list) {
72762306a36Sopenharmony_ci		list_for_each_entry(srch_phy, &mr_sas_port->phy_list,
72862306a36Sopenharmony_ci		    port_siblings) {
72962306a36Sopenharmony_ci			if (srch_phy != mr_sas_phy)
73062306a36Sopenharmony_ci				continue;
73162306a36Sopenharmony_ci			if ((mr_sas_port->num_phys == 1) &&
73262306a36Sopenharmony_ci			    !mrioc->reset_in_progress)
73362306a36Sopenharmony_ci				mpi3mr_delete_sas_port(mrioc, mr_sas_port);
73462306a36Sopenharmony_ci			else
73562306a36Sopenharmony_ci				mpi3mr_delete_sas_phy(mrioc, mr_sas_port,
73662306a36Sopenharmony_ci				    mr_sas_phy);
73762306a36Sopenharmony_ci			return;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci/**
74362306a36Sopenharmony_ci * mpi3mr_sas_port_sanity_check - sanity check while adding port
74462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
74562306a36Sopenharmony_ci * @mr_sas_node: Internal sas node object (expander or host)
74662306a36Sopenharmony_ci * @sas_address: SAS address of device/expander
74762306a36Sopenharmony_ci * @hba_port: HBA port entry
74862306a36Sopenharmony_ci *
74962306a36Sopenharmony_ci * Verifies whether the Phys attached to a device with the given
75062306a36Sopenharmony_ci * SAS address already belongs to an existing sas port if so
75162306a36Sopenharmony_ci * will remove those phys from the sas port
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * Return: None.
75462306a36Sopenharmony_ci */
75562306a36Sopenharmony_cistatic void mpi3mr_sas_port_sanity_check(struct mpi3mr_ioc *mrioc,
75662306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node, u64 sas_address,
75762306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	int i;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	for (i = 0; i < mr_sas_node->num_phys; i++) {
76262306a36Sopenharmony_ci		if ((mr_sas_node->phy[i].remote_identify.sas_address !=
76362306a36Sopenharmony_ci		    sas_address) || (mr_sas_node->phy[i].hba_port != hba_port))
76462306a36Sopenharmony_ci			continue;
76562306a36Sopenharmony_ci		if (mr_sas_node->phy[i].phy_belongs_to_port == 1)
76662306a36Sopenharmony_ci			mpi3mr_del_phy_from_an_existing_port(mrioc,
76762306a36Sopenharmony_ci			    mr_sas_node, &mr_sas_node->phy[i]);
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/**
77262306a36Sopenharmony_ci * mpi3mr_set_identify - set identify for phys and end devices
77362306a36Sopenharmony_ci * @mrioc: Adapter instance reference
77462306a36Sopenharmony_ci * @handle: Firmware device handle
77562306a36Sopenharmony_ci * @identify: SAS transport layer's identify info
77662306a36Sopenharmony_ci *
77762306a36Sopenharmony_ci * Populates sas identify info for a specific device.
77862306a36Sopenharmony_ci *
77962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
78062306a36Sopenharmony_ci */
78162306a36Sopenharmony_cistatic int mpi3mr_set_identify(struct mpi3mr_ioc *mrioc, u16 handle,
78262306a36Sopenharmony_ci	struct sas_identify *identify)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	struct mpi3_device_page0 device_pg0;
78662306a36Sopenharmony_ci	struct mpi3_device0_sas_sata_format *sasinf;
78762306a36Sopenharmony_ci	u16 device_info;
78862306a36Sopenharmony_ci	u16 ioc_status;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (mrioc->reset_in_progress) {
79162306a36Sopenharmony_ci		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
79262306a36Sopenharmony_ci		return -EFAULT;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &device_pg0,
79662306a36Sopenharmony_ci	    sizeof(device_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE, handle))) {
79762306a36Sopenharmony_ci		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
79862306a36Sopenharmony_ci		return -ENXIO;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
80262306a36Sopenharmony_ci		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
80362306a36Sopenharmony_ci		    handle, ioc_status, __FILE__, __LINE__, __func__);
80462306a36Sopenharmony_ci		return -EIO;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	memset(identify, 0, sizeof(struct sas_identify));
80862306a36Sopenharmony_ci	sasinf = &device_pg0.device_specific.sas_sata_format;
80962306a36Sopenharmony_ci	device_info = le16_to_cpu(sasinf->device_info);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* sas_address */
81262306a36Sopenharmony_ci	identify->sas_address = le64_to_cpu(sasinf->sas_address);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/* phy number of the parent device this device is linked to */
81562306a36Sopenharmony_ci	identify->phy_identifier = sasinf->phy_num;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* device_type */
81862306a36Sopenharmony_ci	switch (device_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) {
81962306a36Sopenharmony_ci	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE:
82062306a36Sopenharmony_ci		identify->device_type = SAS_PHY_UNUSED;
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE:
82362306a36Sopenharmony_ci		identify->device_type = SAS_END_DEVICE;
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci	case MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER:
82662306a36Sopenharmony_ci		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* initiator_port_protocols */
83162306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_SSP_INITIATOR)
83262306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
83362306a36Sopenharmony_ci	/* MPI3.0 doesn't have define for SATA INIT so setting both here*/
83462306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_STP_INITIATOR)
83562306a36Sopenharmony_ci		identify->initiator_port_protocols |= (SAS_PROTOCOL_STP |
83662306a36Sopenharmony_ci		    SAS_PROTOCOL_SATA);
83762306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_SMP_INITIATOR)
83862306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	/* target_port_protocols */
84162306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_SSP_TARGET)
84262306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
84362306a36Sopenharmony_ci	/* MPI3.0 doesn't have define for STP Target so setting both here*/
84462306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET)
84562306a36Sopenharmony_ci		identify->target_port_protocols |= (SAS_PROTOCOL_STP |
84662306a36Sopenharmony_ci		    SAS_PROTOCOL_SATA);
84762306a36Sopenharmony_ci	if (device_info & MPI3_SAS_DEVICE_INFO_SMP_TARGET)
84862306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
84962306a36Sopenharmony_ci	return 0;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci/**
85362306a36Sopenharmony_ci * mpi3mr_add_host_phy - report sas_host phy to SAS transport
85462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
85562306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object
85662306a36Sopenharmony_ci * @phy_pg0: SAS phy page 0
85762306a36Sopenharmony_ci * @parent_dev: Prent device class object
85862306a36Sopenharmony_ci *
85962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
86062306a36Sopenharmony_ci */
86162306a36Sopenharmony_cistatic int mpi3mr_add_host_phy(struct mpi3mr_ioc *mrioc,
86262306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy, struct mpi3_sas_phy_page0 phy_pg0,
86362306a36Sopenharmony_ci	struct device *parent_dev)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct sas_phy *phy;
86662306a36Sopenharmony_ci	int phy_index = mr_sas_phy->phy_id;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
87062306a36Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
87162306a36Sopenharmony_ci	if (!phy) {
87262306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
87362306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
87462306a36Sopenharmony_ci		return -1;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci	if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
87762306a36Sopenharmony_ci	    &mr_sas_phy->identify))) {
87862306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
87962306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
88062306a36Sopenharmony_ci		sas_phy_free(phy);
88162306a36Sopenharmony_ci		return -1;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci	phy->identify = mr_sas_phy->identify;
88462306a36Sopenharmony_ci	mr_sas_phy->attached_handle = le16_to_cpu(phy_pg0.attached_dev_handle);
88562306a36Sopenharmony_ci	if (mr_sas_phy->attached_handle)
88662306a36Sopenharmony_ci		mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
88762306a36Sopenharmony_ci		    &mr_sas_phy->remote_identify);
88862306a36Sopenharmony_ci	phy->identify.phy_identifier = mr_sas_phy->phy_id;
88962306a36Sopenharmony_ci	phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
89062306a36Sopenharmony_ci	    (phy_pg0.negotiated_link_rate &
89162306a36Sopenharmony_ci	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
89262306a36Sopenharmony_ci	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
89362306a36Sopenharmony_ci	phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
89462306a36Sopenharmony_ci	    phy_pg0.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
89562306a36Sopenharmony_ci	phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
89662306a36Sopenharmony_ci	    phy_pg0.hw_link_rate >> 4);
89762306a36Sopenharmony_ci	phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
89862306a36Sopenharmony_ci	    phy_pg0.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
89962306a36Sopenharmony_ci	phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
90062306a36Sopenharmony_ci	    phy_pg0.programmed_link_rate >> 4);
90162306a36Sopenharmony_ci	phy->hostdata = mr_sas_phy->hba_port;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if ((sas_phy_add(phy))) {
90462306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
90562306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
90662306a36Sopenharmony_ci		sas_phy_free(phy);
90762306a36Sopenharmony_ci		return -1;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
91062306a36Sopenharmony_ci		dev_info(&phy->dev,
91162306a36Sopenharmony_ci		    "add: handle(0x%04x), sas_address(0x%016llx)\n"
91262306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
91362306a36Sopenharmony_ci		    mr_sas_phy->handle, (unsigned long long)
91462306a36Sopenharmony_ci		    mr_sas_phy->identify.sas_address,
91562306a36Sopenharmony_ci		    mr_sas_phy->attached_handle,
91662306a36Sopenharmony_ci		    (unsigned long long)
91762306a36Sopenharmony_ci		    mr_sas_phy->remote_identify.sas_address);
91862306a36Sopenharmony_ci	mr_sas_phy->phy = phy;
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/**
92362306a36Sopenharmony_ci * mpi3mr_add_expander_phy - report expander phy to transport
92462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
92562306a36Sopenharmony_ci * @mr_sas_phy: Internal Phy object
92662306a36Sopenharmony_ci * @expander_pg1: SAS Expander page 1
92762306a36Sopenharmony_ci * @parent_dev: Parent device class object
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
93062306a36Sopenharmony_ci */
93162306a36Sopenharmony_cistatic int mpi3mr_add_expander_phy(struct mpi3mr_ioc *mrioc,
93262306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy,
93362306a36Sopenharmony_ci	struct mpi3_sas_expander_page1 expander_pg1,
93462306a36Sopenharmony_ci	struct device *parent_dev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct sas_phy *phy;
93762306a36Sopenharmony_ci	int phy_index = mr_sas_phy->phy_id;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mr_sas_phy->port_siblings);
94062306a36Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
94162306a36Sopenharmony_ci	if (!phy) {
94262306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
94362306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
94462306a36Sopenharmony_ci		return -1;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci	if ((mpi3mr_set_identify(mrioc, mr_sas_phy->handle,
94762306a36Sopenharmony_ci	    &mr_sas_phy->identify))) {
94862306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
94962306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
95062306a36Sopenharmony_ci		sas_phy_free(phy);
95162306a36Sopenharmony_ci		return -1;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	phy->identify = mr_sas_phy->identify;
95462306a36Sopenharmony_ci	mr_sas_phy->attached_handle =
95562306a36Sopenharmony_ci	    le16_to_cpu(expander_pg1.attached_dev_handle);
95662306a36Sopenharmony_ci	if (mr_sas_phy->attached_handle)
95762306a36Sopenharmony_ci		mpi3mr_set_identify(mrioc, mr_sas_phy->attached_handle,
95862306a36Sopenharmony_ci		    &mr_sas_phy->remote_identify);
95962306a36Sopenharmony_ci	phy->identify.phy_identifier = mr_sas_phy->phy_id;
96062306a36Sopenharmony_ci	phy->negotiated_linkrate = mpi3mr_convert_phy_link_rate(
96162306a36Sopenharmony_ci	    (expander_pg1.negotiated_link_rate &
96262306a36Sopenharmony_ci	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
96362306a36Sopenharmony_ci	    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
96462306a36Sopenharmony_ci	phy->minimum_linkrate_hw = mpi3mr_convert_phy_link_rate(
96562306a36Sopenharmony_ci	    expander_pg1.hw_link_rate & MPI3_SAS_HWRATE_MIN_RATE_MASK);
96662306a36Sopenharmony_ci	phy->maximum_linkrate_hw = mpi3mr_convert_phy_link_rate(
96762306a36Sopenharmony_ci	    expander_pg1.hw_link_rate >> 4);
96862306a36Sopenharmony_ci	phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
96962306a36Sopenharmony_ci	    expander_pg1.programmed_link_rate & MPI3_SAS_PRATE_MIN_RATE_MASK);
97062306a36Sopenharmony_ci	phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
97162306a36Sopenharmony_ci	    expander_pg1.programmed_link_rate >> 4);
97262306a36Sopenharmony_ci	phy->hostdata = mr_sas_phy->hba_port;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if ((sas_phy_add(phy))) {
97562306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
97662306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
97762306a36Sopenharmony_ci		sas_phy_free(phy);
97862306a36Sopenharmony_ci		return -1;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
98162306a36Sopenharmony_ci		dev_info(&phy->dev,
98262306a36Sopenharmony_ci		    "add: handle(0x%04x), sas_address(0x%016llx)\n"
98362306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
98462306a36Sopenharmony_ci		    mr_sas_phy->handle, (unsigned long long)
98562306a36Sopenharmony_ci		    mr_sas_phy->identify.sas_address,
98662306a36Sopenharmony_ci		    mr_sas_phy->attached_handle,
98762306a36Sopenharmony_ci		    (unsigned long long)
98862306a36Sopenharmony_ci		    mr_sas_phy->remote_identify.sas_address);
98962306a36Sopenharmony_ci	mr_sas_phy->phy = phy;
99062306a36Sopenharmony_ci	return 0;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci/**
99462306a36Sopenharmony_ci * mpi3mr_alloc_hba_port - alloc hba port object
99562306a36Sopenharmony_ci * @mrioc: Adapter instance reference
99662306a36Sopenharmony_ci * @port_id: Port number
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci * Alloc memory for hba port object.
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic struct mpi3mr_hba_port *
100162306a36Sopenharmony_cimpi3mr_alloc_hba_port(struct mpi3mr_ioc *mrioc, u16 port_id)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	hba_port = kzalloc(sizeof(struct mpi3mr_hba_port),
100662306a36Sopenharmony_ci	    GFP_KERNEL);
100762306a36Sopenharmony_ci	if (!hba_port)
100862306a36Sopenharmony_ci		return NULL;
100962306a36Sopenharmony_ci	hba_port->port_id = port_id;
101062306a36Sopenharmony_ci	ioc_info(mrioc, "hba_port entry: %p, port: %d is added to hba_port list\n",
101162306a36Sopenharmony_ci	    hba_port, hba_port->port_id);
101262306a36Sopenharmony_ci	list_add_tail(&hba_port->list, &mrioc->hba_port_table_list);
101362306a36Sopenharmony_ci	return hba_port;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci/**
101762306a36Sopenharmony_ci * mpi3mr_get_hba_port_by_id - find hba port by id
101862306a36Sopenharmony_ci * @mrioc: Adapter instance reference
101962306a36Sopenharmony_ci * @port_id - Port ID to search
102062306a36Sopenharmony_ci *
102162306a36Sopenharmony_ci * Return: mpi3mr_hba_port reference for the matched port
102262306a36Sopenharmony_ci */
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistruct mpi3mr_hba_port *mpi3mr_get_hba_port_by_id(struct mpi3mr_ioc *mrioc,
102562306a36Sopenharmony_ci	u8 port_id)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct mpi3mr_hba_port *port, *port_next;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	list_for_each_entry_safe(port, port_next,
103062306a36Sopenharmony_ci	    &mrioc->hba_port_table_list, list) {
103162306a36Sopenharmony_ci		if (port->port_id != port_id)
103262306a36Sopenharmony_ci			continue;
103362306a36Sopenharmony_ci		if (port->flags & MPI3MR_HBA_PORT_FLAG_DIRTY)
103462306a36Sopenharmony_ci			continue;
103562306a36Sopenharmony_ci		return port;
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return NULL;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci/**
104262306a36Sopenharmony_ci * mpi3mr_update_links - refreshing SAS phy link changes
104362306a36Sopenharmony_ci * @mrioc: Adapter instance reference
104462306a36Sopenharmony_ci * @sas_address_parent: SAS address of parent expander or host
104562306a36Sopenharmony_ci * @handle: Firmware device handle of attached device
104662306a36Sopenharmony_ci * @phy_number: Phy number
104762306a36Sopenharmony_ci * @link_rate: New link rate
104862306a36Sopenharmony_ci * @hba_port: HBA port entry
104962306a36Sopenharmony_ci *
105062306a36Sopenharmony_ci * Return: None.
105162306a36Sopenharmony_ci */
105262306a36Sopenharmony_civoid mpi3mr_update_links(struct mpi3mr_ioc *mrioc,
105362306a36Sopenharmony_ci	u64 sas_address_parent, u16 handle, u8 phy_number, u8 link_rate,
105462306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	unsigned long flags;
105762306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node;
105862306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (mrioc->reset_in_progress)
106162306a36Sopenharmony_ci		return;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
106462306a36Sopenharmony_ci	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
106562306a36Sopenharmony_ci	    sas_address_parent, hba_port);
106662306a36Sopenharmony_ci	if (!mr_sas_node) {
106762306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
106862306a36Sopenharmony_ci		return;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	mr_sas_phy = &mr_sas_node->phy[phy_number];
107262306a36Sopenharmony_ci	mr_sas_phy->attached_handle = handle;
107362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
107462306a36Sopenharmony_ci	if (handle && (link_rate >= MPI3_SAS_NEG_LINK_RATE_1_5)) {
107562306a36Sopenharmony_ci		mpi3mr_set_identify(mrioc, handle,
107662306a36Sopenharmony_ci		    &mr_sas_phy->remote_identify);
107762306a36Sopenharmony_ci		mpi3mr_add_phy_to_an_existing_port(mrioc, mr_sas_node,
107862306a36Sopenharmony_ci		    mr_sas_phy, mr_sas_phy->remote_identify.sas_address,
107962306a36Sopenharmony_ci		    hba_port);
108062306a36Sopenharmony_ci	} else
108162306a36Sopenharmony_ci		memset(&mr_sas_phy->remote_identify, 0, sizeof(struct
108262306a36Sopenharmony_ci		    sas_identify));
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (mr_sas_phy->phy)
108562306a36Sopenharmony_ci		mr_sas_phy->phy->negotiated_linkrate =
108662306a36Sopenharmony_ci		    mpi3mr_convert_phy_link_rate(link_rate);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
108962306a36Sopenharmony_ci		dev_info(&mr_sas_phy->phy->dev,
109062306a36Sopenharmony_ci		    "refresh: parent sas_address(0x%016llx),\n"
109162306a36Sopenharmony_ci		    "\tlink_rate(0x%02x), phy(%d)\n"
109262306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_address(0x%016llx)\n",
109362306a36Sopenharmony_ci		    (unsigned long long)sas_address_parent,
109462306a36Sopenharmony_ci		    link_rate, phy_number, handle, (unsigned long long)
109562306a36Sopenharmony_ci		    mr_sas_phy->remote_identify.sas_address);
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci/**
109962306a36Sopenharmony_ci * mpi3mr_sas_host_refresh - refreshing sas host object contents
110062306a36Sopenharmony_ci * @mrioc: Adapter instance reference
110162306a36Sopenharmony_ci *
110262306a36Sopenharmony_ci * This function refreshes the controllers phy information and
110362306a36Sopenharmony_ci * updates the SAS transport layer with updated information,
110462306a36Sopenharmony_ci * this is executed for each device addition or device info
110562306a36Sopenharmony_ci * change events
110662306a36Sopenharmony_ci *
110762306a36Sopenharmony_ci * Return: None.
110862306a36Sopenharmony_ci */
110962306a36Sopenharmony_civoid mpi3mr_sas_host_refresh(struct mpi3mr_ioc *mrioc)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	int i;
111262306a36Sopenharmony_ci	u8 link_rate;
111362306a36Sopenharmony_ci	u16 sz, port_id, attached_handle;
111462306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	dprint_transport_info(mrioc,
111762306a36Sopenharmony_ci	    "updating handles for sas_host(0x%016llx)\n",
111862306a36Sopenharmony_ci	    (unsigned long long)mrioc->sas_hba.sas_address);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
112162306a36Sopenharmony_ci	    (mrioc->sas_hba.num_phys *
112262306a36Sopenharmony_ci	     sizeof(struct mpi3_sas_io_unit0_phy_data));
112362306a36Sopenharmony_ci	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
112462306a36Sopenharmony_ci	if (!sas_io_unit_pg0)
112562306a36Sopenharmony_ci		return;
112662306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
112762306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
112862306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
112962306a36Sopenharmony_ci		goto out;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	mrioc->sas_hba.handle = 0;
113362306a36Sopenharmony_ci	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
113462306a36Sopenharmony_ci		if (sas_io_unit_pg0->phy_data[i].phy_flags &
113562306a36Sopenharmony_ci		    (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
113662306a36Sopenharmony_ci		     MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
113762306a36Sopenharmony_ci			continue;
113862306a36Sopenharmony_ci		link_rate =
113962306a36Sopenharmony_ci		    sas_io_unit_pg0->phy_data[i].negotiated_link_rate >> 4;
114062306a36Sopenharmony_ci		if (!mrioc->sas_hba.handle)
114162306a36Sopenharmony_ci			mrioc->sas_hba.handle = le16_to_cpu(
114262306a36Sopenharmony_ci			    sas_io_unit_pg0->phy_data[i].controller_dev_handle);
114362306a36Sopenharmony_ci		port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
114462306a36Sopenharmony_ci		if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
114562306a36Sopenharmony_ci			if (!mpi3mr_alloc_hba_port(mrioc, port_id))
114662306a36Sopenharmony_ci				goto out;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci		mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
114962306a36Sopenharmony_ci		attached_handle = le16_to_cpu(
115062306a36Sopenharmony_ci		    sas_io_unit_pg0->phy_data[i].attached_dev_handle);
115162306a36Sopenharmony_ci		if (attached_handle && link_rate < MPI3_SAS_NEG_LINK_RATE_1_5)
115262306a36Sopenharmony_ci			link_rate = MPI3_SAS_NEG_LINK_RATE_1_5;
115362306a36Sopenharmony_ci		mrioc->sas_hba.phy[i].hba_port =
115462306a36Sopenharmony_ci			mpi3mr_get_hba_port_by_id(mrioc, port_id);
115562306a36Sopenharmony_ci		mpi3mr_update_links(mrioc, mrioc->sas_hba.sas_address,
115662306a36Sopenharmony_ci		    attached_handle, i, link_rate,
115762306a36Sopenharmony_ci		    mrioc->sas_hba.phy[i].hba_port);
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci out:
116062306a36Sopenharmony_ci	kfree(sas_io_unit_pg0);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/**
116462306a36Sopenharmony_ci * mpi3mr_sas_host_add - create sas host object
116562306a36Sopenharmony_ci * @mrioc: Adapter instance reference
116662306a36Sopenharmony_ci *
116762306a36Sopenharmony_ci * This function creates the controllers phy information and
116862306a36Sopenharmony_ci * updates the SAS transport layer with updated information,
116962306a36Sopenharmony_ci * this is executed for first device addition or device info
117062306a36Sopenharmony_ci * change event.
117162306a36Sopenharmony_ci *
117262306a36Sopenharmony_ci * Return: None.
117362306a36Sopenharmony_ci */
117462306a36Sopenharmony_civoid mpi3mr_sas_host_add(struct mpi3mr_ioc *mrioc)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	int i;
117762306a36Sopenharmony_ci	u16 sz, num_phys = 1, port_id, ioc_status;
117862306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
117962306a36Sopenharmony_ci	struct mpi3_sas_phy_page0 phy_pg0;
118062306a36Sopenharmony_ci	struct mpi3_device_page0 dev_pg0;
118162306a36Sopenharmony_ci	struct mpi3_enclosure_page0 encl_pg0;
118262306a36Sopenharmony_ci	struct mpi3_device0_sas_sata_format *sasinf;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
118562306a36Sopenharmony_ci	    (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
118662306a36Sopenharmony_ci	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
118762306a36Sopenharmony_ci	if (!sas_io_unit_pg0)
118862306a36Sopenharmony_ci		return;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
119162306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
119262306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
119362306a36Sopenharmony_ci		goto out;
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci	num_phys = sas_io_unit_pg0->num_phys;
119662306a36Sopenharmony_ci	kfree(sas_io_unit_pg0);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	mrioc->sas_hba.host_node = 1;
119962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mrioc->sas_hba.sas_port_list);
120062306a36Sopenharmony_ci	mrioc->sas_hba.parent_dev = &mrioc->shost->shost_gendev;
120162306a36Sopenharmony_ci	mrioc->sas_hba.phy = kcalloc(num_phys,
120262306a36Sopenharmony_ci	    sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
120362306a36Sopenharmony_ci	if (!mrioc->sas_hba.phy)
120462306a36Sopenharmony_ci		return;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	mrioc->sas_hba.num_phys = num_phys;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
120962306a36Sopenharmony_ci	    (num_phys * sizeof(struct mpi3_sas_io_unit0_phy_data));
121062306a36Sopenharmony_ci	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
121162306a36Sopenharmony_ci	if (!sas_io_unit_pg0)
121262306a36Sopenharmony_ci		return;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
121562306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
121662306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
121762306a36Sopenharmony_ci		goto out;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	mrioc->sas_hba.handle = 0;
122162306a36Sopenharmony_ci	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
122262306a36Sopenharmony_ci		if (sas_io_unit_pg0->phy_data[i].phy_flags &
122362306a36Sopenharmony_ci		    (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
122462306a36Sopenharmony_ci		    MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))
122562306a36Sopenharmony_ci			continue;
122662306a36Sopenharmony_ci		if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
122762306a36Sopenharmony_ci		    sizeof(struct mpi3_sas_phy_page0),
122862306a36Sopenharmony_ci		    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, i)) {
122962306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
123062306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
123162306a36Sopenharmony_ci			goto out;
123262306a36Sopenharmony_ci		}
123362306a36Sopenharmony_ci		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
123462306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
123562306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
123662306a36Sopenharmony_ci			goto out;
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		if (!mrioc->sas_hba.handle)
124062306a36Sopenharmony_ci			mrioc->sas_hba.handle = le16_to_cpu(
124162306a36Sopenharmony_ci			    sas_io_unit_pg0->phy_data[i].controller_dev_handle);
124262306a36Sopenharmony_ci		port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci		if (!(mpi3mr_get_hba_port_by_id(mrioc, port_id)))
124562306a36Sopenharmony_ci			if (!mpi3mr_alloc_hba_port(mrioc, port_id))
124662306a36Sopenharmony_ci				goto out;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci		mrioc->sas_hba.phy[i].handle = mrioc->sas_hba.handle;
124962306a36Sopenharmony_ci		mrioc->sas_hba.phy[i].phy_id = i;
125062306a36Sopenharmony_ci		mrioc->sas_hba.phy[i].hba_port =
125162306a36Sopenharmony_ci		    mpi3mr_get_hba_port_by_id(mrioc, port_id);
125262306a36Sopenharmony_ci		mpi3mr_add_host_phy(mrioc, &mrioc->sas_hba.phy[i],
125362306a36Sopenharmony_ci		    phy_pg0, mrioc->sas_hba.parent_dev);
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci	if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
125662306a36Sopenharmony_ci	    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
125762306a36Sopenharmony_ci	    mrioc->sas_hba.handle))) {
125862306a36Sopenharmony_ci		ioc_err(mrioc, "%s: device page0 read failed\n", __func__);
125962306a36Sopenharmony_ci		goto out;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
126262306a36Sopenharmony_ci		ioc_err(mrioc, "device page read failed for handle(0x%04x), with ioc_status(0x%04x) failure at %s:%d/%s()!\n",
126362306a36Sopenharmony_ci		    mrioc->sas_hba.handle, ioc_status, __FILE__, __LINE__,
126462306a36Sopenharmony_ci		    __func__);
126562306a36Sopenharmony_ci		goto out;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci	mrioc->sas_hba.enclosure_handle =
126862306a36Sopenharmony_ci	    le16_to_cpu(dev_pg0.enclosure_handle);
126962306a36Sopenharmony_ci	sasinf = &dev_pg0.device_specific.sas_sata_format;
127062306a36Sopenharmony_ci	mrioc->sas_hba.sas_address =
127162306a36Sopenharmony_ci	    le64_to_cpu(sasinf->sas_address);
127262306a36Sopenharmony_ci	ioc_info(mrioc,
127362306a36Sopenharmony_ci	    "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
127462306a36Sopenharmony_ci	    mrioc->sas_hba.handle,
127562306a36Sopenharmony_ci	    (unsigned long long) mrioc->sas_hba.sas_address,
127662306a36Sopenharmony_ci	    mrioc->sas_hba.num_phys);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (mrioc->sas_hba.enclosure_handle) {
127962306a36Sopenharmony_ci		if (!(mpi3mr_cfg_get_enclosure_pg0(mrioc, &ioc_status,
128062306a36Sopenharmony_ci		    &encl_pg0, sizeof(encl_pg0),
128162306a36Sopenharmony_ci		    MPI3_ENCLOS_PGAD_FORM_HANDLE,
128262306a36Sopenharmony_ci		    mrioc->sas_hba.enclosure_handle)) &&
128362306a36Sopenharmony_ci		    (ioc_status == MPI3_IOCSTATUS_SUCCESS))
128462306a36Sopenharmony_ci			mrioc->sas_hba.enclosure_logical_id =
128562306a36Sopenharmony_ci				le64_to_cpu(encl_pg0.enclosure_logical_id);
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ciout:
128962306a36Sopenharmony_ci	kfree(sas_io_unit_pg0);
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci/**
129362306a36Sopenharmony_ci * mpi3mr_sas_port_add - Expose the SAS device to the SAS TL
129462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
129562306a36Sopenharmony_ci * @handle: Firmware device handle of the attached device
129662306a36Sopenharmony_ci * @sas_address_parent: sas address of parent expander or host
129762306a36Sopenharmony_ci * @hba_port: HBA port entry
129862306a36Sopenharmony_ci *
129962306a36Sopenharmony_ci * This function creates a new sas port object for the given end
130062306a36Sopenharmony_ci * device matching sas address and hba_port and adds it to the
130162306a36Sopenharmony_ci * sas_node's sas_port_list and expose the attached sas device
130262306a36Sopenharmony_ci * to the SAS transport layer through sas_rphy_add.
130362306a36Sopenharmony_ci *
130462306a36Sopenharmony_ci * Returns a valid mpi3mr_sas_port reference or NULL.
130562306a36Sopenharmony_ci */
130662306a36Sopenharmony_cistatic struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc,
130762306a36Sopenharmony_ci	u16 handle, u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy, *next;
131062306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port;
131162306a36Sopenharmony_ci	unsigned long flags;
131262306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node;
131362306a36Sopenharmony_ci	struct sas_rphy *rphy;
131462306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev = NULL;
131562306a36Sopenharmony_ci	int i;
131662306a36Sopenharmony_ci	struct sas_port *port;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (!hba_port) {
131962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
132062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
132162306a36Sopenharmony_ci		return NULL;
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	mr_sas_port = kzalloc(sizeof(struct mpi3mr_sas_port), GFP_KERNEL);
132562306a36Sopenharmony_ci	if (!mr_sas_port)
132662306a36Sopenharmony_ci		return NULL;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	INIT_LIST_HEAD(&mr_sas_port->port_list);
132962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mr_sas_port->phy_list);
133062306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
133162306a36Sopenharmony_ci	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
133262306a36Sopenharmony_ci	    sas_address_parent, hba_port);
133362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (!mr_sas_node) {
133662306a36Sopenharmony_ci		ioc_err(mrioc, "%s:could not find parent sas_address(0x%016llx)!\n",
133762306a36Sopenharmony_ci		    __func__, (unsigned long long)sas_address_parent);
133862306a36Sopenharmony_ci		goto out_fail;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if ((mpi3mr_set_identify(mrioc, handle,
134262306a36Sopenharmony_ci	    &mr_sas_port->remote_identify))) {
134362306a36Sopenharmony_ci		ioc_err(mrioc,  "failure at %s:%d/%s()!\n",
134462306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
134562306a36Sopenharmony_ci		goto out_fail;
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (mr_sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
134962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
135062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
135162306a36Sopenharmony_ci		goto out_fail;
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	mr_sas_port->hba_port = hba_port;
135562306a36Sopenharmony_ci	mpi3mr_sas_port_sanity_check(mrioc, mr_sas_node,
135662306a36Sopenharmony_ci	    mr_sas_port->remote_identify.sas_address, hba_port);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	for (i = 0; i < mr_sas_node->num_phys; i++) {
135962306a36Sopenharmony_ci		if ((mr_sas_node->phy[i].remote_identify.sas_address !=
136062306a36Sopenharmony_ci		    mr_sas_port->remote_identify.sas_address) ||
136162306a36Sopenharmony_ci		    (mr_sas_node->phy[i].hba_port != hba_port))
136262306a36Sopenharmony_ci			continue;
136362306a36Sopenharmony_ci		list_add_tail(&mr_sas_node->phy[i].port_siblings,
136462306a36Sopenharmony_ci		    &mr_sas_port->phy_list);
136562306a36Sopenharmony_ci		mr_sas_port->num_phys++;
136662306a36Sopenharmony_ci		mr_sas_port->phy_mask |= (1 << i);
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	if (!mr_sas_port->num_phys) {
137062306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
137162306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
137262306a36Sopenharmony_ci		goto out_fail;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	mr_sas_port->lowest_phy = ffs(mr_sas_port->phy_mask) - 1;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
137862306a36Sopenharmony_ci		tgtdev = mpi3mr_get_tgtdev_by_addr(mrioc,
137962306a36Sopenharmony_ci		    mr_sas_port->remote_identify.sas_address,
138062306a36Sopenharmony_ci		    mr_sas_port->hba_port);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		if (!tgtdev) {
138362306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
138462306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
138562306a36Sopenharmony_ci			goto out_fail;
138662306a36Sopenharmony_ci		}
138762306a36Sopenharmony_ci		tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 1;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	if (!mr_sas_node->parent_dev) {
139162306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
139262306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
139362306a36Sopenharmony_ci		goto out_fail;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	port = sas_port_alloc_num(mr_sas_node->parent_dev);
139762306a36Sopenharmony_ci	if ((sas_port_add(port))) {
139862306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
139962306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
140062306a36Sopenharmony_ci		goto out_fail;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	list_for_each_entry(mr_sas_phy, &mr_sas_port->phy_list,
140462306a36Sopenharmony_ci	    port_siblings) {
140562306a36Sopenharmony_ci		if ((mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
140662306a36Sopenharmony_ci			dev_info(&port->dev,
140762306a36Sopenharmony_ci			    "add: handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
140862306a36Sopenharmony_ci			    handle, (unsigned long long)
140962306a36Sopenharmony_ci			    mr_sas_port->remote_identify.sas_address,
141062306a36Sopenharmony_ci			    mr_sas_phy->phy_id);
141162306a36Sopenharmony_ci		sas_port_add_phy(port, mr_sas_phy->phy);
141262306a36Sopenharmony_ci		mr_sas_phy->phy_belongs_to_port = 1;
141362306a36Sopenharmony_ci		mr_sas_phy->hba_port = hba_port;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	mr_sas_port->port = port;
141762306a36Sopenharmony_ci	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
141862306a36Sopenharmony_ci		rphy = sas_end_device_alloc(port);
141962306a36Sopenharmony_ci		tgtdev->dev_spec.sas_sata_inf.rphy = rphy;
142062306a36Sopenharmony_ci	} else {
142162306a36Sopenharmony_ci		rphy = sas_expander_alloc(port,
142262306a36Sopenharmony_ci		    mr_sas_port->remote_identify.device_type);
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci	rphy->identify = mr_sas_port->remote_identify;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (mrioc->current_event)
142762306a36Sopenharmony_ci		mrioc->current_event->pending_at_sml = 1;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	if ((sas_rphy_add(rphy))) {
143062306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
143162306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci	if (mr_sas_port->remote_identify.device_type == SAS_END_DEVICE) {
143462306a36Sopenharmony_ci		tgtdev->dev_spec.sas_sata_inf.pend_sas_rphy_add = 0;
143562306a36Sopenharmony_ci		tgtdev->dev_spec.sas_sata_inf.sas_transport_attached = 1;
143662306a36Sopenharmony_ci		mpi3mr_tgtdev_put(tgtdev);
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	dev_info(&rphy->dev,
144062306a36Sopenharmony_ci	    "%s: added: handle(0x%04x), sas_address(0x%016llx)\n",
144162306a36Sopenharmony_ci	    __func__, handle, (unsigned long long)
144262306a36Sopenharmony_ci	    mr_sas_port->remote_identify.sas_address);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	mr_sas_port->rphy = rphy;
144562306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
144662306a36Sopenharmony_ci	list_add_tail(&mr_sas_port->port_list, &mr_sas_node->sas_port_list);
144762306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (mrioc->current_event) {
145062306a36Sopenharmony_ci		mrioc->current_event->pending_at_sml = 0;
145162306a36Sopenharmony_ci		if (mrioc->current_event->discard)
145262306a36Sopenharmony_ci			mpi3mr_print_device_event_notice(mrioc, true);
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* fill in report manufacture */
145662306a36Sopenharmony_ci	if (mr_sas_port->remote_identify.device_type ==
145762306a36Sopenharmony_ci	    SAS_EDGE_EXPANDER_DEVICE ||
145862306a36Sopenharmony_ci	    mr_sas_port->remote_identify.device_type ==
145962306a36Sopenharmony_ci	    SAS_FANOUT_EXPANDER_DEVICE)
146062306a36Sopenharmony_ci		mpi3mr_report_manufacture(mrioc,
146162306a36Sopenharmony_ci		    mr_sas_port->remote_identify.sas_address,
146262306a36Sopenharmony_ci		    rphy_to_expander_device(rphy), hba_port->port_id);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	return mr_sas_port;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci out_fail:
146762306a36Sopenharmony_ci	list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list,
146862306a36Sopenharmony_ci	    port_siblings)
146962306a36Sopenharmony_ci		list_del(&mr_sas_phy->port_siblings);
147062306a36Sopenharmony_ci	kfree(mr_sas_port);
147162306a36Sopenharmony_ci	return NULL;
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci/**
147562306a36Sopenharmony_ci * mpi3mr_sas_port_remove - remove port from the list
147662306a36Sopenharmony_ci * @mrioc: Adapter instance reference
147762306a36Sopenharmony_ci * @sas_address: SAS address of attached device
147862306a36Sopenharmony_ci * @sas_address_parent: SAS address of parent expander or host
147962306a36Sopenharmony_ci * @hba_port: HBA port entry
148062306a36Sopenharmony_ci *
148162306a36Sopenharmony_ci * Removing object and freeing associated memory from the
148262306a36Sopenharmony_ci * sas_port_list.
148362306a36Sopenharmony_ci *
148462306a36Sopenharmony_ci * Return: None
148562306a36Sopenharmony_ci */
148662306a36Sopenharmony_cistatic void mpi3mr_sas_port_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
148762306a36Sopenharmony_ci	u64 sas_address_parent, struct mpi3mr_hba_port *hba_port)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	int i;
149062306a36Sopenharmony_ci	unsigned long flags;
149162306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port, *next;
149262306a36Sopenharmony_ci	struct mpi3mr_sas_node *mr_sas_node;
149362306a36Sopenharmony_ci	u8 found = 0;
149462306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy, *next_phy;
149562306a36Sopenharmony_ci	struct mpi3mr_hba_port *srch_port, *hba_port_next = NULL;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	if (!hba_port)
149862306a36Sopenharmony_ci		return;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
150162306a36Sopenharmony_ci	mr_sas_node = __mpi3mr_sas_node_find_by_sas_address(mrioc,
150262306a36Sopenharmony_ci	    sas_address_parent, hba_port);
150362306a36Sopenharmony_ci	if (!mr_sas_node) {
150462306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
150562306a36Sopenharmony_ci		return;
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci	list_for_each_entry_safe(mr_sas_port, next, &mr_sas_node->sas_port_list,
150862306a36Sopenharmony_ci	    port_list) {
150962306a36Sopenharmony_ci		if (mr_sas_port->remote_identify.sas_address != sas_address)
151062306a36Sopenharmony_ci			continue;
151162306a36Sopenharmony_ci		if (mr_sas_port->hba_port != hba_port)
151262306a36Sopenharmony_ci			continue;
151362306a36Sopenharmony_ci		found = 1;
151462306a36Sopenharmony_ci		list_del(&mr_sas_port->port_list);
151562306a36Sopenharmony_ci		goto out;
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci out:
151962306a36Sopenharmony_ci	if (!found) {
152062306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
152162306a36Sopenharmony_ci		return;
152262306a36Sopenharmony_ci	}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	if (mr_sas_node->host_node) {
152562306a36Sopenharmony_ci		list_for_each_entry_safe(srch_port, hba_port_next,
152662306a36Sopenharmony_ci		    &mrioc->hba_port_table_list, list) {
152762306a36Sopenharmony_ci			if (srch_port != hba_port)
152862306a36Sopenharmony_ci				continue;
152962306a36Sopenharmony_ci			ioc_info(mrioc,
153062306a36Sopenharmony_ci			    "removing hba_port entry: %p port: %d from hba_port list\n",
153162306a36Sopenharmony_ci			    srch_port, srch_port->port_id);
153262306a36Sopenharmony_ci			list_del(&hba_port->list);
153362306a36Sopenharmony_ci			kfree(hba_port);
153462306a36Sopenharmony_ci			break;
153562306a36Sopenharmony_ci		}
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	for (i = 0; i < mr_sas_node->num_phys; i++) {
153962306a36Sopenharmony_ci		if (mr_sas_node->phy[i].remote_identify.sas_address ==
154062306a36Sopenharmony_ci		    sas_address)
154162306a36Sopenharmony_ci			memset(&mr_sas_node->phy[i].remote_identify, 0,
154262306a36Sopenharmony_ci			    sizeof(struct sas_identify));
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	if (mrioc->current_event)
154862306a36Sopenharmony_ci		mrioc->current_event->pending_at_sml = 1;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	list_for_each_entry_safe(mr_sas_phy, next_phy,
155162306a36Sopenharmony_ci	    &mr_sas_port->phy_list, port_siblings) {
155262306a36Sopenharmony_ci		if ((!mrioc->stop_drv_processing) &&
155362306a36Sopenharmony_ci		    (mrioc->logging_level & MPI3_DEBUG_TRANSPORT_INFO))
155462306a36Sopenharmony_ci			dev_info(&mr_sas_port->port->dev,
155562306a36Sopenharmony_ci			    "remove: sas_address(0x%016llx), phy(%d)\n",
155662306a36Sopenharmony_ci			    (unsigned long long)
155762306a36Sopenharmony_ci			    mr_sas_port->remote_identify.sas_address,
155862306a36Sopenharmony_ci			    mr_sas_phy->phy_id);
155962306a36Sopenharmony_ci		mr_sas_phy->phy_belongs_to_port = 0;
156062306a36Sopenharmony_ci		if (!mrioc->stop_drv_processing)
156162306a36Sopenharmony_ci			sas_port_delete_phy(mr_sas_port->port,
156262306a36Sopenharmony_ci			    mr_sas_phy->phy);
156362306a36Sopenharmony_ci		list_del(&mr_sas_phy->port_siblings);
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci	if (!mrioc->stop_drv_processing)
156662306a36Sopenharmony_ci		sas_port_delete(mr_sas_port->port);
156762306a36Sopenharmony_ci	ioc_info(mrioc, "%s: removed sas_address(0x%016llx)\n",
156862306a36Sopenharmony_ci	    __func__, (unsigned long long)sas_address);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	if (mrioc->current_event) {
157162306a36Sopenharmony_ci		mrioc->current_event->pending_at_sml = 0;
157262306a36Sopenharmony_ci		if (mrioc->current_event->discard)
157362306a36Sopenharmony_ci			mpi3mr_print_device_event_notice(mrioc, false);
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	kfree(mr_sas_port);
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci/**
158062306a36Sopenharmony_ci * struct host_port - host port details
158162306a36Sopenharmony_ci * @sas_address: SAS Address of the attached device
158262306a36Sopenharmony_ci * @phy_mask: phy mask of host port
158362306a36Sopenharmony_ci * @handle: Device Handle of attached device
158462306a36Sopenharmony_ci * @iounit_port_id: port ID
158562306a36Sopenharmony_ci * @used: host port is already matched with sas port from sas_port_list
158662306a36Sopenharmony_ci * @lowest_phy: lowest phy ID of host port
158762306a36Sopenharmony_ci */
158862306a36Sopenharmony_cistruct host_port {
158962306a36Sopenharmony_ci	u64	sas_address;
159062306a36Sopenharmony_ci	u32	phy_mask;
159162306a36Sopenharmony_ci	u16	handle;
159262306a36Sopenharmony_ci	u8	iounit_port_id;
159362306a36Sopenharmony_ci	u8	used;
159462306a36Sopenharmony_ci	u8	lowest_phy;
159562306a36Sopenharmony_ci};
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci/**
159862306a36Sopenharmony_ci * mpi3mr_update_mr_sas_port - update sas port objects during reset
159962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
160062306a36Sopenharmony_ci * @h_port: host_port object
160162306a36Sopenharmony_ci * @mr_sas_port: sas_port objects which needs to be updated
160262306a36Sopenharmony_ci *
160362306a36Sopenharmony_ci * Update the port ID of sas port object. Also add the phys if new phys got
160462306a36Sopenharmony_ci * added to current sas port and remove the phys if some phys are moved
160562306a36Sopenharmony_ci * out of the current sas port.
160662306a36Sopenharmony_ci *
160762306a36Sopenharmony_ci * Return: Nothing.
160862306a36Sopenharmony_ci */
160962306a36Sopenharmony_cistatic void
161062306a36Sopenharmony_cimpi3mr_update_mr_sas_port(struct mpi3mr_ioc *mrioc, struct host_port *h_port,
161162306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port)
161262306a36Sopenharmony_ci{
161362306a36Sopenharmony_ci	struct mpi3mr_sas_phy *mr_sas_phy;
161462306a36Sopenharmony_ci	u32 phy_mask_xor;
161562306a36Sopenharmony_ci	u64 phys_to_be_added, phys_to_be_removed;
161662306a36Sopenharmony_ci	int i;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	h_port->used = 1;
161962306a36Sopenharmony_ci	mr_sas_port->marked_responding = 1;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	dev_info(&mr_sas_port->port->dev,
162262306a36Sopenharmony_ci	    "sas_address(0x%016llx), old: port_id %d phy_mask 0x%x, new: port_id %d phy_mask:0x%x\n",
162362306a36Sopenharmony_ci	    mr_sas_port->remote_identify.sas_address,
162462306a36Sopenharmony_ci	    mr_sas_port->hba_port->port_id, mr_sas_port->phy_mask,
162562306a36Sopenharmony_ci	    h_port->iounit_port_id, h_port->phy_mask);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	mr_sas_port->hba_port->port_id = h_port->iounit_port_id;
162862306a36Sopenharmony_ci	mr_sas_port->hba_port->flags &= ~MPI3MR_HBA_PORT_FLAG_DIRTY;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	/* Get the newly added phys bit map & removed phys bit map */
163162306a36Sopenharmony_ci	phy_mask_xor = mr_sas_port->phy_mask ^ h_port->phy_mask;
163262306a36Sopenharmony_ci	phys_to_be_added = h_port->phy_mask & phy_mask_xor;
163362306a36Sopenharmony_ci	phys_to_be_removed = mr_sas_port->phy_mask & phy_mask_xor;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/*
163662306a36Sopenharmony_ci	 * Register these new phys to current mr_sas_port's port.
163762306a36Sopenharmony_ci	 * if these phys are previously registered with another port
163862306a36Sopenharmony_ci	 * then delete these phys from that port first.
163962306a36Sopenharmony_ci	 */
164062306a36Sopenharmony_ci	for_each_set_bit(i, (ulong *) &phys_to_be_added, BITS_PER_TYPE(u32)) {
164162306a36Sopenharmony_ci		mr_sas_phy = &mrioc->sas_hba.phy[i];
164262306a36Sopenharmony_ci		if (mr_sas_phy->phy_belongs_to_port)
164362306a36Sopenharmony_ci			mpi3mr_del_phy_from_an_existing_port(mrioc,
164462306a36Sopenharmony_ci			    &mrioc->sas_hba, mr_sas_phy);
164562306a36Sopenharmony_ci		mpi3mr_add_phy_to_an_existing_port(mrioc,
164662306a36Sopenharmony_ci		    &mrioc->sas_hba, mr_sas_phy,
164762306a36Sopenharmony_ci		    mr_sas_port->remote_identify.sas_address,
164862306a36Sopenharmony_ci		    mr_sas_port->hba_port);
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	/* Delete the phys which are not part of current mr_sas_port's port. */
165262306a36Sopenharmony_ci	for_each_set_bit(i, (ulong *) &phys_to_be_removed, BITS_PER_TYPE(u32)) {
165362306a36Sopenharmony_ci		mr_sas_phy = &mrioc->sas_hba.phy[i];
165462306a36Sopenharmony_ci		if (mr_sas_phy->phy_belongs_to_port)
165562306a36Sopenharmony_ci			mpi3mr_del_phy_from_an_existing_port(mrioc,
165662306a36Sopenharmony_ci			    &mrioc->sas_hba, mr_sas_phy);
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci/**
166162306a36Sopenharmony_ci * mpi3mr_refresh_sas_ports - update host's sas ports during reset
166262306a36Sopenharmony_ci * @mrioc: Adapter instance reference
166362306a36Sopenharmony_ci *
166462306a36Sopenharmony_ci * Update the host's sas ports during reset by checking whether
166562306a36Sopenharmony_ci * sas ports are still intact or not. Add/remove phys if any hba
166662306a36Sopenharmony_ci * phys are (moved in)/(moved out) of sas port. Also update
166762306a36Sopenharmony_ci * io_unit_port if it got changed during reset.
166862306a36Sopenharmony_ci *
166962306a36Sopenharmony_ci * Return: Nothing.
167062306a36Sopenharmony_ci */
167162306a36Sopenharmony_civoid
167262306a36Sopenharmony_cimpi3mr_refresh_sas_ports(struct mpi3mr_ioc *mrioc)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct host_port h_port[32];
167562306a36Sopenharmony_ci	int i, j, found, host_port_count = 0, port_idx;
167662306a36Sopenharmony_ci	u16 sz, attached_handle, ioc_status;
167762306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
167862306a36Sopenharmony_ci	struct mpi3_device_page0 dev_pg0;
167962306a36Sopenharmony_ci	struct mpi3_device0_sas_sata_format *sasinf;
168062306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
168362306a36Sopenharmony_ci		(mrioc->sas_hba.num_phys *
168462306a36Sopenharmony_ci		 sizeof(struct mpi3_sas_io_unit0_phy_data));
168562306a36Sopenharmony_ci	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
168662306a36Sopenharmony_ci	if (!sas_io_unit_pg0)
168762306a36Sopenharmony_ci		return;
168862306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
168962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
169062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
169162306a36Sopenharmony_ci		goto out;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	/* Create a new expander port table */
169562306a36Sopenharmony_ci	for (i = 0; i < mrioc->sas_hba.num_phys; i++) {
169662306a36Sopenharmony_ci		attached_handle = le16_to_cpu(
169762306a36Sopenharmony_ci		    sas_io_unit_pg0->phy_data[i].attached_dev_handle);
169862306a36Sopenharmony_ci		if (!attached_handle)
169962306a36Sopenharmony_ci			continue;
170062306a36Sopenharmony_ci		found = 0;
170162306a36Sopenharmony_ci		for (j = 0; j < host_port_count; j++) {
170262306a36Sopenharmony_ci			if (h_port[j].handle == attached_handle) {
170362306a36Sopenharmony_ci				h_port[j].phy_mask |= (1 << i);
170462306a36Sopenharmony_ci				found = 1;
170562306a36Sopenharmony_ci				break;
170662306a36Sopenharmony_ci			}
170762306a36Sopenharmony_ci		}
170862306a36Sopenharmony_ci		if (found)
170962306a36Sopenharmony_ci			continue;
171062306a36Sopenharmony_ci		if ((mpi3mr_cfg_get_dev_pg0(mrioc, &ioc_status, &dev_pg0,
171162306a36Sopenharmony_ci		    sizeof(dev_pg0), MPI3_DEVICE_PGAD_FORM_HANDLE,
171262306a36Sopenharmony_ci		    attached_handle))) {
171362306a36Sopenharmony_ci			dprint_reset(mrioc,
171462306a36Sopenharmony_ci			    "failed to read dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
171562306a36Sopenharmony_ci			    attached_handle, __FILE__, __LINE__, __func__);
171662306a36Sopenharmony_ci			continue;
171762306a36Sopenharmony_ci		}
171862306a36Sopenharmony_ci		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
171962306a36Sopenharmony_ci			dprint_reset(mrioc,
172062306a36Sopenharmony_ci			    "ioc_status(0x%x) while reading dev_pg0 for handle(0x%04x) at %s:%d/%s()!\n",
172162306a36Sopenharmony_ci			    ioc_status, attached_handle,
172262306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
172362306a36Sopenharmony_ci			continue;
172462306a36Sopenharmony_ci		}
172562306a36Sopenharmony_ci		sasinf = &dev_pg0.device_specific.sas_sata_format;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci		port_idx = host_port_count;
172862306a36Sopenharmony_ci		h_port[port_idx].sas_address = le64_to_cpu(sasinf->sas_address);
172962306a36Sopenharmony_ci		h_port[port_idx].handle = attached_handle;
173062306a36Sopenharmony_ci		h_port[port_idx].phy_mask = (1 << i);
173162306a36Sopenharmony_ci		h_port[port_idx].iounit_port_id = sas_io_unit_pg0->phy_data[i].io_unit_port;
173262306a36Sopenharmony_ci		h_port[port_idx].lowest_phy = sasinf->phy_num;
173362306a36Sopenharmony_ci		h_port[port_idx].used = 0;
173462306a36Sopenharmony_ci		host_port_count++;
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	if (!host_port_count)
173862306a36Sopenharmony_ci		goto out;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (mrioc->logging_level & MPI3_DEBUG_RESET) {
174162306a36Sopenharmony_ci		ioc_info(mrioc, "Host port details before reset\n");
174262306a36Sopenharmony_ci		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
174362306a36Sopenharmony_ci		    port_list) {
174462306a36Sopenharmony_ci			ioc_info(mrioc,
174562306a36Sopenharmony_ci			    "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%x), lowest phy id:%d\n",
174662306a36Sopenharmony_ci			    mr_sas_port->hba_port->port_id,
174762306a36Sopenharmony_ci			    mr_sas_port->remote_identify.sas_address,
174862306a36Sopenharmony_ci			    mr_sas_port->phy_mask, mr_sas_port->lowest_phy);
174962306a36Sopenharmony_ci		}
175062306a36Sopenharmony_ci		mr_sas_port = NULL;
175162306a36Sopenharmony_ci		ioc_info(mrioc, "Host port details after reset\n");
175262306a36Sopenharmony_ci		for (i = 0; i < host_port_count; i++) {
175362306a36Sopenharmony_ci			ioc_info(mrioc,
175462306a36Sopenharmony_ci			    "port_id:%d, sas_address:(0x%016llx), phy_mask:(0x%x), lowest phy id:%d\n",
175562306a36Sopenharmony_ci			    h_port[i].iounit_port_id, h_port[i].sas_address,
175662306a36Sopenharmony_ci			    h_port[i].phy_mask, h_port[i].lowest_phy);
175762306a36Sopenharmony_ci		}
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	/* mark all host sas port entries as dirty */
176162306a36Sopenharmony_ci	list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
176262306a36Sopenharmony_ci	    port_list) {
176362306a36Sopenharmony_ci		mr_sas_port->marked_responding = 0;
176462306a36Sopenharmony_ci		mr_sas_port->hba_port->flags |= MPI3MR_HBA_PORT_FLAG_DIRTY;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	/* First check for matching lowest phy */
176862306a36Sopenharmony_ci	for (i = 0; i < host_port_count; i++) {
176962306a36Sopenharmony_ci		mr_sas_port = NULL;
177062306a36Sopenharmony_ci		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
177162306a36Sopenharmony_ci		    port_list) {
177262306a36Sopenharmony_ci			if (mr_sas_port->marked_responding)
177362306a36Sopenharmony_ci				continue;
177462306a36Sopenharmony_ci			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
177562306a36Sopenharmony_ci				continue;
177662306a36Sopenharmony_ci			if (h_port[i].lowest_phy == mr_sas_port->lowest_phy) {
177762306a36Sopenharmony_ci				mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
177862306a36Sopenharmony_ci				break;
177962306a36Sopenharmony_ci			}
178062306a36Sopenharmony_ci		}
178162306a36Sopenharmony_ci	}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	/* In case if lowest phy is got enabled or disabled during reset */
178462306a36Sopenharmony_ci	for (i = 0; i < host_port_count; i++) {
178562306a36Sopenharmony_ci		if (h_port[i].used)
178662306a36Sopenharmony_ci			continue;
178762306a36Sopenharmony_ci		mr_sas_port = NULL;
178862306a36Sopenharmony_ci		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
178962306a36Sopenharmony_ci		    port_list) {
179062306a36Sopenharmony_ci			if (mr_sas_port->marked_responding)
179162306a36Sopenharmony_ci				continue;
179262306a36Sopenharmony_ci			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
179362306a36Sopenharmony_ci				continue;
179462306a36Sopenharmony_ci			if (h_port[i].phy_mask & mr_sas_port->phy_mask) {
179562306a36Sopenharmony_ci				mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
179662306a36Sopenharmony_ci				break;
179762306a36Sopenharmony_ci			}
179862306a36Sopenharmony_ci		}
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	/* In case if expander cable is removed & connected to another HBA port during reset */
180262306a36Sopenharmony_ci	for (i = 0; i < host_port_count; i++) {
180362306a36Sopenharmony_ci		if (h_port[i].used)
180462306a36Sopenharmony_ci			continue;
180562306a36Sopenharmony_ci		mr_sas_port = NULL;
180662306a36Sopenharmony_ci		list_for_each_entry(mr_sas_port, &mrioc->sas_hba.sas_port_list,
180762306a36Sopenharmony_ci		    port_list) {
180862306a36Sopenharmony_ci			if (mr_sas_port->marked_responding)
180962306a36Sopenharmony_ci				continue;
181062306a36Sopenharmony_ci			if (h_port[i].sas_address != mr_sas_port->remote_identify.sas_address)
181162306a36Sopenharmony_ci				continue;
181262306a36Sopenharmony_ci			mpi3mr_update_mr_sas_port(mrioc, &h_port[i], mr_sas_port);
181362306a36Sopenharmony_ci			break;
181462306a36Sopenharmony_ci		}
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ciout:
181762306a36Sopenharmony_ci	kfree(sas_io_unit_pg0);
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci/**
182162306a36Sopenharmony_ci * mpi3mr_refresh_expanders - Refresh expander device exposure
182262306a36Sopenharmony_ci * @mrioc: Adapter instance reference
182362306a36Sopenharmony_ci *
182462306a36Sopenharmony_ci * This is executed post controller reset to identify any
182562306a36Sopenharmony_ci * missing expander devices during reset and remove from the upper layers
182662306a36Sopenharmony_ci * or expose any newly detected expander device to the upper layers.
182762306a36Sopenharmony_ci *
182862306a36Sopenharmony_ci * Return: Nothing.
182962306a36Sopenharmony_ci */
183062306a36Sopenharmony_civoid
183162306a36Sopenharmony_cimpi3mr_refresh_expanders(struct mpi3mr_ioc *mrioc)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander, *sas_expander_next;
183462306a36Sopenharmony_ci	struct mpi3_sas_expander_page0 expander_pg0;
183562306a36Sopenharmony_ci	u16 ioc_status, handle;
183662306a36Sopenharmony_ci	u64 sas_address;
183762306a36Sopenharmony_ci	int i;
183862306a36Sopenharmony_ci	unsigned long flags;
183962306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
184262306a36Sopenharmony_ci	list_for_each_entry(sas_expander, &mrioc->sas_expander_list, list) {
184362306a36Sopenharmony_ci		sas_expander->non_responding = 1;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	sas_expander = NULL;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	handle = 0xffff;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* Search for responding expander devices and add them if they are newly got added */
185262306a36Sopenharmony_ci	while (true) {
185362306a36Sopenharmony_ci		if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
185462306a36Sopenharmony_ci		    sizeof(struct mpi3_sas_expander_page0),
185562306a36Sopenharmony_ci		    MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
185662306a36Sopenharmony_ci			dprint_reset(mrioc,
185762306a36Sopenharmony_ci			    "failed to read exp pg0 for handle(0x%04x) at %s:%d/%s()!\n",
185862306a36Sopenharmony_ci			    handle, __FILE__, __LINE__, __func__);
185962306a36Sopenharmony_ci			break;
186062306a36Sopenharmony_ci		}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
186362306a36Sopenharmony_ci			dprint_reset(mrioc,
186462306a36Sopenharmony_ci			   "ioc_status(0x%x) while reading exp pg0 for handle:(0x%04x), %s:%d/%s()!\n",
186562306a36Sopenharmony_ci			   ioc_status, handle, __FILE__, __LINE__, __func__);
186662306a36Sopenharmony_ci			break;
186762306a36Sopenharmony_ci		}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci		handle = le16_to_cpu(expander_pg0.dev_handle);
187062306a36Sopenharmony_ci		sas_address = le64_to_cpu(expander_pg0.sas_address);
187162306a36Sopenharmony_ci		hba_port = mpi3mr_get_hba_port_by_id(mrioc, expander_pg0.io_unit_port);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci		if (!hba_port) {
187462306a36Sopenharmony_ci			mpi3mr_sas_host_refresh(mrioc);
187562306a36Sopenharmony_ci			mpi3mr_expander_add(mrioc, handle);
187662306a36Sopenharmony_ci			continue;
187762306a36Sopenharmony_ci		}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
188062306a36Sopenharmony_ci		sas_expander =
188162306a36Sopenharmony_ci		    mpi3mr_expander_find_by_sas_address(mrioc,
188262306a36Sopenharmony_ci		    sas_address, hba_port);
188362306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci		if (!sas_expander) {
188662306a36Sopenharmony_ci			mpi3mr_sas_host_refresh(mrioc);
188762306a36Sopenharmony_ci			mpi3mr_expander_add(mrioc, handle);
188862306a36Sopenharmony_ci			continue;
188962306a36Sopenharmony_ci		}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci		sas_expander->non_responding = 0;
189262306a36Sopenharmony_ci		if (sas_expander->handle == handle)
189362306a36Sopenharmony_ci			continue;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci		sas_expander->handle = handle;
189662306a36Sopenharmony_ci		for (i = 0 ; i < sas_expander->num_phys ; i++)
189762306a36Sopenharmony_ci			sas_expander->phy[i].handle = handle;
189862306a36Sopenharmony_ci	}
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	/*
190162306a36Sopenharmony_ci	 * Delete non responding expander devices and the corresponding
190262306a36Sopenharmony_ci	 * hba_port if the non responding expander device's parent device
190362306a36Sopenharmony_ci	 * is a host node.
190462306a36Sopenharmony_ci	 */
190562306a36Sopenharmony_ci	sas_expander = NULL;
190662306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
190762306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(sas_expander, sas_expander_next,
190862306a36Sopenharmony_ci	    &mrioc->sas_expander_list, list) {
190962306a36Sopenharmony_ci		if (sas_expander->non_responding) {
191062306a36Sopenharmony_ci			spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
191162306a36Sopenharmony_ci			mpi3mr_expander_node_remove(mrioc, sas_expander);
191262306a36Sopenharmony_ci			spin_lock_irqsave(&mrioc->sas_node_lock, flags);
191362306a36Sopenharmony_ci		}
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci/**
191962306a36Sopenharmony_ci * mpi3mr_expander_node_add - insert an expander to the list.
192062306a36Sopenharmony_ci * @mrioc: Adapter instance reference
192162306a36Sopenharmony_ci * @sas_expander: Expander sas node
192262306a36Sopenharmony_ci * Context: This function will acquire sas_node_lock.
192362306a36Sopenharmony_ci *
192462306a36Sopenharmony_ci * Adding new object to the ioc->sas_expander_list.
192562306a36Sopenharmony_ci *
192662306a36Sopenharmony_ci * Return: None.
192762306a36Sopenharmony_ci */
192862306a36Sopenharmony_cistatic void mpi3mr_expander_node_add(struct mpi3mr_ioc *mrioc,
192962306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander)
193062306a36Sopenharmony_ci{
193162306a36Sopenharmony_ci	unsigned long flags;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
193462306a36Sopenharmony_ci	list_add_tail(&sas_expander->list, &mrioc->sas_expander_list);
193562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
193662306a36Sopenharmony_ci}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci/**
193962306a36Sopenharmony_ci * mpi3mr_expander_add -  Create expander object
194062306a36Sopenharmony_ci * @mrioc: Adapter instance reference
194162306a36Sopenharmony_ci * @handle: Expander firmware device handle
194262306a36Sopenharmony_ci *
194362306a36Sopenharmony_ci * This function creating expander object, stored in
194462306a36Sopenharmony_ci * sas_expander_list and expose it to the SAS transport
194562306a36Sopenharmony_ci * layer.
194662306a36Sopenharmony_ci *
194762306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
194862306a36Sopenharmony_ci */
194962306a36Sopenharmony_ciint mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle)
195062306a36Sopenharmony_ci{
195162306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander;
195262306a36Sopenharmony_ci	struct mpi3mr_enclosure_node *enclosure_dev;
195362306a36Sopenharmony_ci	struct mpi3_sas_expander_page0 expander_pg0;
195462306a36Sopenharmony_ci	struct mpi3_sas_expander_page1 expander_pg1;
195562306a36Sopenharmony_ci	u16 ioc_status, parent_handle, temp_handle;
195662306a36Sopenharmony_ci	u64 sas_address, sas_address_parent = 0;
195762306a36Sopenharmony_ci	int i;
195862306a36Sopenharmony_ci	unsigned long flags;
195962306a36Sopenharmony_ci	u8 port_id, link_rate;
196062306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port = NULL;
196162306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port;
196262306a36Sopenharmony_ci	u32 phynum_handle;
196362306a36Sopenharmony_ci	int rc = 0;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	if (!handle)
196662306a36Sopenharmony_ci		return -1;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	if (mrioc->reset_in_progress)
196962306a36Sopenharmony_ci		return -1;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if ((mpi3mr_cfg_get_sas_exp_pg0(mrioc, &ioc_status, &expander_pg0,
197262306a36Sopenharmony_ci	    sizeof(expander_pg0), MPI3_SAS_EXPAND_PGAD_FORM_HANDLE, handle))) {
197362306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
197462306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
197562306a36Sopenharmony_ci		return -1;
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
197962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
198062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
198162306a36Sopenharmony_ci		return -1;
198262306a36Sopenharmony_ci	}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	parent_handle = le16_to_cpu(expander_pg0.parent_dev_handle);
198562306a36Sopenharmony_ci	if (mpi3mr_get_sas_address(mrioc, parent_handle, &sas_address_parent)
198662306a36Sopenharmony_ci	    != 0) {
198762306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
198862306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
198962306a36Sopenharmony_ci		return -1;
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	port_id = expander_pg0.io_unit_port;
199362306a36Sopenharmony_ci	hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
199462306a36Sopenharmony_ci	if (!hba_port) {
199562306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
199662306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
199762306a36Sopenharmony_ci		return -1;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	if (sas_address_parent != mrioc->sas_hba.sas_address) {
200162306a36Sopenharmony_ci		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
200262306a36Sopenharmony_ci		sas_expander =
200362306a36Sopenharmony_ci		   mpi3mr_expander_find_by_sas_address(mrioc,
200462306a36Sopenharmony_ci		    sas_address_parent, hba_port);
200562306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
200662306a36Sopenharmony_ci		if (!sas_expander) {
200762306a36Sopenharmony_ci			rc = mpi3mr_expander_add(mrioc, parent_handle);
200862306a36Sopenharmony_ci			if (rc != 0)
200962306a36Sopenharmony_ci				return rc;
201062306a36Sopenharmony_ci		} else {
201162306a36Sopenharmony_ci			/*
201262306a36Sopenharmony_ci			 * When there is a parent expander present, update it's
201362306a36Sopenharmony_ci			 * phys where child expander is connected with the link
201462306a36Sopenharmony_ci			 * speed, attached dev handle and sas address.
201562306a36Sopenharmony_ci			 */
201662306a36Sopenharmony_ci			for (i = 0 ; i < sas_expander->num_phys ; i++) {
201762306a36Sopenharmony_ci				phynum_handle =
201862306a36Sopenharmony_ci				    (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
201962306a36Sopenharmony_ci				    parent_handle;
202062306a36Sopenharmony_ci				if (mpi3mr_cfg_get_sas_exp_pg1(mrioc,
202162306a36Sopenharmony_ci				    &ioc_status, &expander_pg1,
202262306a36Sopenharmony_ci				    sizeof(expander_pg1),
202362306a36Sopenharmony_ci				    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
202462306a36Sopenharmony_ci				    phynum_handle)) {
202562306a36Sopenharmony_ci					ioc_err(mrioc, "failure at %s:%d/%s()!\n",
202662306a36Sopenharmony_ci					    __FILE__, __LINE__, __func__);
202762306a36Sopenharmony_ci					rc = -1;
202862306a36Sopenharmony_ci					return rc;
202962306a36Sopenharmony_ci				}
203062306a36Sopenharmony_ci				if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
203162306a36Sopenharmony_ci					ioc_err(mrioc, "failure at %s:%d/%s()!\n",
203262306a36Sopenharmony_ci					    __FILE__, __LINE__, __func__);
203362306a36Sopenharmony_ci					rc = -1;
203462306a36Sopenharmony_ci					return rc;
203562306a36Sopenharmony_ci				}
203662306a36Sopenharmony_ci				temp_handle = le16_to_cpu(
203762306a36Sopenharmony_ci				    expander_pg1.attached_dev_handle);
203862306a36Sopenharmony_ci				if (temp_handle != handle)
203962306a36Sopenharmony_ci					continue;
204062306a36Sopenharmony_ci				link_rate = (expander_pg1.negotiated_link_rate &
204162306a36Sopenharmony_ci				    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
204262306a36Sopenharmony_ci				    MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
204362306a36Sopenharmony_ci				mpi3mr_update_links(mrioc, sas_address_parent,
204462306a36Sopenharmony_ci				    handle, i, link_rate, hba_port);
204562306a36Sopenharmony_ci			}
204662306a36Sopenharmony_ci		}
204762306a36Sopenharmony_ci	}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
205062306a36Sopenharmony_ci	sas_address = le64_to_cpu(expander_pg0.sas_address);
205162306a36Sopenharmony_ci	sas_expander = mpi3mr_expander_find_by_sas_address(mrioc,
205262306a36Sopenharmony_ci	    sas_address, hba_port);
205362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (sas_expander)
205662306a36Sopenharmony_ci		return 0;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	sas_expander = kzalloc(sizeof(struct mpi3mr_sas_node),
205962306a36Sopenharmony_ci	    GFP_KERNEL);
206062306a36Sopenharmony_ci	if (!sas_expander)
206162306a36Sopenharmony_ci		return -ENOMEM;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	sas_expander->handle = handle;
206462306a36Sopenharmony_ci	sas_expander->num_phys = expander_pg0.num_phys;
206562306a36Sopenharmony_ci	sas_expander->sas_address_parent = sas_address_parent;
206662306a36Sopenharmony_ci	sas_expander->sas_address = sas_address;
206762306a36Sopenharmony_ci	sas_expander->hba_port = hba_port;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	ioc_info(mrioc,
207062306a36Sopenharmony_ci	    "expander_add: handle(0x%04x), parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
207162306a36Sopenharmony_ci	    handle, parent_handle, (unsigned long long)
207262306a36Sopenharmony_ci	    sas_expander->sas_address, sas_expander->num_phys);
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (!sas_expander->num_phys) {
207562306a36Sopenharmony_ci		rc = -1;
207662306a36Sopenharmony_ci		goto out_fail;
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci	sas_expander->phy = kcalloc(sas_expander->num_phys,
207962306a36Sopenharmony_ci	    sizeof(struct mpi3mr_sas_phy), GFP_KERNEL);
208062306a36Sopenharmony_ci	if (!sas_expander->phy) {
208162306a36Sopenharmony_ci		rc = -1;
208262306a36Sopenharmony_ci		goto out_fail;
208362306a36Sopenharmony_ci	}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	INIT_LIST_HEAD(&sas_expander->sas_port_list);
208662306a36Sopenharmony_ci	mr_sas_port = mpi3mr_sas_port_add(mrioc, handle, sas_address_parent,
208762306a36Sopenharmony_ci	    sas_expander->hba_port);
208862306a36Sopenharmony_ci	if (!mr_sas_port) {
208962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
209062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
209162306a36Sopenharmony_ci		rc = -1;
209262306a36Sopenharmony_ci		goto out_fail;
209362306a36Sopenharmony_ci	}
209462306a36Sopenharmony_ci	sas_expander->parent_dev = &mr_sas_port->rphy->dev;
209562306a36Sopenharmony_ci	sas_expander->rphy = mr_sas_port->rphy;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	for (i = 0 ; i < sas_expander->num_phys ; i++) {
209862306a36Sopenharmony_ci		phynum_handle = (i << MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT) |
209962306a36Sopenharmony_ci		    handle;
210062306a36Sopenharmony_ci		if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
210162306a36Sopenharmony_ci		    &expander_pg1, sizeof(expander_pg1),
210262306a36Sopenharmony_ci		    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
210362306a36Sopenharmony_ci		    phynum_handle)) {
210462306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
210562306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
210662306a36Sopenharmony_ci			rc = -1;
210762306a36Sopenharmony_ci			goto out_fail;
210862306a36Sopenharmony_ci		}
210962306a36Sopenharmony_ci		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
211062306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
211162306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
211262306a36Sopenharmony_ci			rc = -1;
211362306a36Sopenharmony_ci			goto out_fail;
211462306a36Sopenharmony_ci		}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci		sas_expander->phy[i].handle = handle;
211762306a36Sopenharmony_ci		sas_expander->phy[i].phy_id = i;
211862306a36Sopenharmony_ci		sas_expander->phy[i].hba_port = hba_port;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci		if ((mpi3mr_add_expander_phy(mrioc, &sas_expander->phy[i],
212162306a36Sopenharmony_ci		    expander_pg1, sas_expander->parent_dev))) {
212262306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
212362306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
212462306a36Sopenharmony_ci			rc = -1;
212562306a36Sopenharmony_ci			goto out_fail;
212662306a36Sopenharmony_ci		}
212762306a36Sopenharmony_ci	}
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	if (sas_expander->enclosure_handle) {
213062306a36Sopenharmony_ci		enclosure_dev =
213162306a36Sopenharmony_ci			mpi3mr_enclosure_find_by_handle(mrioc,
213262306a36Sopenharmony_ci						sas_expander->enclosure_handle);
213362306a36Sopenharmony_ci		if (enclosure_dev)
213462306a36Sopenharmony_ci			sas_expander->enclosure_logical_id = le64_to_cpu(
213562306a36Sopenharmony_ci			    enclosure_dev->pg0.enclosure_logical_id);
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	mpi3mr_expander_node_add(mrioc, sas_expander);
213962306a36Sopenharmony_ci	return 0;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ciout_fail:
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (mr_sas_port)
214462306a36Sopenharmony_ci		mpi3mr_sas_port_remove(mrioc,
214562306a36Sopenharmony_ci		    sas_expander->sas_address,
214662306a36Sopenharmony_ci		    sas_address_parent, sas_expander->hba_port);
214762306a36Sopenharmony_ci	kfree(sas_expander->phy);
214862306a36Sopenharmony_ci	kfree(sas_expander);
214962306a36Sopenharmony_ci	return rc;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci/**
215362306a36Sopenharmony_ci * mpi3mr_expander_node_remove - recursive removal of expander.
215462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
215562306a36Sopenharmony_ci * @sas_expander: Expander device object
215662306a36Sopenharmony_ci *
215762306a36Sopenharmony_ci * Removes expander object and freeing associated memory from
215862306a36Sopenharmony_ci * the sas_expander_list and removes the same from SAS TL, if
215962306a36Sopenharmony_ci * one of the attached device is an expander then it recursively
216062306a36Sopenharmony_ci * removes the expander device too.
216162306a36Sopenharmony_ci *
216262306a36Sopenharmony_ci * Return nothing.
216362306a36Sopenharmony_ci */
216462306a36Sopenharmony_civoid mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc,
216562306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	struct mpi3mr_sas_port *mr_sas_port, *next;
216862306a36Sopenharmony_ci	unsigned long flags;
216962306a36Sopenharmony_ci	u8 port_id;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	/* remove sibling ports attached to this expander */
217262306a36Sopenharmony_ci	list_for_each_entry_safe(mr_sas_port, next,
217362306a36Sopenharmony_ci	   &sas_expander->sas_port_list, port_list) {
217462306a36Sopenharmony_ci		if (mrioc->reset_in_progress)
217562306a36Sopenharmony_ci			return;
217662306a36Sopenharmony_ci		if (mr_sas_port->remote_identify.device_type ==
217762306a36Sopenharmony_ci		    SAS_END_DEVICE)
217862306a36Sopenharmony_ci			mpi3mr_remove_device_by_sas_address(mrioc,
217962306a36Sopenharmony_ci			    mr_sas_port->remote_identify.sas_address,
218062306a36Sopenharmony_ci			    mr_sas_port->hba_port);
218162306a36Sopenharmony_ci		else if (mr_sas_port->remote_identify.device_type ==
218262306a36Sopenharmony_ci		    SAS_EDGE_EXPANDER_DEVICE ||
218362306a36Sopenharmony_ci		    mr_sas_port->remote_identify.device_type ==
218462306a36Sopenharmony_ci		    SAS_FANOUT_EXPANDER_DEVICE)
218562306a36Sopenharmony_ci			mpi3mr_expander_remove(mrioc,
218662306a36Sopenharmony_ci			    mr_sas_port->remote_identify.sas_address,
218762306a36Sopenharmony_ci			    mr_sas_port->hba_port);
218862306a36Sopenharmony_ci	}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	port_id = sas_expander->hba_port->port_id;
219162306a36Sopenharmony_ci	mpi3mr_sas_port_remove(mrioc, sas_expander->sas_address,
219262306a36Sopenharmony_ci	    sas_expander->sas_address_parent, sas_expander->hba_port);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	ioc_info(mrioc, "expander_remove: handle(0x%04x), sas_addr(0x%016llx), port:%d\n",
219562306a36Sopenharmony_ci	    sas_expander->handle, (unsigned long long)
219662306a36Sopenharmony_ci	    sas_expander->sas_address, port_id);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
219962306a36Sopenharmony_ci	list_del(&sas_expander->list);
220062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	kfree(sas_expander->phy);
220362306a36Sopenharmony_ci	kfree(sas_expander);
220462306a36Sopenharmony_ci}
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci/**
220762306a36Sopenharmony_ci * mpi3mr_expander_remove - Remove expander object
220862306a36Sopenharmony_ci * @mrioc: Adapter instance reference
220962306a36Sopenharmony_ci * @sas_address: Remove expander sas_address
221062306a36Sopenharmony_ci * @hba_port: HBA port reference
221162306a36Sopenharmony_ci *
221262306a36Sopenharmony_ci * This function remove expander object, stored in
221362306a36Sopenharmony_ci * mrioc->sas_expander_list and removes it from the SAS TL by
221462306a36Sopenharmony_ci * calling mpi3mr_expander_node_remove().
221562306a36Sopenharmony_ci *
221662306a36Sopenharmony_ci * Return: None
221762306a36Sopenharmony_ci */
221862306a36Sopenharmony_civoid mpi3mr_expander_remove(struct mpi3mr_ioc *mrioc, u64 sas_address,
221962306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port)
222062306a36Sopenharmony_ci{
222162306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander;
222262306a36Sopenharmony_ci	unsigned long flags;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	if (mrioc->reset_in_progress)
222562306a36Sopenharmony_ci		return;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	if (!hba_port)
222862306a36Sopenharmony_ci		return;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->sas_node_lock, flags);
223162306a36Sopenharmony_ci	sas_expander = mpi3mr_expander_find_by_sas_address(mrioc, sas_address,
223262306a36Sopenharmony_ci	    hba_port);
223362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
223462306a36Sopenharmony_ci	if (sas_expander)
223562306a36Sopenharmony_ci		mpi3mr_expander_node_remove(mrioc, sas_expander);
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci/**
224062306a36Sopenharmony_ci * mpi3mr_get_sas_negotiated_logical_linkrate - get linkrate
224162306a36Sopenharmony_ci * @mrioc: Adapter instance reference
224262306a36Sopenharmony_ci * @tgtdev: Target device
224362306a36Sopenharmony_ci *
224462306a36Sopenharmony_ci * This function identifies whether the target device is
224562306a36Sopenharmony_ci * attached directly or through expander and issues sas phy
224662306a36Sopenharmony_ci * page0 or expander phy page1 and gets the link rate, if there
224762306a36Sopenharmony_ci * is any failure in reading the pages then this returns link
224862306a36Sopenharmony_ci * rate of 1.5.
224962306a36Sopenharmony_ci *
225062306a36Sopenharmony_ci * Return: logical link rate.
225162306a36Sopenharmony_ci */
225262306a36Sopenharmony_cistatic u8 mpi3mr_get_sas_negotiated_logical_linkrate(struct mpi3mr_ioc *mrioc,
225362306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev)
225462306a36Sopenharmony_ci{
225562306a36Sopenharmony_ci	u8 link_rate = MPI3_SAS_NEG_LINK_RATE_1_5, phy_number;
225662306a36Sopenharmony_ci	struct mpi3_sas_expander_page1 expander_pg1;
225762306a36Sopenharmony_ci	struct mpi3_sas_phy_page0 phy_pg0;
225862306a36Sopenharmony_ci	u32 phynum_handle;
225962306a36Sopenharmony_ci	u16 ioc_status;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
226262306a36Sopenharmony_ci	if (!(tgtdev->devpg0_flag & MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED)) {
226362306a36Sopenharmony_ci		phynum_handle = ((phy_number<<MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT)
226462306a36Sopenharmony_ci				 | tgtdev->parent_handle);
226562306a36Sopenharmony_ci		if (mpi3mr_cfg_get_sas_exp_pg1(mrioc, &ioc_status,
226662306a36Sopenharmony_ci		    &expander_pg1, sizeof(expander_pg1),
226762306a36Sopenharmony_ci		    MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM,
226862306a36Sopenharmony_ci		    phynum_handle)) {
226962306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
227062306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
227162306a36Sopenharmony_ci			goto out;
227262306a36Sopenharmony_ci		}
227362306a36Sopenharmony_ci		if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
227462306a36Sopenharmony_ci			ioc_err(mrioc, "failure at %s:%d/%s()!\n",
227562306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
227662306a36Sopenharmony_ci			goto out;
227762306a36Sopenharmony_ci		}
227862306a36Sopenharmony_ci		link_rate = (expander_pg1.negotiated_link_rate &
227962306a36Sopenharmony_ci			     MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
228062306a36Sopenharmony_ci			MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
228162306a36Sopenharmony_ci		goto out;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
228462306a36Sopenharmony_ci	    sizeof(struct mpi3_sas_phy_page0),
228562306a36Sopenharmony_ci	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy_number)) {
228662306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
228762306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
228862306a36Sopenharmony_ci		goto out;
228962306a36Sopenharmony_ci	}
229062306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
229162306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
229262306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
229362306a36Sopenharmony_ci		goto out;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci	link_rate = (phy_pg0.negotiated_link_rate &
229662306a36Sopenharmony_ci		     MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK) >>
229762306a36Sopenharmony_ci		MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT;
229862306a36Sopenharmony_ciout:
229962306a36Sopenharmony_ci	return link_rate;
230062306a36Sopenharmony_ci}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci/**
230362306a36Sopenharmony_ci * mpi3mr_report_tgtdev_to_sas_transport - expose dev to SAS TL
230462306a36Sopenharmony_ci * @mrioc: Adapter instance reference
230562306a36Sopenharmony_ci * @tgtdev: Target device
230662306a36Sopenharmony_ci *
230762306a36Sopenharmony_ci * This function exposes the target device after
230862306a36Sopenharmony_ci * preparing host_phy, setting up link rate etc.
230962306a36Sopenharmony_ci *
231062306a36Sopenharmony_ci * Return: 0 on success, non-zero for failure.
231162306a36Sopenharmony_ci */
231262306a36Sopenharmony_ciint mpi3mr_report_tgtdev_to_sas_transport(struct mpi3mr_ioc *mrioc,
231362306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev)
231462306a36Sopenharmony_ci{
231562306a36Sopenharmony_ci	int retval = 0;
231662306a36Sopenharmony_ci	u8 link_rate, parent_phy_number;
231762306a36Sopenharmony_ci	u64 sas_address_parent, sas_address;
231862306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port;
231962306a36Sopenharmony_ci	u8 port_id;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
232262306a36Sopenharmony_ci	    !mrioc->sas_transport_enabled)
232362306a36Sopenharmony_ci		return -1;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
232662306a36Sopenharmony_ci	if (!mrioc->sas_hba.num_phys)
232762306a36Sopenharmony_ci		mpi3mr_sas_host_add(mrioc);
232862306a36Sopenharmony_ci	else
232962306a36Sopenharmony_ci		mpi3mr_sas_host_refresh(mrioc);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	if (mpi3mr_get_sas_address(mrioc, tgtdev->parent_handle,
233262306a36Sopenharmony_ci	    &sas_address_parent) != 0) {
233362306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
233462306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
233562306a36Sopenharmony_ci		return -1;
233662306a36Sopenharmony_ci	}
233762306a36Sopenharmony_ci	tgtdev->dev_spec.sas_sata_inf.sas_address_parent = sas_address_parent;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	parent_phy_number = tgtdev->dev_spec.sas_sata_inf.phy_id;
234062306a36Sopenharmony_ci	port_id = tgtdev->io_unit_port;
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	hba_port = mpi3mr_get_hba_port_by_id(mrioc, port_id);
234362306a36Sopenharmony_ci	if (!hba_port) {
234462306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
234562306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
234662306a36Sopenharmony_ci		return -1;
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci	tgtdev->dev_spec.sas_sata_inf.hba_port = hba_port;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	link_rate = mpi3mr_get_sas_negotiated_logical_linkrate(mrioc, tgtdev);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	mpi3mr_update_links(mrioc, sas_address_parent, tgtdev->dev_handle,
235362306a36Sopenharmony_ci	    parent_phy_number, link_rate, hba_port);
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	tgtdev->host_exposed = 1;
235662306a36Sopenharmony_ci	if (!mpi3mr_sas_port_add(mrioc, tgtdev->dev_handle,
235762306a36Sopenharmony_ci	    sas_address_parent, hba_port)) {
235862306a36Sopenharmony_ci		retval = -1;
235962306a36Sopenharmony_ci		} else if ((!tgtdev->starget) && (!mrioc->is_driver_loading)) {
236062306a36Sopenharmony_ci			mpi3mr_sas_port_remove(mrioc, sas_address,
236162306a36Sopenharmony_ci			    sas_address_parent, hba_port);
236262306a36Sopenharmony_ci		retval = -1;
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci	if (retval) {
236562306a36Sopenharmony_ci		tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
236662306a36Sopenharmony_ci		tgtdev->host_exposed = 0;
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci	return retval;
236962306a36Sopenharmony_ci}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci/**
237262306a36Sopenharmony_ci * mpi3mr_remove_tgtdev_from_sas_transport - remove from SAS TL
237362306a36Sopenharmony_ci * @mrioc: Adapter instance reference
237462306a36Sopenharmony_ci * @tgtdev: Target device
237562306a36Sopenharmony_ci *
237662306a36Sopenharmony_ci * This function removes the target device
237762306a36Sopenharmony_ci *
237862306a36Sopenharmony_ci * Return: None.
237962306a36Sopenharmony_ci */
238062306a36Sopenharmony_civoid mpi3mr_remove_tgtdev_from_sas_transport(struct mpi3mr_ioc *mrioc,
238162306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev)
238262306a36Sopenharmony_ci{
238362306a36Sopenharmony_ci	u64 sas_address_parent, sas_address;
238462306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	if ((tgtdev->dev_type != MPI3_DEVICE_DEVFORM_SAS_SATA) ||
238762306a36Sopenharmony_ci	    !mrioc->sas_transport_enabled)
238862306a36Sopenharmony_ci		return;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	hba_port = tgtdev->dev_spec.sas_sata_inf.hba_port;
239162306a36Sopenharmony_ci	sas_address = tgtdev->dev_spec.sas_sata_inf.sas_address;
239262306a36Sopenharmony_ci	sas_address_parent = tgtdev->dev_spec.sas_sata_inf.sas_address_parent;
239362306a36Sopenharmony_ci	mpi3mr_sas_port_remove(mrioc, sas_address, sas_address_parent,
239462306a36Sopenharmony_ci	    hba_port);
239562306a36Sopenharmony_ci	tgtdev->host_exposed = 0;
239662306a36Sopenharmony_ci	tgtdev->dev_spec.sas_sata_inf.hba_port = NULL;
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci/**
240062306a36Sopenharmony_ci * mpi3mr_get_port_id_by_sas_phy -  Get port ID of the given phy
240162306a36Sopenharmony_ci * @phy: SAS transport layer phy object
240262306a36Sopenharmony_ci *
240362306a36Sopenharmony_ci * Return: Port number for valid ID else 0xFFFF
240462306a36Sopenharmony_ci */
240562306a36Sopenharmony_cistatic inline u8 mpi3mr_get_port_id_by_sas_phy(struct sas_phy *phy)
240662306a36Sopenharmony_ci{
240762306a36Sopenharmony_ci	u8 port_id = 0xFF;
240862306a36Sopenharmony_ci	struct mpi3mr_hba_port *hba_port = phy->hostdata;
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	if (hba_port)
241162306a36Sopenharmony_ci		port_id = hba_port->port_id;
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	return port_id;
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci/**
241762306a36Sopenharmony_ci * mpi3mr_get_port_id_by_rphy - Get Port number from SAS rphy
241862306a36Sopenharmony_ci *
241962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
242062306a36Sopenharmony_ci * @rphy: SAS transport layer remote phy object
242162306a36Sopenharmony_ci *
242262306a36Sopenharmony_ci * Retrieves HBA port number in which the device pointed by the
242362306a36Sopenharmony_ci * rphy object is attached with.
242462306a36Sopenharmony_ci *
242562306a36Sopenharmony_ci * Return: Valid port number on success else OxFFFF.
242662306a36Sopenharmony_ci */
242762306a36Sopenharmony_cistatic u8 mpi3mr_get_port_id_by_rphy(struct mpi3mr_ioc *mrioc, struct sas_rphy *rphy)
242862306a36Sopenharmony_ci{
242962306a36Sopenharmony_ci	struct mpi3mr_sas_node *sas_expander;
243062306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev;
243162306a36Sopenharmony_ci	unsigned long flags;
243262306a36Sopenharmony_ci	u8 port_id = 0xFF;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	if (!rphy)
243562306a36Sopenharmony_ci		return port_id;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
243862306a36Sopenharmony_ci	    rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) {
243962306a36Sopenharmony_ci		spin_lock_irqsave(&mrioc->sas_node_lock, flags);
244062306a36Sopenharmony_ci		list_for_each_entry(sas_expander, &mrioc->sas_expander_list,
244162306a36Sopenharmony_ci		    list) {
244262306a36Sopenharmony_ci			if (sas_expander->rphy == rphy) {
244362306a36Sopenharmony_ci				port_id = sas_expander->hba_port->port_id;
244462306a36Sopenharmony_ci				break;
244562306a36Sopenharmony_ci			}
244662306a36Sopenharmony_ci		}
244762306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->sas_node_lock, flags);
244862306a36Sopenharmony_ci	} else if (rphy->identify.device_type == SAS_END_DEVICE) {
244962306a36Sopenharmony_ci		spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci		tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
245262306a36Sopenharmony_ci			    rphy->identify.sas_address, rphy);
245362306a36Sopenharmony_ci		if (tgtdev && tgtdev->dev_spec.sas_sata_inf.hba_port) {
245462306a36Sopenharmony_ci			port_id =
245562306a36Sopenharmony_ci				tgtdev->dev_spec.sas_sata_inf.hba_port->port_id;
245662306a36Sopenharmony_ci			mpi3mr_tgtdev_put(tgtdev);
245762306a36Sopenharmony_ci		}
245862306a36Sopenharmony_ci		spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
245962306a36Sopenharmony_ci	}
246062306a36Sopenharmony_ci	return port_id;
246162306a36Sopenharmony_ci}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_cistatic inline struct mpi3mr_ioc *phy_to_mrioc(struct sas_phy *phy)
246462306a36Sopenharmony_ci{
246562306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	return shost_priv(shost);
246862306a36Sopenharmony_ci}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_cistatic inline struct mpi3mr_ioc *rphy_to_mrioc(struct sas_rphy *rphy)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	return shost_priv(shost);
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci/* report phy error log structure */
247862306a36Sopenharmony_cistruct phy_error_log_request {
247962306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
248062306a36Sopenharmony_ci	u8 function; /* 0x11 */
248162306a36Sopenharmony_ci	u8 allocated_response_length;
248262306a36Sopenharmony_ci	u8 request_length; /* 02 */
248362306a36Sopenharmony_ci	u8 reserved_1[5];
248462306a36Sopenharmony_ci	u8 phy_identifier;
248562306a36Sopenharmony_ci	u8 reserved_2[2];
248662306a36Sopenharmony_ci};
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci/* report phy error log reply structure */
248962306a36Sopenharmony_cistruct phy_error_log_reply {
249062306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
249162306a36Sopenharmony_ci	u8 function; /* 0x11 */
249262306a36Sopenharmony_ci	u8 function_result;
249362306a36Sopenharmony_ci	u8 response_length;
249462306a36Sopenharmony_ci	__be16 expander_change_count;
249562306a36Sopenharmony_ci	u8 reserved_1[3];
249662306a36Sopenharmony_ci	u8 phy_identifier;
249762306a36Sopenharmony_ci	u8 reserved_2[2];
249862306a36Sopenharmony_ci	__be32 invalid_dword;
249962306a36Sopenharmony_ci	__be32 running_disparity_error;
250062306a36Sopenharmony_ci	__be32 loss_of_dword_sync;
250162306a36Sopenharmony_ci	__be32 phy_reset_problem;
250262306a36Sopenharmony_ci};
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci/**
250662306a36Sopenharmony_ci * mpi3mr_get_expander_phy_error_log - return expander counters:
250762306a36Sopenharmony_ci * @mrioc: Adapter instance reference
250862306a36Sopenharmony_ci * @phy: The SAS transport layer phy object
250962306a36Sopenharmony_ci *
251062306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
251162306a36Sopenharmony_ci *
251262306a36Sopenharmony_ci */
251362306a36Sopenharmony_cistatic int mpi3mr_get_expander_phy_error_log(struct mpi3mr_ioc *mrioc,
251462306a36Sopenharmony_ci	struct sas_phy *phy)
251562306a36Sopenharmony_ci{
251662306a36Sopenharmony_ci	struct mpi3_smp_passthrough_request mpi_request;
251762306a36Sopenharmony_ci	struct mpi3_smp_passthrough_reply mpi_reply;
251862306a36Sopenharmony_ci	struct phy_error_log_request *phy_error_log_request;
251962306a36Sopenharmony_ci	struct phy_error_log_reply *phy_error_log_reply;
252062306a36Sopenharmony_ci	int rc;
252162306a36Sopenharmony_ci	void *psge;
252262306a36Sopenharmony_ci	void *data_out = NULL;
252362306a36Sopenharmony_ci	dma_addr_t data_out_dma, data_in_dma;
252462306a36Sopenharmony_ci	u32 data_out_sz, data_in_sz, sz;
252562306a36Sopenharmony_ci	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
252662306a36Sopenharmony_ci	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
252762306a36Sopenharmony_ci	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
252862306a36Sopenharmony_ci	u16 ioc_status;
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	if (mrioc->reset_in_progress) {
253162306a36Sopenharmony_ci		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
253262306a36Sopenharmony_ci		return -EFAULT;
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	data_out_sz = sizeof(struct phy_error_log_request);
253662306a36Sopenharmony_ci	data_in_sz = sizeof(struct phy_error_log_reply);
253762306a36Sopenharmony_ci	sz = data_out_sz + data_in_sz;
253862306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
253962306a36Sopenharmony_ci	    GFP_KERNEL);
254062306a36Sopenharmony_ci	if (!data_out) {
254162306a36Sopenharmony_ci		rc = -ENOMEM;
254262306a36Sopenharmony_ci		goto out;
254362306a36Sopenharmony_ci	}
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	data_in_dma = data_out_dma + data_out_sz;
254662306a36Sopenharmony_ci	phy_error_log_reply = data_out + data_out_sz;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	rc = -EINVAL;
254962306a36Sopenharmony_ci	memset(data_out, 0, sz);
255062306a36Sopenharmony_ci	phy_error_log_request = data_out;
255162306a36Sopenharmony_ci	phy_error_log_request->smp_frame_type = 0x40;
255262306a36Sopenharmony_ci	phy_error_log_request->function = 0x11;
255362306a36Sopenharmony_ci	phy_error_log_request->request_length = 2;
255462306a36Sopenharmony_ci	phy_error_log_request->allocated_response_length = 0;
255562306a36Sopenharmony_ci	phy_error_log_request->phy_identifier = phy->number;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	memset(&mpi_request, 0, request_sz);
255862306a36Sopenharmony_ci	memset(&mpi_reply, 0, reply_sz);
255962306a36Sopenharmony_ci	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
256062306a36Sopenharmony_ci	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
256162306a36Sopenharmony_ci	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
256262306a36Sopenharmony_ci	mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	psge = &mpi_request.request_sge;
256562306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	psge = &mpi_request.response_sge;
256862306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	dprint_transport_info(mrioc,
257162306a36Sopenharmony_ci	    "sending phy error log SMP request to sas_address(0x%016llx), phy_id(%d)\n",
257262306a36Sopenharmony_ci	    (unsigned long long)phy->identify.sas_address, phy->number);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
257562306a36Sopenharmony_ci	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
257662306a36Sopenharmony_ci		goto out;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	dprint_transport_info(mrioc,
257962306a36Sopenharmony_ci	    "phy error log SMP request completed with ioc_status(0x%04x)\n",
258062306a36Sopenharmony_ci	    ioc_status);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
258362306a36Sopenharmony_ci		dprint_transport_info(mrioc,
258462306a36Sopenharmony_ci		    "phy error log - reply data transfer size(%d)\n",
258562306a36Sopenharmony_ci		    le16_to_cpu(mpi_reply.response_data_length));
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci		if (le16_to_cpu(mpi_reply.response_data_length) !=
258862306a36Sopenharmony_ci		    sizeof(struct phy_error_log_reply))
258962306a36Sopenharmony_ci			goto out;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci		dprint_transport_info(mrioc,
259262306a36Sopenharmony_ci		    "phy error log - function_result(%d)\n",
259362306a36Sopenharmony_ci		    phy_error_log_reply->function_result);
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci		phy->invalid_dword_count =
259662306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->invalid_dword);
259762306a36Sopenharmony_ci		phy->running_disparity_error_count =
259862306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->running_disparity_error);
259962306a36Sopenharmony_ci		phy->loss_of_dword_sync_count =
260062306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
260162306a36Sopenharmony_ci		phy->phy_reset_problem_count =
260262306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->phy_reset_problem);
260362306a36Sopenharmony_ci		rc = 0;
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ciout:
260762306a36Sopenharmony_ci	if (data_out)
260862306a36Sopenharmony_ci		dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
260962306a36Sopenharmony_ci		    data_out_dma);
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	return rc;
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci/**
261562306a36Sopenharmony_ci * mpi3mr_transport_get_linkerrors - return phy error counters
261662306a36Sopenharmony_ci * @phy: The SAS transport layer phy object
261762306a36Sopenharmony_ci *
261862306a36Sopenharmony_ci * This function retrieves the phy error log information of the
261962306a36Sopenharmony_ci * HBA or expander for which the phy belongs to
262062306a36Sopenharmony_ci *
262162306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
262262306a36Sopenharmony_ci */
262362306a36Sopenharmony_cistatic int mpi3mr_transport_get_linkerrors(struct sas_phy *phy)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
262662306a36Sopenharmony_ci	struct mpi3_sas_phy_page1 phy_pg1;
262762306a36Sopenharmony_ci	int rc = 0;
262862306a36Sopenharmony_ci	u16 ioc_status;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	rc = mpi3mr_parent_present(mrioc, phy);
263162306a36Sopenharmony_ci	if (rc)
263262306a36Sopenharmony_ci		return rc;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
263562306a36Sopenharmony_ci		return mpi3mr_get_expander_phy_error_log(mrioc, phy);
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	memset(&phy_pg1, 0, sizeof(struct mpi3_sas_phy_page1));
263862306a36Sopenharmony_ci	/* get hba phy error logs */
263962306a36Sopenharmony_ci	if ((mpi3mr_cfg_get_sas_phy_pg1(mrioc, &ioc_status, &phy_pg1,
264062306a36Sopenharmony_ci	    sizeof(struct mpi3_sas_phy_page1),
264162306a36Sopenharmony_ci	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number))) {
264262306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
264362306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
264462306a36Sopenharmony_ci		return -ENXIO;
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
264862306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
264962306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
265062306a36Sopenharmony_ci		return -ENXIO;
265162306a36Sopenharmony_ci	}
265262306a36Sopenharmony_ci	phy->invalid_dword_count = le32_to_cpu(phy_pg1.invalid_dword_count);
265362306a36Sopenharmony_ci	phy->running_disparity_error_count =
265462306a36Sopenharmony_ci		le32_to_cpu(phy_pg1.running_disparity_error_count);
265562306a36Sopenharmony_ci	phy->loss_of_dword_sync_count =
265662306a36Sopenharmony_ci		le32_to_cpu(phy_pg1.loss_dword_synch_count);
265762306a36Sopenharmony_ci	phy->phy_reset_problem_count =
265862306a36Sopenharmony_ci		le32_to_cpu(phy_pg1.phy_reset_problem_count);
265962306a36Sopenharmony_ci	return 0;
266062306a36Sopenharmony_ci}
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci/**
266362306a36Sopenharmony_ci * mpi3mr_transport_get_enclosure_identifier - Get Enclosure ID
266462306a36Sopenharmony_ci * @rphy: The SAS transport layer remote phy object
266562306a36Sopenharmony_ci * @identifier: Enclosure identifier to be returned
266662306a36Sopenharmony_ci *
266762306a36Sopenharmony_ci * Returns the enclosure id for the device pointed by the remote
266862306a36Sopenharmony_ci * phy object.
266962306a36Sopenharmony_ci *
267062306a36Sopenharmony_ci * Return: 0 on success or -ENXIO
267162306a36Sopenharmony_ci */
267262306a36Sopenharmony_cistatic int
267362306a36Sopenharmony_cimpi3mr_transport_get_enclosure_identifier(struct sas_rphy *rphy,
267462306a36Sopenharmony_ci	u64 *identifier)
267562306a36Sopenharmony_ci{
267662306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
267762306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev = NULL;
267862306a36Sopenharmony_ci	unsigned long flags;
267962306a36Sopenharmony_ci	int rc;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
268262306a36Sopenharmony_ci	tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
268362306a36Sopenharmony_ci	    rphy->identify.sas_address, rphy);
268462306a36Sopenharmony_ci	if (tgtdev) {
268562306a36Sopenharmony_ci		*identifier =
268662306a36Sopenharmony_ci			tgtdev->enclosure_logical_id;
268762306a36Sopenharmony_ci		rc = 0;
268862306a36Sopenharmony_ci		mpi3mr_tgtdev_put(tgtdev);
268962306a36Sopenharmony_ci	} else {
269062306a36Sopenharmony_ci		*identifier = 0;
269162306a36Sopenharmony_ci		rc = -ENXIO;
269262306a36Sopenharmony_ci	}
269362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	return rc;
269662306a36Sopenharmony_ci}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci/**
269962306a36Sopenharmony_ci * mpi3mr_transport_get_bay_identifier - Get bay ID
270062306a36Sopenharmony_ci * @rphy: The SAS transport layer remote phy object
270162306a36Sopenharmony_ci *
270262306a36Sopenharmony_ci * Returns the slot id for the device pointed by the remote phy
270362306a36Sopenharmony_ci * object.
270462306a36Sopenharmony_ci *
270562306a36Sopenharmony_ci * Return: Valid slot ID on success or -ENXIO
270662306a36Sopenharmony_ci */
270762306a36Sopenharmony_cistatic int
270862306a36Sopenharmony_cimpi3mr_transport_get_bay_identifier(struct sas_rphy *rphy)
270962306a36Sopenharmony_ci{
271062306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = rphy_to_mrioc(rphy);
271162306a36Sopenharmony_ci	struct mpi3mr_tgt_dev *tgtdev = NULL;
271262306a36Sopenharmony_ci	unsigned long flags;
271362306a36Sopenharmony_ci	int rc;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
271662306a36Sopenharmony_ci	tgtdev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc,
271762306a36Sopenharmony_ci	    rphy->identify.sas_address, rphy);
271862306a36Sopenharmony_ci	if (tgtdev) {
271962306a36Sopenharmony_ci		rc = tgtdev->slot;
272062306a36Sopenharmony_ci		mpi3mr_tgtdev_put(tgtdev);
272162306a36Sopenharmony_ci	} else
272262306a36Sopenharmony_ci		rc = -ENXIO;
272362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	return rc;
272662306a36Sopenharmony_ci}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci/* phy control request structure */
272962306a36Sopenharmony_cistruct phy_control_request {
273062306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
273162306a36Sopenharmony_ci	u8 function; /* 0x91 */
273262306a36Sopenharmony_ci	u8 allocated_response_length;
273362306a36Sopenharmony_ci	u8 request_length; /* 0x09 */
273462306a36Sopenharmony_ci	u16 expander_change_count;
273562306a36Sopenharmony_ci	u8 reserved_1[3];
273662306a36Sopenharmony_ci	u8 phy_identifier;
273762306a36Sopenharmony_ci	u8 phy_operation;
273862306a36Sopenharmony_ci	u8 reserved_2[13];
273962306a36Sopenharmony_ci	u64 attached_device_name;
274062306a36Sopenharmony_ci	u8 programmed_min_physical_link_rate;
274162306a36Sopenharmony_ci	u8 programmed_max_physical_link_rate;
274262306a36Sopenharmony_ci	u8 reserved_3[6];
274362306a36Sopenharmony_ci};
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci/* phy control reply structure */
274662306a36Sopenharmony_cistruct phy_control_reply {
274762306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
274862306a36Sopenharmony_ci	u8 function; /* 0x11 */
274962306a36Sopenharmony_ci	u8 function_result;
275062306a36Sopenharmony_ci	u8 response_length;
275162306a36Sopenharmony_ci};
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci#define SMP_PHY_CONTROL_LINK_RESET	(0x01)
275462306a36Sopenharmony_ci#define SMP_PHY_CONTROL_HARD_RESET	(0x02)
275562306a36Sopenharmony_ci#define SMP_PHY_CONTROL_DISABLE		(0x03)
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci/**
275862306a36Sopenharmony_ci * mpi3mr_expander_phy_control - expander phy control
275962306a36Sopenharmony_ci * @mrioc: Adapter instance reference
276062306a36Sopenharmony_ci * @phy: The SAS transport layer phy object
276162306a36Sopenharmony_ci * @phy_operation: The phy operation to be executed
276262306a36Sopenharmony_ci *
276362306a36Sopenharmony_ci * Issues SMP passthru phy control request to execute a specific
276462306a36Sopenharmony_ci * phy operation for a given expander device.
276562306a36Sopenharmony_ci *
276662306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
276762306a36Sopenharmony_ci */
276862306a36Sopenharmony_cistatic int
276962306a36Sopenharmony_cimpi3mr_expander_phy_control(struct mpi3mr_ioc *mrioc,
277062306a36Sopenharmony_ci	struct sas_phy *phy, u8 phy_operation)
277162306a36Sopenharmony_ci{
277262306a36Sopenharmony_ci	struct mpi3_smp_passthrough_request mpi_request;
277362306a36Sopenharmony_ci	struct mpi3_smp_passthrough_reply mpi_reply;
277462306a36Sopenharmony_ci	struct phy_control_request *phy_control_request;
277562306a36Sopenharmony_ci	struct phy_control_reply *phy_control_reply;
277662306a36Sopenharmony_ci	int rc;
277762306a36Sopenharmony_ci	void *psge;
277862306a36Sopenharmony_ci	void *data_out = NULL;
277962306a36Sopenharmony_ci	dma_addr_t data_out_dma;
278062306a36Sopenharmony_ci	dma_addr_t data_in_dma;
278162306a36Sopenharmony_ci	size_t data_in_sz;
278262306a36Sopenharmony_ci	size_t data_out_sz;
278362306a36Sopenharmony_ci	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
278462306a36Sopenharmony_ci	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
278562306a36Sopenharmony_ci	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
278662306a36Sopenharmony_ci	u16 ioc_status;
278762306a36Sopenharmony_ci	u16 sz;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	if (mrioc->reset_in_progress) {
279062306a36Sopenharmony_ci		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
279162306a36Sopenharmony_ci		return -EFAULT;
279262306a36Sopenharmony_ci	}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	data_out_sz = sizeof(struct phy_control_request);
279562306a36Sopenharmony_ci	data_in_sz = sizeof(struct phy_control_reply);
279662306a36Sopenharmony_ci	sz = data_out_sz + data_in_sz;
279762306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&mrioc->pdev->dev, sz, &data_out_dma,
279862306a36Sopenharmony_ci	    GFP_KERNEL);
279962306a36Sopenharmony_ci	if (!data_out) {
280062306a36Sopenharmony_ci		rc = -ENOMEM;
280162306a36Sopenharmony_ci		goto out;
280262306a36Sopenharmony_ci	}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	data_in_dma = data_out_dma + data_out_sz;
280562306a36Sopenharmony_ci	phy_control_reply = data_out + data_out_sz;
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	rc = -EINVAL;
280862306a36Sopenharmony_ci	memset(data_out, 0, sz);
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	phy_control_request = data_out;
281162306a36Sopenharmony_ci	phy_control_request->smp_frame_type = 0x40;
281262306a36Sopenharmony_ci	phy_control_request->function = 0x91;
281362306a36Sopenharmony_ci	phy_control_request->request_length = 9;
281462306a36Sopenharmony_ci	phy_control_request->allocated_response_length = 0;
281562306a36Sopenharmony_ci	phy_control_request->phy_identifier = phy->number;
281662306a36Sopenharmony_ci	phy_control_request->phy_operation = phy_operation;
281762306a36Sopenharmony_ci	phy_control_request->programmed_min_physical_link_rate =
281862306a36Sopenharmony_ci	    phy->minimum_linkrate << 4;
281962306a36Sopenharmony_ci	phy_control_request->programmed_max_physical_link_rate =
282062306a36Sopenharmony_ci	    phy->maximum_linkrate << 4;
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	memset(&mpi_request, 0, request_sz);
282362306a36Sopenharmony_ci	memset(&mpi_reply, 0, reply_sz);
282462306a36Sopenharmony_ci	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
282562306a36Sopenharmony_ci	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
282662306a36Sopenharmony_ci	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_sas_phy(phy);
282762306a36Sopenharmony_ci	mpi_request.sas_address = cpu_to_le64(phy->identify.sas_address);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	psge = &mpi_request.request_sge;
283062306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_out_sz, data_out_dma);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	psge = &mpi_request.response_sge;
283362306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, data_in_sz, data_in_dma);
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	dprint_transport_info(mrioc,
283662306a36Sopenharmony_ci	    "sending phy control SMP request to sas_address(0x%016llx), phy_id(%d) opcode(%d)\n",
283762306a36Sopenharmony_ci	    (unsigned long long)phy->identify.sas_address, phy->number,
283862306a36Sopenharmony_ci	    phy_operation);
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
284162306a36Sopenharmony_ci	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status))
284262306a36Sopenharmony_ci		goto out;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	dprint_transport_info(mrioc,
284562306a36Sopenharmony_ci	    "phy control SMP request completed with ioc_status(0x%04x)\n",
284662306a36Sopenharmony_ci	    ioc_status);
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	if (ioc_status == MPI3_IOCSTATUS_SUCCESS) {
284962306a36Sopenharmony_ci		dprint_transport_info(mrioc,
285062306a36Sopenharmony_ci		    "phy control - reply data transfer size(%d)\n",
285162306a36Sopenharmony_ci		    le16_to_cpu(mpi_reply.response_data_length));
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		if (le16_to_cpu(mpi_reply.response_data_length) !=
285462306a36Sopenharmony_ci		    sizeof(struct phy_control_reply))
285562306a36Sopenharmony_ci			goto out;
285662306a36Sopenharmony_ci		dprint_transport_info(mrioc,
285762306a36Sopenharmony_ci		    "phy control - function_result(%d)\n",
285862306a36Sopenharmony_ci		    phy_control_reply->function_result);
285962306a36Sopenharmony_ci		rc = 0;
286062306a36Sopenharmony_ci	}
286162306a36Sopenharmony_ci out:
286262306a36Sopenharmony_ci	if (data_out)
286362306a36Sopenharmony_ci		dma_free_coherent(&mrioc->pdev->dev, sz, data_out,
286462306a36Sopenharmony_ci		    data_out_dma);
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	return rc;
286762306a36Sopenharmony_ci}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci/**
287062306a36Sopenharmony_ci * mpi3mr_transport_phy_reset - Reset a given phy
287162306a36Sopenharmony_ci * @phy: The SAS transport layer phy object
287262306a36Sopenharmony_ci * @hard_reset: Flag to indicate the type of reset
287362306a36Sopenharmony_ci *
287462306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
287562306a36Sopenharmony_ci */
287662306a36Sopenharmony_cistatic int
287762306a36Sopenharmony_cimpi3mr_transport_phy_reset(struct sas_phy *phy, int hard_reset)
287862306a36Sopenharmony_ci{
287962306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
288062306a36Sopenharmony_ci	struct mpi3_iounit_control_request mpi_request;
288162306a36Sopenharmony_ci	struct mpi3_iounit_control_reply mpi_reply;
288262306a36Sopenharmony_ci	u16 request_sz = sizeof(struct mpi3_iounit_control_request);
288362306a36Sopenharmony_ci	u16 reply_sz = sizeof(struct mpi3_iounit_control_reply);
288462306a36Sopenharmony_ci	int rc = 0;
288562306a36Sopenharmony_ci	u16 ioc_status;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	rc = mpi3mr_parent_present(mrioc, phy);
288862306a36Sopenharmony_ci	if (rc)
288962306a36Sopenharmony_ci		return rc;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	/* handle expander phys */
289262306a36Sopenharmony_ci	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
289362306a36Sopenharmony_ci		return mpi3mr_expander_phy_control(mrioc, phy,
289462306a36Sopenharmony_ci		    (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
289562306a36Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	/* handle hba phys */
289862306a36Sopenharmony_ci	memset(&mpi_request, 0, request_sz);
289962306a36Sopenharmony_ci	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
290062306a36Sopenharmony_ci	mpi_request.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
290162306a36Sopenharmony_ci	mpi_request.operation = MPI3_CTRL_OP_SAS_PHY_CONTROL;
290262306a36Sopenharmony_ci	mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_ACTION_INDEX] =
290362306a36Sopenharmony_ci		(hard_reset ? MPI3_CTRL_ACTION_HARD_RESET :
290462306a36Sopenharmony_ci		 MPI3_CTRL_ACTION_LINK_RESET);
290562306a36Sopenharmony_ci	mpi_request.param8[MPI3_CTRL_OP_SAS_PHY_CONTROL_PARAM8_PHY_INDEX] =
290662306a36Sopenharmony_ci		phy->number;
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	dprint_transport_info(mrioc,
290962306a36Sopenharmony_ci	    "sending phy reset request to sas_address(0x%016llx), phy_id(%d) hard_reset(%d)\n",
291062306a36Sopenharmony_ci	    (unsigned long long)phy->identify.sas_address, phy->number,
291162306a36Sopenharmony_ci	    hard_reset);
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	if (mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
291462306a36Sopenharmony_ci	    &mpi_reply, reply_sz, MPI3MR_INTADMCMD_TIMEOUT, &ioc_status)) {
291562306a36Sopenharmony_ci		rc = -EAGAIN;
291662306a36Sopenharmony_ci		goto out;
291762306a36Sopenharmony_ci	}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	dprint_transport_info(mrioc,
292062306a36Sopenharmony_ci	    "phy reset request completed with ioc_status(0x%04x)\n",
292162306a36Sopenharmony_ci	    ioc_status);
292262306a36Sopenharmony_ciout:
292362306a36Sopenharmony_ci	return rc;
292462306a36Sopenharmony_ci}
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci/**
292762306a36Sopenharmony_ci * mpi3mr_transport_phy_enable - enable/disable phys
292862306a36Sopenharmony_ci * @phy: The SAS transport layer phy object
292962306a36Sopenharmony_ci * @enable: flag to enable/disable, enable phy when true
293062306a36Sopenharmony_ci *
293162306a36Sopenharmony_ci * This function enables/disables a given by executing required
293262306a36Sopenharmony_ci * configuration page changes or expander phy control command
293362306a36Sopenharmony_ci *
293462306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
293562306a36Sopenharmony_ci */
293662306a36Sopenharmony_cistatic int
293762306a36Sopenharmony_cimpi3mr_transport_phy_enable(struct sas_phy *phy, int enable)
293862306a36Sopenharmony_ci{
293962306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
294062306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page0 *sas_io_unit_pg0 = NULL;
294162306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
294262306a36Sopenharmony_ci	u16 sz;
294362306a36Sopenharmony_ci	int rc = 0;
294462306a36Sopenharmony_ci	int i, discovery_active;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	rc = mpi3mr_parent_present(mrioc, phy);
294762306a36Sopenharmony_ci	if (rc)
294862306a36Sopenharmony_ci		return rc;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	/* handle expander phys */
295162306a36Sopenharmony_ci	if (phy->identify.sas_address != mrioc->sas_hba.sas_address)
295262306a36Sopenharmony_ci		return mpi3mr_expander_phy_control(mrioc, phy,
295362306a36Sopenharmony_ci		    (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
295462306a36Sopenharmony_ci		    SMP_PHY_CONTROL_DISABLE);
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	/* handle hba phys */
295762306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page0, phy_data) +
295862306a36Sopenharmony_ci		(mrioc->sas_hba.num_phys *
295962306a36Sopenharmony_ci		 sizeof(struct mpi3_sas_io_unit0_phy_data));
296062306a36Sopenharmony_ci	sas_io_unit_pg0 = kzalloc(sz, GFP_KERNEL);
296162306a36Sopenharmony_ci	if (!sas_io_unit_pg0) {
296262306a36Sopenharmony_ci		rc = -ENOMEM;
296362306a36Sopenharmony_ci		goto out;
296462306a36Sopenharmony_ci	}
296562306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg0(mrioc, sas_io_unit_pg0, sz)) {
296662306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
296762306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
296862306a36Sopenharmony_ci		rc = -ENXIO;
296962306a36Sopenharmony_ci		goto out;
297062306a36Sopenharmony_ci	}
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	/* unable to enable/disable phys when discovery is active */
297362306a36Sopenharmony_ci	for (i = 0, discovery_active = 0; i < mrioc->sas_hba.num_phys ; i++) {
297462306a36Sopenharmony_ci		if (sas_io_unit_pg0->phy_data[i].port_flags &
297562306a36Sopenharmony_ci		    MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS) {
297662306a36Sopenharmony_ci			ioc_err(mrioc,
297762306a36Sopenharmony_ci			    "discovery is active on port = %d, phy = %d\n"
297862306a36Sopenharmony_ci			    "\tunable to enable/disable phys, try again later!\n",
297962306a36Sopenharmony_ci			    sas_io_unit_pg0->phy_data[i].io_unit_port, i);
298062306a36Sopenharmony_ci			discovery_active = 1;
298162306a36Sopenharmony_ci		}
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	if (discovery_active) {
298562306a36Sopenharmony_ci		rc = -EAGAIN;
298662306a36Sopenharmony_ci		goto out;
298762306a36Sopenharmony_ci	}
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	if ((sas_io_unit_pg0->phy_data[phy->number].phy_flags &
299062306a36Sopenharmony_ci	     (MPI3_SASIOUNIT0_PHYFLAGS_HOST_PHY |
299162306a36Sopenharmony_ci	      MPI3_SASIOUNIT0_PHYFLAGS_VIRTUAL_PHY))) {
299262306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
299362306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
299462306a36Sopenharmony_ci		rc = -ENXIO;
299562306a36Sopenharmony_ci		goto out;
299662306a36Sopenharmony_ci	}
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci	/* read sas_iounit page 1 */
299962306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
300062306a36Sopenharmony_ci		(mrioc->sas_hba.num_phys *
300162306a36Sopenharmony_ci		 sizeof(struct mpi3_sas_io_unit1_phy_data));
300262306a36Sopenharmony_ci	sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
300362306a36Sopenharmony_ci	if (!sas_io_unit_pg1) {
300462306a36Sopenharmony_ci		rc = -ENOMEM;
300562306a36Sopenharmony_ci		goto out;
300662306a36Sopenharmony_ci	}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
300962306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
301062306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
301162306a36Sopenharmony_ci		rc = -ENXIO;
301262306a36Sopenharmony_ci		goto out;
301362306a36Sopenharmony_ci	}
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	if (enable)
301662306a36Sopenharmony_ci		sas_io_unit_pg1->phy_data[phy->number].phy_flags
301762306a36Sopenharmony_ci		    &= ~MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
301862306a36Sopenharmony_ci	else
301962306a36Sopenharmony_ci		sas_io_unit_pg1->phy_data[phy->number].phy_flags
302062306a36Sopenharmony_ci		    |= MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz);
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	/* link reset */
302562306a36Sopenharmony_ci	if (enable)
302662306a36Sopenharmony_ci		mpi3mr_transport_phy_reset(phy, 0);
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci out:
302962306a36Sopenharmony_ci	kfree(sas_io_unit_pg1);
303062306a36Sopenharmony_ci	kfree(sas_io_unit_pg0);
303162306a36Sopenharmony_ci	return rc;
303262306a36Sopenharmony_ci}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci/**
303562306a36Sopenharmony_ci * mpi3mr_transport_phy_speed - set phy min/max speed
303662306a36Sopenharmony_ci * @phy: The SAS transport later phy object
303762306a36Sopenharmony_ci * @rates: Rates defined as in sas_phy_linkrates
303862306a36Sopenharmony_ci *
303962306a36Sopenharmony_ci * This function sets the link rates given in the rates
304062306a36Sopenharmony_ci * argument to the given phy by executing required configuration
304162306a36Sopenharmony_ci * page changes or expander phy control command
304262306a36Sopenharmony_ci *
304362306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
304462306a36Sopenharmony_ci */
304562306a36Sopenharmony_cistatic int
304662306a36Sopenharmony_cimpi3mr_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
304762306a36Sopenharmony_ci{
304862306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = phy_to_mrioc(phy);
304962306a36Sopenharmony_ci	struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1 = NULL;
305062306a36Sopenharmony_ci	struct mpi3_sas_phy_page0 phy_pg0;
305162306a36Sopenharmony_ci	u16 sz, ioc_status;
305262306a36Sopenharmony_ci	int rc = 0;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	rc = mpi3mr_parent_present(mrioc, phy);
305562306a36Sopenharmony_ci	if (rc)
305662306a36Sopenharmony_ci		return rc;
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	if (!rates->minimum_linkrate)
305962306a36Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate;
306062306a36Sopenharmony_ci	else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
306162306a36Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate_hw;
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci	if (!rates->maximum_linkrate)
306462306a36Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate;
306562306a36Sopenharmony_ci	else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
306662306a36Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate_hw;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	/* handle expander phys */
306962306a36Sopenharmony_ci	if (phy->identify.sas_address != mrioc->sas_hba.sas_address) {
307062306a36Sopenharmony_ci		phy->minimum_linkrate = rates->minimum_linkrate;
307162306a36Sopenharmony_ci		phy->maximum_linkrate = rates->maximum_linkrate;
307262306a36Sopenharmony_ci		return mpi3mr_expander_phy_control(mrioc, phy,
307362306a36Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
307462306a36Sopenharmony_ci	}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	/* handle hba phys */
307762306a36Sopenharmony_ci	sz = offsetof(struct mpi3_sas_io_unit_page1, phy_data) +
307862306a36Sopenharmony_ci		(mrioc->sas_hba.num_phys *
307962306a36Sopenharmony_ci		 sizeof(struct mpi3_sas_io_unit1_phy_data));
308062306a36Sopenharmony_ci	sas_io_unit_pg1 = kzalloc(sz, GFP_KERNEL);
308162306a36Sopenharmony_ci	if (!sas_io_unit_pg1) {
308262306a36Sopenharmony_ci		rc = -ENOMEM;
308362306a36Sopenharmony_ci		goto out;
308462306a36Sopenharmony_ci	}
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	if (mpi3mr_cfg_get_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
308762306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
308862306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
308962306a36Sopenharmony_ci		rc = -ENXIO;
309062306a36Sopenharmony_ci		goto out;
309162306a36Sopenharmony_ci	}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	sas_io_unit_pg1->phy_data[phy->number].max_min_link_rate =
309462306a36Sopenharmony_ci		(rates->minimum_linkrate + (rates->maximum_linkrate << 4));
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	if (mpi3mr_cfg_set_sas_io_unit_pg1(mrioc, sas_io_unit_pg1, sz)) {
309762306a36Sopenharmony_ci		ioc_err(mrioc, "failure at %s:%d/%s()!\n",
309862306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
309962306a36Sopenharmony_ci		rc = -ENXIO;
310062306a36Sopenharmony_ci		goto out;
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	/* link reset */
310462306a36Sopenharmony_ci	mpi3mr_transport_phy_reset(phy, 0);
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	/* read phy page 0, then update the rates in the sas transport phy */
310762306a36Sopenharmony_ci	if (!mpi3mr_cfg_get_sas_phy_pg0(mrioc, &ioc_status, &phy_pg0,
310862306a36Sopenharmony_ci	    sizeof(struct mpi3_sas_phy_page0),
310962306a36Sopenharmony_ci	    MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER, phy->number) &&
311062306a36Sopenharmony_ci	    (ioc_status == MPI3_IOCSTATUS_SUCCESS)) {
311162306a36Sopenharmony_ci		phy->minimum_linkrate = mpi3mr_convert_phy_link_rate(
311262306a36Sopenharmony_ci		    phy_pg0.programmed_link_rate &
311362306a36Sopenharmony_ci		    MPI3_SAS_PRATE_MIN_RATE_MASK);
311462306a36Sopenharmony_ci		phy->maximum_linkrate = mpi3mr_convert_phy_link_rate(
311562306a36Sopenharmony_ci		    phy_pg0.programmed_link_rate >> 4);
311662306a36Sopenharmony_ci		phy->negotiated_linkrate =
311762306a36Sopenharmony_ci			mpi3mr_convert_phy_link_rate(
311862306a36Sopenharmony_ci			    (phy_pg0.negotiated_link_rate &
311962306a36Sopenharmony_ci			    MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK)
312062306a36Sopenharmony_ci			    >> MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT);
312162306a36Sopenharmony_ci	}
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ciout:
312462306a36Sopenharmony_ci	kfree(sas_io_unit_pg1);
312562306a36Sopenharmony_ci	return rc;
312662306a36Sopenharmony_ci}
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci/**
312962306a36Sopenharmony_ci * mpi3mr_map_smp_buffer - map BSG dma buffer
313062306a36Sopenharmony_ci * @dev: Generic device reference
313162306a36Sopenharmony_ci * @buf: BSG buffer pointer
313262306a36Sopenharmony_ci * @dma_addr: Physical address holder
313362306a36Sopenharmony_ci * @dma_len: Mapped DMA buffer length.
313462306a36Sopenharmony_ci * @p: Virtual address holder
313562306a36Sopenharmony_ci *
313662306a36Sopenharmony_ci * This function maps the DMAable buffer
313762306a36Sopenharmony_ci *
313862306a36Sopenharmony_ci * Return: 0 on success, non-zero on failure
313962306a36Sopenharmony_ci */
314062306a36Sopenharmony_cistatic int
314162306a36Sopenharmony_cimpi3mr_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
314262306a36Sopenharmony_ci		dma_addr_t *dma_addr, size_t *dma_len, void **p)
314362306a36Sopenharmony_ci{
314462306a36Sopenharmony_ci	/* Check if the request is split across multiple segments */
314562306a36Sopenharmony_ci	if (buf->sg_cnt > 1) {
314662306a36Sopenharmony_ci		*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
314762306a36Sopenharmony_ci				GFP_KERNEL);
314862306a36Sopenharmony_ci		if (!*p)
314962306a36Sopenharmony_ci			return -ENOMEM;
315062306a36Sopenharmony_ci		*dma_len = buf->payload_len;
315162306a36Sopenharmony_ci	} else {
315262306a36Sopenharmony_ci		if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
315362306a36Sopenharmony_ci			return -ENOMEM;
315462306a36Sopenharmony_ci		*dma_addr = sg_dma_address(buf->sg_list);
315562306a36Sopenharmony_ci		*dma_len = sg_dma_len(buf->sg_list);
315662306a36Sopenharmony_ci		*p = NULL;
315762306a36Sopenharmony_ci	}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	return 0;
316062306a36Sopenharmony_ci}
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_ci/**
316362306a36Sopenharmony_ci * mpi3mr_unmap_smp_buffer - unmap BSG dma buffer
316462306a36Sopenharmony_ci * @dev: Generic device reference
316562306a36Sopenharmony_ci * @buf: BSG buffer pointer
316662306a36Sopenharmony_ci * @dma_addr: Physical address to be unmapped
316762306a36Sopenharmony_ci * @p: Virtual address
316862306a36Sopenharmony_ci *
316962306a36Sopenharmony_ci * This function unmaps the DMAable buffer
317062306a36Sopenharmony_ci */
317162306a36Sopenharmony_cistatic void
317262306a36Sopenharmony_cimpi3mr_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
317362306a36Sopenharmony_ci		dma_addr_t dma_addr, void *p)
317462306a36Sopenharmony_ci{
317562306a36Sopenharmony_ci	if (p)
317662306a36Sopenharmony_ci		dma_free_coherent(dev, buf->payload_len, p, dma_addr);
317762306a36Sopenharmony_ci	else
317862306a36Sopenharmony_ci		dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
317962306a36Sopenharmony_ci}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci/**
318262306a36Sopenharmony_ci * mpi3mr_transport_smp_handler - handler for smp passthru
318362306a36Sopenharmony_ci * @job: BSG job reference
318462306a36Sopenharmony_ci * @shost: SCSI host object reference
318562306a36Sopenharmony_ci * @rphy: SAS transport rphy object pointing the expander
318662306a36Sopenharmony_ci *
318762306a36Sopenharmony_ci * This is used primarily by smp utils for sending the SMP
318862306a36Sopenharmony_ci * commands to the expanders attached to the controller
318962306a36Sopenharmony_ci */
319062306a36Sopenharmony_cistatic void
319162306a36Sopenharmony_cimpi3mr_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
319262306a36Sopenharmony_ci	struct sas_rphy *rphy)
319362306a36Sopenharmony_ci{
319462306a36Sopenharmony_ci	struct mpi3mr_ioc *mrioc = shost_priv(shost);
319562306a36Sopenharmony_ci	struct mpi3_smp_passthrough_request mpi_request;
319662306a36Sopenharmony_ci	struct mpi3_smp_passthrough_reply mpi_reply;
319762306a36Sopenharmony_ci	int rc;
319862306a36Sopenharmony_ci	void *psge;
319962306a36Sopenharmony_ci	dma_addr_t dma_addr_in;
320062306a36Sopenharmony_ci	dma_addr_t dma_addr_out;
320162306a36Sopenharmony_ci	void *addr_in = NULL;
320262306a36Sopenharmony_ci	void *addr_out = NULL;
320362306a36Sopenharmony_ci	size_t dma_len_in;
320462306a36Sopenharmony_ci	size_t dma_len_out;
320562306a36Sopenharmony_ci	unsigned int reslen = 0;
320662306a36Sopenharmony_ci	u16 request_sz = sizeof(struct mpi3_smp_passthrough_request);
320762306a36Sopenharmony_ci	u16 reply_sz = sizeof(struct mpi3_smp_passthrough_reply);
320862306a36Sopenharmony_ci	u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
320962306a36Sopenharmony_ci	u16 ioc_status;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	if (mrioc->reset_in_progress) {
321262306a36Sopenharmony_ci		ioc_err(mrioc, "%s: host reset in progress!\n", __func__);
321362306a36Sopenharmony_ci		rc = -EFAULT;
321462306a36Sopenharmony_ci		goto out;
321562306a36Sopenharmony_ci	}
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci	rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
321862306a36Sopenharmony_ci	    &dma_addr_out, &dma_len_out, &addr_out);
321962306a36Sopenharmony_ci	if (rc)
322062306a36Sopenharmony_ci		goto out;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	if (addr_out)
322362306a36Sopenharmony_ci		sg_copy_to_buffer(job->request_payload.sg_list,
322462306a36Sopenharmony_ci		    job->request_payload.sg_cnt, addr_out,
322562306a36Sopenharmony_ci		    job->request_payload.payload_len);
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	rc = mpi3mr_map_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
322862306a36Sopenharmony_ci			&dma_addr_in, &dma_len_in, &addr_in);
322962306a36Sopenharmony_ci	if (rc)
323062306a36Sopenharmony_ci		goto unmap_out;
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci	memset(&mpi_request, 0, request_sz);
323362306a36Sopenharmony_ci	memset(&mpi_reply, 0, reply_sz);
323462306a36Sopenharmony_ci	mpi_request.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_TRANSPORT_CMDS);
323562306a36Sopenharmony_ci	mpi_request.function = MPI3_FUNCTION_SMP_PASSTHROUGH;
323662306a36Sopenharmony_ci	mpi_request.io_unit_port = (u8) mpi3mr_get_port_id_by_rphy(mrioc, rphy);
323762306a36Sopenharmony_ci	mpi_request.sas_address = ((rphy) ?
323862306a36Sopenharmony_ci	    cpu_to_le64(rphy->identify.sas_address) :
323962306a36Sopenharmony_ci	    cpu_to_le64(mrioc->sas_hba.sas_address));
324062306a36Sopenharmony_ci	psge = &mpi_request.request_sge;
324162306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, dma_len_out - 4, dma_addr_out);
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	psge = &mpi_request.response_sge;
324462306a36Sopenharmony_ci	mpi3mr_add_sg_single(psge, sgl_flags, dma_len_in - 4, dma_addr_in);
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	dprint_transport_info(mrioc, "sending SMP request\n");
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	rc = mpi3mr_post_transport_req(mrioc, &mpi_request, request_sz,
324962306a36Sopenharmony_ci				       &mpi_reply, reply_sz,
325062306a36Sopenharmony_ci				       MPI3MR_INTADMCMD_TIMEOUT, &ioc_status);
325162306a36Sopenharmony_ci	if (rc)
325262306a36Sopenharmony_ci		goto unmap_in;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	dprint_transport_info(mrioc,
325562306a36Sopenharmony_ci	    "SMP request completed with ioc_status(0x%04x)\n", ioc_status);
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	dprint_transport_info(mrioc,
325862306a36Sopenharmony_ci		    "SMP request - reply data transfer size(%d)\n",
325962306a36Sopenharmony_ci		    le16_to_cpu(mpi_reply.response_data_length));
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	memcpy(job->reply, &mpi_reply, reply_sz);
326262306a36Sopenharmony_ci	job->reply_len = reply_sz;
326362306a36Sopenharmony_ci	reslen = le16_to_cpu(mpi_reply.response_data_length);
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci	if (addr_in)
326662306a36Sopenharmony_ci		sg_copy_from_buffer(job->reply_payload.sg_list,
326762306a36Sopenharmony_ci				job->reply_payload.sg_cnt, addr_in,
326862306a36Sopenharmony_ci				job->reply_payload.payload_len);
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci	rc = 0;
327162306a36Sopenharmony_ciunmap_in:
327262306a36Sopenharmony_ci	mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->reply_payload,
327362306a36Sopenharmony_ci			dma_addr_in, addr_in);
327462306a36Sopenharmony_ciunmap_out:
327562306a36Sopenharmony_ci	mpi3mr_unmap_smp_buffer(&mrioc->pdev->dev, &job->request_payload,
327662306a36Sopenharmony_ci			dma_addr_out, addr_out);
327762306a36Sopenharmony_ciout:
327862306a36Sopenharmony_ci	bsg_job_done(job, rc, reslen);
327962306a36Sopenharmony_ci}
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_cistruct sas_function_template mpi3mr_transport_functions = {
328262306a36Sopenharmony_ci	.get_linkerrors		= mpi3mr_transport_get_linkerrors,
328362306a36Sopenharmony_ci	.get_enclosure_identifier = mpi3mr_transport_get_enclosure_identifier,
328462306a36Sopenharmony_ci	.get_bay_identifier	= mpi3mr_transport_get_bay_identifier,
328562306a36Sopenharmony_ci	.phy_reset		= mpi3mr_transport_phy_reset,
328662306a36Sopenharmony_ci	.phy_enable		= mpi3mr_transport_phy_enable,
328762306a36Sopenharmony_ci	.set_phy_speed		= mpi3mr_transport_phy_speed,
328862306a36Sopenharmony_ci	.smp_handler		= mpi3mr_transport_smp_handler,
328962306a36Sopenharmony_ci};
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_cistruct scsi_transport_template *mpi3mr_transport_template;
3292