18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * SAS Transport Layer for MPT (Message Passing Technology) based controllers
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c
58c2ecf20Sopenharmony_ci * Copyright (C) 2012-2014  LSI Corporation
68c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Avago Technologies
78c2ecf20Sopenharmony_ci *  (mailto: MPT-FusionLinux.pdl@avagotech.com)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License
118c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 2
128c2ecf20Sopenharmony_ci * of the License, or (at your option) any later version.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
158c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178c2ecf20Sopenharmony_ci * GNU General Public License for more details.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * NO WARRANTY
208c2ecf20Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
218c2ecf20Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
228c2ecf20Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
238c2ecf20Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
248c2ecf20Sopenharmony_ci * solely responsible for determining the appropriateness of using and
258c2ecf20Sopenharmony_ci * distributing the Program and assumes all risks associated with its
268c2ecf20Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to
278c2ecf20Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data,
288c2ecf20Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations.
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci * DISCLAIMER OF LIABILITY
318c2ecf20Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
328c2ecf20Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
338c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
348c2ecf20Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
358c2ecf20Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
368c2ecf20Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
378c2ecf20Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
408c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
418c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
428c2ecf20Sopenharmony_ci * USA.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include <linux/module.h>
468c2ecf20Sopenharmony_ci#include <linux/kernel.h>
478c2ecf20Sopenharmony_ci#include <linux/init.h>
488c2ecf20Sopenharmony_ci#include <linux/errno.h>
498c2ecf20Sopenharmony_ci#include <linux/sched.h>
508c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
518c2ecf20Sopenharmony_ci#include <linux/delay.h>
528c2ecf20Sopenharmony_ci#include <linux/pci.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
558c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
568c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
578c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
588c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_sas.h>
598c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#include "mpt3sas_base.h"
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/**
648c2ecf20Sopenharmony_ci * _transport_sas_node_find_by_sas_address - sas node search
658c2ecf20Sopenharmony_ci * @ioc: per adapter object
668c2ecf20Sopenharmony_ci * @sas_address: sas address of expander or sas host
678c2ecf20Sopenharmony_ci * Context: Calling function should acquire ioc->sas_node_lock.
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * Search for either hba phys or expander device based on handle, then returns
708c2ecf20Sopenharmony_ci * the sas_node object.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic struct _sas_node *
738c2ecf20Sopenharmony_ci_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
748c2ecf20Sopenharmony_ci	u64 sas_address)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	if (ioc->sas_hba.sas_address == sas_address)
778c2ecf20Sopenharmony_ci		return &ioc->sas_hba;
788c2ecf20Sopenharmony_ci	else
798c2ecf20Sopenharmony_ci		return mpt3sas_scsih_expander_find_by_sas_address(ioc,
808c2ecf20Sopenharmony_ci		    sas_address);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/**
848c2ecf20Sopenharmony_ci * _transport_convert_phy_link_rate -
858c2ecf20Sopenharmony_ci * @link_rate: link rate returned from mpt firmware
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci * Convert link_rate from mpi fusion into sas_transport form.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic enum sas_linkrate
908c2ecf20Sopenharmony_ci_transport_convert_phy_link_rate(u8 link_rate)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	enum sas_linkrate rc;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	switch (link_rate) {
958c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_1_5:
968c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_1_5_GBPS;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_3_0:
998c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_3_0_GBPS;
1008c2ecf20Sopenharmony_ci		break;
1018c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_6_0:
1028c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_6_0_GBPS;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	case MPI25_SAS_NEG_LINK_RATE_12_0:
1058c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_12_0_GBPS;
1068c2ecf20Sopenharmony_ci		break;
1078c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
1088c2ecf20Sopenharmony_ci		rc = SAS_PHY_DISABLED;
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
1118c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_FAILED;
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
1148c2ecf20Sopenharmony_ci		rc = SAS_SATA_PORT_SELECTOR;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
1178c2ecf20Sopenharmony_ci		rc = SAS_PHY_RESET_IN_PROGRESS;
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
1228c2ecf20Sopenharmony_ci	case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
1238c2ecf20Sopenharmony_ci		rc = SAS_LINK_RATE_UNKNOWN;
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	return rc;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/**
1308c2ecf20Sopenharmony_ci * _transport_set_identify - set identify for phys and end devices
1318c2ecf20Sopenharmony_ci * @ioc: per adapter object
1328c2ecf20Sopenharmony_ci * @handle: device handle
1338c2ecf20Sopenharmony_ci * @identify: sas identify info
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * Populates sas identify info.
1368c2ecf20Sopenharmony_ci *
1378c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic int
1408c2ecf20Sopenharmony_ci_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle,
1418c2ecf20Sopenharmony_ci	struct sas_identify *identify)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	Mpi2SasDevicePage0_t sas_device_pg0;
1448c2ecf20Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
1458c2ecf20Sopenharmony_ci	u32 device_info;
1468c2ecf20Sopenharmony_ci	u32 ioc_status;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
1498c2ecf20Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
1508c2ecf20Sopenharmony_ci		return -EFAULT;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
1548c2ecf20Sopenharmony_ci	    MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
1558c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
1568c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
1578c2ecf20Sopenharmony_ci		return -ENXIO;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
1618c2ecf20Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
1628c2ecf20Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1638c2ecf20Sopenharmony_ci		ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x) failure at %s:%d/%s()!\n",
1648c2ecf20Sopenharmony_ci			handle, ioc_status, __FILE__, __LINE__, __func__);
1658c2ecf20Sopenharmony_ci		return -EIO;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	memset(identify, 0, sizeof(struct sas_identify));
1698c2ecf20Sopenharmony_ci	device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* sas_address */
1728c2ecf20Sopenharmony_ci	identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* phy number of the parent device this device is linked to */
1758c2ecf20Sopenharmony_ci	identify->phy_identifier = sas_device_pg0.PhyNum;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* device_type */
1788c2ecf20Sopenharmony_ci	switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
1798c2ecf20Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
1808c2ecf20Sopenharmony_ci		identify->device_type = SAS_PHY_UNUSED;
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_END_DEVICE:
1838c2ecf20Sopenharmony_ci		identify->device_type = SAS_END_DEVICE;
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
1868c2ecf20Sopenharmony_ci		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci	case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
1898c2ecf20Sopenharmony_ci		identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* initiator_port_protocols */
1948c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
1958c2ecf20Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
1968c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
1978c2ecf20Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
1988c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
1998c2ecf20Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
2008c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
2018c2ecf20Sopenharmony_ci		identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* target_port_protocols */
2048c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
2058c2ecf20Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SSP;
2068c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
2078c2ecf20Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_STP;
2088c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
2098c2ecf20Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SMP;
2108c2ecf20Sopenharmony_ci	if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
2118c2ecf20Sopenharmony_ci		identify->target_port_protocols |= SAS_PROTOCOL_SATA;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/**
2178c2ecf20Sopenharmony_ci * mpt3sas_transport_done -  internal transport layer callback handler.
2188c2ecf20Sopenharmony_ci * @ioc: per adapter object
2198c2ecf20Sopenharmony_ci * @smid: system request message index
2208c2ecf20Sopenharmony_ci * @msix_index: MSIX table index supplied by the OS
2218c2ecf20Sopenharmony_ci * @reply: reply message frame(lower 32bit addr)
2228c2ecf20Sopenharmony_ci *
2238c2ecf20Sopenharmony_ci * Callback handler when sending internal generated transport cmds.
2248c2ecf20Sopenharmony_ci * The callback index passed is `ioc->transport_cb_idx`
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * Return: 1 meaning mf should be freed from _base_interrupt
2278c2ecf20Sopenharmony_ci *         0 means the mf is freed from this function.
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_ciu8
2308c2ecf20Sopenharmony_cimpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
2318c2ecf20Sopenharmony_ci	u32 reply)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	MPI2DefaultReply_t *mpi_reply;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	mpi_reply =  mpt3sas_base_get_reply_virt_addr(ioc, reply);
2368c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED)
2378c2ecf20Sopenharmony_ci		return 1;
2388c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.smid != smid)
2398c2ecf20Sopenharmony_ci		return 1;
2408c2ecf20Sopenharmony_ci	ioc->transport_cmds.status |= MPT3_CMD_COMPLETE;
2418c2ecf20Sopenharmony_ci	if (mpi_reply) {
2428c2ecf20Sopenharmony_ci		memcpy(ioc->transport_cmds.reply, mpi_reply,
2438c2ecf20Sopenharmony_ci		    mpi_reply->MsgLength*4);
2448c2ecf20Sopenharmony_ci		ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	ioc->transport_cmds.status &= ~MPT3_CMD_PENDING;
2478c2ecf20Sopenharmony_ci	complete(&ioc->transport_cmds.done);
2488c2ecf20Sopenharmony_ci	return 1;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* report manufacture request structure */
2528c2ecf20Sopenharmony_cistruct rep_manu_request {
2538c2ecf20Sopenharmony_ci	u8 smp_frame_type;
2548c2ecf20Sopenharmony_ci	u8 function;
2558c2ecf20Sopenharmony_ci	u8 reserved;
2568c2ecf20Sopenharmony_ci	u8 request_length;
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/* report manufacture reply structure */
2608c2ecf20Sopenharmony_cistruct rep_manu_reply {
2618c2ecf20Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
2628c2ecf20Sopenharmony_ci	u8 function; /* 0x01 */
2638c2ecf20Sopenharmony_ci	u8 function_result;
2648c2ecf20Sopenharmony_ci	u8 response_length;
2658c2ecf20Sopenharmony_ci	u16 expander_change_count;
2668c2ecf20Sopenharmony_ci	u8 reserved0[2];
2678c2ecf20Sopenharmony_ci	u8 sas_format;
2688c2ecf20Sopenharmony_ci	u8 reserved2[3];
2698c2ecf20Sopenharmony_ci	u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
2708c2ecf20Sopenharmony_ci	u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
2718c2ecf20Sopenharmony_ci	u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
2728c2ecf20Sopenharmony_ci	u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
2738c2ecf20Sopenharmony_ci	u16 component_id;
2748c2ecf20Sopenharmony_ci	u8 component_revision_id;
2758c2ecf20Sopenharmony_ci	u8 reserved3;
2768c2ecf20Sopenharmony_ci	u8 vendor_specific[8];
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/**
2808c2ecf20Sopenharmony_ci * transport_expander_report_manufacture - obtain SMP report_manufacture
2818c2ecf20Sopenharmony_ci * @ioc: per adapter object
2828c2ecf20Sopenharmony_ci * @sas_address: expander sas address
2838c2ecf20Sopenharmony_ci * @edev: the sas_expander_device object
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Fills in the sas_expander_device object when SMP port is created.
2868c2ecf20Sopenharmony_ci *
2878c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic int
2908c2ecf20Sopenharmony_ci_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc,
2918c2ecf20Sopenharmony_ci	u64 sas_address, struct sas_expander_device *edev)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
2948c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
2958c2ecf20Sopenharmony_ci	struct rep_manu_reply *manufacture_reply;
2968c2ecf20Sopenharmony_ci	struct rep_manu_request *manufacture_request;
2978c2ecf20Sopenharmony_ci	int rc;
2988c2ecf20Sopenharmony_ci	u16 smid;
2998c2ecf20Sopenharmony_ci	void *psge;
3008c2ecf20Sopenharmony_ci	u8 issue_reset = 0;
3018c2ecf20Sopenharmony_ci	void *data_out = NULL;
3028c2ecf20Sopenharmony_ci	dma_addr_t data_out_dma;
3038c2ecf20Sopenharmony_ci	dma_addr_t data_in_dma;
3048c2ecf20Sopenharmony_ci	size_t data_in_sz;
3058c2ecf20Sopenharmony_ci	size_t data_out_sz;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
3088c2ecf20Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
3098c2ecf20Sopenharmony_ci		return -EFAULT;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
3158c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
3168c2ecf20Sopenharmony_ci		rc = -EAGAIN;
3178c2ecf20Sopenharmony_ci		goto out;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
3228c2ecf20Sopenharmony_ci	if (rc)
3238c2ecf20Sopenharmony_ci		goto out;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
3268c2ecf20Sopenharmony_ci	if (!smid) {
3278c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
3288c2ecf20Sopenharmony_ci		rc = -EAGAIN;
3298c2ecf20Sopenharmony_ci		goto out;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	rc = 0;
3338c2ecf20Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
3348c2ecf20Sopenharmony_ci	ioc->transport_cmds.smid = smid;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	data_out_sz = sizeof(struct rep_manu_request);
3378c2ecf20Sopenharmony_ci	data_in_sz = sizeof(struct rep_manu_reply);
3388c2ecf20Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
3398c2ecf20Sopenharmony_ci			&data_out_dma, GFP_KERNEL);
3408c2ecf20Sopenharmony_ci	if (!data_out) {
3418c2ecf20Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
3428c2ecf20Sopenharmony_ci		    __LINE__, __func__);
3438c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3448c2ecf20Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
3458c2ecf20Sopenharmony_ci		goto out;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	data_in_dma = data_out_dma + sizeof(struct rep_manu_request);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	manufacture_request = data_out;
3518c2ecf20Sopenharmony_ci	manufacture_request->smp_frame_type = 0x40;
3528c2ecf20Sopenharmony_ci	manufacture_request->function = 1;
3538c2ecf20Sopenharmony_ci	manufacture_request->reserved = 0;
3548c2ecf20Sopenharmony_ci	manufacture_request->request_length = 0;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
3578c2ecf20Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
3588c2ecf20Sopenharmony_ci	mpi_request->PhysicalPort = 0xFF;
3598c2ecf20Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(sas_address);
3608c2ecf20Sopenharmony_ci	mpi_request->RequestDataLength = cpu_to_le16(data_out_sz);
3618c2ecf20Sopenharmony_ci	psge = &mpi_request->SGL;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
3648c2ecf20Sopenharmony_ci	    data_in_sz);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	dtransportprintk(ioc,
3678c2ecf20Sopenharmony_ci			 ioc_info(ioc, "report_manufacture - send to sas_addr(0x%016llx)\n",
3688c2ecf20Sopenharmony_ci				  (u64)sas_address));
3698c2ecf20Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
3708c2ecf20Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
3718c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
3748c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
3758c2ecf20Sopenharmony_ci		_debug_dump_mf(mpi_request,
3768c2ecf20Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
3778c2ecf20Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
3788c2ecf20Sopenharmony_ci			issue_reset = 1;
3798c2ecf20Sopenharmony_ci		goto issue_host_reset;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "report_manufacture - complete\n"));
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
3858c2ecf20Sopenharmony_ci		u8 *tmp;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
3908c2ecf20Sopenharmony_ci				 ioc_info(ioc, "report_manufacture - reply data transfer size(%d)\n",
3918c2ecf20Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
3948c2ecf20Sopenharmony_ci		    sizeof(struct rep_manu_reply))
3958c2ecf20Sopenharmony_ci			goto out;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		manufacture_reply = data_out + sizeof(struct rep_manu_request);
3988c2ecf20Sopenharmony_ci		strncpy(edev->vendor_id, manufacture_reply->vendor_id,
3998c2ecf20Sopenharmony_ci		     SAS_EXPANDER_VENDOR_ID_LEN);
4008c2ecf20Sopenharmony_ci		strncpy(edev->product_id, manufacture_reply->product_id,
4018c2ecf20Sopenharmony_ci		     SAS_EXPANDER_PRODUCT_ID_LEN);
4028c2ecf20Sopenharmony_ci		strncpy(edev->product_rev, manufacture_reply->product_rev,
4038c2ecf20Sopenharmony_ci		     SAS_EXPANDER_PRODUCT_REV_LEN);
4048c2ecf20Sopenharmony_ci		edev->level = manufacture_reply->sas_format & 1;
4058c2ecf20Sopenharmony_ci		if (edev->level) {
4068c2ecf20Sopenharmony_ci			strncpy(edev->component_vendor_id,
4078c2ecf20Sopenharmony_ci			    manufacture_reply->component_vendor_id,
4088c2ecf20Sopenharmony_ci			     SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
4098c2ecf20Sopenharmony_ci			tmp = (u8 *)&manufacture_reply->component_id;
4108c2ecf20Sopenharmony_ci			edev->component_id = tmp[0] << 8 | tmp[1];
4118c2ecf20Sopenharmony_ci			edev->component_revision_id =
4128c2ecf20Sopenharmony_ci			    manufacture_reply->component_revision_id;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci	} else
4158c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
4168c2ecf20Sopenharmony_ci				 ioc_info(ioc, "report_manufacture - no reply\n"));
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci issue_host_reset:
4198c2ecf20Sopenharmony_ci	if (issue_reset)
4208c2ecf20Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
4218c2ecf20Sopenharmony_ci out:
4228c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
4238c2ecf20Sopenharmony_ci	if (data_out)
4248c2ecf20Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz,
4258c2ecf20Sopenharmony_ci		    data_out, data_out_dma);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
4288c2ecf20Sopenharmony_ci	return rc;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/**
4338c2ecf20Sopenharmony_ci * _transport_delete_port - helper function to removing a port
4348c2ecf20Sopenharmony_ci * @ioc: per adapter object
4358c2ecf20Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_cistatic void
4388c2ecf20Sopenharmony_ci_transport_delete_port(struct MPT3SAS_ADAPTER *ioc,
4398c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
4428c2ecf20Sopenharmony_ci	enum sas_device_type device_type =
4438c2ecf20Sopenharmony_ci	    mpt3sas_port->remote_identify.device_type;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
4468c2ecf20Sopenharmony_ci	    "remove: sas_addr(0x%016llx)\n",
4478c2ecf20Sopenharmony_ci	    (unsigned long long) sas_address);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	ioc->logging_level |= MPT_DEBUG_TRANSPORT;
4508c2ecf20Sopenharmony_ci	if (device_type == SAS_END_DEVICE)
4518c2ecf20Sopenharmony_ci		mpt3sas_device_remove_by_sas_address(ioc, sas_address);
4528c2ecf20Sopenharmony_ci	else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
4538c2ecf20Sopenharmony_ci	    device_type == SAS_FANOUT_EXPANDER_DEVICE)
4548c2ecf20Sopenharmony_ci		mpt3sas_expander_remove(ioc, sas_address);
4558c2ecf20Sopenharmony_ci	ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/**
4598c2ecf20Sopenharmony_ci * _transport_delete_phy - helper function to removing single phy from port
4608c2ecf20Sopenharmony_ci * @ioc: per adapter object
4618c2ecf20Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
4628c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_cistatic void
4658c2ecf20Sopenharmony_ci_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc,
4668c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port, struct _sas_phy *mpt3sas_phy)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
4718c2ecf20Sopenharmony_ci	    "remove: sas_addr(0x%016llx), phy(%d)\n",
4728c2ecf20Sopenharmony_ci	    (unsigned long long) sas_address, mpt3sas_phy->phy_id);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	list_del(&mpt3sas_phy->port_siblings);
4758c2ecf20Sopenharmony_ci	mpt3sas_port->num_phys--;
4768c2ecf20Sopenharmony_ci	sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy);
4778c2ecf20Sopenharmony_ci	mpt3sas_phy->phy_belongs_to_port = 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci/**
4818c2ecf20Sopenharmony_ci * _transport_add_phy - helper function to adding single phy to port
4828c2ecf20Sopenharmony_ci * @ioc: per adapter object
4838c2ecf20Sopenharmony_ci * @mpt3sas_port: mpt3sas per port object
4848c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
4858c2ecf20Sopenharmony_ci */
4868c2ecf20Sopenharmony_cistatic void
4878c2ecf20Sopenharmony_ci_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port,
4888c2ecf20Sopenharmony_ci	struct _sas_phy *mpt3sas_phy)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	u64 sas_address = mpt3sas_port->remote_identify.sas_address;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
4938c2ecf20Sopenharmony_ci	    "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
4948c2ecf20Sopenharmony_ci	    sas_address, mpt3sas_phy->phy_id);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	list_add_tail(&mpt3sas_phy->port_siblings, &mpt3sas_port->phy_list);
4978c2ecf20Sopenharmony_ci	mpt3sas_port->num_phys++;
4988c2ecf20Sopenharmony_ci	sas_port_add_phy(mpt3sas_port->port, mpt3sas_phy->phy);
4998c2ecf20Sopenharmony_ci	mpt3sas_phy->phy_belongs_to_port = 1;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/**
5038c2ecf20Sopenharmony_ci * _transport_add_phy_to_an_existing_port - adding new phy to existing port
5048c2ecf20Sopenharmony_ci * @ioc: per adapter object
5058c2ecf20Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
5068c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
5078c2ecf20Sopenharmony_ci * @sas_address: sas address of device/expander were phy needs to be added to
5088c2ecf20Sopenharmony_ci */
5098c2ecf20Sopenharmony_cistatic void
5108c2ecf20Sopenharmony_ci_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
5118c2ecf20Sopenharmony_ci	struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy,
5128c2ecf20Sopenharmony_ci	u64 sas_address)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port;
5158c2ecf20Sopenharmony_ci	struct _sas_phy *phy_srch;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (mpt3sas_phy->phy_belongs_to_port == 1)
5188c2ecf20Sopenharmony_ci		return;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list,
5218c2ecf20Sopenharmony_ci	    port_list) {
5228c2ecf20Sopenharmony_ci		if (mpt3sas_port->remote_identify.sas_address !=
5238c2ecf20Sopenharmony_ci		    sas_address)
5248c2ecf20Sopenharmony_ci			continue;
5258c2ecf20Sopenharmony_ci		list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
5268c2ecf20Sopenharmony_ci		    port_siblings) {
5278c2ecf20Sopenharmony_ci			if (phy_srch == mpt3sas_phy)
5288c2ecf20Sopenharmony_ci				return;
5298c2ecf20Sopenharmony_ci		}
5308c2ecf20Sopenharmony_ci		_transport_add_phy(ioc, mpt3sas_port, mpt3sas_phy);
5318c2ecf20Sopenharmony_ci		return;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci/**
5378c2ecf20Sopenharmony_ci * _transport_del_phy_from_an_existing_port - delete phy from existing port
5388c2ecf20Sopenharmony_ci * @ioc: per adapter object
5398c2ecf20Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
5408c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
5418c2ecf20Sopenharmony_ci */
5428c2ecf20Sopenharmony_cistatic void
5438c2ecf20Sopenharmony_ci_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
5448c2ecf20Sopenharmony_ci	struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port, *next;
5478c2ecf20Sopenharmony_ci	struct _sas_phy *phy_srch;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (mpt3sas_phy->phy_belongs_to_port == 0)
5508c2ecf20Sopenharmony_ci		return;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
5538c2ecf20Sopenharmony_ci	    port_list) {
5548c2ecf20Sopenharmony_ci		list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
5558c2ecf20Sopenharmony_ci		    port_siblings) {
5568c2ecf20Sopenharmony_ci			if (phy_srch != mpt3sas_phy)
5578c2ecf20Sopenharmony_ci				continue;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci			if (mpt3sas_port->num_phys == 1)
5608c2ecf20Sopenharmony_ci				_transport_delete_port(ioc, mpt3sas_port);
5618c2ecf20Sopenharmony_ci			else
5628c2ecf20Sopenharmony_ci				_transport_delete_phy(ioc, mpt3sas_port,
5638c2ecf20Sopenharmony_ci				    mpt3sas_phy);
5648c2ecf20Sopenharmony_ci			return;
5658c2ecf20Sopenharmony_ci		}
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * _transport_sanity_check - sanity check when adding a new port
5718c2ecf20Sopenharmony_ci * @ioc: per adapter object
5728c2ecf20Sopenharmony_ci * @sas_node: sas node object (either expander or sas host)
5738c2ecf20Sopenharmony_ci * @sas_address: sas address of device being added
5748c2ecf20Sopenharmony_ci *
5758c2ecf20Sopenharmony_ci * See the explanation above from _transport_delete_duplicate_port
5768c2ecf20Sopenharmony_ci */
5778c2ecf20Sopenharmony_cistatic void
5788c2ecf20Sopenharmony_ci_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node,
5798c2ecf20Sopenharmony_ci	u64 sas_address)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	int i;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
5848c2ecf20Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address != sas_address)
5858c2ecf20Sopenharmony_ci			continue;
5868c2ecf20Sopenharmony_ci		if (sas_node->phy[i].phy_belongs_to_port == 1)
5878c2ecf20Sopenharmony_ci			_transport_del_phy_from_an_existing_port(ioc, sas_node,
5888c2ecf20Sopenharmony_ci			    &sas_node->phy[i]);
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/**
5938c2ecf20Sopenharmony_ci * mpt3sas_transport_port_add - insert port to the list
5948c2ecf20Sopenharmony_ci * @ioc: per adapter object
5958c2ecf20Sopenharmony_ci * @handle: handle of attached device
5968c2ecf20Sopenharmony_ci * @sas_address: sas address of parent expander or sas host
5978c2ecf20Sopenharmony_ci * Context: This function will acquire ioc->sas_node_lock.
5988c2ecf20Sopenharmony_ci *
5998c2ecf20Sopenharmony_ci * Adding new port object to the sas_node->sas_port_list.
6008c2ecf20Sopenharmony_ci *
6018c2ecf20Sopenharmony_ci * Return: mpt3sas_port.
6028c2ecf20Sopenharmony_ci */
6038c2ecf20Sopenharmony_cistruct _sas_port *
6048c2ecf20Sopenharmony_cimpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
6058c2ecf20Sopenharmony_ci	u64 sas_address)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct _sas_phy *mpt3sas_phy, *next;
6088c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port;
6098c2ecf20Sopenharmony_ci	unsigned long flags;
6108c2ecf20Sopenharmony_ci	struct _sas_node *sas_node;
6118c2ecf20Sopenharmony_ci	struct sas_rphy *rphy;
6128c2ecf20Sopenharmony_ci	struct _sas_device *sas_device = NULL;
6138c2ecf20Sopenharmony_ci	int i;
6148c2ecf20Sopenharmony_ci	struct sas_port *port;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	mpt3sas_port = kzalloc(sizeof(struct _sas_port),
6178c2ecf20Sopenharmony_ci	    GFP_KERNEL);
6188c2ecf20Sopenharmony_ci	if (!mpt3sas_port) {
6198c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6208c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6218c2ecf20Sopenharmony_ci		return NULL;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_port->port_list);
6258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_port->phy_list);
6268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
6278c2ecf20Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
6288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (!sas_node) {
6318c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: Could not find parent sas_address(0x%016llx)!\n",
6328c2ecf20Sopenharmony_ci			__func__, (u64)sas_address);
6338c2ecf20Sopenharmony_ci		goto out_fail;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if ((_transport_set_identify(ioc, handle,
6378c2ecf20Sopenharmony_ci	    &mpt3sas_port->remote_identify))) {
6388c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6398c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6408c2ecf20Sopenharmony_ci		goto out_fail;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
6448c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6458c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6468c2ecf20Sopenharmony_ci		goto out_fail;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	_transport_sanity_check(ioc, sas_node,
6508c2ecf20Sopenharmony_ci	    mpt3sas_port->remote_identify.sas_address);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
6538c2ecf20Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address !=
6548c2ecf20Sopenharmony_ci		    mpt3sas_port->remote_identify.sas_address)
6558c2ecf20Sopenharmony_ci			continue;
6568c2ecf20Sopenharmony_ci		list_add_tail(&sas_node->phy[i].port_siblings,
6578c2ecf20Sopenharmony_ci		    &mpt3sas_port->phy_list);
6588c2ecf20Sopenharmony_ci		mpt3sas_port->num_phys++;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (!mpt3sas_port->num_phys) {
6628c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6638c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6648c2ecf20Sopenharmony_ci		goto out_fail;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (!sas_node->parent_dev) {
6688c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6698c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6708c2ecf20Sopenharmony_ci		goto out_fail;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci	port = sas_port_alloc_num(sas_node->parent_dev);
6738c2ecf20Sopenharmony_ci	if (!port || (sas_port_add(port))) {
6748c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
6758c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
6768c2ecf20Sopenharmony_ci		goto out_fail;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	list_for_each_entry(mpt3sas_phy, &mpt3sas_port->phy_list,
6808c2ecf20Sopenharmony_ci	    port_siblings) {
6818c2ecf20Sopenharmony_ci		if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
6828c2ecf20Sopenharmony_ci			dev_printk(KERN_INFO, &port->dev,
6838c2ecf20Sopenharmony_ci				"add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n",
6848c2ecf20Sopenharmony_ci				handle, (unsigned long long)
6858c2ecf20Sopenharmony_ci			    mpt3sas_port->remote_identify.sas_address,
6868c2ecf20Sopenharmony_ci			    mpt3sas_phy->phy_id);
6878c2ecf20Sopenharmony_ci		sas_port_add_phy(port, mpt3sas_phy->phy);
6888c2ecf20Sopenharmony_ci		mpt3sas_phy->phy_belongs_to_port = 1;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	mpt3sas_port->port = port;
6928c2ecf20Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE)
6938c2ecf20Sopenharmony_ci		rphy = sas_end_device_alloc(port);
6948c2ecf20Sopenharmony_ci	else
6958c2ecf20Sopenharmony_ci		rphy = sas_expander_alloc(port,
6968c2ecf20Sopenharmony_ci		    mpt3sas_port->remote_identify.device_type);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (!rphy) {
6998c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
7008c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
7018c2ecf20Sopenharmony_ci		goto out_delete_port;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	rphy->identify = mpt3sas_port->remote_identify;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
7078c2ecf20Sopenharmony_ci		sas_device = mpt3sas_get_sdev_by_addr(ioc,
7088c2ecf20Sopenharmony_ci				    mpt3sas_port->remote_identify.sas_address);
7098c2ecf20Sopenharmony_ci		if (!sas_device) {
7108c2ecf20Sopenharmony_ci			dfailprintk(ioc,
7118c2ecf20Sopenharmony_ci				    ioc_info(ioc, "failure at %s:%d/%s()!\n",
7128c2ecf20Sopenharmony_ci					     __FILE__, __LINE__, __func__));
7138c2ecf20Sopenharmony_ci			goto out_fail;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci		sas_device->pend_sas_rphy_add = 1;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if ((sas_rphy_add(rphy))) {
7198c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
7208c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
7218c2ecf20Sopenharmony_ci		sas_rphy_free(rphy);
7228c2ecf20Sopenharmony_ci		rphy = NULL;
7238c2ecf20Sopenharmony_ci		goto out_delete_port;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
7278c2ecf20Sopenharmony_ci		sas_device->pend_sas_rphy_add = 0;
7288c2ecf20Sopenharmony_ci		sas_device_put(sas_device);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	dev_info(&rphy->dev,
7328c2ecf20Sopenharmony_ci	    "add: handle(0x%04x), sas_addr(0x%016llx)\n", handle,
7338c2ecf20Sopenharmony_ci	    (unsigned long long)mpt3sas_port->remote_identify.sas_address);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	mpt3sas_port->rphy = rphy;
7368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
7378c2ecf20Sopenharmony_ci	list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list);
7388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	/* fill in report manufacture */
7418c2ecf20Sopenharmony_ci	if (mpt3sas_port->remote_identify.device_type ==
7428c2ecf20Sopenharmony_ci	    MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
7438c2ecf20Sopenharmony_ci	    mpt3sas_port->remote_identify.device_type ==
7448c2ecf20Sopenharmony_ci	    MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
7458c2ecf20Sopenharmony_ci		_transport_expander_report_manufacture(ioc,
7468c2ecf20Sopenharmony_ci		    mpt3sas_port->remote_identify.sas_address,
7478c2ecf20Sopenharmony_ci		    rphy_to_expander_device(rphy));
7488c2ecf20Sopenharmony_ci	return mpt3sas_port;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ciout_delete_port:
7518c2ecf20Sopenharmony_ci	sas_port_delete(port);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ciout_fail:
7548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list,
7558c2ecf20Sopenharmony_ci	    port_siblings)
7568c2ecf20Sopenharmony_ci		list_del(&mpt3sas_phy->port_siblings);
7578c2ecf20Sopenharmony_ci	kfree(mpt3sas_port);
7588c2ecf20Sopenharmony_ci	return NULL;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci/**
7628c2ecf20Sopenharmony_ci * mpt3sas_transport_port_remove - remove port from the list
7638c2ecf20Sopenharmony_ci * @ioc: per adapter object
7648c2ecf20Sopenharmony_ci * @sas_address: sas address of attached device
7658c2ecf20Sopenharmony_ci * @sas_address_parent: sas address of parent expander or sas host
7668c2ecf20Sopenharmony_ci * Context: This function will acquire ioc->sas_node_lock.
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * Removing object and freeing associated memory from the
7698c2ecf20Sopenharmony_ci * ioc->sas_port_list.
7708c2ecf20Sopenharmony_ci */
7718c2ecf20Sopenharmony_civoid
7728c2ecf20Sopenharmony_cimpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
7738c2ecf20Sopenharmony_ci	u64 sas_address_parent)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	int i;
7768c2ecf20Sopenharmony_ci	unsigned long flags;
7778c2ecf20Sopenharmony_ci	struct _sas_port *mpt3sas_port, *next;
7788c2ecf20Sopenharmony_ci	struct _sas_node *sas_node;
7798c2ecf20Sopenharmony_ci	u8 found = 0;
7808c2ecf20Sopenharmony_ci	struct _sas_phy *mpt3sas_phy, *next_phy;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
7838c2ecf20Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc,
7848c2ecf20Sopenharmony_ci	    sas_address_parent);
7858c2ecf20Sopenharmony_ci	if (!sas_node) {
7868c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
7878c2ecf20Sopenharmony_ci		return;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
7908c2ecf20Sopenharmony_ci	    port_list) {
7918c2ecf20Sopenharmony_ci		if (mpt3sas_port->remote_identify.sas_address != sas_address)
7928c2ecf20Sopenharmony_ci			continue;
7938c2ecf20Sopenharmony_ci		found = 1;
7948c2ecf20Sopenharmony_ci		list_del(&mpt3sas_port->port_list);
7958c2ecf20Sopenharmony_ci		goto out;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci out:
7988c2ecf20Sopenharmony_ci	if (!found) {
7998c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
8008c2ecf20Sopenharmony_ci		return;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	for (i = 0; i < sas_node->num_phys; i++) {
8048c2ecf20Sopenharmony_ci		if (sas_node->phy[i].remote_identify.sas_address == sas_address)
8058c2ecf20Sopenharmony_ci			memset(&sas_node->phy[i].remote_identify, 0 ,
8068c2ecf20Sopenharmony_ci			    sizeof(struct sas_identify));
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mpt3sas_phy, next_phy,
8128c2ecf20Sopenharmony_ci	    &mpt3sas_port->phy_list, port_siblings) {
8138c2ecf20Sopenharmony_ci		if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
8148c2ecf20Sopenharmony_ci			dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
8158c2ecf20Sopenharmony_ci			    "remove: sas_addr(0x%016llx), phy(%d)\n",
8168c2ecf20Sopenharmony_ci			    (unsigned long long)
8178c2ecf20Sopenharmony_ci			    mpt3sas_port->remote_identify.sas_address,
8188c2ecf20Sopenharmony_ci			    mpt3sas_phy->phy_id);
8198c2ecf20Sopenharmony_ci		mpt3sas_phy->phy_belongs_to_port = 0;
8208c2ecf20Sopenharmony_ci		if (!ioc->remove_host)
8218c2ecf20Sopenharmony_ci			sas_port_delete_phy(mpt3sas_port->port,
8228c2ecf20Sopenharmony_ci						mpt3sas_phy->phy);
8238c2ecf20Sopenharmony_ci		list_del(&mpt3sas_phy->port_siblings);
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci	if (!ioc->remove_host)
8268c2ecf20Sopenharmony_ci		sas_port_delete(mpt3sas_port->port);
8278c2ecf20Sopenharmony_ci	ioc_info(ioc, "%s: removed: sas_addr(0x%016llx)\n",
8288c2ecf20Sopenharmony_ci	    __func__, (unsigned long long)sas_address);
8298c2ecf20Sopenharmony_ci	kfree(mpt3sas_port);
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci/**
8338c2ecf20Sopenharmony_ci * mpt3sas_transport_add_host_phy - report sas_host phy to transport
8348c2ecf20Sopenharmony_ci * @ioc: per adapter object
8358c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
8368c2ecf20Sopenharmony_ci * @phy_pg0: sas phy page 0
8378c2ecf20Sopenharmony_ci * @parent_dev: parent device class object
8388c2ecf20Sopenharmony_ci *
8398c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
8408c2ecf20Sopenharmony_ci */
8418c2ecf20Sopenharmony_ciint
8428c2ecf20Sopenharmony_cimpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
8438c2ecf20Sopenharmony_ci	*mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	struct sas_phy *phy;
8468c2ecf20Sopenharmony_ci	int phy_index = mpt3sas_phy->phy_id;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
8508c2ecf20Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
8518c2ecf20Sopenharmony_ci	if (!phy) {
8528c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
8538c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
8548c2ecf20Sopenharmony_ci		return -1;
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci	if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
8578c2ecf20Sopenharmony_ci	    &mpt3sas_phy->identify))) {
8588c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
8598c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
8608c2ecf20Sopenharmony_ci		sas_phy_free(phy);
8618c2ecf20Sopenharmony_ci		return -1;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci	phy->identify = mpt3sas_phy->identify;
8648c2ecf20Sopenharmony_ci	mpt3sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
8658c2ecf20Sopenharmony_ci	if (mpt3sas_phy->attached_handle)
8668c2ecf20Sopenharmony_ci		_transport_set_identify(ioc, mpt3sas_phy->attached_handle,
8678c2ecf20Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
8688c2ecf20Sopenharmony_ci	phy->identify.phy_identifier = mpt3sas_phy->phy_id;
8698c2ecf20Sopenharmony_ci	phy->negotiated_linkrate = _transport_convert_phy_link_rate(
8708c2ecf20Sopenharmony_ci	    phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
8718c2ecf20Sopenharmony_ci	phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
8728c2ecf20Sopenharmony_ci	    phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
8738c2ecf20Sopenharmony_ci	phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
8748c2ecf20Sopenharmony_ci	    phy_pg0.HwLinkRate >> 4);
8758c2ecf20Sopenharmony_ci	phy->minimum_linkrate = _transport_convert_phy_link_rate(
8768c2ecf20Sopenharmony_ci	    phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
8778c2ecf20Sopenharmony_ci	phy->maximum_linkrate = _transport_convert_phy_link_rate(
8788c2ecf20Sopenharmony_ci	    phy_pg0.ProgrammedLinkRate >> 4);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if ((sas_phy_add(phy))) {
8818c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
8828c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
8838c2ecf20Sopenharmony_ci		sas_phy_free(phy);
8848c2ecf20Sopenharmony_ci		return -1;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
8878c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &phy->dev,
8888c2ecf20Sopenharmony_ci		    "add: handle(0x%04x), sas_addr(0x%016llx)\n"
8898c2ecf20Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
8908c2ecf20Sopenharmony_ci		    mpt3sas_phy->handle, (unsigned long long)
8918c2ecf20Sopenharmony_ci		    mpt3sas_phy->identify.sas_address,
8928c2ecf20Sopenharmony_ci		    mpt3sas_phy->attached_handle,
8938c2ecf20Sopenharmony_ci		    (unsigned long long)
8948c2ecf20Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
8958c2ecf20Sopenharmony_ci	mpt3sas_phy->phy = phy;
8968c2ecf20Sopenharmony_ci	return 0;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci/**
9018c2ecf20Sopenharmony_ci * mpt3sas_transport_add_expander_phy - report expander phy to transport
9028c2ecf20Sopenharmony_ci * @ioc: per adapter object
9038c2ecf20Sopenharmony_ci * @mpt3sas_phy: mpt3sas per phy object
9048c2ecf20Sopenharmony_ci * @expander_pg1: expander page 1
9058c2ecf20Sopenharmony_ci * @parent_dev: parent device class object
9068c2ecf20Sopenharmony_ci *
9078c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
9088c2ecf20Sopenharmony_ci */
9098c2ecf20Sopenharmony_ciint
9108c2ecf20Sopenharmony_cimpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
9118c2ecf20Sopenharmony_ci	*mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
9128c2ecf20Sopenharmony_ci	struct device *parent_dev)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	struct sas_phy *phy;
9158c2ecf20Sopenharmony_ci	int phy_index = mpt3sas_phy->phy_id;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
9188c2ecf20Sopenharmony_ci	phy = sas_phy_alloc(parent_dev, phy_index);
9198c2ecf20Sopenharmony_ci	if (!phy) {
9208c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
9218c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
9228c2ecf20Sopenharmony_ci		return -1;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
9258c2ecf20Sopenharmony_ci	    &mpt3sas_phy->identify))) {
9268c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
9278c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
9288c2ecf20Sopenharmony_ci		sas_phy_free(phy);
9298c2ecf20Sopenharmony_ci		return -1;
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci	phy->identify = mpt3sas_phy->identify;
9328c2ecf20Sopenharmony_ci	mpt3sas_phy->attached_handle =
9338c2ecf20Sopenharmony_ci	    le16_to_cpu(expander_pg1.AttachedDevHandle);
9348c2ecf20Sopenharmony_ci	if (mpt3sas_phy->attached_handle)
9358c2ecf20Sopenharmony_ci		_transport_set_identify(ioc, mpt3sas_phy->attached_handle,
9368c2ecf20Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
9378c2ecf20Sopenharmony_ci	phy->identify.phy_identifier = mpt3sas_phy->phy_id;
9388c2ecf20Sopenharmony_ci	phy->negotiated_linkrate = _transport_convert_phy_link_rate(
9398c2ecf20Sopenharmony_ci	    expander_pg1.NegotiatedLinkRate &
9408c2ecf20Sopenharmony_ci	    MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
9418c2ecf20Sopenharmony_ci	phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
9428c2ecf20Sopenharmony_ci	    expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
9438c2ecf20Sopenharmony_ci	phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
9448c2ecf20Sopenharmony_ci	    expander_pg1.HwLinkRate >> 4);
9458c2ecf20Sopenharmony_ci	phy->minimum_linkrate = _transport_convert_phy_link_rate(
9468c2ecf20Sopenharmony_ci	    expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
9478c2ecf20Sopenharmony_ci	phy->maximum_linkrate = _transport_convert_phy_link_rate(
9488c2ecf20Sopenharmony_ci	    expander_pg1.ProgrammedLinkRate >> 4);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if ((sas_phy_add(phy))) {
9518c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
9528c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
9538c2ecf20Sopenharmony_ci		sas_phy_free(phy);
9548c2ecf20Sopenharmony_ci		return -1;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
9578c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &phy->dev,
9588c2ecf20Sopenharmony_ci		    "add: handle(0x%04x), sas_addr(0x%016llx)\n"
9598c2ecf20Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
9608c2ecf20Sopenharmony_ci		    mpt3sas_phy->handle, (unsigned long long)
9618c2ecf20Sopenharmony_ci		    mpt3sas_phy->identify.sas_address,
9628c2ecf20Sopenharmony_ci		    mpt3sas_phy->attached_handle,
9638c2ecf20Sopenharmony_ci		    (unsigned long long)
9648c2ecf20Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
9658c2ecf20Sopenharmony_ci	mpt3sas_phy->phy = phy;
9668c2ecf20Sopenharmony_ci	return 0;
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci/**
9708c2ecf20Sopenharmony_ci * mpt3sas_transport_update_links - refreshing phy link changes
9718c2ecf20Sopenharmony_ci * @ioc: per adapter object
9728c2ecf20Sopenharmony_ci * @sas_address: sas address of parent expander or sas host
9738c2ecf20Sopenharmony_ci * @handle: attached device handle
9748c2ecf20Sopenharmony_ci * @phy_number: phy number
9758c2ecf20Sopenharmony_ci * @link_rate: new link rate
9768c2ecf20Sopenharmony_ci */
9778c2ecf20Sopenharmony_civoid
9788c2ecf20Sopenharmony_cimpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
9798c2ecf20Sopenharmony_ci	u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	unsigned long flags;
9828c2ecf20Sopenharmony_ci	struct _sas_node *sas_node;
9838c2ecf20Sopenharmony_ci	struct _sas_phy *mpt3sas_phy;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery)
9868c2ecf20Sopenharmony_ci		return;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
9898c2ecf20Sopenharmony_ci	sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
9908c2ecf20Sopenharmony_ci	if (!sas_node) {
9918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
9928c2ecf20Sopenharmony_ci		return;
9938c2ecf20Sopenharmony_ci	}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	mpt3sas_phy = &sas_node->phy[phy_number];
9968c2ecf20Sopenharmony_ci	mpt3sas_phy->attached_handle = handle;
9978c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
9988c2ecf20Sopenharmony_ci	if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
9998c2ecf20Sopenharmony_ci		_transport_set_identify(ioc, handle,
10008c2ecf20Sopenharmony_ci		    &mpt3sas_phy->remote_identify);
10018c2ecf20Sopenharmony_ci		_transport_add_phy_to_an_existing_port(ioc, sas_node,
10028c2ecf20Sopenharmony_ci		    mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address);
10038c2ecf20Sopenharmony_ci	} else
10048c2ecf20Sopenharmony_ci		memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct
10058c2ecf20Sopenharmony_ci		    sas_identify));
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (mpt3sas_phy->phy)
10088c2ecf20Sopenharmony_ci		mpt3sas_phy->phy->negotiated_linkrate =
10098c2ecf20Sopenharmony_ci		    _transport_convert_phy_link_rate(link_rate);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
10128c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
10138c2ecf20Sopenharmony_ci		    "refresh: parent sas_addr(0x%016llx),\n"
10148c2ecf20Sopenharmony_ci		    "\tlink_rate(0x%02x), phy(%d)\n"
10158c2ecf20Sopenharmony_ci		    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
10168c2ecf20Sopenharmony_ci		    (unsigned long long)sas_address,
10178c2ecf20Sopenharmony_ci		    link_rate, phy_number, handle, (unsigned long long)
10188c2ecf20Sopenharmony_ci		    mpt3sas_phy->remote_identify.sas_address);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic inline void *
10228c2ecf20Sopenharmony_ciphy_to_ioc(struct sas_phy *phy)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
10258c2ecf20Sopenharmony_ci	return shost_priv(shost);
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic inline void *
10298c2ecf20Sopenharmony_cirphy_to_ioc(struct sas_rphy *rphy)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
10328c2ecf20Sopenharmony_ci	return shost_priv(shost);
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci/* report phy error log structure */
10368c2ecf20Sopenharmony_cistruct phy_error_log_request {
10378c2ecf20Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
10388c2ecf20Sopenharmony_ci	u8 function; /* 0x11 */
10398c2ecf20Sopenharmony_ci	u8 allocated_response_length;
10408c2ecf20Sopenharmony_ci	u8 request_length; /* 02 */
10418c2ecf20Sopenharmony_ci	u8 reserved_1[5];
10428c2ecf20Sopenharmony_ci	u8 phy_identifier;
10438c2ecf20Sopenharmony_ci	u8 reserved_2[2];
10448c2ecf20Sopenharmony_ci};
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci/* report phy error log reply structure */
10478c2ecf20Sopenharmony_cistruct phy_error_log_reply {
10488c2ecf20Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
10498c2ecf20Sopenharmony_ci	u8 function; /* 0x11 */
10508c2ecf20Sopenharmony_ci	u8 function_result;
10518c2ecf20Sopenharmony_ci	u8 response_length;
10528c2ecf20Sopenharmony_ci	__be16 expander_change_count;
10538c2ecf20Sopenharmony_ci	u8 reserved_1[3];
10548c2ecf20Sopenharmony_ci	u8 phy_identifier;
10558c2ecf20Sopenharmony_ci	u8 reserved_2[2];
10568c2ecf20Sopenharmony_ci	__be32 invalid_dword;
10578c2ecf20Sopenharmony_ci	__be32 running_disparity_error;
10588c2ecf20Sopenharmony_ci	__be32 loss_of_dword_sync;
10598c2ecf20Sopenharmony_ci	__be32 phy_reset_problem;
10608c2ecf20Sopenharmony_ci};
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci/**
10638c2ecf20Sopenharmony_ci * _transport_get_expander_phy_error_log - return expander counters
10648c2ecf20Sopenharmony_ci * @ioc: per adapter object
10658c2ecf20Sopenharmony_ci * @phy: The sas phy object
10668c2ecf20Sopenharmony_ci *
10678c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
10688c2ecf20Sopenharmony_ci *
10698c2ecf20Sopenharmony_ci */
10708c2ecf20Sopenharmony_cistatic int
10718c2ecf20Sopenharmony_ci_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc,
10728c2ecf20Sopenharmony_ci	struct sas_phy *phy)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
10758c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
10768c2ecf20Sopenharmony_ci	struct phy_error_log_request *phy_error_log_request;
10778c2ecf20Sopenharmony_ci	struct phy_error_log_reply *phy_error_log_reply;
10788c2ecf20Sopenharmony_ci	int rc;
10798c2ecf20Sopenharmony_ci	u16 smid;
10808c2ecf20Sopenharmony_ci	void *psge;
10818c2ecf20Sopenharmony_ci	u8 issue_reset = 0;
10828c2ecf20Sopenharmony_ci	void *data_out = NULL;
10838c2ecf20Sopenharmony_ci	dma_addr_t data_out_dma;
10848c2ecf20Sopenharmony_ci	u32 sz;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
10878c2ecf20Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
10888c2ecf20Sopenharmony_ci		return -EFAULT;
10898c2ecf20Sopenharmony_ci	}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
10948c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
10958c2ecf20Sopenharmony_ci		rc = -EAGAIN;
10968c2ecf20Sopenharmony_ci		goto out;
10978c2ecf20Sopenharmony_ci	}
10988c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
11018c2ecf20Sopenharmony_ci	if (rc)
11028c2ecf20Sopenharmony_ci		goto out;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
11058c2ecf20Sopenharmony_ci	if (!smid) {
11068c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
11078c2ecf20Sopenharmony_ci		rc = -EAGAIN;
11088c2ecf20Sopenharmony_ci		goto out;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
11128c2ecf20Sopenharmony_ci	ioc->transport_cmds.smid = smid;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	sz = sizeof(struct phy_error_log_request) +
11158c2ecf20Sopenharmony_ci	    sizeof(struct phy_error_log_reply);
11168c2ecf20Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
11178c2ecf20Sopenharmony_ci			GFP_KERNEL);
11188c2ecf20Sopenharmony_ci	if (!data_out) {
11198c2ecf20Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
11208c2ecf20Sopenharmony_ci		    __LINE__, __func__);
11218c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11228c2ecf20Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
11238c2ecf20Sopenharmony_ci		goto out;
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	rc = -EINVAL;
11278c2ecf20Sopenharmony_ci	memset(data_out, 0, sz);
11288c2ecf20Sopenharmony_ci	phy_error_log_request = data_out;
11298c2ecf20Sopenharmony_ci	phy_error_log_request->smp_frame_type = 0x40;
11308c2ecf20Sopenharmony_ci	phy_error_log_request->function = 0x11;
11318c2ecf20Sopenharmony_ci	phy_error_log_request->request_length = 2;
11328c2ecf20Sopenharmony_ci	phy_error_log_request->allocated_response_length = 0;
11338c2ecf20Sopenharmony_ci	phy_error_log_request->phy_identifier = phy->number;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
11368c2ecf20Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
11378c2ecf20Sopenharmony_ci	mpi_request->PhysicalPort = 0xFF;
11388c2ecf20Sopenharmony_ci	mpi_request->VF_ID = 0; /* TODO */
11398c2ecf20Sopenharmony_ci	mpi_request->VP_ID = 0;
11408c2ecf20Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
11418c2ecf20Sopenharmony_ci	mpi_request->RequestDataLength =
11428c2ecf20Sopenharmony_ci	    cpu_to_le16(sizeof(struct phy_error_log_request));
11438c2ecf20Sopenharmony_ci	psge = &mpi_request->SGL;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma,
11468c2ecf20Sopenharmony_ci		sizeof(struct phy_error_log_request),
11478c2ecf20Sopenharmony_ci	    data_out_dma + sizeof(struct phy_error_log_request),
11488c2ecf20Sopenharmony_ci	    sizeof(struct phy_error_log_reply));
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	dtransportprintk(ioc,
11518c2ecf20Sopenharmony_ci			 ioc_info(ioc, "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n",
11528c2ecf20Sopenharmony_ci				  (u64)phy->identify.sas_address,
11538c2ecf20Sopenharmony_ci				  phy->number));
11548c2ecf20Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
11558c2ecf20Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
11568c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
11598c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
11608c2ecf20Sopenharmony_ci		_debug_dump_mf(mpi_request,
11618c2ecf20Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
11628c2ecf20Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
11638c2ecf20Sopenharmony_ci			issue_reset = 1;
11648c2ecf20Sopenharmony_ci		goto issue_host_reset;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "phy_error_log - complete\n"));
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
11748c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - reply data transfer size(%d)\n",
11758c2ecf20Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
11788c2ecf20Sopenharmony_ci		    sizeof(struct phy_error_log_reply))
11798c2ecf20Sopenharmony_ci			goto out;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		phy_error_log_reply = data_out +
11828c2ecf20Sopenharmony_ci		    sizeof(struct phy_error_log_request);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
11858c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - function_result(%d)\n",
11868c2ecf20Sopenharmony_ci					  phy_error_log_reply->function_result));
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci		phy->invalid_dword_count =
11898c2ecf20Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->invalid_dword);
11908c2ecf20Sopenharmony_ci		phy->running_disparity_error_count =
11918c2ecf20Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->running_disparity_error);
11928c2ecf20Sopenharmony_ci		phy->loss_of_dword_sync_count =
11938c2ecf20Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
11948c2ecf20Sopenharmony_ci		phy->phy_reset_problem_count =
11958c2ecf20Sopenharmony_ci		    be32_to_cpu(phy_error_log_reply->phy_reset_problem);
11968c2ecf20Sopenharmony_ci		rc = 0;
11978c2ecf20Sopenharmony_ci	} else
11988c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
11998c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_error_log - no reply\n"));
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci issue_host_reset:
12028c2ecf20Sopenharmony_ci	if (issue_reset)
12038c2ecf20Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
12048c2ecf20Sopenharmony_ci out:
12058c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
12068c2ecf20Sopenharmony_ci	if (data_out)
12078c2ecf20Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, sz, data_out, data_out_dma);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
12108c2ecf20Sopenharmony_ci	return rc;
12118c2ecf20Sopenharmony_ci}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci/**
12148c2ecf20Sopenharmony_ci * _transport_get_linkerrors - return phy counters for both hba and expanders
12158c2ecf20Sopenharmony_ci * @phy: The sas phy object
12168c2ecf20Sopenharmony_ci *
12178c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
12188c2ecf20Sopenharmony_ci *
12198c2ecf20Sopenharmony_ci */
12208c2ecf20Sopenharmony_cistatic int
12218c2ecf20Sopenharmony_ci_transport_get_linkerrors(struct sas_phy *phy)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
12248c2ecf20Sopenharmony_ci	unsigned long flags;
12258c2ecf20Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
12268c2ecf20Sopenharmony_ci	Mpi2SasPhyPage1_t phy_pg1;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
12298c2ecf20Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
12308c2ecf20Sopenharmony_ci	    phy->identify.sas_address) == NULL) {
12318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
12328c2ecf20Sopenharmony_ci		return -EINVAL;
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
12378c2ecf20Sopenharmony_ci		return _transport_get_expander_phy_error_log(ioc, phy);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	/* get hba phy error logs */
12408c2ecf20Sopenharmony_ci	if ((mpt3sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
12418c2ecf20Sopenharmony_ci		    phy->number))) {
12428c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
12438c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
12448c2ecf20Sopenharmony_ci		return -ENXIO;
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
12488c2ecf20Sopenharmony_ci		ioc_info(ioc, "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n",
12498c2ecf20Sopenharmony_ci			 phy->number,
12508c2ecf20Sopenharmony_ci			 le16_to_cpu(mpi_reply.IOCStatus),
12518c2ecf20Sopenharmony_ci			 le32_to_cpu(mpi_reply.IOCLogInfo));
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
12548c2ecf20Sopenharmony_ci	phy->running_disparity_error_count =
12558c2ecf20Sopenharmony_ci	    le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
12568c2ecf20Sopenharmony_ci	phy->loss_of_dword_sync_count =
12578c2ecf20Sopenharmony_ci	    le32_to_cpu(phy_pg1.LossDwordSynchCount);
12588c2ecf20Sopenharmony_ci	phy->phy_reset_problem_count =
12598c2ecf20Sopenharmony_ci	    le32_to_cpu(phy_pg1.PhyResetProblemCount);
12608c2ecf20Sopenharmony_ci	return 0;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci/**
12648c2ecf20Sopenharmony_ci * _transport_get_enclosure_identifier -
12658c2ecf20Sopenharmony_ci * @rphy: The sas phy object
12668c2ecf20Sopenharmony_ci * @identifier: ?
12678c2ecf20Sopenharmony_ci *
12688c2ecf20Sopenharmony_ci * Obtain the enclosure logical id for an expander.
12698c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
12708c2ecf20Sopenharmony_ci */
12718c2ecf20Sopenharmony_cistatic int
12728c2ecf20Sopenharmony_ci_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
12738c2ecf20Sopenharmony_ci{
12748c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
12758c2ecf20Sopenharmony_ci	struct _sas_device *sas_device;
12768c2ecf20Sopenharmony_ci	unsigned long flags;
12778c2ecf20Sopenharmony_ci	int rc;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_device_lock, flags);
12808c2ecf20Sopenharmony_ci	sas_device = __mpt3sas_get_sdev_by_addr(ioc,
12818c2ecf20Sopenharmony_ci	    rphy->identify.sas_address);
12828c2ecf20Sopenharmony_ci	if (sas_device) {
12838c2ecf20Sopenharmony_ci		*identifier = sas_device->enclosure_logical_id;
12848c2ecf20Sopenharmony_ci		rc = 0;
12858c2ecf20Sopenharmony_ci		sas_device_put(sas_device);
12868c2ecf20Sopenharmony_ci	} else {
12878c2ecf20Sopenharmony_ci		*identifier = 0;
12888c2ecf20Sopenharmony_ci		rc = -ENXIO;
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
12928c2ecf20Sopenharmony_ci	return rc;
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci/**
12968c2ecf20Sopenharmony_ci * _transport_get_bay_identifier -
12978c2ecf20Sopenharmony_ci * @rphy: The sas phy object
12988c2ecf20Sopenharmony_ci *
12998c2ecf20Sopenharmony_ci * Return: the slot id for a device that resides inside an enclosure.
13008c2ecf20Sopenharmony_ci */
13018c2ecf20Sopenharmony_cistatic int
13028c2ecf20Sopenharmony_ci_transport_get_bay_identifier(struct sas_rphy *rphy)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
13058c2ecf20Sopenharmony_ci	struct _sas_device *sas_device;
13068c2ecf20Sopenharmony_ci	unsigned long flags;
13078c2ecf20Sopenharmony_ci	int rc;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_device_lock, flags);
13108c2ecf20Sopenharmony_ci	sas_device = __mpt3sas_get_sdev_by_addr(ioc,
13118c2ecf20Sopenharmony_ci	    rphy->identify.sas_address);
13128c2ecf20Sopenharmony_ci	if (sas_device) {
13138c2ecf20Sopenharmony_ci		rc = sas_device->slot;
13148c2ecf20Sopenharmony_ci		sas_device_put(sas_device);
13158c2ecf20Sopenharmony_ci	} else {
13168c2ecf20Sopenharmony_ci		rc = -ENXIO;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
13198c2ecf20Sopenharmony_ci	return rc;
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci/* phy control request structure */
13238c2ecf20Sopenharmony_cistruct phy_control_request {
13248c2ecf20Sopenharmony_ci	u8 smp_frame_type; /* 0x40 */
13258c2ecf20Sopenharmony_ci	u8 function; /* 0x91 */
13268c2ecf20Sopenharmony_ci	u8 allocated_response_length;
13278c2ecf20Sopenharmony_ci	u8 request_length; /* 0x09 */
13288c2ecf20Sopenharmony_ci	u16 expander_change_count;
13298c2ecf20Sopenharmony_ci	u8 reserved_1[3];
13308c2ecf20Sopenharmony_ci	u8 phy_identifier;
13318c2ecf20Sopenharmony_ci	u8 phy_operation;
13328c2ecf20Sopenharmony_ci	u8 reserved_2[13];
13338c2ecf20Sopenharmony_ci	u64 attached_device_name;
13348c2ecf20Sopenharmony_ci	u8 programmed_min_physical_link_rate;
13358c2ecf20Sopenharmony_ci	u8 programmed_max_physical_link_rate;
13368c2ecf20Sopenharmony_ci	u8 reserved_3[6];
13378c2ecf20Sopenharmony_ci};
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci/* phy control reply structure */
13408c2ecf20Sopenharmony_cistruct phy_control_reply {
13418c2ecf20Sopenharmony_ci	u8 smp_frame_type; /* 0x41 */
13428c2ecf20Sopenharmony_ci	u8 function; /* 0x11 */
13438c2ecf20Sopenharmony_ci	u8 function_result;
13448c2ecf20Sopenharmony_ci	u8 response_length;
13458c2ecf20Sopenharmony_ci};
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci#define SMP_PHY_CONTROL_LINK_RESET	(0x01)
13488c2ecf20Sopenharmony_ci#define SMP_PHY_CONTROL_HARD_RESET	(0x02)
13498c2ecf20Sopenharmony_ci#define SMP_PHY_CONTROL_DISABLE		(0x03)
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci/**
13528c2ecf20Sopenharmony_ci * _transport_expander_phy_control - expander phy control
13538c2ecf20Sopenharmony_ci * @ioc: per adapter object
13548c2ecf20Sopenharmony_ci * @phy: The sas phy object
13558c2ecf20Sopenharmony_ci * @phy_operation: ?
13568c2ecf20Sopenharmony_ci *
13578c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
13588c2ecf20Sopenharmony_ci *
13598c2ecf20Sopenharmony_ci */
13608c2ecf20Sopenharmony_cistatic int
13618c2ecf20Sopenharmony_ci_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc,
13628c2ecf20Sopenharmony_ci	struct sas_phy *phy, u8 phy_operation)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
13658c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
13668c2ecf20Sopenharmony_ci	struct phy_control_request *phy_control_request;
13678c2ecf20Sopenharmony_ci	struct phy_control_reply *phy_control_reply;
13688c2ecf20Sopenharmony_ci	int rc;
13698c2ecf20Sopenharmony_ci	u16 smid;
13708c2ecf20Sopenharmony_ci	void *psge;
13718c2ecf20Sopenharmony_ci	u8 issue_reset = 0;
13728c2ecf20Sopenharmony_ci	void *data_out = NULL;
13738c2ecf20Sopenharmony_ci	dma_addr_t data_out_dma;
13748c2ecf20Sopenharmony_ci	u32 sz;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
13778c2ecf20Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
13788c2ecf20Sopenharmony_ci		return -EFAULT;
13798c2ecf20Sopenharmony_ci	}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	mutex_lock(&ioc->transport_cmds.mutex);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
13848c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n", __func__);
13858c2ecf20Sopenharmony_ci		rc = -EAGAIN;
13868c2ecf20Sopenharmony_ci		goto out;
13878c2ecf20Sopenharmony_ci	}
13888c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
13918c2ecf20Sopenharmony_ci	if (rc)
13928c2ecf20Sopenharmony_ci		goto out;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
13958c2ecf20Sopenharmony_ci	if (!smid) {
13968c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
13978c2ecf20Sopenharmony_ci		rc = -EAGAIN;
13988c2ecf20Sopenharmony_ci		goto out;
13998c2ecf20Sopenharmony_ci	}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
14028c2ecf20Sopenharmony_ci	ioc->transport_cmds.smid = smid;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	sz = sizeof(struct phy_control_request) +
14058c2ecf20Sopenharmony_ci	    sizeof(struct phy_control_reply);
14068c2ecf20Sopenharmony_ci	data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma,
14078c2ecf20Sopenharmony_ci			GFP_KERNEL);
14088c2ecf20Sopenharmony_ci	if (!data_out) {
14098c2ecf20Sopenharmony_ci		pr_err("failure at %s:%d/%s()!\n", __FILE__,
14108c2ecf20Sopenharmony_ci		    __LINE__, __func__);
14118c2ecf20Sopenharmony_ci		rc = -ENOMEM;
14128c2ecf20Sopenharmony_ci		mpt3sas_base_free_smid(ioc, smid);
14138c2ecf20Sopenharmony_ci		goto out;
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	rc = -EINVAL;
14178c2ecf20Sopenharmony_ci	memset(data_out, 0, sz);
14188c2ecf20Sopenharmony_ci	phy_control_request = data_out;
14198c2ecf20Sopenharmony_ci	phy_control_request->smp_frame_type = 0x40;
14208c2ecf20Sopenharmony_ci	phy_control_request->function = 0x91;
14218c2ecf20Sopenharmony_ci	phy_control_request->request_length = 9;
14228c2ecf20Sopenharmony_ci	phy_control_request->allocated_response_length = 0;
14238c2ecf20Sopenharmony_ci	phy_control_request->phy_identifier = phy->number;
14248c2ecf20Sopenharmony_ci	phy_control_request->phy_operation = phy_operation;
14258c2ecf20Sopenharmony_ci	phy_control_request->programmed_min_physical_link_rate =
14268c2ecf20Sopenharmony_ci	    phy->minimum_linkrate << 4;
14278c2ecf20Sopenharmony_ci	phy_control_request->programmed_max_physical_link_rate =
14288c2ecf20Sopenharmony_ci	    phy->maximum_linkrate << 4;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
14318c2ecf20Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
14328c2ecf20Sopenharmony_ci	mpi_request->PhysicalPort = 0xFF;
14338c2ecf20Sopenharmony_ci	mpi_request->VF_ID = 0; /* TODO */
14348c2ecf20Sopenharmony_ci	mpi_request->VP_ID = 0;
14358c2ecf20Sopenharmony_ci	mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
14368c2ecf20Sopenharmony_ci	mpi_request->RequestDataLength =
14378c2ecf20Sopenharmony_ci	    cpu_to_le16(sizeof(struct phy_error_log_request));
14388c2ecf20Sopenharmony_ci	psge = &mpi_request->SGL;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	ioc->build_sg(ioc, psge, data_out_dma,
14418c2ecf20Sopenharmony_ci			    sizeof(struct phy_control_request),
14428c2ecf20Sopenharmony_ci	    data_out_dma + sizeof(struct phy_control_request),
14438c2ecf20Sopenharmony_ci	    sizeof(struct phy_control_reply));
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	dtransportprintk(ioc,
14468c2ecf20Sopenharmony_ci			 ioc_info(ioc, "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n",
14478c2ecf20Sopenharmony_ci				  (u64)phy->identify.sas_address,
14488c2ecf20Sopenharmony_ci				  phy->number, phy_operation));
14498c2ecf20Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
14508c2ecf20Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
14518c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
14548c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
14558c2ecf20Sopenharmony_ci		_debug_dump_mf(mpi_request,
14568c2ecf20Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
14578c2ecf20Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
14588c2ecf20Sopenharmony_ci			issue_reset = 1;
14598c2ecf20Sopenharmony_ci		goto issue_host_reset;
14608c2ecf20Sopenharmony_ci	}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "phy_control - complete\n"));
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci		mpi_reply = ioc->transport_cmds.reply;
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
14698c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_control - reply data transfer size(%d)\n",
14708c2ecf20Sopenharmony_ci					  le16_to_cpu(mpi_reply->ResponseDataLength)));
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci		if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
14738c2ecf20Sopenharmony_ci		    sizeof(struct phy_control_reply))
14748c2ecf20Sopenharmony_ci			goto out;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci		phy_control_reply = data_out +
14778c2ecf20Sopenharmony_ci		    sizeof(struct phy_control_request);
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
14808c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_control - function_result(%d)\n",
14818c2ecf20Sopenharmony_ci					  phy_control_reply->function_result));
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		rc = 0;
14848c2ecf20Sopenharmony_ci	} else
14858c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
14868c2ecf20Sopenharmony_ci				 ioc_info(ioc, "phy_control - no reply\n"));
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci issue_host_reset:
14898c2ecf20Sopenharmony_ci	if (issue_reset)
14908c2ecf20Sopenharmony_ci		mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
14918c2ecf20Sopenharmony_ci out:
14928c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
14938c2ecf20Sopenharmony_ci	if (data_out)
14948c2ecf20Sopenharmony_ci		dma_free_coherent(&ioc->pdev->dev, sz, data_out,
14958c2ecf20Sopenharmony_ci				data_out_dma);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
14988c2ecf20Sopenharmony_ci	return rc;
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci/**
15028c2ecf20Sopenharmony_ci * _transport_phy_reset -
15038c2ecf20Sopenharmony_ci * @phy: The sas phy object
15048c2ecf20Sopenharmony_ci * @hard_reset:
15058c2ecf20Sopenharmony_ci *
15068c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
15078c2ecf20Sopenharmony_ci */
15088c2ecf20Sopenharmony_cistatic int
15098c2ecf20Sopenharmony_ci_transport_phy_reset(struct sas_phy *phy, int hard_reset)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
15128c2ecf20Sopenharmony_ci	Mpi2SasIoUnitControlReply_t mpi_reply;
15138c2ecf20Sopenharmony_ci	Mpi2SasIoUnitControlRequest_t mpi_request;
15148c2ecf20Sopenharmony_ci	unsigned long flags;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
15178c2ecf20Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
15188c2ecf20Sopenharmony_ci	    phy->identify.sas_address) == NULL) {
15198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
15208c2ecf20Sopenharmony_ci		return -EINVAL;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	/* handle expander phys */
15258c2ecf20Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
15268c2ecf20Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
15278c2ecf20Sopenharmony_ci		    (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
15288c2ecf20Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	/* handle hba phys */
15318c2ecf20Sopenharmony_ci	memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
15328c2ecf20Sopenharmony_ci	mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
15338c2ecf20Sopenharmony_ci	mpi_request.Operation = hard_reset ?
15348c2ecf20Sopenharmony_ci	    MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
15358c2ecf20Sopenharmony_ci	mpi_request.PhyNum = phy->number;
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
15388c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
15398c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
15408c2ecf20Sopenharmony_ci		return -ENXIO;
15418c2ecf20Sopenharmony_ci	}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
15448c2ecf20Sopenharmony_ci		ioc_info(ioc, "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
15458c2ecf20Sopenharmony_ci			 phy->number, le16_to_cpu(mpi_reply.IOCStatus),
15468c2ecf20Sopenharmony_ci			 le32_to_cpu(mpi_reply.IOCLogInfo));
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	return 0;
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci/**
15528c2ecf20Sopenharmony_ci * _transport_phy_enable - enable/disable phys
15538c2ecf20Sopenharmony_ci * @phy: The sas phy object
15548c2ecf20Sopenharmony_ci * @enable: enable phy when true
15558c2ecf20Sopenharmony_ci *
15568c2ecf20Sopenharmony_ci * Only support sas_host direct attached phys.
15578c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
15588c2ecf20Sopenharmony_ci */
15598c2ecf20Sopenharmony_cistatic int
15608c2ecf20Sopenharmony_ci_transport_phy_enable(struct sas_phy *phy, int enable)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
15638c2ecf20Sopenharmony_ci	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
15648c2ecf20Sopenharmony_ci	Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
15658c2ecf20Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
15668c2ecf20Sopenharmony_ci	u16 ioc_status;
15678c2ecf20Sopenharmony_ci	u16 sz;
15688c2ecf20Sopenharmony_ci	int rc = 0;
15698c2ecf20Sopenharmony_ci	unsigned long flags;
15708c2ecf20Sopenharmony_ci	int i, discovery_active;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
15738c2ecf20Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
15748c2ecf20Sopenharmony_ci	    phy->identify.sas_address) == NULL) {
15758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
15768c2ecf20Sopenharmony_ci		return -EINVAL;
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	/* handle expander phys */
15818c2ecf20Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address)
15828c2ecf20Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
15838c2ecf20Sopenharmony_ci		    (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
15848c2ecf20Sopenharmony_ci		    SMP_PHY_CONTROL_DISABLE);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	/* handle hba phys */
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* read sas_iounit page 0 */
15898c2ecf20Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
15908c2ecf20Sopenharmony_ci	    sizeof(Mpi2SasIOUnit0PhyData_t));
15918c2ecf20Sopenharmony_ci	sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
15928c2ecf20Sopenharmony_ci	if (!sas_iounit_pg0) {
15938c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
15948c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
15958c2ecf20Sopenharmony_ci		rc = -ENOMEM;
15968c2ecf20Sopenharmony_ci		goto out;
15978c2ecf20Sopenharmony_ci	}
15988c2ecf20Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
15998c2ecf20Sopenharmony_ci	    sas_iounit_pg0, sz))) {
16008c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
16018c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
16028c2ecf20Sopenharmony_ci		rc = -ENXIO;
16038c2ecf20Sopenharmony_ci		goto out;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
16068c2ecf20Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
16078c2ecf20Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
16088c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
16098c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
16108c2ecf20Sopenharmony_ci		rc = -EIO;
16118c2ecf20Sopenharmony_ci		goto out;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/* unable to enable/disable phys when when discovery is active */
16158c2ecf20Sopenharmony_ci	for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) {
16168c2ecf20Sopenharmony_ci		if (sas_iounit_pg0->PhyData[i].PortFlags &
16178c2ecf20Sopenharmony_ci		    MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
16188c2ecf20Sopenharmony_ci			ioc_err(ioc, "discovery is active on port = %d, phy = %d: unable to enable/disable phys, try again later!\n",
16198c2ecf20Sopenharmony_ci				sas_iounit_pg0->PhyData[i].Port, i);
16208c2ecf20Sopenharmony_ci			discovery_active = 1;
16218c2ecf20Sopenharmony_ci		}
16228c2ecf20Sopenharmony_ci	}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	if (discovery_active) {
16258c2ecf20Sopenharmony_ci		rc = -EAGAIN;
16268c2ecf20Sopenharmony_ci		goto out;
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	/* read sas_iounit page 1 */
16308c2ecf20Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
16318c2ecf20Sopenharmony_ci	    sizeof(Mpi2SasIOUnit1PhyData_t));
16328c2ecf20Sopenharmony_ci	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
16338c2ecf20Sopenharmony_ci	if (!sas_iounit_pg1) {
16348c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
16358c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
16368c2ecf20Sopenharmony_ci		rc = -ENOMEM;
16378c2ecf20Sopenharmony_ci		goto out;
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
16408c2ecf20Sopenharmony_ci	    sas_iounit_pg1, sz))) {
16418c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
16428c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
16438c2ecf20Sopenharmony_ci		rc = -ENXIO;
16448c2ecf20Sopenharmony_ci		goto out;
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
16478c2ecf20Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
16488c2ecf20Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
16498c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
16508c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
16518c2ecf20Sopenharmony_ci		rc = -EIO;
16528c2ecf20Sopenharmony_ci		goto out;
16538c2ecf20Sopenharmony_ci	}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	/* copy Port/PortFlags/PhyFlags from page 0 */
16568c2ecf20Sopenharmony_ci	for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
16578c2ecf20Sopenharmony_ci		sas_iounit_pg1->PhyData[i].Port =
16588c2ecf20Sopenharmony_ci		    sas_iounit_pg0->PhyData[i].Port;
16598c2ecf20Sopenharmony_ci		sas_iounit_pg1->PhyData[i].PortFlags =
16608c2ecf20Sopenharmony_ci		    (sas_iounit_pg0->PhyData[i].PortFlags &
16618c2ecf20Sopenharmony_ci		    MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG);
16628c2ecf20Sopenharmony_ci		sas_iounit_pg1->PhyData[i].PhyFlags =
16638c2ecf20Sopenharmony_ci		    (sas_iounit_pg0->PhyData[i].PhyFlags &
16648c2ecf20Sopenharmony_ci		    (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED +
16658c2ecf20Sopenharmony_ci		    MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED));
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	if (enable)
16698c2ecf20Sopenharmony_ci		sas_iounit_pg1->PhyData[phy->number].PhyFlags
16708c2ecf20Sopenharmony_ci		    &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
16718c2ecf20Sopenharmony_ci	else
16728c2ecf20Sopenharmony_ci		sas_iounit_pg1->PhyData[phy->number].PhyFlags
16738c2ecf20Sopenharmony_ci		    |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	/* link reset */
16788c2ecf20Sopenharmony_ci	if (enable)
16798c2ecf20Sopenharmony_ci		_transport_phy_reset(phy, 0);
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci out:
16828c2ecf20Sopenharmony_ci	kfree(sas_iounit_pg1);
16838c2ecf20Sopenharmony_ci	kfree(sas_iounit_pg0);
16848c2ecf20Sopenharmony_ci	return rc;
16858c2ecf20Sopenharmony_ci}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci/**
16888c2ecf20Sopenharmony_ci * _transport_phy_speed - set phy min/max link rates
16898c2ecf20Sopenharmony_ci * @phy: The sas phy object
16908c2ecf20Sopenharmony_ci * @rates: rates defined in sas_phy_linkrates
16918c2ecf20Sopenharmony_ci *
16928c2ecf20Sopenharmony_ci * Only support sas_host direct attached phys.
16938c2ecf20Sopenharmony_ci *
16948c2ecf20Sopenharmony_ci * Return: 0 for success, non-zero for failure.
16958c2ecf20Sopenharmony_ci */
16968c2ecf20Sopenharmony_cistatic int
16978c2ecf20Sopenharmony_ci_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
17008c2ecf20Sopenharmony_ci	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
17018c2ecf20Sopenharmony_ci	Mpi2SasPhyPage0_t phy_pg0;
17028c2ecf20Sopenharmony_ci	Mpi2ConfigReply_t mpi_reply;
17038c2ecf20Sopenharmony_ci	u16 ioc_status;
17048c2ecf20Sopenharmony_ci	u16 sz;
17058c2ecf20Sopenharmony_ci	int i;
17068c2ecf20Sopenharmony_ci	int rc = 0;
17078c2ecf20Sopenharmony_ci	unsigned long flags;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ioc->sas_node_lock, flags);
17108c2ecf20Sopenharmony_ci	if (_transport_sas_node_find_by_sas_address(ioc,
17118c2ecf20Sopenharmony_ci	    phy->identify.sas_address) == NULL) {
17128c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
17138c2ecf20Sopenharmony_ci		return -EINVAL;
17148c2ecf20Sopenharmony_ci	}
17158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	if (!rates->minimum_linkrate)
17188c2ecf20Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate;
17198c2ecf20Sopenharmony_ci	else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
17208c2ecf20Sopenharmony_ci		rates->minimum_linkrate = phy->minimum_linkrate_hw;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	if (!rates->maximum_linkrate)
17238c2ecf20Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate;
17248c2ecf20Sopenharmony_ci	else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
17258c2ecf20Sopenharmony_ci		rates->maximum_linkrate = phy->maximum_linkrate_hw;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	/* handle expander phys */
17288c2ecf20Sopenharmony_ci	if (phy->identify.sas_address != ioc->sas_hba.sas_address) {
17298c2ecf20Sopenharmony_ci		phy->minimum_linkrate = rates->minimum_linkrate;
17308c2ecf20Sopenharmony_ci		phy->maximum_linkrate = rates->maximum_linkrate;
17318c2ecf20Sopenharmony_ci		return _transport_expander_phy_control(ioc, phy,
17328c2ecf20Sopenharmony_ci		    SMP_PHY_CONTROL_LINK_RESET);
17338c2ecf20Sopenharmony_ci	}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	/* handle hba phys */
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	/* sas_iounit page 1 */
17388c2ecf20Sopenharmony_ci	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
17398c2ecf20Sopenharmony_ci	    sizeof(Mpi2SasIOUnit1PhyData_t));
17408c2ecf20Sopenharmony_ci	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
17418c2ecf20Sopenharmony_ci	if (!sas_iounit_pg1) {
17428c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
17438c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
17448c2ecf20Sopenharmony_ci		rc = -ENOMEM;
17458c2ecf20Sopenharmony_ci		goto out;
17468c2ecf20Sopenharmony_ci	}
17478c2ecf20Sopenharmony_ci	if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
17488c2ecf20Sopenharmony_ci	    sas_iounit_pg1, sz))) {
17498c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
17508c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
17518c2ecf20Sopenharmony_ci		rc = -ENXIO;
17528c2ecf20Sopenharmony_ci		goto out;
17538c2ecf20Sopenharmony_ci	}
17548c2ecf20Sopenharmony_ci	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
17558c2ecf20Sopenharmony_ci	    MPI2_IOCSTATUS_MASK;
17568c2ecf20Sopenharmony_ci	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
17578c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
17588c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
17598c2ecf20Sopenharmony_ci		rc = -EIO;
17608c2ecf20Sopenharmony_ci		goto out;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	for (i = 0; i < ioc->sas_hba.num_phys; i++) {
17648c2ecf20Sopenharmony_ci		if (phy->number != i) {
17658c2ecf20Sopenharmony_ci			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
17668c2ecf20Sopenharmony_ci			    (ioc->sas_hba.phy[i].phy->minimum_linkrate +
17678c2ecf20Sopenharmony_ci			    (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
17688c2ecf20Sopenharmony_ci		} else {
17698c2ecf20Sopenharmony_ci			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
17708c2ecf20Sopenharmony_ci			    (rates->minimum_linkrate +
17718c2ecf20Sopenharmony_ci			    (rates->maximum_linkrate << 4));
17728c2ecf20Sopenharmony_ci		}
17738c2ecf20Sopenharmony_ci	}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
17768c2ecf20Sopenharmony_ci	    sz)) {
17778c2ecf20Sopenharmony_ci		ioc_err(ioc, "failure at %s:%d/%s()!\n",
17788c2ecf20Sopenharmony_ci			__FILE__, __LINE__, __func__);
17798c2ecf20Sopenharmony_ci		rc = -ENXIO;
17808c2ecf20Sopenharmony_ci		goto out;
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	/* link reset */
17848c2ecf20Sopenharmony_ci	_transport_phy_reset(phy, 0);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/* read phy page 0, then update the rates in the sas transport phy */
17878c2ecf20Sopenharmony_ci	if (!mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
17888c2ecf20Sopenharmony_ci	    phy->number)) {
17898c2ecf20Sopenharmony_ci		phy->minimum_linkrate = _transport_convert_phy_link_rate(
17908c2ecf20Sopenharmony_ci		    phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
17918c2ecf20Sopenharmony_ci		phy->maximum_linkrate = _transport_convert_phy_link_rate(
17928c2ecf20Sopenharmony_ci		    phy_pg0.ProgrammedLinkRate >> 4);
17938c2ecf20Sopenharmony_ci		phy->negotiated_linkrate = _transport_convert_phy_link_rate(
17948c2ecf20Sopenharmony_ci		    phy_pg0.NegotiatedLinkRate &
17958c2ecf20Sopenharmony_ci		    MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci out:
17998c2ecf20Sopenharmony_ci	kfree(sas_iounit_pg1);
18008c2ecf20Sopenharmony_ci	return rc;
18018c2ecf20Sopenharmony_ci}
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_cistatic int
18048c2ecf20Sopenharmony_ci_transport_map_smp_buffer(struct device *dev, struct bsg_buffer *buf,
18058c2ecf20Sopenharmony_ci		dma_addr_t *dma_addr, size_t *dma_len, void **p)
18068c2ecf20Sopenharmony_ci{
18078c2ecf20Sopenharmony_ci	/* Check if the request is split across multiple segments */
18088c2ecf20Sopenharmony_ci	if (buf->sg_cnt > 1) {
18098c2ecf20Sopenharmony_ci		*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
18108c2ecf20Sopenharmony_ci				GFP_KERNEL);
18118c2ecf20Sopenharmony_ci		if (!*p)
18128c2ecf20Sopenharmony_ci			return -ENOMEM;
18138c2ecf20Sopenharmony_ci		*dma_len = buf->payload_len;
18148c2ecf20Sopenharmony_ci	} else {
18158c2ecf20Sopenharmony_ci		if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL))
18168c2ecf20Sopenharmony_ci			return -ENOMEM;
18178c2ecf20Sopenharmony_ci		*dma_addr = sg_dma_address(buf->sg_list);
18188c2ecf20Sopenharmony_ci		*dma_len = sg_dma_len(buf->sg_list);
18198c2ecf20Sopenharmony_ci		*p = NULL;
18208c2ecf20Sopenharmony_ci	}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	return 0;
18238c2ecf20Sopenharmony_ci}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic void
18268c2ecf20Sopenharmony_ci_transport_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf,
18278c2ecf20Sopenharmony_ci		dma_addr_t dma_addr, void *p)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	if (p)
18308c2ecf20Sopenharmony_ci		dma_free_coherent(dev, buf->payload_len, p, dma_addr);
18318c2ecf20Sopenharmony_ci	else
18328c2ecf20Sopenharmony_ci		dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci/**
18368c2ecf20Sopenharmony_ci * _transport_smp_handler - transport portal for smp passthru
18378c2ecf20Sopenharmony_ci * @job: ?
18388c2ecf20Sopenharmony_ci * @shost: shost object
18398c2ecf20Sopenharmony_ci * @rphy: sas transport rphy object
18408c2ecf20Sopenharmony_ci *
18418c2ecf20Sopenharmony_ci * This used primarily for smp_utils.
18428c2ecf20Sopenharmony_ci * Example:
18438c2ecf20Sopenharmony_ci *           smp_rep_general /sys/class/bsg/expander-5:0
18448c2ecf20Sopenharmony_ci */
18458c2ecf20Sopenharmony_cistatic void
18468c2ecf20Sopenharmony_ci_transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
18478c2ecf20Sopenharmony_ci		struct sas_rphy *rphy)
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
18508c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughRequest_t *mpi_request;
18518c2ecf20Sopenharmony_ci	Mpi2SmpPassthroughReply_t *mpi_reply;
18528c2ecf20Sopenharmony_ci	int rc;
18538c2ecf20Sopenharmony_ci	u16 smid;
18548c2ecf20Sopenharmony_ci	void *psge;
18558c2ecf20Sopenharmony_ci	dma_addr_t dma_addr_in;
18568c2ecf20Sopenharmony_ci	dma_addr_t dma_addr_out;
18578c2ecf20Sopenharmony_ci	void *addr_in = NULL;
18588c2ecf20Sopenharmony_ci	void *addr_out = NULL;
18598c2ecf20Sopenharmony_ci	size_t dma_len_in;
18608c2ecf20Sopenharmony_ci	size_t dma_len_out;
18618c2ecf20Sopenharmony_ci	unsigned int reslen = 0;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	if (ioc->shost_recovery || ioc->pci_error_recovery) {
18648c2ecf20Sopenharmony_ci		ioc_info(ioc, "%s: host reset in progress!\n", __func__);
18658c2ecf20Sopenharmony_ci		rc = -EFAULT;
18668c2ecf20Sopenharmony_ci		goto job_done;
18678c2ecf20Sopenharmony_ci	}
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
18708c2ecf20Sopenharmony_ci	if (rc)
18718c2ecf20Sopenharmony_ci		goto job_done;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
18748c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: transport_cmds in use\n",
18758c2ecf20Sopenharmony_ci			__func__);
18768c2ecf20Sopenharmony_ci		rc = -EAGAIN;
18778c2ecf20Sopenharmony_ci		goto out;
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_PENDING;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->request_payload,
18828c2ecf20Sopenharmony_ci			&dma_addr_out, &dma_len_out, &addr_out);
18838c2ecf20Sopenharmony_ci	if (rc)
18848c2ecf20Sopenharmony_ci		goto out;
18858c2ecf20Sopenharmony_ci	if (addr_out) {
18868c2ecf20Sopenharmony_ci		sg_copy_to_buffer(job->request_payload.sg_list,
18878c2ecf20Sopenharmony_ci				job->request_payload.sg_cnt, addr_out,
18888c2ecf20Sopenharmony_ci				job->request_payload.payload_len);
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
18928c2ecf20Sopenharmony_ci			&dma_addr_in, &dma_len_in, &addr_in);
18938c2ecf20Sopenharmony_ci	if (rc)
18948c2ecf20Sopenharmony_ci		goto unmap_out;
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ci	rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT);
18978c2ecf20Sopenharmony_ci	if (rc)
18988c2ecf20Sopenharmony_ci		goto unmap_in;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
19018c2ecf20Sopenharmony_ci	if (!smid) {
19028c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: failed obtaining a smid\n", __func__);
19038c2ecf20Sopenharmony_ci		rc = -EAGAIN;
19048c2ecf20Sopenharmony_ci		goto unmap_in;
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	rc = 0;
19088c2ecf20Sopenharmony_ci	mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
19098c2ecf20Sopenharmony_ci	ioc->transport_cmds.smid = smid;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
19128c2ecf20Sopenharmony_ci	mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
19138c2ecf20Sopenharmony_ci	mpi_request->PhysicalPort = 0xFF;
19148c2ecf20Sopenharmony_ci	mpi_request->SASAddress = (rphy) ?
19158c2ecf20Sopenharmony_ci	    cpu_to_le64(rphy->identify.sas_address) :
19168c2ecf20Sopenharmony_ci	    cpu_to_le64(ioc->sas_hba.sas_address);
19178c2ecf20Sopenharmony_ci	mpi_request->RequestDataLength = cpu_to_le16(dma_len_out - 4);
19188c2ecf20Sopenharmony_ci	psge = &mpi_request->SGL;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	ioc->build_sg(ioc, psge, dma_addr_out, dma_len_out - 4, dma_addr_in,
19218c2ecf20Sopenharmony_ci			dma_len_in - 4);
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	dtransportprintk(ioc,
19248c2ecf20Sopenharmony_ci			 ioc_info(ioc, "%s: sending smp request\n", __func__));
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	init_completion(&ioc->transport_cmds.done);
19278c2ecf20Sopenharmony_ci	ioc->put_smid_default(ioc, smid);
19288c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
19318c2ecf20Sopenharmony_ci		ioc_err(ioc, "%s: timeout\n", __func__);
19328c2ecf20Sopenharmony_ci		_debug_dump_mf(mpi_request,
19338c2ecf20Sopenharmony_ci		    sizeof(Mpi2SmpPassthroughRequest_t)/4);
19348c2ecf20Sopenharmony_ci		if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) {
19358c2ecf20Sopenharmony_ci			mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER);
19368c2ecf20Sopenharmony_ci			rc = -ETIMEDOUT;
19378c2ecf20Sopenharmony_ci			goto unmap_in;
19388c2ecf20Sopenharmony_ci		}
19398c2ecf20Sopenharmony_ci	}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	dtransportprintk(ioc, ioc_info(ioc, "%s - complete\n", __func__));
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	if (!(ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID)) {
19448c2ecf20Sopenharmony_ci		dtransportprintk(ioc,
19458c2ecf20Sopenharmony_ci				 ioc_info(ioc, "%s: no reply\n", __func__));
19468c2ecf20Sopenharmony_ci		rc = -ENXIO;
19478c2ecf20Sopenharmony_ci		goto unmap_in;
19488c2ecf20Sopenharmony_ci	}
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	mpi_reply = ioc->transport_cmds.reply;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	dtransportprintk(ioc,
19538c2ecf20Sopenharmony_ci			 ioc_info(ioc, "%s: reply data transfer size(%d)\n",
19548c2ecf20Sopenharmony_ci				  __func__,
19558c2ecf20Sopenharmony_ci				  le16_to_cpu(mpi_reply->ResponseDataLength)));
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	memcpy(job->reply, mpi_reply, sizeof(*mpi_reply));
19588c2ecf20Sopenharmony_ci	job->reply_len = sizeof(*mpi_reply);
19598c2ecf20Sopenharmony_ci	reslen = le16_to_cpu(mpi_reply->ResponseDataLength);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	if (addr_in) {
19628c2ecf20Sopenharmony_ci		sg_copy_to_buffer(job->reply_payload.sg_list,
19638c2ecf20Sopenharmony_ci				job->reply_payload.sg_cnt, addr_in,
19648c2ecf20Sopenharmony_ci				job->reply_payload.payload_len);
19658c2ecf20Sopenharmony_ci	}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	rc = 0;
19688c2ecf20Sopenharmony_ci unmap_in:
19698c2ecf20Sopenharmony_ci	_transport_unmap_smp_buffer(&ioc->pdev->dev, &job->reply_payload,
19708c2ecf20Sopenharmony_ci			dma_addr_in, addr_in);
19718c2ecf20Sopenharmony_ci unmap_out:
19728c2ecf20Sopenharmony_ci	_transport_unmap_smp_buffer(&ioc->pdev->dev, &job->request_payload,
19738c2ecf20Sopenharmony_ci			dma_addr_out, addr_out);
19748c2ecf20Sopenharmony_ci out:
19758c2ecf20Sopenharmony_ci	ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
19768c2ecf20Sopenharmony_ci	mutex_unlock(&ioc->transport_cmds.mutex);
19778c2ecf20Sopenharmony_cijob_done:
19788c2ecf20Sopenharmony_ci	bsg_job_done(job, rc, reslen);
19798c2ecf20Sopenharmony_ci}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistruct sas_function_template mpt3sas_transport_functions = {
19828c2ecf20Sopenharmony_ci	.get_linkerrors		= _transport_get_linkerrors,
19838c2ecf20Sopenharmony_ci	.get_enclosure_identifier = _transport_get_enclosure_identifier,
19848c2ecf20Sopenharmony_ci	.get_bay_identifier	= _transport_get_bay_identifier,
19858c2ecf20Sopenharmony_ci	.phy_reset		= _transport_phy_reset,
19868c2ecf20Sopenharmony_ci	.phy_enable		= _transport_phy_enable,
19878c2ecf20Sopenharmony_ci	.set_phy_speed		= _transport_phy_speed,
19888c2ecf20Sopenharmony_ci	.smp_handler		= _transport_smp_handler,
19898c2ecf20Sopenharmony_ci};
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cistruct scsi_transport_template *mpt3sas_transport_template;
1992