162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * SAS Transport Layer for MPT (Message Passing Technology) based controllers
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c
562306a36Sopenharmony_ci * Copyright (C) 2012-2014  LSI Corporation
662306a36Sopenharmony_ci * Copyright (C) 2013-2014 Avago Technologies
762306a36Sopenharmony_ci *  (mailto: MPT-FusionLinux.pdl@avagotech.com)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
1062306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License
1162306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2
1262306a36Sopenharmony_ci * of the License, or (at your option) any later version.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful,
1562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1762306a36Sopenharmony_ci * GNU General Public License for more details.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * NO WARRANTY
2062306a36Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2162306a36Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2262306a36Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2362306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2462306a36Sopenharmony_ci * solely responsible for determining the appropriateness of using and
2562306a36Sopenharmony_ci * distributing the Program and assumes all risks associated with its
2662306a36Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to
2762306a36Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data,
2862306a36Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations.
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci * DISCLAIMER OF LIABILITY
3162306a36Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3262306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3362306a36Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3462306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3562306a36Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3662306a36Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3762306a36Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License
4062306a36Sopenharmony_ci * along with this program; if not, write to the Free Software
4162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
4262306a36Sopenharmony_ci * USA.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <linux/module.h>
4662306a36Sopenharmony_ci#include <linux/kernel.h>
4762306a36Sopenharmony_ci#include <linux/init.h>
4862306a36Sopenharmony_ci#include <linux/errno.h>
4962306a36Sopenharmony_ci#include <linux/sched.h>
5062306a36Sopenharmony_ci#include <linux/workqueue.h>
5162306a36Sopenharmony_ci#include <linux/delay.h>
5262306a36Sopenharmony_ci#include <linux/pci.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#include <scsi/scsi.h>
5562306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
5662306a36Sopenharmony_ci#include <scsi/scsi_device.h>
5762306a36Sopenharmony_ci#include <scsi/scsi_host.h>
5862306a36Sopenharmony_ci#include <scsi/scsi_transport_sas.h>
5962306a36Sopenharmony_ci#include <scsi/scsi_dbg.h>
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#include "mpt3sas_base.h"
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * _transport_get_port_id_by_sas_phy - get zone's port id that Phy belong to
6562306a36Sopenharmony_ci * @phy: sas_phy object
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Return Port number
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistatic inline u8
7062306a36Sopenharmony_ci_transport_get_port_id_by_sas_phy(struct sas_phy *phy)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	u8 port_id = 0xFF;
7362306a36Sopenharmony_ci	struct hba_port *port = phy->hostdata;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (port)
7662306a36Sopenharmony_ci		port_id = port->port_id;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return port_id;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * _transport_sas_node_find_by_sas_address - sas node search
8362306a36Sopenharmony_ci * @ioc: per adapter object
8462306a36Sopenharmony_ci * @sas_address: sas address of expander or sas host
8562306a36Sopenharmony_ci * @port: hba port entry
8662306a36Sopenharmony_ci * Context: Calling function should acquire ioc->sas_node_lock.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Search for either hba phys or expander device based on handle, then returns
8962306a36Sopenharmony_ci * the sas_node object.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic struct _sas_node *
9262306a36Sopenharmony_ci_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
9362306a36Sopenharmony_ci	u64 sas_address, struct hba_port *port)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	if (ioc->sas_hba.sas_address == sas_address)
9662306a36Sopenharmony_ci		return &ioc->sas_hba;
9762306a36Sopenharmony_ci	else
9862306a36Sopenharmony_ci		return mpt3sas_scsih_expander_find_by_sas_address(ioc,
9962306a36Sopenharmony_ci		    sas_address, port);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * _transport_get_port_id_by_rphy - Get Port number from rphy object
10462306a36Sopenharmony_ci * @ioc: per adapter object
10562306a36Sopenharmony_ci * @rphy: sas_rphy object
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * Returns Port number.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic u8
11062306a36Sopenharmony_ci_transport_get_port_id_by_rphy(struct MPT3SAS_ADAPTER *ioc,
11162306a36Sopenharmony_ci	struct sas_rphy *rphy)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct _sas_node *sas_expander;
11462306a36Sopenharmony_ci	struct _sas_device *sas_device;
11562306a36Sopenharmony_ci	unsigned long flags;
11662306a36Sopenharmony_ci	u8 port_id = 0xFF;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (!rphy)
11962306a36Sopenharmony_ci		return port_id;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
12262306a36Sopenharmony_ci	    rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) {
12362306a36Sopenharmony_ci		spin_lock_irqsave(&ioc->sas_node_lock, flags);
12462306a36Sopenharmony_ci		list_for_each_entry(sas_expander,
12562306a36Sopenharmony_ci		    &ioc->sas_expander_list, list) {
12662306a36Sopenharmony_ci			if (sas_expander->rphy == rphy) {
12762306a36Sopenharmony_ci				port_id = sas_expander->port->port_id;
12862306a36Sopenharmony_ci				break;
12962306a36Sopenharmony_ci			}
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
13262306a36Sopenharmony_ci	} else if (rphy->identify.device_type == SAS_END_DEVICE) {
13362306a36Sopenharmony_ci		spin_lock_irqsave(&ioc->sas_device_lock, flags);
13462306a36Sopenharmony_ci		sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
13562306a36Sopenharmony_ci		if (sas_device) {
13662306a36Sopenharmony_ci			port_id = sas_device->port->port_id;
13762306a36Sopenharmony_ci			sas_device_put(sas_device);
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return port_id;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * _transport_convert_phy_link_rate -
14762306a36Sopenharmony_ci * @link_rate: link rate returned from mpt firmware
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * Convert link_rate from mpi fusion into sas_transport form.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic enum sas_linkrate
15262306a36Sopenharmony_ci_transport_convert_phy_link_rate(u8 link_rate)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	enum sas_linkrate rc;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	switch (link_rate) {
15762306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_1_5:
15862306a36Sopenharmony_ci		rc = SAS_LINK_RATE_1_5_GBPS;
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_3_0:
16162306a36Sopenharmony_ci		rc = SAS_LINK_RATE_3_0_GBPS;
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_6_0:
16462306a36Sopenharmony_ci		rc = SAS_LINK_RATE_6_0_GBPS;
16562306a36Sopenharmony_ci		break;
16662306a36Sopenharmony_ci	case MPI25_SAS_NEG_LINK_RATE_12_0:
16762306a36Sopenharmony_ci		rc = SAS_LINK_RATE_12_0_GBPS;
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
17062306a36Sopenharmony_ci		rc = SAS_PHY_DISABLED;
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
17362306a36Sopenharmony_ci		rc = SAS_LINK_RATE_FAILED;
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
17662306a36Sopenharmony_ci		rc = SAS_SATA_PORT_SELECTOR;
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
17962306a36Sopenharmony_ci		rc = SAS_PHY_RESET_IN_PROGRESS;
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	default:
18362306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
18462306a36Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
18562306a36Sopenharmony_ci		rc = SAS_LINK_RATE_UNKNOWN;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	return rc;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/**
19262306a36Sopenharmony_ci * _transport_set_identify - set identify for phys and end devices
19362306a36Sopenharmony_ci * @ioc: per adapter object
19462306a36Sopenharmony_ci * @handle: device handle
19562306a36Sopenharmony_ci * @identify: sas identify info
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Populates sas identify info.
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic int
20262306a36Sopenharmony_ci_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle,
20362306a36Sopenharmony_ci	struct sas_identify *identify)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	Mpi2SasDevicePage0_t sas_device_pg0;
20662306a36Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
20762306a36Sopenharmony_ci	u32 device_info;
20862306a36Sopenharmony_ci	u32 ioc_status;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
21162306a36Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
21262306a36Sopenharmony_ci		return -EFAULT;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
21662306a36Sopenharmony_ci	    MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
21762306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
21862306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
21962306a36Sopenharmony_ci		return -ENXIO;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
22362306a36Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
22462306a36Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
22562306a36Sopenharmony_ci		ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x) failure at %s:%d/%s()!\n",
22662306a36Sopenharmony_ci			handle, ioc_status, __FILE__, __LINE__, __func__);
22762306a36Sopenharmony_ci		return -EIO;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	memset(identify, 0, sizeof(struct sas_identify));
23162306a36Sopenharmony_ci	device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* sas_address */
23462306a36Sopenharmony_ci	identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* phy number of the parent device this device is linked to */
23762306a36Sopenharmony_ci	identify->phy_identifier = sas_device_pg0.PhyNum;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* device_type */
24062306a36Sopenharmony_ci	switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
24162306a36Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
24262306a36Sopenharmony_ci		identify->device_type = SAS_PHY_UNUSED;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_END_DEVICE:
24562306a36Sopenharmony_ci		identify->device_type = SAS_END_DEVICE;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
24862306a36Sopenharmony_ci		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
25162306a36Sopenharmony_ci		identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
25262306a36Sopenharmony_ci		break;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* initiator_port_protocols */
25662306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
25762306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
25862306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
25962306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
26062306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
26162306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
26262306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
26362306a36Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* target_port_protocols */
26662306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
26762306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
26862306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
26962306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_STP;
27062306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
27162306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
27262306a36Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
27362306a36Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SATA;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/**
27962306a36Sopenharmony_ci * mpt3sas_transport_done -  internal transport layer callback handler.
28062306a36Sopenharmony_ci * @ioc: per adapter object
28162306a36Sopenharmony_ci * @smid: system request message index
28262306a36Sopenharmony_ci * @msix_index: MSIX table index supplied by the OS
28362306a36Sopenharmony_ci * @reply: reply message frame(lower 32bit addr)
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci * Callback handler when sending internal generated transport cmds.
28662306a36Sopenharmony_ci * The callback index passed is `ioc->transport_cb_idx`
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Return: 1 meaning mf should be freed from _base_interrupt
28962306a36Sopenharmony_ci *         0 means the mf is freed from this function.
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_ciu8
29262306a36Sopenharmony_cimpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
29362306a36Sopenharmony_ci	u32 reply)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	MPI2DefaultReply_t *mpi_reply;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	mpi_reply =  mpt3sas_base_get_reply_virt_addr(ioc, reply);
29862306a36Sopenharmony_ci	if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED)
29962306a36Sopenharmony_ci		return 1;
30062306a36Sopenharmony_ci	if (ioc->transport_cmds.smid != smid)
30162306a36Sopenharmony_ci		return 1;
30262306a36Sopenharmony_ci	ioc->transport_cmds.status |= MPT3_CMD_COMPLETE;
30362306a36Sopenharmony_ci	if (mpi_reply) {
30462306a36Sopenharmony_ci		memcpy(ioc->transport_cmds.reply, mpi_reply,
30562306a36Sopenharmony_ci		    mpi_reply->MsgLength*4);
30662306a36Sopenharmony_ci		ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	ioc->transport_cmds.status &= ~MPT3_CMD_PENDING;
30962306a36Sopenharmony_ci	complete(&ioc->transport_cmds.done);
31062306a36Sopenharmony_ci	return 1;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* report manufacture request structure */
31462306a36Sopenharmony_cistruct rep_manu_request {
31562306a36Sopenharmony_ci	u8 smp_frame_type;
31662306a36Sopenharmony_ci	u8 function;
31762306a36Sopenharmony_ci	u8 reserved;
31862306a36Sopenharmony_ci	u8 request_length;
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/* report manufacture reply structure */
32262306a36Sopenharmony_cistruct rep_manu_reply {
32362306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
32462306a36Sopenharmony_ci	u8 function; /* 0x01 */
32562306a36Sopenharmony_ci	u8 function_result;
32662306a36Sopenharmony_ci	u8 response_length;
32762306a36Sopenharmony_ci	u16 expander_change_count;
32862306a36Sopenharmony_ci	u8 reserved0[2];
32962306a36Sopenharmony_ci	u8 sas_format;
33062306a36Sopenharmony_ci	u8 reserved2[3];
33162306a36Sopenharmony_ci	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
33262306a36Sopenharmony_ci	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
33362306a36Sopenharmony_ci	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
33462306a36Sopenharmony_ci	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
33562306a36Sopenharmony_ci	u16 component_id;
33662306a36Sopenharmony_ci	u8 component_revision_id;
33762306a36Sopenharmony_ci	u8 reserved3;
33862306a36Sopenharmony_ci	u8 vendor_specific[8];
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/**
34262306a36Sopenharmony_ci * _transport_expander_report_manufacture - obtain SMP report_manufacture
34362306a36Sopenharmony_ci * @ioc: per adapter object
34462306a36Sopenharmony_ci * @sas_address: expander sas address
34562306a36Sopenharmony_ci * @edev: the sas_expander_device object
34662306a36Sopenharmony_ci * @port_id: Port ID number
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * Fills in the sas_expander_device object when SMP port is created.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic int
35362306a36Sopenharmony_ci_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc,
35462306a36Sopenharmony_ci	u64 sas_address, struct sas_expander_device *edev, u8 port_id)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
35762306a36Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
35862306a36Sopenharmony_ci	struct rep_manu_reply *manufacture_reply;
35962306a36Sopenharmony_ci	struct rep_manu_request *manufacture_request;
36062306a36Sopenharmony_ci	int rc;
36162306a36Sopenharmony_ci	u16 smid;
36262306a36Sopenharmony_ci	void *psge;
36362306a36Sopenharmony_ci	u8 issue_reset = 0;
36462306a36Sopenharmony_ci	void *data_out = NULL;
36562306a36Sopenharmony_ci	dma_addr_t data_out_dma;
36662306a36Sopenharmony_ci	dma_addr_t data_in_dma;
36762306a36Sopenharmony_ci	size_t data_in_sz;
36862306a36Sopenharmony_ci	size_t data_out_sz;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
37162306a36Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
37262306a36Sopenharmony_ci		return -EFAULT;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
37862306a36Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
37962306a36Sopenharmony_ci		rc = -EAGAIN;
38062306a36Sopenharmony_ci		goto out;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
38562306a36Sopenharmony_ci	if (rc)
38662306a36Sopenharmony_ci		goto out;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
38962306a36Sopenharmony_ci	if (!smid) {
39062306a36Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
39162306a36Sopenharmony_ci		rc = -EAGAIN;
39262306a36Sopenharmony_ci		goto out;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	rc = 0;
39662306a36Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
39762306a36Sopenharmony_ci	ioc->transport_cmds.smid = smid;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	data_out_sz = sizeof(struct rep_manu_request);
40062306a36Sopenharmony_ci	data_in_sz = sizeof(struct rep_manu_reply);
40162306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
40262306a36Sopenharmony_ci			&data_out_dma, GFP_KERNEL);
40362306a36Sopenharmony_ci	if (!data_out) {
40462306a36Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
40562306a36Sopenharmony_ci		    __LINE__, __func__);
40662306a36Sopenharmony_ci		rc = -ENOMEM;
40762306a36Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
40862306a36Sopenharmony_ci		goto out;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	data_in_dma = data_out_dma + sizeof(struct rep_manu_request);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	manufacture_request = data_out;
41462306a36Sopenharmony_ci	manufacture_request->smp_frame_type = 0x40;
41562306a36Sopenharmony_ci	manufacture_request->function = 1;
41662306a36Sopenharmony_ci	manufacture_request->reserved = 0;
41762306a36Sopenharmony_ci	manufacture_request->request_length = 0;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
42062306a36Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
42162306a36Sopenharmony_ci	mpi_request->PhysicalPort = port_id;
42262306a36Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(sas_address);
42362306a36Sopenharmony_ci	mpi_request->RequestDataLength = cpu_to_le16(data_out_sz);
42462306a36Sopenharmony_ci	psge = &mpi_request->SGL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
42762306a36Sopenharmony_ci	    data_in_sz);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	dtransportprintk(ioc,
43062306a36Sopenharmony_ci			 ioc_info(ioc, "report_manufacture - send to sas_addr(0x%016llx)\n",
43162306a36Sopenharmony_ci				  (u64)sas_address));
43262306a36Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
43362306a36Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
43462306a36Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
43762306a36Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
43862306a36Sopenharmony_ci		_debug_dump_mf(mpi_request,
43962306a36Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
44062306a36Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
44162306a36Sopenharmony_ci			issue_reset = 1;
44262306a36Sopenharmony_ci		goto issue_host_reset;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "report_manufacture - complete\n"));
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
44862306a36Sopenharmony_ci		u8 *tmp;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		dtransportprintk(ioc,
45362306a36Sopenharmony_ci				 ioc_info(ioc, "report_manufacture - reply data transfer size(%d)\n",
45462306a36Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
45762306a36Sopenharmony_ci		    sizeof(struct rep_manu_reply))
45862306a36Sopenharmony_ci			goto out;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		manufacture_reply = data_out + sizeof(struct rep_manu_request);
46162306a36Sopenharmony_ci		strncpy(edev->vendor_id, manufacture_reply->vendor_id,
46262306a36Sopenharmony_ci		     SAS_EXPANDER_VENDOR_ID_LEN);
46362306a36Sopenharmony_ci		strncpy(edev->product_id, manufacture_reply->product_id,
46462306a36Sopenharmony_ci		     SAS_EXPANDER_PRODUCT_ID_LEN);
46562306a36Sopenharmony_ci		strncpy(edev->product_rev, manufacture_reply->product_rev,
46662306a36Sopenharmony_ci		     SAS_EXPANDER_PRODUCT_REV_LEN);
46762306a36Sopenharmony_ci		edev->level = manufacture_reply->sas_format & 1;
46862306a36Sopenharmony_ci		if (edev->level) {
46962306a36Sopenharmony_ci			strncpy(edev->component_vendor_id,
47062306a36Sopenharmony_ci			    manufacture_reply->component_vendor_id,
47162306a36Sopenharmony_ci			     SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
47262306a36Sopenharmony_ci			tmp = (u8 *)&manufacture_reply->component_id;
47362306a36Sopenharmony_ci			edev->component_id = tmp[0] << 8 | tmp[1];
47462306a36Sopenharmony_ci			edev->component_revision_id =
47562306a36Sopenharmony_ci			    manufacture_reply->component_revision_id;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci	} else
47862306a36Sopenharmony_ci		dtransportprintk(ioc,
47962306a36Sopenharmony_ci				 ioc_info(ioc, "report_manufacture - no reply\n"));
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci issue_host_reset:
48262306a36Sopenharmony_ci	if (issue_reset)
48362306a36Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
48462306a36Sopenharmony_ci out:
48562306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
48662306a36Sopenharmony_ci	if (data_out)
48762306a36Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
48862306a36Sopenharmony_ci		    data_out, data_out_dma);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
49162306a36Sopenharmony_ci	return rc;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * _transport_delete_port - helper function to removing a port
49762306a36Sopenharmony_ci * @ioc: per adapter object
49862306a36Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
49962306a36Sopenharmony_ci */
50062306a36Sopenharmony_cistatic void
50162306a36Sopenharmony_ci_transport_delete_port(struct MPT3SAS_ADAPTER *ioc,
50262306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
50562306a36Sopenharmony_ci	struct hba_port *port = mpt3sas_port->hba_port;
50662306a36Sopenharmony_ci	enum sas_device_type device_type =
50762306a36Sopenharmony_ci	    mpt3sas_port->remote_identify.device_type;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
51062306a36Sopenharmony_ci	    "remove: sas_addr(0x%016llx)\n",
51162306a36Sopenharmony_ci	    (unsigned long long) sas_address);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ioc->logging_level |= MPT_DEBUG_TRANSPORT;
51462306a36Sopenharmony_ci	if (device_type == SAS_END_DEVICE)
51562306a36Sopenharmony_ci		mpt3sas_device_remove_by_sas_address(ioc,
51662306a36Sopenharmony_ci		    sas_address, port);
51762306a36Sopenharmony_ci	else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
51862306a36Sopenharmony_ci	    device_type == SAS_FANOUT_EXPANDER_DEVICE)
51962306a36Sopenharmony_ci		mpt3sas_expander_remove(ioc, sas_address, port);
52062306a36Sopenharmony_ci	ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/**
52462306a36Sopenharmony_ci * _transport_delete_phy - helper function to removing single phy from port
52562306a36Sopenharmony_ci * @ioc: per adapter object
52662306a36Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
52762306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic void
53062306a36Sopenharmony_ci_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc,
53162306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port, struct _sas_phy *mpt3sas_phy)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
53662306a36Sopenharmony_ci	    "remove: sas_addr(0x%016llx), phy(%d)\n",
53762306a36Sopenharmony_ci	    (unsigned long long) sas_address, mpt3sas_phy->phy_id);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	list_del(&mpt3sas_phy->port_siblings);
54062306a36Sopenharmony_ci	mpt3sas_port->num_phys--;
54162306a36Sopenharmony_ci	sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy);
54262306a36Sopenharmony_ci	mpt3sas_phy->phy_belongs_to_port = 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/**
54662306a36Sopenharmony_ci * _transport_add_phy - helper function to adding single phy to port
54762306a36Sopenharmony_ci * @ioc: per adapter object
54862306a36Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
54962306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
55062306a36Sopenharmony_ci */
55162306a36Sopenharmony_cistatic void
55262306a36Sopenharmony_ci_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port,
55362306a36Sopenharmony_ci	struct _sas_phy *mpt3sas_phy)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
55862306a36Sopenharmony_ci	    "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
55962306a36Sopenharmony_ci	    sas_address, mpt3sas_phy->phy_id);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	list_add_tail(&mpt3sas_phy->port_siblings, &mpt3sas_port->phy_list);
56262306a36Sopenharmony_ci	mpt3sas_port->num_phys++;
56362306a36Sopenharmony_ci	sas_port_add_phy(mpt3sas_port->port, mpt3sas_phy->phy);
56462306a36Sopenharmony_ci	mpt3sas_phy->phy_belongs_to_port = 1;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci/**
56862306a36Sopenharmony_ci * mpt3sas_transport_add_phy_to_an_existing_port - adding new phy to existing port
56962306a36Sopenharmony_ci * @ioc: per adapter object
57062306a36Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
57162306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
57262306a36Sopenharmony_ci * @sas_address: sas address of device/expander were phy needs to be added to
57362306a36Sopenharmony_ci * @port: hba port entry
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_civoid
57662306a36Sopenharmony_cimpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
57762306a36Sopenharmony_ci	struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy,
57862306a36Sopenharmony_ci	u64 sas_address, struct hba_port *port)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port;
58162306a36Sopenharmony_ci	struct _sas_phy *phy_srch;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (mpt3sas_phy->phy_belongs_to_port == 1)
58462306a36Sopenharmony_ci		return;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (!port)
58762306a36Sopenharmony_ci		return;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list,
59062306a36Sopenharmony_ci	    port_list) {
59162306a36Sopenharmony_ci		if (mpt3sas_port->remote_identify.sas_address !=
59262306a36Sopenharmony_ci		    sas_address)
59362306a36Sopenharmony_ci			continue;
59462306a36Sopenharmony_ci		if (mpt3sas_port->hba_port != port)
59562306a36Sopenharmony_ci			continue;
59662306a36Sopenharmony_ci		list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
59762306a36Sopenharmony_ci		    port_siblings) {
59862306a36Sopenharmony_ci			if (phy_srch == mpt3sas_phy)
59962306a36Sopenharmony_ci				return;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci		_transport_add_phy(ioc, mpt3sas_port, mpt3sas_phy);
60262306a36Sopenharmony_ci		return;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/**
60862306a36Sopenharmony_ci * mpt3sas_transport_del_phy_from_an_existing_port - delete phy from existing port
60962306a36Sopenharmony_ci * @ioc: per adapter object
61062306a36Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
61162306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
61262306a36Sopenharmony_ci */
61362306a36Sopenharmony_civoid
61462306a36Sopenharmony_cimpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
61562306a36Sopenharmony_ci	struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port, *next;
61862306a36Sopenharmony_ci	struct _sas_phy *phy_srch;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (mpt3sas_phy->phy_belongs_to_port == 0)
62162306a36Sopenharmony_ci		return;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
62462306a36Sopenharmony_ci	    port_list) {
62562306a36Sopenharmony_ci		list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
62662306a36Sopenharmony_ci		    port_siblings) {
62762306a36Sopenharmony_ci			if (phy_srch != mpt3sas_phy)
62862306a36Sopenharmony_ci				continue;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci			/*
63162306a36Sopenharmony_ci			 * Don't delete port during host reset,
63262306a36Sopenharmony_ci			 * just delete phy.
63362306a36Sopenharmony_ci			 */
63462306a36Sopenharmony_ci			if (mpt3sas_port->num_phys == 1 && !ioc->shost_recovery)
63562306a36Sopenharmony_ci				_transport_delete_port(ioc, mpt3sas_port);
63662306a36Sopenharmony_ci			else
63762306a36Sopenharmony_ci				_transport_delete_phy(ioc, mpt3sas_port,
63862306a36Sopenharmony_ci				    mpt3sas_phy);
63962306a36Sopenharmony_ci			return;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/**
64562306a36Sopenharmony_ci * _transport_sanity_check - sanity check when adding a new port
64662306a36Sopenharmony_ci * @ioc: per adapter object
64762306a36Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
64862306a36Sopenharmony_ci * @sas_address: sas address of device being added
64962306a36Sopenharmony_ci * @port: hba port entry
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * See the explanation above from _transport_delete_duplicate_port
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic void
65462306a36Sopenharmony_ci_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node,
65562306a36Sopenharmony_ci	u64 sas_address, struct hba_port *port)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	int i;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
66062306a36Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address != sas_address)
66162306a36Sopenharmony_ci			continue;
66262306a36Sopenharmony_ci		if (sas_node->phy[i].port != port)
66362306a36Sopenharmony_ci			continue;
66462306a36Sopenharmony_ci		if (sas_node->phy[i].phy_belongs_to_port == 1)
66562306a36Sopenharmony_ci			mpt3sas_transport_del_phy_from_an_existing_port(ioc,
66662306a36Sopenharmony_ci			    sas_node, &sas_node->phy[i]);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/**
67162306a36Sopenharmony_ci * mpt3sas_transport_port_add - insert port to the list
67262306a36Sopenharmony_ci * @ioc: per adapter object
67362306a36Sopenharmony_ci * @handle: handle of attached device
67462306a36Sopenharmony_ci * @sas_address: sas address of parent expander or sas host
67562306a36Sopenharmony_ci * @hba_port: hba port entry
67662306a36Sopenharmony_ci * Context: This function will acquire ioc->sas_node_lock.
67762306a36Sopenharmony_ci *
67862306a36Sopenharmony_ci * Adding new port object to the sas_node->sas_port_list.
67962306a36Sopenharmony_ci *
68062306a36Sopenharmony_ci * Return: mpt3sas_port.
68162306a36Sopenharmony_ci */
68262306a36Sopenharmony_cistruct _sas_port *
68362306a36Sopenharmony_cimpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
68462306a36Sopenharmony_ci	u64 sas_address, struct hba_port *hba_port)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct _sas_phy *mpt3sas_phy, *next;
68762306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port;
68862306a36Sopenharmony_ci	unsigned long flags;
68962306a36Sopenharmony_ci	struct _sas_node *sas_node;
69062306a36Sopenharmony_ci	struct sas_rphy *rphy;
69162306a36Sopenharmony_ci	struct _sas_device *sas_device = NULL;
69262306a36Sopenharmony_ci	int i;
69362306a36Sopenharmony_ci	struct sas_port *port;
69462306a36Sopenharmony_ci	struct virtual_phy *vphy = NULL;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!hba_port) {
69762306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
69862306a36Sopenharmony_ci		    __FILE__, __LINE__, __func__);
69962306a36Sopenharmony_ci		return NULL;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	mpt3sas_port = kzalloc(sizeof(struct _sas_port),
70362306a36Sopenharmony_ci	    GFP_KERNEL);
70462306a36Sopenharmony_ci	if (!mpt3sas_port) {
70562306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
70662306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
70762306a36Sopenharmony_ci		return NULL;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_port->port_list);
71162306a36Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_port->phy_list);
71262306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
71362306a36Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc,
71462306a36Sopenharmony_ci	    sas_address, hba_port);
71562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (!sas_node) {
71862306a36Sopenharmony_ci		ioc_err(ioc, "%s: Could not find parent sas_address(0x%016llx)!\n",
71962306a36Sopenharmony_ci			__func__, (u64)sas_address);
72062306a36Sopenharmony_ci		goto out_fail;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if ((_transport_set_identify(ioc, handle,
72462306a36Sopenharmony_ci	    &mpt3sas_port->remote_identify))) {
72562306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
72662306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
72762306a36Sopenharmony_ci		goto out_fail;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
73162306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
73262306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
73362306a36Sopenharmony_ci		goto out_fail;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	mpt3sas_port->hba_port = hba_port;
73762306a36Sopenharmony_ci	_transport_sanity_check(ioc, sas_node,
73862306a36Sopenharmony_ci	    mpt3sas_port->remote_identify.sas_address, hba_port);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
74162306a36Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address !=
74262306a36Sopenharmony_ci		    mpt3sas_port->remote_identify.sas_address)
74362306a36Sopenharmony_ci			continue;
74462306a36Sopenharmony_ci		if (sas_node->phy[i].port != hba_port)
74562306a36Sopenharmony_ci			continue;
74662306a36Sopenharmony_ci		list_add_tail(&sas_node->phy[i].port_siblings,
74762306a36Sopenharmony_ci		    &mpt3sas_port->phy_list);
74862306a36Sopenharmony_ci		mpt3sas_port->num_phys++;
74962306a36Sopenharmony_ci		if (sas_node->handle <= ioc->sas_hba.num_phys) {
75062306a36Sopenharmony_ci			if (!sas_node->phy[i].hba_vphy) {
75162306a36Sopenharmony_ci				hba_port->phy_mask |= (1 << i);
75262306a36Sopenharmony_ci				continue;
75362306a36Sopenharmony_ci			}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci			vphy = mpt3sas_get_vphy_by_phy(ioc, hba_port, i);
75662306a36Sopenharmony_ci			if (!vphy) {
75762306a36Sopenharmony_ci				ioc_err(ioc, "failure at %s:%d/%s()!\n",
75862306a36Sopenharmony_ci				    __FILE__, __LINE__, __func__);
75962306a36Sopenharmony_ci				goto out_fail;
76062306a36Sopenharmony_ci			}
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (!mpt3sas_port->num_phys) {
76562306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
76662306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
76762306a36Sopenharmony_ci		goto out_fail;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
77162306a36Sopenharmony_ci		sas_device = mpt3sas_get_sdev_by_addr(ioc,
77262306a36Sopenharmony_ci		    mpt3sas_port->remote_identify.sas_address,
77362306a36Sopenharmony_ci		    mpt3sas_port->hba_port);
77462306a36Sopenharmony_ci		if (!sas_device) {
77562306a36Sopenharmony_ci			ioc_err(ioc, "failure at %s:%d/%s()!\n",
77662306a36Sopenharmony_ci			    __FILE__, __LINE__, __func__);
77762306a36Sopenharmony_ci			goto out_fail;
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci		sas_device->pend_sas_rphy_add = 1;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (!sas_node->parent_dev) {
78362306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
78462306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
78562306a36Sopenharmony_ci		goto out_fail;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci	port = sas_port_alloc_num(sas_node->parent_dev);
78862306a36Sopenharmony_ci	if (!port || (sas_port_add(port))) {
78962306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
79062306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
79162306a36Sopenharmony_ci		goto out_fail;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	list_for_each_entry(mpt3sas_phy, &mpt3sas_port->phy_list,
79562306a36Sopenharmony_ci	    port_siblings) {
79662306a36Sopenharmony_ci		if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
79762306a36Sopenharmony_ci			dev_printk(KERN_INFO, &port->dev,
79862306a36Sopenharmony_ci				"add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n",
79962306a36Sopenharmony_ci				handle, (unsigned long long)
80062306a36Sopenharmony_ci			    mpt3sas_port->remote_identify.sas_address,
80162306a36Sopenharmony_ci			    mpt3sas_phy->phy_id);
80262306a36Sopenharmony_ci		sas_port_add_phy(port, mpt3sas_phy->phy);
80362306a36Sopenharmony_ci		mpt3sas_phy->phy_belongs_to_port = 1;
80462306a36Sopenharmony_ci		mpt3sas_phy->port = hba_port;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	mpt3sas_port->port = port;
80862306a36Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
80962306a36Sopenharmony_ci		rphy = sas_end_device_alloc(port);
81062306a36Sopenharmony_ci		sas_device->rphy = rphy;
81162306a36Sopenharmony_ci		if (sas_node->handle <= ioc->sas_hba.num_phys) {
81262306a36Sopenharmony_ci			if (!vphy)
81362306a36Sopenharmony_ci				hba_port->sas_address =
81462306a36Sopenharmony_ci				    sas_device->sas_address;
81562306a36Sopenharmony_ci			else
81662306a36Sopenharmony_ci				vphy->sas_address =
81762306a36Sopenharmony_ci				    sas_device->sas_address;
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci	} else {
82062306a36Sopenharmony_ci		rphy = sas_expander_alloc(port,
82162306a36Sopenharmony_ci		    mpt3sas_port->remote_identify.device_type);
82262306a36Sopenharmony_ci		if (sas_node->handle <= ioc->sas_hba.num_phys)
82362306a36Sopenharmony_ci			hba_port->sas_address =
82462306a36Sopenharmony_ci			    mpt3sas_port->remote_identify.sas_address;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (!rphy) {
82862306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
82962306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
83062306a36Sopenharmony_ci		goto out_delete_port;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	rphy->identify = mpt3sas_port->remote_identify;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if ((sas_rphy_add(rphy))) {
83662306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
83762306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
83862306a36Sopenharmony_ci		sas_rphy_free(rphy);
83962306a36Sopenharmony_ci		rphy = NULL;
84062306a36Sopenharmony_ci		goto out_delete_port;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
84462306a36Sopenharmony_ci		sas_device->pend_sas_rphy_add = 0;
84562306a36Sopenharmony_ci		sas_device_put(sas_device);
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	dev_info(&rphy->dev,
84962306a36Sopenharmony_ci	    "add: handle(0x%04x), sas_addr(0x%016llx)\n", handle,
85062306a36Sopenharmony_ci	    (unsigned long long)mpt3sas_port->remote_identify.sas_address);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	mpt3sas_port->rphy = rphy;
85362306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
85462306a36Sopenharmony_ci	list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list);
85562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* fill in report manufacture */
85862306a36Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type ==
85962306a36Sopenharmony_ci	    MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
86062306a36Sopenharmony_ci	    mpt3sas_port->remote_identify.device_type ==
86162306a36Sopenharmony_ci	    MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
86262306a36Sopenharmony_ci		_transport_expander_report_manufacture(ioc,
86362306a36Sopenharmony_ci		    mpt3sas_port->remote_identify.sas_address,
86462306a36Sopenharmony_ci		    rphy_to_expander_device(rphy), hba_port->port_id);
86562306a36Sopenharmony_ci	return mpt3sas_port;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ciout_delete_port:
86862306a36Sopenharmony_ci	sas_port_delete(port);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciout_fail:
87162306a36Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list,
87262306a36Sopenharmony_ci	    port_siblings)
87362306a36Sopenharmony_ci		list_del(&mpt3sas_phy->port_siblings);
87462306a36Sopenharmony_ci	kfree(mpt3sas_port);
87562306a36Sopenharmony_ci	return NULL;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/**
87962306a36Sopenharmony_ci * mpt3sas_transport_port_remove - remove port from the list
88062306a36Sopenharmony_ci * @ioc: per adapter object
88162306a36Sopenharmony_ci * @sas_address: sas address of attached device
88262306a36Sopenharmony_ci * @sas_address_parent: sas address of parent expander or sas host
88362306a36Sopenharmony_ci * @port: hba port entry
88462306a36Sopenharmony_ci * Context: This function will acquire ioc->sas_node_lock.
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci * Removing object and freeing associated memory from the
88762306a36Sopenharmony_ci * ioc->sas_port_list.
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_civoid
89062306a36Sopenharmony_cimpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
89162306a36Sopenharmony_ci	u64 sas_address_parent, struct hba_port *port)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	int i;
89462306a36Sopenharmony_ci	unsigned long flags;
89562306a36Sopenharmony_ci	struct _sas_port *mpt3sas_port, *next;
89662306a36Sopenharmony_ci	struct _sas_node *sas_node;
89762306a36Sopenharmony_ci	u8 found = 0;
89862306a36Sopenharmony_ci	struct _sas_phy *mpt3sas_phy, *next_phy;
89962306a36Sopenharmony_ci	struct hba_port *hba_port_next, *hba_port = NULL;
90062306a36Sopenharmony_ci	struct virtual_phy *vphy, *vphy_next = NULL;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (!port)
90362306a36Sopenharmony_ci		return;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
90662306a36Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc,
90762306a36Sopenharmony_ci	    sas_address_parent, port);
90862306a36Sopenharmony_ci	if (!sas_node) {
90962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
91062306a36Sopenharmony_ci		return;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
91362306a36Sopenharmony_ci	    port_list) {
91462306a36Sopenharmony_ci		if (mpt3sas_port->remote_identify.sas_address != sas_address)
91562306a36Sopenharmony_ci			continue;
91662306a36Sopenharmony_ci		if (mpt3sas_port->hba_port != port)
91762306a36Sopenharmony_ci			continue;
91862306a36Sopenharmony_ci		found = 1;
91962306a36Sopenharmony_ci		list_del(&mpt3sas_port->port_list);
92062306a36Sopenharmony_ci		goto out;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci out:
92362306a36Sopenharmony_ci	if (!found) {
92462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
92562306a36Sopenharmony_ci		return;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (sas_node->handle <= ioc->sas_hba.num_phys &&
92962306a36Sopenharmony_ci	    (ioc->multipath_on_hba)) {
93062306a36Sopenharmony_ci		if (port->vphys_mask) {
93162306a36Sopenharmony_ci			list_for_each_entry_safe(vphy, vphy_next,
93262306a36Sopenharmony_ci			    &port->vphys_list, list) {
93362306a36Sopenharmony_ci				if (vphy->sas_address != sas_address)
93462306a36Sopenharmony_ci					continue;
93562306a36Sopenharmony_ci				ioc_info(ioc,
93662306a36Sopenharmony_ci				    "remove vphy entry: %p of port:%p,from %d port's vphys list\n",
93762306a36Sopenharmony_ci				    vphy, port, port->port_id);
93862306a36Sopenharmony_ci				port->vphys_mask &= ~vphy->phy_mask;
93962306a36Sopenharmony_ci				list_del(&vphy->list);
94062306a36Sopenharmony_ci				kfree(vphy);
94162306a36Sopenharmony_ci			}
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		list_for_each_entry_safe(hba_port, hba_port_next,
94562306a36Sopenharmony_ci		    &ioc->port_table_list, list) {
94662306a36Sopenharmony_ci			if (hba_port != port)
94762306a36Sopenharmony_ci				continue;
94862306a36Sopenharmony_ci			/*
94962306a36Sopenharmony_ci			 * Delete hba_port object if
95062306a36Sopenharmony_ci			 *  - hba_port object's sas address matches with current
95162306a36Sopenharmony_ci			 *    removed device's sas address and no vphy's
95262306a36Sopenharmony_ci			 *    associated with it.
95362306a36Sopenharmony_ci			 *  - Current removed device is a vSES device and
95462306a36Sopenharmony_ci			 *    none of the other direct attached device have
95562306a36Sopenharmony_ci			 *    this vSES device's port number (hence hba_port
95662306a36Sopenharmony_ci			 *    object sas_address field will be zero).
95762306a36Sopenharmony_ci			 */
95862306a36Sopenharmony_ci			if ((hba_port->sas_address == sas_address ||
95962306a36Sopenharmony_ci			    !hba_port->sas_address) && !hba_port->vphys_mask) {
96062306a36Sopenharmony_ci				ioc_info(ioc,
96162306a36Sopenharmony_ci				    "remove hba_port entry: %p port: %d from hba_port list\n",
96262306a36Sopenharmony_ci				    hba_port, hba_port->port_id);
96362306a36Sopenharmony_ci				list_del(&hba_port->list);
96462306a36Sopenharmony_ci				kfree(hba_port);
96562306a36Sopenharmony_ci			} else if (hba_port->sas_address == sas_address &&
96662306a36Sopenharmony_ci			    hba_port->vphys_mask) {
96762306a36Sopenharmony_ci				/*
96862306a36Sopenharmony_ci				 * Current removed device is a non vSES device
96962306a36Sopenharmony_ci				 * and a vSES device has the same port number
97062306a36Sopenharmony_ci				 * as of current device's port number. Hence
97162306a36Sopenharmony_ci				 * only clear the sas_address filed, don't
97262306a36Sopenharmony_ci				 * delete the hba_port object.
97362306a36Sopenharmony_ci				 */
97462306a36Sopenharmony_ci				ioc_info(ioc,
97562306a36Sopenharmony_ci				    "clearing sas_address from hba_port entry: %p port: %d from hba_port list\n",
97662306a36Sopenharmony_ci				    hba_port, hba_port->port_id);
97762306a36Sopenharmony_ci				port->sas_address = 0;
97862306a36Sopenharmony_ci			}
97962306a36Sopenharmony_ci			break;
98062306a36Sopenharmony_ci		}
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
98462306a36Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address == sas_address)
98562306a36Sopenharmony_ci			memset(&sas_node->phy[i].remote_identify, 0 ,
98662306a36Sopenharmony_ci			    sizeof(struct sas_identify));
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_phy, next_phy,
99262306a36Sopenharmony_ci	    &mpt3sas_port->phy_list, port_siblings) {
99362306a36Sopenharmony_ci		if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
99462306a36Sopenharmony_ci			dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
99562306a36Sopenharmony_ci			    "remove: sas_addr(0x%016llx), phy(%d)\n",
99662306a36Sopenharmony_ci			    (unsigned long long)
99762306a36Sopenharmony_ci			    mpt3sas_port->remote_identify.sas_address,
99862306a36Sopenharmony_ci			    mpt3sas_phy->phy_id);
99962306a36Sopenharmony_ci		mpt3sas_phy->phy_belongs_to_port = 0;
100062306a36Sopenharmony_ci		if (!ioc->remove_host)
100162306a36Sopenharmony_ci			sas_port_delete_phy(mpt3sas_port->port,
100262306a36Sopenharmony_ci						mpt3sas_phy->phy);
100362306a36Sopenharmony_ci		list_del(&mpt3sas_phy->port_siblings);
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	if (!ioc->remove_host)
100662306a36Sopenharmony_ci		sas_port_delete(mpt3sas_port->port);
100762306a36Sopenharmony_ci	ioc_info(ioc, "%s: removed: sas_addr(0x%016llx)\n",
100862306a36Sopenharmony_ci	    __func__, (unsigned long long)sas_address);
100962306a36Sopenharmony_ci	kfree(mpt3sas_port);
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci/**
101362306a36Sopenharmony_ci * mpt3sas_transport_add_host_phy - report sas_host phy to transport
101462306a36Sopenharmony_ci * @ioc: per adapter object
101562306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
101662306a36Sopenharmony_ci * @phy_pg0: sas phy page 0
101762306a36Sopenharmony_ci * @parent_dev: parent device class object
101862306a36Sopenharmony_ci *
101962306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_ciint
102262306a36Sopenharmony_cimpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
102362306a36Sopenharmony_ci	*mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct sas_phy *phy;
102662306a36Sopenharmony_ci	int phy_index = mpt3sas_phy->phy_id;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
103062306a36Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
103162306a36Sopenharmony_ci	if (!phy) {
103262306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
103362306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
103462306a36Sopenharmony_ci		return -1;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci	if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
103762306a36Sopenharmony_ci	    &mpt3sas_phy->identify))) {
103862306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
103962306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
104062306a36Sopenharmony_ci		sas_phy_free(phy);
104162306a36Sopenharmony_ci		return -1;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	phy->identify = mpt3sas_phy->identify;
104462306a36Sopenharmony_ci	mpt3sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
104562306a36Sopenharmony_ci	if (mpt3sas_phy->attached_handle)
104662306a36Sopenharmony_ci		_transport_set_identify(ioc, mpt3sas_phy->attached_handle,
104762306a36Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
104862306a36Sopenharmony_ci	phy->identify.phy_identifier = mpt3sas_phy->phy_id;
104962306a36Sopenharmony_ci	phy->negotiated_linkrate = _transport_convert_phy_link_rate(
105062306a36Sopenharmony_ci	    phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
105162306a36Sopenharmony_ci	phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
105262306a36Sopenharmony_ci	    phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
105362306a36Sopenharmony_ci	phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
105462306a36Sopenharmony_ci	    phy_pg0.HwLinkRate >> 4);
105562306a36Sopenharmony_ci	phy->minimum_linkrate = _transport_convert_phy_link_rate(
105662306a36Sopenharmony_ci	    phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
105762306a36Sopenharmony_ci	phy->maximum_linkrate = _transport_convert_phy_link_rate(
105862306a36Sopenharmony_ci	    phy_pg0.ProgrammedLinkRate >> 4);
105962306a36Sopenharmony_ci	phy->hostdata = mpt3sas_phy->port;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if ((sas_phy_add(phy))) {
106262306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
106362306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
106462306a36Sopenharmony_ci		sas_phy_free(phy);
106562306a36Sopenharmony_ci		return -1;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
106862306a36Sopenharmony_ci		dev_printk(KERN_INFO, &phy->dev,
106962306a36Sopenharmony_ci		    "add: handle(0x%04x), sas_addr(0x%016llx)\n"
107062306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
107162306a36Sopenharmony_ci		    mpt3sas_phy->handle, (unsigned long long)
107262306a36Sopenharmony_ci		    mpt3sas_phy->identify.sas_address,
107362306a36Sopenharmony_ci		    mpt3sas_phy->attached_handle,
107462306a36Sopenharmony_ci		    (unsigned long long)
107562306a36Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
107662306a36Sopenharmony_ci	mpt3sas_phy->phy = phy;
107762306a36Sopenharmony_ci	return 0;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci/**
108262306a36Sopenharmony_ci * mpt3sas_transport_add_expander_phy - report expander phy to transport
108362306a36Sopenharmony_ci * @ioc: per adapter object
108462306a36Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
108562306a36Sopenharmony_ci * @expander_pg1: expander page 1
108662306a36Sopenharmony_ci * @parent_dev: parent device class object
108762306a36Sopenharmony_ci *
108862306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
108962306a36Sopenharmony_ci */
109062306a36Sopenharmony_ciint
109162306a36Sopenharmony_cimpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
109262306a36Sopenharmony_ci	*mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
109362306a36Sopenharmony_ci	struct device *parent_dev)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct sas_phy *phy;
109662306a36Sopenharmony_ci	int phy_index = mpt3sas_phy->phy_id;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
109962306a36Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
110062306a36Sopenharmony_ci	if (!phy) {
110162306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
110262306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
110362306a36Sopenharmony_ci		return -1;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci	if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
110662306a36Sopenharmony_ci	    &mpt3sas_phy->identify))) {
110762306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
110862306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
110962306a36Sopenharmony_ci		sas_phy_free(phy);
111062306a36Sopenharmony_ci		return -1;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci	phy->identify = mpt3sas_phy->identify;
111362306a36Sopenharmony_ci	mpt3sas_phy->attached_handle =
111462306a36Sopenharmony_ci	    le16_to_cpu(expander_pg1.AttachedDevHandle);
111562306a36Sopenharmony_ci	if (mpt3sas_phy->attached_handle)
111662306a36Sopenharmony_ci		_transport_set_identify(ioc, mpt3sas_phy->attached_handle,
111762306a36Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
111862306a36Sopenharmony_ci	phy->identify.phy_identifier = mpt3sas_phy->phy_id;
111962306a36Sopenharmony_ci	phy->negotiated_linkrate = _transport_convert_phy_link_rate(
112062306a36Sopenharmony_ci	    expander_pg1.NegotiatedLinkRate &
112162306a36Sopenharmony_ci	    MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
112262306a36Sopenharmony_ci	phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
112362306a36Sopenharmony_ci	    expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
112462306a36Sopenharmony_ci	phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
112562306a36Sopenharmony_ci	    expander_pg1.HwLinkRate >> 4);
112662306a36Sopenharmony_ci	phy->minimum_linkrate = _transport_convert_phy_link_rate(
112762306a36Sopenharmony_ci	    expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
112862306a36Sopenharmony_ci	phy->maximum_linkrate = _transport_convert_phy_link_rate(
112962306a36Sopenharmony_ci	    expander_pg1.ProgrammedLinkRate >> 4);
113062306a36Sopenharmony_ci	phy->hostdata = mpt3sas_phy->port;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if ((sas_phy_add(phy))) {
113362306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
113462306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
113562306a36Sopenharmony_ci		sas_phy_free(phy);
113662306a36Sopenharmony_ci		return -1;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
113962306a36Sopenharmony_ci		dev_printk(KERN_INFO, &phy->dev,
114062306a36Sopenharmony_ci		    "add: handle(0x%04x), sas_addr(0x%016llx)\n"
114162306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
114262306a36Sopenharmony_ci		    mpt3sas_phy->handle, (unsigned long long)
114362306a36Sopenharmony_ci		    mpt3sas_phy->identify.sas_address,
114462306a36Sopenharmony_ci		    mpt3sas_phy->attached_handle,
114562306a36Sopenharmony_ci		    (unsigned long long)
114662306a36Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
114762306a36Sopenharmony_ci	mpt3sas_phy->phy = phy;
114862306a36Sopenharmony_ci	return 0;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci/**
115262306a36Sopenharmony_ci * mpt3sas_transport_update_links - refreshing phy link changes
115362306a36Sopenharmony_ci * @ioc: per adapter object
115462306a36Sopenharmony_ci * @sas_address: sas address of parent expander or sas host
115562306a36Sopenharmony_ci * @handle: attached device handle
115662306a36Sopenharmony_ci * @phy_number: phy number
115762306a36Sopenharmony_ci * @link_rate: new link rate
115862306a36Sopenharmony_ci * @port: hba port entry
115962306a36Sopenharmony_ci *
116062306a36Sopenharmony_ci * Return nothing.
116162306a36Sopenharmony_ci */
116262306a36Sopenharmony_civoid
116362306a36Sopenharmony_cimpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
116462306a36Sopenharmony_ci	u64 sas_address, u16 handle, u8 phy_number, u8 link_rate,
116562306a36Sopenharmony_ci	struct hba_port *port)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	unsigned long flags;
116862306a36Sopenharmony_ci	struct _sas_node *sas_node;
116962306a36Sopenharmony_ci	struct _sas_phy *mpt3sas_phy;
117062306a36Sopenharmony_ci	struct hba_port *hba_port = NULL;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery)
117362306a36Sopenharmony_ci		return;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
117662306a36Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc,
117762306a36Sopenharmony_ci	    sas_address, port);
117862306a36Sopenharmony_ci	if (!sas_node) {
117962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
118062306a36Sopenharmony_ci		return;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	mpt3sas_phy = &sas_node->phy[phy_number];
118462306a36Sopenharmony_ci	mpt3sas_phy->attached_handle = handle;
118562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
118662306a36Sopenharmony_ci	if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
118762306a36Sopenharmony_ci		_transport_set_identify(ioc, handle,
118862306a36Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
118962306a36Sopenharmony_ci		if ((sas_node->handle <= ioc->sas_hba.num_phys) &&
119062306a36Sopenharmony_ci		    (ioc->multipath_on_hba)) {
119162306a36Sopenharmony_ci			list_for_each_entry(hba_port,
119262306a36Sopenharmony_ci			    &ioc->port_table_list, list) {
119362306a36Sopenharmony_ci				if (hba_port->sas_address == sas_address &&
119462306a36Sopenharmony_ci				    hba_port == port)
119562306a36Sopenharmony_ci					hba_port->phy_mask |=
119662306a36Sopenharmony_ci					    (1 << mpt3sas_phy->phy_id);
119762306a36Sopenharmony_ci			}
119862306a36Sopenharmony_ci		}
119962306a36Sopenharmony_ci		mpt3sas_transport_add_phy_to_an_existing_port(ioc, sas_node,
120062306a36Sopenharmony_ci		    mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address,
120162306a36Sopenharmony_ci		    port);
120262306a36Sopenharmony_ci	} else
120362306a36Sopenharmony_ci		memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct
120462306a36Sopenharmony_ci		    sas_identify));
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (mpt3sas_phy->phy)
120762306a36Sopenharmony_ci		mpt3sas_phy->phy->negotiated_linkrate =
120862306a36Sopenharmony_ci		    _transport_convert_phy_link_rate(link_rate);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
121162306a36Sopenharmony_ci		dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
121262306a36Sopenharmony_ci		    "refresh: parent sas_addr(0x%016llx),\n"
121362306a36Sopenharmony_ci		    "\tlink_rate(0x%02x), phy(%d)\n"
121462306a36Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
121562306a36Sopenharmony_ci		    (unsigned long long)sas_address,
121662306a36Sopenharmony_ci		    link_rate, phy_number, handle, (unsigned long long)
121762306a36Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic inline void *
122162306a36Sopenharmony_ciphy_to_ioc(struct sas_phy *phy)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
122462306a36Sopenharmony_ci	return shost_priv(shost);
122562306a36Sopenharmony_ci}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic inline void *
122862306a36Sopenharmony_cirphy_to_ioc(struct sas_rphy *rphy)
122962306a36Sopenharmony_ci{
123062306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
123162306a36Sopenharmony_ci	return shost_priv(shost);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci/* report phy error log structure */
123562306a36Sopenharmony_cistruct phy_error_log_request {
123662306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
123762306a36Sopenharmony_ci	u8 function; /* 0x11 */
123862306a36Sopenharmony_ci	u8 allocated_response_length;
123962306a36Sopenharmony_ci	u8 request_length; /* 02 */
124062306a36Sopenharmony_ci	u8 reserved_1[5];
124162306a36Sopenharmony_ci	u8 phy_identifier;
124262306a36Sopenharmony_ci	u8 reserved_2[2];
124362306a36Sopenharmony_ci};
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci/* report phy error log reply structure */
124662306a36Sopenharmony_cistruct phy_error_log_reply {
124762306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
124862306a36Sopenharmony_ci	u8 function; /* 0x11 */
124962306a36Sopenharmony_ci	u8 function_result;
125062306a36Sopenharmony_ci	u8 response_length;
125162306a36Sopenharmony_ci	__be16 expander_change_count;
125262306a36Sopenharmony_ci	u8 reserved_1[3];
125362306a36Sopenharmony_ci	u8 phy_identifier;
125462306a36Sopenharmony_ci	u8 reserved_2[2];
125562306a36Sopenharmony_ci	__be32 invalid_dword;
125662306a36Sopenharmony_ci	__be32 running_disparity_error;
125762306a36Sopenharmony_ci	__be32 loss_of_dword_sync;
125862306a36Sopenharmony_ci	__be32 phy_reset_problem;
125962306a36Sopenharmony_ci};
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci/**
126262306a36Sopenharmony_ci * _transport_get_expander_phy_error_log - return expander counters
126362306a36Sopenharmony_ci * @ioc: per adapter object
126462306a36Sopenharmony_ci * @phy: The sas phy object
126562306a36Sopenharmony_ci *
126662306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci */
126962306a36Sopenharmony_cistatic int
127062306a36Sopenharmony_ci_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc,
127162306a36Sopenharmony_ci	struct sas_phy *phy)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
127462306a36Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
127562306a36Sopenharmony_ci	struct phy_error_log_request *phy_error_log_request;
127662306a36Sopenharmony_ci	struct phy_error_log_reply *phy_error_log_reply;
127762306a36Sopenharmony_ci	int rc;
127862306a36Sopenharmony_ci	u16 smid;
127962306a36Sopenharmony_ci	void *psge;
128062306a36Sopenharmony_ci	u8 issue_reset = 0;
128162306a36Sopenharmony_ci	void *data_out = NULL;
128262306a36Sopenharmony_ci	dma_addr_t data_out_dma;
128362306a36Sopenharmony_ci	u32 sz;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
128662306a36Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
128762306a36Sopenharmony_ci		return -EFAULT;
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
129362306a36Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
129462306a36Sopenharmony_ci		rc = -EAGAIN;
129562306a36Sopenharmony_ci		goto out;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
130062306a36Sopenharmony_ci	if (rc)
130162306a36Sopenharmony_ci		goto out;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
130462306a36Sopenharmony_ci	if (!smid) {
130562306a36Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
130662306a36Sopenharmony_ci		rc = -EAGAIN;
130762306a36Sopenharmony_ci		goto out;
130862306a36Sopenharmony_ci	}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
131162306a36Sopenharmony_ci	ioc->transport_cmds.smid = smid;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	sz = sizeof(struct phy_error_log_request) +
131462306a36Sopenharmony_ci	    sizeof(struct phy_error_log_reply);
131562306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
131662306a36Sopenharmony_ci			GFP_KERNEL);
131762306a36Sopenharmony_ci	if (!data_out) {
131862306a36Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
131962306a36Sopenharmony_ci		    __LINE__, __func__);
132062306a36Sopenharmony_ci		rc = -ENOMEM;
132162306a36Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
132262306a36Sopenharmony_ci		goto out;
132362306a36Sopenharmony_ci	}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	rc = -EINVAL;
132662306a36Sopenharmony_ci	memset(data_out, 0, sz);
132762306a36Sopenharmony_ci	phy_error_log_request = data_out;
132862306a36Sopenharmony_ci	phy_error_log_request->smp_frame_type = 0x40;
132962306a36Sopenharmony_ci	phy_error_log_request->function = 0x11;
133062306a36Sopenharmony_ci	phy_error_log_request->request_length = 2;
133162306a36Sopenharmony_ci	phy_error_log_request->allocated_response_length = 0;
133262306a36Sopenharmony_ci	phy_error_log_request->phy_identifier = phy->number;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
133562306a36Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
133662306a36Sopenharmony_ci	mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy);
133762306a36Sopenharmony_ci	mpi_request->VF_ID = 0; /* TODO */
133862306a36Sopenharmony_ci	mpi_request->VP_ID = 0;
133962306a36Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
134062306a36Sopenharmony_ci	mpi_request->RequestDataLength =
134162306a36Sopenharmony_ci	    cpu_to_le16(sizeof(struct phy_error_log_request));
134262306a36Sopenharmony_ci	psge = &mpi_request->SGL;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma,
134562306a36Sopenharmony_ci		sizeof(struct phy_error_log_request),
134662306a36Sopenharmony_ci	    data_out_dma + sizeof(struct phy_error_log_request),
134762306a36Sopenharmony_ci	    sizeof(struct phy_error_log_reply));
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	dtransportprintk(ioc,
135062306a36Sopenharmony_ci			 ioc_info(ioc, "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n",
135162306a36Sopenharmony_ci				  (u64)phy->identify.sas_address,
135262306a36Sopenharmony_ci				  phy->number));
135362306a36Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
135462306a36Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
135562306a36Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
135862306a36Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
135962306a36Sopenharmony_ci		_debug_dump_mf(mpi_request,
136062306a36Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
136162306a36Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
136262306a36Sopenharmony_ci			issue_reset = 1;
136362306a36Sopenharmony_ci		goto issue_host_reset;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "phy_error_log - complete\n"));
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci		dtransportprintk(ioc,
137362306a36Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - reply data transfer size(%d)\n",
137462306a36Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
137762306a36Sopenharmony_ci		    sizeof(struct phy_error_log_reply))
137862306a36Sopenharmony_ci			goto out;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		phy_error_log_reply = data_out +
138162306a36Sopenharmony_ci		    sizeof(struct phy_error_log_request);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci		dtransportprintk(ioc,
138462306a36Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - function_result(%d)\n",
138562306a36Sopenharmony_ci					  phy_error_log_reply->function_result));
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		phy->invalid_dword_count =
138862306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->invalid_dword);
138962306a36Sopenharmony_ci		phy->running_disparity_error_count =
139062306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->running_disparity_error);
139162306a36Sopenharmony_ci		phy->loss_of_dword_sync_count =
139262306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
139362306a36Sopenharmony_ci		phy->phy_reset_problem_count =
139462306a36Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->phy_reset_problem);
139562306a36Sopenharmony_ci		rc = 0;
139662306a36Sopenharmony_ci	} else
139762306a36Sopenharmony_ci		dtransportprintk(ioc,
139862306a36Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - no reply\n"));
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci issue_host_reset:
140162306a36Sopenharmony_ci	if (issue_reset)
140262306a36Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
140362306a36Sopenharmony_ci out:
140462306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
140562306a36Sopenharmony_ci	if (data_out)
140662306a36Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, sz, data_out, data_out_dma);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
140962306a36Sopenharmony_ci	return rc;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci/**
141362306a36Sopenharmony_ci * _transport_get_linkerrors - return phy counters for both hba and expanders
141462306a36Sopenharmony_ci * @phy: The sas phy object
141562306a36Sopenharmony_ci *
141662306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
141762306a36Sopenharmony_ci *
141862306a36Sopenharmony_ci */
141962306a36Sopenharmony_cistatic int
142062306a36Sopenharmony_ci_transport_get_linkerrors(struct sas_phy *phy)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
142362306a36Sopenharmony_ci	unsigned long flags;
142462306a36Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
142562306a36Sopenharmony_ci	Mpi2SasPhyPage1_t phy_pg1;
142662306a36Sopenharmony_ci	struct hba_port *port = phy->hostdata;
142762306a36Sopenharmony_ci	int port_id = port->port_id;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
143062306a36Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
143162306a36Sopenharmony_ci	    phy->identify.sas_address,
143262306a36Sopenharmony_ci	    mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
143362306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
143462306a36Sopenharmony_ci		return -EINVAL;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
143962306a36Sopenharmony_ci		return _transport_get_expander_phy_error_log(ioc, phy);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* get hba phy error logs */
144262306a36Sopenharmony_ci	if ((mpt3sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
144362306a36Sopenharmony_ci		    phy->number))) {
144462306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
144562306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
144662306a36Sopenharmony_ci		return -ENXIO;
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
145062306a36Sopenharmony_ci		ioc_info(ioc, "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n",
145162306a36Sopenharmony_ci			 phy->number,
145262306a36Sopenharmony_ci			 le16_to_cpu(mpi_reply.IOCStatus),
145362306a36Sopenharmony_ci			 le32_to_cpu(mpi_reply.IOCLogInfo));
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
145662306a36Sopenharmony_ci	phy->running_disparity_error_count =
145762306a36Sopenharmony_ci	    le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
145862306a36Sopenharmony_ci	phy->loss_of_dword_sync_count =
145962306a36Sopenharmony_ci	    le32_to_cpu(phy_pg1.LossDwordSynchCount);
146062306a36Sopenharmony_ci	phy->phy_reset_problem_count =
146162306a36Sopenharmony_ci	    le32_to_cpu(phy_pg1.PhyResetProblemCount);
146262306a36Sopenharmony_ci	return 0;
146362306a36Sopenharmony_ci}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci/**
146662306a36Sopenharmony_ci * _transport_get_enclosure_identifier -
146762306a36Sopenharmony_ci * @rphy: The sas phy object
146862306a36Sopenharmony_ci * @identifier: ?
146962306a36Sopenharmony_ci *
147062306a36Sopenharmony_ci * Obtain the enclosure logical id for an expander.
147162306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
147262306a36Sopenharmony_ci */
147362306a36Sopenharmony_cistatic int
147462306a36Sopenharmony_ci_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
147762306a36Sopenharmony_ci	struct _sas_device *sas_device;
147862306a36Sopenharmony_ci	unsigned long flags;
147962306a36Sopenharmony_ci	int rc;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_device_lock, flags);
148262306a36Sopenharmony_ci	sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
148362306a36Sopenharmony_ci	if (sas_device) {
148462306a36Sopenharmony_ci		*identifier = sas_device->enclosure_logical_id;
148562306a36Sopenharmony_ci		rc = 0;
148662306a36Sopenharmony_ci		sas_device_put(sas_device);
148762306a36Sopenharmony_ci	} else {
148862306a36Sopenharmony_ci		*identifier = 0;
148962306a36Sopenharmony_ci		rc = -ENXIO;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
149362306a36Sopenharmony_ci	return rc;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci/**
149762306a36Sopenharmony_ci * _transport_get_bay_identifier -
149862306a36Sopenharmony_ci * @rphy: The sas phy object
149962306a36Sopenharmony_ci *
150062306a36Sopenharmony_ci * Return: the slot id for a device that resides inside an enclosure.
150162306a36Sopenharmony_ci */
150262306a36Sopenharmony_cistatic int
150362306a36Sopenharmony_ci_transport_get_bay_identifier(struct sas_rphy *rphy)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
150662306a36Sopenharmony_ci	struct _sas_device *sas_device;
150762306a36Sopenharmony_ci	unsigned long flags;
150862306a36Sopenharmony_ci	int rc;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_device_lock, flags);
151162306a36Sopenharmony_ci	sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy);
151262306a36Sopenharmony_ci	if (sas_device) {
151362306a36Sopenharmony_ci		rc = sas_device->slot;
151462306a36Sopenharmony_ci		sas_device_put(sas_device);
151562306a36Sopenharmony_ci	} else {
151662306a36Sopenharmony_ci		rc = -ENXIO;
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
151962306a36Sopenharmony_ci	return rc;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci/* phy control request structure */
152362306a36Sopenharmony_cistruct phy_control_request {
152462306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
152562306a36Sopenharmony_ci	u8 function; /* 0x91 */
152662306a36Sopenharmony_ci	u8 allocated_response_length;
152762306a36Sopenharmony_ci	u8 request_length; /* 0x09 */
152862306a36Sopenharmony_ci	u16 expander_change_count;
152962306a36Sopenharmony_ci	u8 reserved_1[3];
153062306a36Sopenharmony_ci	u8 phy_identifier;
153162306a36Sopenharmony_ci	u8 phy_operation;
153262306a36Sopenharmony_ci	u8 reserved_2[13];
153362306a36Sopenharmony_ci	u64 attached_device_name;
153462306a36Sopenharmony_ci	u8 programmed_min_physical_link_rate;
153562306a36Sopenharmony_ci	u8 programmed_max_physical_link_rate;
153662306a36Sopenharmony_ci	u8 reserved_3[6];
153762306a36Sopenharmony_ci};
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci/* phy control reply structure */
154062306a36Sopenharmony_cistruct phy_control_reply {
154162306a36Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
154262306a36Sopenharmony_ci	u8 function; /* 0x11 */
154362306a36Sopenharmony_ci	u8 function_result;
154462306a36Sopenharmony_ci	u8 response_length;
154562306a36Sopenharmony_ci};
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci#define SMP_PHY_CONTROL_LINK_RESET	(0x01)
154862306a36Sopenharmony_ci#define SMP_PHY_CONTROL_HARD_RESET	(0x02)
154962306a36Sopenharmony_ci#define SMP_PHY_CONTROL_DISABLE		(0x03)
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci/**
155262306a36Sopenharmony_ci * _transport_expander_phy_control - expander phy control
155362306a36Sopenharmony_ci * @ioc: per adapter object
155462306a36Sopenharmony_ci * @phy: The sas phy object
155562306a36Sopenharmony_ci * @phy_operation: ?
155662306a36Sopenharmony_ci *
155762306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
155862306a36Sopenharmony_ci *
155962306a36Sopenharmony_ci */
156062306a36Sopenharmony_cistatic int
156162306a36Sopenharmony_ci_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc,
156262306a36Sopenharmony_ci	struct sas_phy *phy, u8 phy_operation)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
156562306a36Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
156662306a36Sopenharmony_ci	struct phy_control_request *phy_control_request;
156762306a36Sopenharmony_ci	struct phy_control_reply *phy_control_reply;
156862306a36Sopenharmony_ci	int rc;
156962306a36Sopenharmony_ci	u16 smid;
157062306a36Sopenharmony_ci	void *psge;
157162306a36Sopenharmony_ci	u8 issue_reset = 0;
157262306a36Sopenharmony_ci	void *data_out = NULL;
157362306a36Sopenharmony_ci	dma_addr_t data_out_dma;
157462306a36Sopenharmony_ci	u32 sz;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
157762306a36Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
157862306a36Sopenharmony_ci		return -EFAULT;
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
158462306a36Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
158562306a36Sopenharmony_ci		rc = -EAGAIN;
158662306a36Sopenharmony_ci		goto out;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
159162306a36Sopenharmony_ci	if (rc)
159262306a36Sopenharmony_ci		goto out;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
159562306a36Sopenharmony_ci	if (!smid) {
159662306a36Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
159762306a36Sopenharmony_ci		rc = -EAGAIN;
159862306a36Sopenharmony_ci		goto out;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
160262306a36Sopenharmony_ci	ioc->transport_cmds.smid = smid;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	sz = sizeof(struct phy_control_request) +
160562306a36Sopenharmony_ci	    sizeof(struct phy_control_reply);
160662306a36Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
160762306a36Sopenharmony_ci			GFP_KERNEL);
160862306a36Sopenharmony_ci	if (!data_out) {
160962306a36Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
161062306a36Sopenharmony_ci		    __LINE__, __func__);
161162306a36Sopenharmony_ci		rc = -ENOMEM;
161262306a36Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
161362306a36Sopenharmony_ci		goto out;
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	rc = -EINVAL;
161762306a36Sopenharmony_ci	memset(data_out, 0, sz);
161862306a36Sopenharmony_ci	phy_control_request = data_out;
161962306a36Sopenharmony_ci	phy_control_request->smp_frame_type = 0x40;
162062306a36Sopenharmony_ci	phy_control_request->function = 0x91;
162162306a36Sopenharmony_ci	phy_control_request->request_length = 9;
162262306a36Sopenharmony_ci	phy_control_request->allocated_response_length = 0;
162362306a36Sopenharmony_ci	phy_control_request->phy_identifier = phy->number;
162462306a36Sopenharmony_ci	phy_control_request->phy_operation = phy_operation;
162562306a36Sopenharmony_ci	phy_control_request->programmed_min_physical_link_rate =
162662306a36Sopenharmony_ci	    phy->minimum_linkrate << 4;
162762306a36Sopenharmony_ci	phy_control_request->programmed_max_physical_link_rate =
162862306a36Sopenharmony_ci	    phy->maximum_linkrate << 4;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
163162306a36Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
163262306a36Sopenharmony_ci	mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy);
163362306a36Sopenharmony_ci	mpi_request->VF_ID = 0; /* TODO */
163462306a36Sopenharmony_ci	mpi_request->VP_ID = 0;
163562306a36Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
163662306a36Sopenharmony_ci	mpi_request->RequestDataLength =
163762306a36Sopenharmony_ci	    cpu_to_le16(sizeof(struct phy_error_log_request));
163862306a36Sopenharmony_ci	psge = &mpi_request->SGL;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma,
164162306a36Sopenharmony_ci			    sizeof(struct phy_control_request),
164262306a36Sopenharmony_ci	    data_out_dma + sizeof(struct phy_control_request),
164362306a36Sopenharmony_ci	    sizeof(struct phy_control_reply));
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	dtransportprintk(ioc,
164662306a36Sopenharmony_ci			 ioc_info(ioc, "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n",
164762306a36Sopenharmony_ci				  (u64)phy->identify.sas_address,
164862306a36Sopenharmony_ci				  phy->number, phy_operation));
164962306a36Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
165062306a36Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
165162306a36Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
165462306a36Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
165562306a36Sopenharmony_ci		_debug_dump_mf(mpi_request,
165662306a36Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
165762306a36Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
165862306a36Sopenharmony_ci			issue_reset = 1;
165962306a36Sopenharmony_ci		goto issue_host_reset;
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "phy_control - complete\n"));
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci		dtransportprintk(ioc,
166962306a36Sopenharmony_ci				 ioc_info(ioc, "phy_control - reply data transfer size(%d)\n",
167062306a36Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
167362306a36Sopenharmony_ci		    sizeof(struct phy_control_reply))
167462306a36Sopenharmony_ci			goto out;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		phy_control_reply = data_out +
167762306a36Sopenharmony_ci		    sizeof(struct phy_control_request);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci		dtransportprintk(ioc,
168062306a36Sopenharmony_ci				 ioc_info(ioc, "phy_control - function_result(%d)\n",
168162306a36Sopenharmony_ci					  phy_control_reply->function_result));
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci		rc = 0;
168462306a36Sopenharmony_ci	} else
168562306a36Sopenharmony_ci		dtransportprintk(ioc,
168662306a36Sopenharmony_ci				 ioc_info(ioc, "phy_control - no reply\n"));
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci issue_host_reset:
168962306a36Sopenharmony_ci	if (issue_reset)
169062306a36Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
169162306a36Sopenharmony_ci out:
169262306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
169362306a36Sopenharmony_ci	if (data_out)
169462306a36Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, sz, data_out,
169562306a36Sopenharmony_ci				data_out_dma);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
169862306a36Sopenharmony_ci	return rc;
169962306a36Sopenharmony_ci}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci/**
170262306a36Sopenharmony_ci * _transport_phy_reset -
170362306a36Sopenharmony_ci * @phy: The sas phy object
170462306a36Sopenharmony_ci * @hard_reset:
170562306a36Sopenharmony_ci *
170662306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
170762306a36Sopenharmony_ci */
170862306a36Sopenharmony_cistatic int
170962306a36Sopenharmony_ci_transport_phy_reset(struct sas_phy *phy, int hard_reset)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
171262306a36Sopenharmony_ci	Mpi2SasIoUnitControlReply_t mpi_reply;
171362306a36Sopenharmony_ci	Mpi2SasIoUnitControlRequest_t mpi_request;
171462306a36Sopenharmony_ci	struct hba_port *port = phy->hostdata;
171562306a36Sopenharmony_ci	int port_id = port->port_id;
171662306a36Sopenharmony_ci	unsigned long flags;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
171962306a36Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
172062306a36Sopenharmony_ci	    phy->identify.sas_address,
172162306a36Sopenharmony_ci	    mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
172262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
172362306a36Sopenharmony_ci		return -EINVAL;
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	/* handle expander phys */
172862306a36Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
172962306a36Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
173062306a36Sopenharmony_ci		    (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
173162306a36Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	/* handle hba phys */
173462306a36Sopenharmony_ci	memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
173562306a36Sopenharmony_ci	mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
173662306a36Sopenharmony_ci	mpi_request.Operation = hard_reset ?
173762306a36Sopenharmony_ci	    MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
173862306a36Sopenharmony_ci	mpi_request.PhyNum = phy->number;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
174162306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
174262306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
174362306a36Sopenharmony_ci		return -ENXIO;
174462306a36Sopenharmony_ci	}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
174762306a36Sopenharmony_ci		ioc_info(ioc, "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
174862306a36Sopenharmony_ci			 phy->number, le16_to_cpu(mpi_reply.IOCStatus),
174962306a36Sopenharmony_ci			 le32_to_cpu(mpi_reply.IOCLogInfo));
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	return 0;
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci/**
175562306a36Sopenharmony_ci * _transport_phy_enable - enable/disable phys
175662306a36Sopenharmony_ci * @phy: The sas phy object
175762306a36Sopenharmony_ci * @enable: enable phy when true
175862306a36Sopenharmony_ci *
175962306a36Sopenharmony_ci * Only support sas_host direct attached phys.
176062306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
176162306a36Sopenharmony_ci */
176262306a36Sopenharmony_cistatic int
176362306a36Sopenharmony_ci_transport_phy_enable(struct sas_phy *phy, int enable)
176462306a36Sopenharmony_ci{
176562306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
176662306a36Sopenharmony_ci	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
176762306a36Sopenharmony_ci	Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
176862306a36Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
176962306a36Sopenharmony_ci	u16 ioc_status;
177062306a36Sopenharmony_ci	u16 sz;
177162306a36Sopenharmony_ci	int rc = 0;
177262306a36Sopenharmony_ci	unsigned long flags;
177362306a36Sopenharmony_ci	int i, discovery_active;
177462306a36Sopenharmony_ci	struct hba_port *port = phy->hostdata;
177562306a36Sopenharmony_ci	int port_id = port->port_id;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
177862306a36Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
177962306a36Sopenharmony_ci	    phy->identify.sas_address,
178062306a36Sopenharmony_ci	    mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
178162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
178262306a36Sopenharmony_ci		return -EINVAL;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* handle expander phys */
178762306a36Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
178862306a36Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
178962306a36Sopenharmony_ci		    (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
179062306a36Sopenharmony_ci		    SMP_PHY_CONTROL_DISABLE);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	/* handle hba phys */
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	/* read sas_iounit page 0 */
179562306a36Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
179662306a36Sopenharmony_ci	    sizeof(Mpi2SasIOUnit0PhyData_t));
179762306a36Sopenharmony_ci	sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
179862306a36Sopenharmony_ci	if (!sas_iounit_pg0) {
179962306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
180062306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
180162306a36Sopenharmony_ci		rc = -ENOMEM;
180262306a36Sopenharmony_ci		goto out;
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
180562306a36Sopenharmony_ci	    sas_iounit_pg0, sz))) {
180662306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
180762306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
180862306a36Sopenharmony_ci		rc = -ENXIO;
180962306a36Sopenharmony_ci		goto out;
181062306a36Sopenharmony_ci	}
181162306a36Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
181262306a36Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
181362306a36Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
181462306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
181562306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
181662306a36Sopenharmony_ci		rc = -EIO;
181762306a36Sopenharmony_ci		goto out;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* unable to enable/disable phys when when discovery is active */
182162306a36Sopenharmony_ci	for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) {
182262306a36Sopenharmony_ci		if (sas_iounit_pg0->PhyData[i].PortFlags &
182362306a36Sopenharmony_ci		    MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
182462306a36Sopenharmony_ci			ioc_err(ioc, "discovery is active on port = %d, phy = %d: unable to enable/disable phys, try again later!\n",
182562306a36Sopenharmony_ci				sas_iounit_pg0->PhyData[i].Port, i);
182662306a36Sopenharmony_ci			discovery_active = 1;
182762306a36Sopenharmony_ci		}
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (discovery_active) {
183162306a36Sopenharmony_ci		rc = -EAGAIN;
183262306a36Sopenharmony_ci		goto out;
183362306a36Sopenharmony_ci	}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	/* read sas_iounit page 1 */
183662306a36Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
183762306a36Sopenharmony_ci	    sizeof(Mpi2SasIOUnit1PhyData_t));
183862306a36Sopenharmony_ci	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
183962306a36Sopenharmony_ci	if (!sas_iounit_pg1) {
184062306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
184162306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
184262306a36Sopenharmony_ci		rc = -ENOMEM;
184362306a36Sopenharmony_ci		goto out;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
184662306a36Sopenharmony_ci	    sas_iounit_pg1, sz))) {
184762306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
184862306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
184962306a36Sopenharmony_ci		rc = -ENXIO;
185062306a36Sopenharmony_ci		goto out;
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
185362306a36Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
185462306a36Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
185562306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
185662306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
185762306a36Sopenharmony_ci		rc = -EIO;
185862306a36Sopenharmony_ci		goto out;
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	/* copy Port/PortFlags/PhyFlags from page 0 */
186262306a36Sopenharmony_ci	for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
186362306a36Sopenharmony_ci		sas_iounit_pg1->PhyData[i].Port =
186462306a36Sopenharmony_ci		    sas_iounit_pg0->PhyData[i].Port;
186562306a36Sopenharmony_ci		sas_iounit_pg1->PhyData[i].PortFlags =
186662306a36Sopenharmony_ci		    (sas_iounit_pg0->PhyData[i].PortFlags &
186762306a36Sopenharmony_ci		    MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG);
186862306a36Sopenharmony_ci		sas_iounit_pg1->PhyData[i].PhyFlags =
186962306a36Sopenharmony_ci		    (sas_iounit_pg0->PhyData[i].PhyFlags &
187062306a36Sopenharmony_ci		    (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED +
187162306a36Sopenharmony_ci		    MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED));
187262306a36Sopenharmony_ci	}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	if (enable)
187562306a36Sopenharmony_ci		sas_iounit_pg1->PhyData[phy->number].PhyFlags
187662306a36Sopenharmony_ci		    &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
187762306a36Sopenharmony_ci	else
187862306a36Sopenharmony_ci		sas_iounit_pg1->PhyData[phy->number].PhyFlags
187962306a36Sopenharmony_ci		    |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	/* link reset */
188462306a36Sopenharmony_ci	if (enable)
188562306a36Sopenharmony_ci		_transport_phy_reset(phy, 0);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci out:
188862306a36Sopenharmony_ci	kfree(sas_iounit_pg1);
188962306a36Sopenharmony_ci	kfree(sas_iounit_pg0);
189062306a36Sopenharmony_ci	return rc;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci/**
189462306a36Sopenharmony_ci * _transport_phy_speed - set phy min/max link rates
189562306a36Sopenharmony_ci * @phy: The sas phy object
189662306a36Sopenharmony_ci * @rates: rates defined in sas_phy_linkrates
189762306a36Sopenharmony_ci *
189862306a36Sopenharmony_ci * Only support sas_host direct attached phys.
189962306a36Sopenharmony_ci *
190062306a36Sopenharmony_ci * Return: 0 for success, non-zero for failure.
190162306a36Sopenharmony_ci */
190262306a36Sopenharmony_cistatic int
190362306a36Sopenharmony_ci_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
190662306a36Sopenharmony_ci	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
190762306a36Sopenharmony_ci	Mpi2SasPhyPage0_t phy_pg0;
190862306a36Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
190962306a36Sopenharmony_ci	u16 ioc_status;
191062306a36Sopenharmony_ci	u16 sz;
191162306a36Sopenharmony_ci	int i;
191262306a36Sopenharmony_ci	int rc = 0;
191362306a36Sopenharmony_ci	unsigned long flags;
191462306a36Sopenharmony_ci	struct hba_port *port = phy->hostdata;
191562306a36Sopenharmony_ci	int port_id = port->port_id;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
191862306a36Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
191962306a36Sopenharmony_ci	    phy->identify.sas_address,
192062306a36Sopenharmony_ci	    mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) {
192162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
192262306a36Sopenharmony_ci		return -EINVAL;
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	if (!rates->minimum_linkrate)
192762306a36Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate;
192862306a36Sopenharmony_ci	else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
192962306a36Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate_hw;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	if (!rates->maximum_linkrate)
193262306a36Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate;
193362306a36Sopenharmony_ci	else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
193462306a36Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate_hw;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	/* handle expander phys */
193762306a36Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address) {
193862306a36Sopenharmony_ci		phy->minimum_linkrate = rates->minimum_linkrate;
193962306a36Sopenharmony_ci		phy->maximum_linkrate = rates->maximum_linkrate;
194062306a36Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
194162306a36Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	/* handle hba phys */
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	/* sas_iounit page 1 */
194762306a36Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
194862306a36Sopenharmony_ci	    sizeof(Mpi2SasIOUnit1PhyData_t));
194962306a36Sopenharmony_ci	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
195062306a36Sopenharmony_ci	if (!sas_iounit_pg1) {
195162306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
195262306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
195362306a36Sopenharmony_ci		rc = -ENOMEM;
195462306a36Sopenharmony_ci		goto out;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
195762306a36Sopenharmony_ci	    sas_iounit_pg1, sz))) {
195862306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
195962306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
196062306a36Sopenharmony_ci		rc = -ENXIO;
196162306a36Sopenharmony_ci		goto out;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
196462306a36Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
196562306a36Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
196662306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
196762306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
196862306a36Sopenharmony_ci		rc = -EIO;
196962306a36Sopenharmony_ci		goto out;
197062306a36Sopenharmony_ci	}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	for (i = 0; i < ioc->sas_hba.num_phys; i++) {
197362306a36Sopenharmony_ci		if (phy->number != i) {
197462306a36Sopenharmony_ci			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
197562306a36Sopenharmony_ci			    (ioc->sas_hba.phy[i].phy->minimum_linkrate +
197662306a36Sopenharmony_ci			    (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
197762306a36Sopenharmony_ci		} else {
197862306a36Sopenharmony_ci			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
197962306a36Sopenharmony_ci			    (rates->minimum_linkrate +
198062306a36Sopenharmony_ci			    (rates->maximum_linkrate << 4));
198162306a36Sopenharmony_ci		}
198262306a36Sopenharmony_ci	}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	if (mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
198562306a36Sopenharmony_ci	    sz)) {
198662306a36Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
198762306a36Sopenharmony_ci			__FILE__, __LINE__, __func__);
198862306a36Sopenharmony_ci		rc = -ENXIO;
198962306a36Sopenharmony_ci		goto out;
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	/* link reset */
199362306a36Sopenharmony_ci	_transport_phy_reset(phy, 0);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	/* read phy page 0, then update the rates in the sas transport phy */
199662306a36Sopenharmony_ci	if (!mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
199762306a36Sopenharmony_ci	    phy->number)) {
199862306a36Sopenharmony_ci		phy->minimum_linkrate = _transport_convert_phy_link_rate(
199962306a36Sopenharmony_ci		    phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
200062306a36Sopenharmony_ci		phy->maximum_linkrate = _transport_convert_phy_link_rate(
200162306a36Sopenharmony_ci		    phy_pg0.ProgrammedLinkRate >> 4);
200262306a36Sopenharmony_ci		phy->negotiated_linkrate = _transport_convert_phy_link_rate(
200362306a36Sopenharmony_ci		    phy_pg0.NegotiatedLinkRate &
200462306a36Sopenharmony_ci		    MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci out:
200862306a36Sopenharmony_ci	kfree(sas_iounit_pg1);
200962306a36Sopenharmony_ci	return rc;
201062306a36Sopenharmony_ci}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_cistatic int
201362306a36Sopenharmony_ci_transport_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
201462306a36Sopenharmony_ci		dma_addr_t *dma_addr, size_t *dma_len, void **p)
201562306a36Sopenharmony_ci{
201662306a36Sopenharmony_ci	/* Check if the request is split across multiple segments */
201762306a36Sopenharmony_ci	if (buf->sg_cnt > 1) {
201862306a36Sopenharmony_ci		*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
201962306a36Sopenharmony_ci				GFP_KERNEL);
202062306a36Sopenharmony_ci		if (!*p)
202162306a36Sopenharmony_ci			return -ENOMEM;
202262306a36Sopenharmony_ci		*dma_len = buf->payload_len;
202362306a36Sopenharmony_ci	} else {
202462306a36Sopenharmony_ci		if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
202562306a36Sopenharmony_ci			return -ENOMEM;
202662306a36Sopenharmony_ci		*dma_addr = sg_dma_address(buf->sg_list);
202762306a36Sopenharmony_ci		*dma_len = sg_dma_len(buf->sg_list);
202862306a36Sopenharmony_ci		*p = NULL;
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	return 0;
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic void
203562306a36Sopenharmony_ci_transport_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
203662306a36Sopenharmony_ci		dma_addr_t dma_addr, void *p)
203762306a36Sopenharmony_ci{
203862306a36Sopenharmony_ci	if (p)
203962306a36Sopenharmony_ci		dma_free_coherent(dev, buf->payload_len, p, dma_addr);
204062306a36Sopenharmony_ci	else
204162306a36Sopenharmony_ci		dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
204262306a36Sopenharmony_ci}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci/**
204562306a36Sopenharmony_ci * _transport_smp_handler - transport portal for smp passthru
204662306a36Sopenharmony_ci * @job: ?
204762306a36Sopenharmony_ci * @shost: shost object
204862306a36Sopenharmony_ci * @rphy: sas transport rphy object
204962306a36Sopenharmony_ci *
205062306a36Sopenharmony_ci * This used primarily for smp_utils.
205162306a36Sopenharmony_ci * Example:
205262306a36Sopenharmony_ci *           smp_rep_general /sys/class/bsg/expander-5:0
205362306a36Sopenharmony_ci */
205462306a36Sopenharmony_cistatic void
205562306a36Sopenharmony_ci_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
205662306a36Sopenharmony_ci		struct sas_rphy *rphy)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
205962306a36Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
206062306a36Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
206162306a36Sopenharmony_ci	int rc;
206262306a36Sopenharmony_ci	u16 smid;
206362306a36Sopenharmony_ci	void *psge;
206462306a36Sopenharmony_ci	dma_addr_t dma_addr_in;
206562306a36Sopenharmony_ci	dma_addr_t dma_addr_out;
206662306a36Sopenharmony_ci	void *addr_in = NULL;
206762306a36Sopenharmony_ci	void *addr_out = NULL;
206862306a36Sopenharmony_ci	size_t dma_len_in;
206962306a36Sopenharmony_ci	size_t dma_len_out;
207062306a36Sopenharmony_ci	unsigned int reslen = 0;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
207362306a36Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
207462306a36Sopenharmony_ci		rc = -EFAULT;
207562306a36Sopenharmony_ci		goto job_done;
207662306a36Sopenharmony_ci	}
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
207962306a36Sopenharmony_ci	if (rc)
208062306a36Sopenharmony_ci		goto job_done;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
208362306a36Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n",
208462306a36Sopenharmony_ci			__func__);
208562306a36Sopenharmony_ci		rc = -EAGAIN;
208662306a36Sopenharmony_ci		goto out;
208762306a36Sopenharmony_ci	}
208862306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->request_payload,
209162306a36Sopenharmony_ci			&dma_addr_out, &dma_len_out, &addr_out);
209262306a36Sopenharmony_ci	if (rc)
209362306a36Sopenharmony_ci		goto out;
209462306a36Sopenharmony_ci	if (addr_out) {
209562306a36Sopenharmony_ci		sg_copy_to_buffer(job->request_payload.sg_list,
209662306a36Sopenharmony_ci				job->request_payload.sg_cnt, addr_out,
209762306a36Sopenharmony_ci				job->request_payload.payload_len);
209862306a36Sopenharmony_ci	}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
210162306a36Sopenharmony_ci			&dma_addr_in, &dma_len_in, &addr_in);
210262306a36Sopenharmony_ci	if (rc)
210362306a36Sopenharmony_ci		goto unmap_out;
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
210662306a36Sopenharmony_ci	if (rc)
210762306a36Sopenharmony_ci		goto unmap_in;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
211062306a36Sopenharmony_ci	if (!smid) {
211162306a36Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
211262306a36Sopenharmony_ci		rc = -EAGAIN;
211362306a36Sopenharmony_ci		goto unmap_in;
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	rc = 0;
211762306a36Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
211862306a36Sopenharmony_ci	ioc->transport_cmds.smid = smid;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
212162306a36Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
212262306a36Sopenharmony_ci	mpi_request->PhysicalPort = _transport_get_port_id_by_rphy(ioc, rphy);
212362306a36Sopenharmony_ci	mpi_request->SASAddress = (rphy) ?
212462306a36Sopenharmony_ci	    cpu_to_le64(rphy->identify.sas_address) :
212562306a36Sopenharmony_ci	    cpu_to_le64(ioc->sas_hba.sas_address);
212662306a36Sopenharmony_ci	mpi_request->RequestDataLength = cpu_to_le16(dma_len_out - 4);
212762306a36Sopenharmony_ci	psge = &mpi_request->SGL;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	ioc->build_sg(ioc, psge, dma_addr_out, dma_len_out - 4, dma_addr_in,
213062306a36Sopenharmony_ci			dma_len_in - 4);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	dtransportprintk(ioc,
213362306a36Sopenharmony_ci			 ioc_info(ioc, "%s: sending smp request\n", __func__));
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
213662306a36Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
213762306a36Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
214062306a36Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
214162306a36Sopenharmony_ci		_debug_dump_mf(mpi_request,
214262306a36Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
214362306a36Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) {
214462306a36Sopenharmony_ci			mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
214562306a36Sopenharmony_ci			rc = -ETIMEDOUT;
214662306a36Sopenharmony_ci			goto unmap_in;
214762306a36Sopenharmony_ci		}
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "%s - complete\n", __func__));
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID)) {
215362306a36Sopenharmony_ci		dtransportprintk(ioc,
215462306a36Sopenharmony_ci				 ioc_info(ioc, "%s: no reply\n", __func__));
215562306a36Sopenharmony_ci		rc = -ENXIO;
215662306a36Sopenharmony_ci		goto unmap_in;
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	mpi_reply = ioc->transport_cmds.reply;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	dtransportprintk(ioc,
216262306a36Sopenharmony_ci			 ioc_info(ioc, "%s: reply data transfer size(%d)\n",
216362306a36Sopenharmony_ci				  __func__,
216462306a36Sopenharmony_ci				  le16_to_cpu(mpi_reply->ResponseDataLength)));
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	memcpy(job->reply, mpi_reply, sizeof(*mpi_reply));
216762306a36Sopenharmony_ci	job->reply_len = sizeof(*mpi_reply);
216862306a36Sopenharmony_ci	reslen = le16_to_cpu(mpi_reply->ResponseDataLength);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	if (addr_in) {
217162306a36Sopenharmony_ci		sg_copy_to_buffer(job->reply_payload.sg_list,
217262306a36Sopenharmony_ci				job->reply_payload.sg_cnt, addr_in,
217362306a36Sopenharmony_ci				job->reply_payload.payload_len);
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	rc = 0;
217762306a36Sopenharmony_ci unmap_in:
217862306a36Sopenharmony_ci	_transport_unmap_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
217962306a36Sopenharmony_ci			dma_addr_in, addr_in);
218062306a36Sopenharmony_ci unmap_out:
218162306a36Sopenharmony_ci	_transport_unmap_smp_buffer(&ioc->pdev->dev, &job->request_payload,
218262306a36Sopenharmony_ci			dma_addr_out, addr_out);
218362306a36Sopenharmony_ci out:
218462306a36Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
218562306a36Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
218662306a36Sopenharmony_cijob_done:
218762306a36Sopenharmony_ci	bsg_job_done(job, rc, reslen);
218862306a36Sopenharmony_ci}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_cistruct sas_function_template mpt3sas_transport_functions = {
219162306a36Sopenharmony_ci	.get_linkerrors		= _transport_get_linkerrors,
219262306a36Sopenharmony_ci	.get_enclosure_identifier = _transport_get_enclosure_identifier,
219362306a36Sopenharmony_ci	.get_bay_identifier	= _transport_get_bay_identifier,
219462306a36Sopenharmony_ci	.phy_reset		= _transport_phy_reset,
219562306a36Sopenharmony_ci	.phy_enable		= _transport_phy_enable,
219662306a36Sopenharmony_ci	.set_phy_speed		= _transport_phy_speed,
219762306a36Sopenharmony_ci	.smp_handler		= _transport_smp_handler,
219862306a36Sopenharmony_ci};
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_cistruct scsi_transport_template *mpt3sas_transport_template;
2201