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