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