1f92363d1SSreekanth Reddy /* 2f92363d1SSreekanth Reddy * SAS Transport Layer for MPT (Message Passing Technology) based controllers 3f92363d1SSreekanth Reddy * 4f92363d1SSreekanth Reddy * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c 5a4ffce0dSSreekanth Reddy * Copyright (C) 2012-2014 LSI Corporation 6a03bd153SSreekanth Reddy * Copyright (C) 2013-2014 Avago Technologies 7a03bd153SSreekanth Reddy * (mailto: MPT-FusionLinux.pdl@avagotech.com) 8f92363d1SSreekanth Reddy * 9f92363d1SSreekanth Reddy * This program is free software; you can redistribute it and/or 10f92363d1SSreekanth Reddy * modify it under the terms of the GNU General Public License 11f92363d1SSreekanth Reddy * as published by the Free Software Foundation; either version 2 12f92363d1SSreekanth Reddy * of the License, or (at your option) any later version. 13f92363d1SSreekanth Reddy * 14f92363d1SSreekanth Reddy * This program is distributed in the hope that it will be useful, 15f92363d1SSreekanth Reddy * but WITHOUT ANY WARRANTY; without even the implied warranty of 16f92363d1SSreekanth Reddy * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17f92363d1SSreekanth Reddy * GNU General Public License for more details. 18f92363d1SSreekanth Reddy * 19f92363d1SSreekanth Reddy * NO WARRANTY 20f92363d1SSreekanth Reddy * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 21f92363d1SSreekanth Reddy * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 22f92363d1SSreekanth Reddy * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 23f92363d1SSreekanth Reddy * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 24f92363d1SSreekanth Reddy * solely responsible for determining the appropriateness of using and 25f92363d1SSreekanth Reddy * distributing the Program and assumes all risks associated with its 26f92363d1SSreekanth Reddy * exercise of rights under this Agreement, including but not limited to 27f92363d1SSreekanth Reddy * the risks and costs of program errors, damage to or loss of data, 28f92363d1SSreekanth Reddy * programs or equipment, and unavailability or interruption of operations. 29f92363d1SSreekanth Reddy 30f92363d1SSreekanth Reddy * DISCLAIMER OF LIABILITY 31f92363d1SSreekanth Reddy * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 32f92363d1SSreekanth Reddy * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33f92363d1SSreekanth Reddy * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 34f92363d1SSreekanth Reddy * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 35f92363d1SSreekanth Reddy * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 36f92363d1SSreekanth Reddy * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 37f92363d1SSreekanth Reddy * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 38f92363d1SSreekanth Reddy 39f92363d1SSreekanth Reddy * You should have received a copy of the GNU General Public License 40f92363d1SSreekanth Reddy * along with this program; if not, write to the Free Software 41f92363d1SSreekanth Reddy * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 42f92363d1SSreekanth Reddy * USA. 43f92363d1SSreekanth Reddy */ 44f92363d1SSreekanth Reddy 45f92363d1SSreekanth Reddy #include <linux/module.h> 46f92363d1SSreekanth Reddy #include <linux/kernel.h> 47f92363d1SSreekanth Reddy #include <linux/init.h> 48f92363d1SSreekanth Reddy #include <linux/errno.h> 49f92363d1SSreekanth Reddy #include <linux/sched.h> 50f92363d1SSreekanth Reddy #include <linux/workqueue.h> 51f92363d1SSreekanth Reddy #include <linux/delay.h> 52f92363d1SSreekanth Reddy #include <linux/pci.h> 53f92363d1SSreekanth Reddy 54f92363d1SSreekanth Reddy #include <scsi/scsi.h> 55f92363d1SSreekanth Reddy #include <scsi/scsi_cmnd.h> 56f92363d1SSreekanth Reddy #include <scsi/scsi_device.h> 57f92363d1SSreekanth Reddy #include <scsi/scsi_host.h> 58f92363d1SSreekanth Reddy #include <scsi/scsi_transport_sas.h> 59f92363d1SSreekanth Reddy #include <scsi/scsi_dbg.h> 60f92363d1SSreekanth Reddy 61f92363d1SSreekanth Reddy #include "mpt3sas_base.h" 62f92363d1SSreekanth Reddy 63f92363d1SSreekanth Reddy /** 649d0348a9SSreekanth Reddy * _transport_get_port_id_by_sas_phy - get zone's port id that Phy belong to 65*54cb88dcSLee Jones * @phy: sas_phy object 669d0348a9SSreekanth Reddy * 679d0348a9SSreekanth Reddy * Return Port number 689d0348a9SSreekanth Reddy */ 699d0348a9SSreekanth Reddy static inline u8 709d0348a9SSreekanth Reddy _transport_get_port_id_by_sas_phy(struct sas_phy *phy) 719d0348a9SSreekanth Reddy { 729d0348a9SSreekanth Reddy u8 port_id = 0xFF; 739d0348a9SSreekanth Reddy struct hba_port *port = phy->hostdata; 749d0348a9SSreekanth Reddy 759d0348a9SSreekanth Reddy if (port) 769d0348a9SSreekanth Reddy port_id = port->port_id; 779d0348a9SSreekanth Reddy 789d0348a9SSreekanth Reddy return port_id; 799d0348a9SSreekanth Reddy } 809d0348a9SSreekanth Reddy 819d0348a9SSreekanth Reddy /** 82f92363d1SSreekanth Reddy * _transport_sas_node_find_by_sas_address - sas node search 83f92363d1SSreekanth Reddy * @ioc: per adapter object 84f92363d1SSreekanth Reddy * @sas_address: sas address of expander or sas host 857d310f24SSreekanth Reddy * @port: hba port entry 86f92363d1SSreekanth Reddy * Context: Calling function should acquire ioc->sas_node_lock. 87f92363d1SSreekanth Reddy * 88f92363d1SSreekanth Reddy * Search for either hba phys or expander device based on handle, then returns 89f92363d1SSreekanth Reddy * the sas_node object. 90f92363d1SSreekanth Reddy */ 91f92363d1SSreekanth Reddy static struct _sas_node * 92f92363d1SSreekanth Reddy _transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, 937d310f24SSreekanth Reddy u64 sas_address, struct hba_port *port) 94f92363d1SSreekanth Reddy { 95f92363d1SSreekanth Reddy if (ioc->sas_hba.sas_address == sas_address) 96f92363d1SSreekanth Reddy return &ioc->sas_hba; 97f92363d1SSreekanth Reddy else 98f92363d1SSreekanth Reddy return mpt3sas_scsih_expander_find_by_sas_address(ioc, 997d310f24SSreekanth Reddy sas_address, port); 100f92363d1SSreekanth Reddy } 101f92363d1SSreekanth Reddy 102f92363d1SSreekanth Reddy /** 1039d0348a9SSreekanth Reddy * _transport_get_port_id_by_rphy - Get Port number from rphy object 1049d0348a9SSreekanth Reddy * @ioc: per adapter object 1059d0348a9SSreekanth Reddy * @rphy: sas_rphy object 1069d0348a9SSreekanth Reddy * 1079d0348a9SSreekanth Reddy * Returns Port number. 1089d0348a9SSreekanth Reddy */ 1099d0348a9SSreekanth Reddy static u8 1109d0348a9SSreekanth Reddy _transport_get_port_id_by_rphy(struct MPT3SAS_ADAPTER *ioc, 1119d0348a9SSreekanth Reddy struct sas_rphy *rphy) 1129d0348a9SSreekanth Reddy { 1139d0348a9SSreekanth Reddy struct _sas_node *sas_expander; 1149d0348a9SSreekanth Reddy struct _sas_device *sas_device; 1159d0348a9SSreekanth Reddy unsigned long flags; 1169d0348a9SSreekanth Reddy u8 port_id = 0xFF; 1179d0348a9SSreekanth Reddy 1189d0348a9SSreekanth Reddy if (!rphy) 1199d0348a9SSreekanth Reddy return port_id; 1209d0348a9SSreekanth Reddy 1219d0348a9SSreekanth Reddy if (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE || 1229d0348a9SSreekanth Reddy rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE) { 1239d0348a9SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 1249d0348a9SSreekanth Reddy list_for_each_entry(sas_expander, 1259d0348a9SSreekanth Reddy &ioc->sas_expander_list, list) { 1269d0348a9SSreekanth Reddy if (sas_expander->rphy == rphy) { 1279d0348a9SSreekanth Reddy port_id = sas_expander->port->port_id; 1289d0348a9SSreekanth Reddy break; 1299d0348a9SSreekanth Reddy } 1309d0348a9SSreekanth Reddy } 1319d0348a9SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1329d0348a9SSreekanth Reddy } else if (rphy->identify.device_type == SAS_END_DEVICE) { 1339d0348a9SSreekanth Reddy spin_lock_irqsave(&ioc->sas_device_lock, flags); 1349d0348a9SSreekanth Reddy sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); 1359d0348a9SSreekanth Reddy if (sas_device) { 1369d0348a9SSreekanth Reddy port_id = sas_device->port->port_id; 1379d0348a9SSreekanth Reddy sas_device_put(sas_device); 1389d0348a9SSreekanth Reddy } 1399d0348a9SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_device_lock, flags); 1409d0348a9SSreekanth Reddy } 1419d0348a9SSreekanth Reddy 1429d0348a9SSreekanth Reddy return port_id; 1439d0348a9SSreekanth Reddy } 1449d0348a9SSreekanth Reddy 1459d0348a9SSreekanth Reddy /** 146f92363d1SSreekanth Reddy * _transport_convert_phy_link_rate - 147f92363d1SSreekanth Reddy * @link_rate: link rate returned from mpt firmware 148f92363d1SSreekanth Reddy * 149f92363d1SSreekanth Reddy * Convert link_rate from mpi fusion into sas_transport form. 150f92363d1SSreekanth Reddy */ 151f92363d1SSreekanth Reddy static enum sas_linkrate 152f92363d1SSreekanth Reddy _transport_convert_phy_link_rate(u8 link_rate) 153f92363d1SSreekanth Reddy { 154f92363d1SSreekanth Reddy enum sas_linkrate rc; 155f92363d1SSreekanth Reddy 156f92363d1SSreekanth Reddy switch (link_rate) { 157f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_1_5: 158f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_1_5_GBPS; 159f92363d1SSreekanth Reddy break; 160f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_3_0: 161f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_3_0_GBPS; 162f92363d1SSreekanth Reddy break; 163f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_6_0: 164f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_6_0_GBPS; 165f92363d1SSreekanth Reddy break; 166f92363d1SSreekanth Reddy case MPI25_SAS_NEG_LINK_RATE_12_0: 167f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_12_0_GBPS; 168f92363d1SSreekanth Reddy break; 169f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: 170f92363d1SSreekanth Reddy rc = SAS_PHY_DISABLED; 171f92363d1SSreekanth Reddy break; 172f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED: 173f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_FAILED; 174f92363d1SSreekanth Reddy break; 175f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR: 176f92363d1SSreekanth Reddy rc = SAS_SATA_PORT_SELECTOR; 177f92363d1SSreekanth Reddy break; 178f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS: 179f92363d1SSreekanth Reddy rc = SAS_PHY_RESET_IN_PROGRESS; 180f92363d1SSreekanth Reddy break; 181f92363d1SSreekanth Reddy 182f92363d1SSreekanth Reddy default: 183f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE: 184f92363d1SSreekanth Reddy case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE: 185f92363d1SSreekanth Reddy rc = SAS_LINK_RATE_UNKNOWN; 186f92363d1SSreekanth Reddy break; 187f92363d1SSreekanth Reddy } 188f92363d1SSreekanth Reddy return rc; 189f92363d1SSreekanth Reddy } 190f92363d1SSreekanth Reddy 191f92363d1SSreekanth Reddy /** 192f92363d1SSreekanth Reddy * _transport_set_identify - set identify for phys and end devices 193f92363d1SSreekanth Reddy * @ioc: per adapter object 194f92363d1SSreekanth Reddy * @handle: device handle 195f92363d1SSreekanth Reddy * @identify: sas identify info 196f92363d1SSreekanth Reddy * 197f92363d1SSreekanth Reddy * Populates sas identify info. 198f92363d1SSreekanth Reddy * 1994beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 200f92363d1SSreekanth Reddy */ 201f92363d1SSreekanth Reddy static int 202f92363d1SSreekanth Reddy _transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle, 203f92363d1SSreekanth Reddy struct sas_identify *identify) 204f92363d1SSreekanth Reddy { 205f92363d1SSreekanth Reddy Mpi2SasDevicePage0_t sas_device_pg0; 206f92363d1SSreekanth Reddy Mpi2ConfigReply_t mpi_reply; 207f92363d1SSreekanth Reddy u32 device_info; 208f92363d1SSreekanth Reddy u32 ioc_status; 209f92363d1SSreekanth Reddy 210f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) { 2114dc74b2eSJoe Perches ioc_info(ioc, "%s: host reset in progress!\n", __func__); 212f92363d1SSreekanth Reddy return -EFAULT; 213f92363d1SSreekanth Reddy } 214f92363d1SSreekanth Reddy 215f92363d1SSreekanth Reddy if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, 216f92363d1SSreekanth Reddy MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { 217919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 218919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 219f92363d1SSreekanth Reddy return -ENXIO; 220f92363d1SSreekanth Reddy } 221f92363d1SSreekanth Reddy 222f92363d1SSreekanth Reddy ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & 223f92363d1SSreekanth Reddy MPI2_IOCSTATUS_MASK; 224f92363d1SSreekanth Reddy if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { 225919d8a3fSJoe Perches ioc_err(ioc, "handle(0x%04x), ioc_status(0x%04x) failure at %s:%d/%s()!\n", 226919d8a3fSJoe Perches handle, ioc_status, __FILE__, __LINE__, __func__); 227f92363d1SSreekanth Reddy return -EIO; 228f92363d1SSreekanth Reddy } 229f92363d1SSreekanth Reddy 230f92363d1SSreekanth Reddy memset(identify, 0, sizeof(struct sas_identify)); 231f92363d1SSreekanth Reddy device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); 232f92363d1SSreekanth Reddy 233f92363d1SSreekanth Reddy /* sas_address */ 234f92363d1SSreekanth Reddy identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); 235f92363d1SSreekanth Reddy 236f92363d1SSreekanth Reddy /* phy number of the parent device this device is linked to */ 237f92363d1SSreekanth Reddy identify->phy_identifier = sas_device_pg0.PhyNum; 238f92363d1SSreekanth Reddy 239f92363d1SSreekanth Reddy /* device_type */ 240f92363d1SSreekanth Reddy switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { 241f92363d1SSreekanth Reddy case MPI2_SAS_DEVICE_INFO_NO_DEVICE: 242f92363d1SSreekanth Reddy identify->device_type = SAS_PHY_UNUSED; 243f92363d1SSreekanth Reddy break; 244f92363d1SSreekanth Reddy case MPI2_SAS_DEVICE_INFO_END_DEVICE: 245f92363d1SSreekanth Reddy identify->device_type = SAS_END_DEVICE; 246f92363d1SSreekanth Reddy break; 247f92363d1SSreekanth Reddy case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER: 248f92363d1SSreekanth Reddy identify->device_type = SAS_EDGE_EXPANDER_DEVICE; 249f92363d1SSreekanth Reddy break; 250f92363d1SSreekanth Reddy case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER: 251f92363d1SSreekanth Reddy identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; 252f92363d1SSreekanth Reddy break; 253f92363d1SSreekanth Reddy } 254f92363d1SSreekanth Reddy 255f92363d1SSreekanth Reddy /* initiator_port_protocols */ 256f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR) 257f92363d1SSreekanth Reddy identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; 258f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR) 259f92363d1SSreekanth Reddy identify->initiator_port_protocols |= SAS_PROTOCOL_STP; 260f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR) 261f92363d1SSreekanth Reddy identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; 262f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST) 263f92363d1SSreekanth Reddy identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; 264f92363d1SSreekanth Reddy 265f92363d1SSreekanth Reddy /* target_port_protocols */ 266f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) 267f92363d1SSreekanth Reddy identify->target_port_protocols |= SAS_PROTOCOL_SSP; 268f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) 269f92363d1SSreekanth Reddy identify->target_port_protocols |= SAS_PROTOCOL_STP; 270f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) 271f92363d1SSreekanth Reddy identify->target_port_protocols |= SAS_PROTOCOL_SMP; 272f92363d1SSreekanth Reddy if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) 273f92363d1SSreekanth Reddy identify->target_port_protocols |= SAS_PROTOCOL_SATA; 274f92363d1SSreekanth Reddy 275f92363d1SSreekanth Reddy return 0; 276f92363d1SSreekanth Reddy } 277f92363d1SSreekanth Reddy 278f92363d1SSreekanth Reddy /** 279f92363d1SSreekanth Reddy * mpt3sas_transport_done - internal transport layer callback handler. 280f92363d1SSreekanth Reddy * @ioc: per adapter object 281f92363d1SSreekanth Reddy * @smid: system request message index 282f92363d1SSreekanth Reddy * @msix_index: MSIX table index supplied by the OS 283f92363d1SSreekanth Reddy * @reply: reply message frame(lower 32bit addr) 284f92363d1SSreekanth Reddy * 285f92363d1SSreekanth Reddy * Callback handler when sending internal generated transport cmds. 286f92363d1SSreekanth Reddy * The callback index passed is `ioc->transport_cb_idx` 287f92363d1SSreekanth Reddy * 2884beb4867SBart Van Assche * Return: 1 meaning mf should be freed from _base_interrupt 289f92363d1SSreekanth Reddy * 0 means the mf is freed from this function. 290f92363d1SSreekanth Reddy */ 291f92363d1SSreekanth Reddy u8 292f92363d1SSreekanth Reddy mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, 293f92363d1SSreekanth Reddy u32 reply) 294f92363d1SSreekanth Reddy { 295f92363d1SSreekanth Reddy MPI2DefaultReply_t *mpi_reply; 296f92363d1SSreekanth Reddy 297f92363d1SSreekanth Reddy mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); 298f92363d1SSreekanth Reddy if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED) 299f92363d1SSreekanth Reddy return 1; 300f92363d1SSreekanth Reddy if (ioc->transport_cmds.smid != smid) 301f92363d1SSreekanth Reddy return 1; 302f92363d1SSreekanth Reddy ioc->transport_cmds.status |= MPT3_CMD_COMPLETE; 303f92363d1SSreekanth Reddy if (mpi_reply) { 304f92363d1SSreekanth Reddy memcpy(ioc->transport_cmds.reply, mpi_reply, 305f92363d1SSreekanth Reddy mpi_reply->MsgLength*4); 306f92363d1SSreekanth Reddy ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID; 307f92363d1SSreekanth Reddy } 308f92363d1SSreekanth Reddy ioc->transport_cmds.status &= ~MPT3_CMD_PENDING; 309f92363d1SSreekanth Reddy complete(&ioc->transport_cmds.done); 310f92363d1SSreekanth Reddy return 1; 311f92363d1SSreekanth Reddy } 312f92363d1SSreekanth Reddy 313f92363d1SSreekanth Reddy /* report manufacture request structure */ 314f92363d1SSreekanth Reddy struct rep_manu_request { 315f92363d1SSreekanth Reddy u8 smp_frame_type; 316f92363d1SSreekanth Reddy u8 function; 317f92363d1SSreekanth Reddy u8 reserved; 318f92363d1SSreekanth Reddy u8 request_length; 319f92363d1SSreekanth Reddy }; 320f92363d1SSreekanth Reddy 321f92363d1SSreekanth Reddy /* report manufacture reply structure */ 322f92363d1SSreekanth Reddy struct rep_manu_reply { 323f92363d1SSreekanth Reddy u8 smp_frame_type; /* 0x41 */ 324f92363d1SSreekanth Reddy u8 function; /* 0x01 */ 325f92363d1SSreekanth Reddy u8 function_result; 326f92363d1SSreekanth Reddy u8 response_length; 327f92363d1SSreekanth Reddy u16 expander_change_count; 328f92363d1SSreekanth Reddy u8 reserved0[2]; 329f92363d1SSreekanth Reddy u8 sas_format; 330f92363d1SSreekanth Reddy u8 reserved2[3]; 331f92363d1SSreekanth Reddy u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; 332f92363d1SSreekanth Reddy u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; 333f92363d1SSreekanth Reddy u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; 334f92363d1SSreekanth Reddy u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; 335f92363d1SSreekanth Reddy u16 component_id; 336f92363d1SSreekanth Reddy u8 component_revision_id; 337f92363d1SSreekanth Reddy u8 reserved3; 338f92363d1SSreekanth Reddy u8 vendor_specific[8]; 339f92363d1SSreekanth Reddy }; 340f92363d1SSreekanth Reddy 341f92363d1SSreekanth Reddy /** 342*54cb88dcSLee Jones * _transport_expander_report_manufacture - obtain SMP report_manufacture 343f92363d1SSreekanth Reddy * @ioc: per adapter object 344f92363d1SSreekanth Reddy * @sas_address: expander sas address 345f92363d1SSreekanth Reddy * @edev: the sas_expander_device object 346*54cb88dcSLee Jones * @port_id: Port ID number 347f92363d1SSreekanth Reddy * 348f92363d1SSreekanth Reddy * Fills in the sas_expander_device object when SMP port is created. 349f92363d1SSreekanth Reddy * 3504beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 351f92363d1SSreekanth Reddy */ 352f92363d1SSreekanth Reddy static int 353f92363d1SSreekanth Reddy _transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc, 3549d0348a9SSreekanth Reddy u64 sas_address, struct sas_expander_device *edev, u8 port_id) 355f92363d1SSreekanth Reddy { 356f92363d1SSreekanth Reddy Mpi2SmpPassthroughRequest_t *mpi_request; 357f92363d1SSreekanth Reddy Mpi2SmpPassthroughReply_t *mpi_reply; 358f92363d1SSreekanth Reddy struct rep_manu_reply *manufacture_reply; 359f92363d1SSreekanth Reddy struct rep_manu_request *manufacture_request; 360f92363d1SSreekanth Reddy int rc; 361f92363d1SSreekanth Reddy u16 smid; 362f92363d1SSreekanth Reddy void *psge; 363f92363d1SSreekanth Reddy u8 issue_reset = 0; 364f92363d1SSreekanth Reddy void *data_out = NULL; 365f92363d1SSreekanth Reddy dma_addr_t data_out_dma; 366f92363d1SSreekanth Reddy dma_addr_t data_in_dma; 367f92363d1SSreekanth Reddy size_t data_in_sz; 368f92363d1SSreekanth Reddy size_t data_out_sz; 369f92363d1SSreekanth Reddy 370f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) { 3714dc74b2eSJoe Perches ioc_info(ioc, "%s: host reset in progress!\n", __func__); 372f92363d1SSreekanth Reddy return -EFAULT; 373f92363d1SSreekanth Reddy } 374f92363d1SSreekanth Reddy 375f92363d1SSreekanth Reddy mutex_lock(&ioc->transport_cmds.mutex); 376f92363d1SSreekanth Reddy 377f92363d1SSreekanth Reddy if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { 378919d8a3fSJoe Perches ioc_err(ioc, "%s: transport_cmds in use\n", __func__); 379f92363d1SSreekanth Reddy rc = -EAGAIN; 380f92363d1SSreekanth Reddy goto out; 381f92363d1SSreekanth Reddy } 382f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_PENDING; 383f92363d1SSreekanth Reddy 384f4305749SSuganath Prabu rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT); 385f4305749SSuganath Prabu if (rc) 386f92363d1SSreekanth Reddy goto out; 387f92363d1SSreekanth Reddy 388f92363d1SSreekanth Reddy smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx); 389f92363d1SSreekanth Reddy if (!smid) { 390919d8a3fSJoe Perches ioc_err(ioc, "%s: failed obtaining a smid\n", __func__); 391f92363d1SSreekanth Reddy rc = -EAGAIN; 392f92363d1SSreekanth Reddy goto out; 393f92363d1SSreekanth Reddy } 394f92363d1SSreekanth Reddy 395f92363d1SSreekanth Reddy rc = 0; 396f92363d1SSreekanth Reddy mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); 397f92363d1SSreekanth Reddy ioc->transport_cmds.smid = smid; 398f92363d1SSreekanth Reddy 399f92363d1SSreekanth Reddy data_out_sz = sizeof(struct rep_manu_request); 400f92363d1SSreekanth Reddy data_in_sz = sizeof(struct rep_manu_reply); 4011c2048bdSChristoph Hellwig data_out = dma_alloc_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz, 4021c2048bdSChristoph Hellwig &data_out_dma, GFP_KERNEL); 403f92363d1SSreekanth Reddy if (!data_out) { 404f92363d1SSreekanth Reddy pr_err("failure at %s:%d/%s()!\n", __FILE__, 405f92363d1SSreekanth Reddy __LINE__, __func__); 406f92363d1SSreekanth Reddy rc = -ENOMEM; 407f92363d1SSreekanth Reddy mpt3sas_base_free_smid(ioc, smid); 408f92363d1SSreekanth Reddy goto out; 409f92363d1SSreekanth Reddy } 410f92363d1SSreekanth Reddy 411f92363d1SSreekanth Reddy data_in_dma = data_out_dma + sizeof(struct rep_manu_request); 412f92363d1SSreekanth Reddy 413f92363d1SSreekanth Reddy manufacture_request = data_out; 414f92363d1SSreekanth Reddy manufacture_request->smp_frame_type = 0x40; 415f92363d1SSreekanth Reddy manufacture_request->function = 1; 416f92363d1SSreekanth Reddy manufacture_request->reserved = 0; 417f92363d1SSreekanth Reddy manufacture_request->request_length = 0; 418f92363d1SSreekanth Reddy 419f92363d1SSreekanth Reddy memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); 420f92363d1SSreekanth Reddy mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 4219d0348a9SSreekanth Reddy mpi_request->PhysicalPort = port_id; 422f92363d1SSreekanth Reddy mpi_request->SASAddress = cpu_to_le64(sas_address); 423f92363d1SSreekanth Reddy mpi_request->RequestDataLength = cpu_to_le16(data_out_sz); 424f92363d1SSreekanth Reddy psge = &mpi_request->SGL; 425f92363d1SSreekanth Reddy 426f92363d1SSreekanth Reddy ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma, 427f92363d1SSreekanth Reddy data_in_sz); 428f92363d1SSreekanth Reddy 429919d8a3fSJoe Perches dtransportprintk(ioc, 430919d8a3fSJoe Perches ioc_info(ioc, "report_manufacture - send to sas_addr(0x%016llx)\n", 431919d8a3fSJoe Perches (u64)sas_address)); 432f92363d1SSreekanth Reddy init_completion(&ioc->transport_cmds.done); 433078a4cc1SSuganath Prabu S ioc->put_smid_default(ioc, smid); 4348bbb1cf6SCalvin Owens wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); 435f92363d1SSreekanth Reddy 436f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { 437919d8a3fSJoe Perches ioc_err(ioc, "%s: timeout\n", __func__); 438f92363d1SSreekanth Reddy _debug_dump_mf(mpi_request, 439f92363d1SSreekanth Reddy sizeof(Mpi2SmpPassthroughRequest_t)/4); 440f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) 441f92363d1SSreekanth Reddy issue_reset = 1; 442f92363d1SSreekanth Reddy goto issue_host_reset; 443f92363d1SSreekanth Reddy } 444f92363d1SSreekanth Reddy 445919d8a3fSJoe Perches dtransportprintk(ioc, ioc_info(ioc, "report_manufacture - complete\n")); 446f92363d1SSreekanth Reddy 447f92363d1SSreekanth Reddy if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { 448f92363d1SSreekanth Reddy u8 *tmp; 449f92363d1SSreekanth Reddy 450f92363d1SSreekanth Reddy mpi_reply = ioc->transport_cmds.reply; 451f92363d1SSreekanth Reddy 452919d8a3fSJoe Perches dtransportprintk(ioc, 453919d8a3fSJoe Perches ioc_info(ioc, "report_manufacture - reply data transfer size(%d)\n", 454919d8a3fSJoe Perches le16_to_cpu(mpi_reply->ResponseDataLength))); 455f92363d1SSreekanth Reddy 456f92363d1SSreekanth Reddy if (le16_to_cpu(mpi_reply->ResponseDataLength) != 457f92363d1SSreekanth Reddy sizeof(struct rep_manu_reply)) 458f92363d1SSreekanth Reddy goto out; 459f92363d1SSreekanth Reddy 460f92363d1SSreekanth Reddy manufacture_reply = data_out + sizeof(struct rep_manu_request); 461f92363d1SSreekanth Reddy strncpy(edev->vendor_id, manufacture_reply->vendor_id, 462f92363d1SSreekanth Reddy SAS_EXPANDER_VENDOR_ID_LEN); 463f92363d1SSreekanth Reddy strncpy(edev->product_id, manufacture_reply->product_id, 464f92363d1SSreekanth Reddy SAS_EXPANDER_PRODUCT_ID_LEN); 465f92363d1SSreekanth Reddy strncpy(edev->product_rev, manufacture_reply->product_rev, 466f92363d1SSreekanth Reddy SAS_EXPANDER_PRODUCT_REV_LEN); 467f92363d1SSreekanth Reddy edev->level = manufacture_reply->sas_format & 1; 468f92363d1SSreekanth Reddy if (edev->level) { 469f92363d1SSreekanth Reddy strncpy(edev->component_vendor_id, 470f92363d1SSreekanth Reddy manufacture_reply->component_vendor_id, 471f92363d1SSreekanth Reddy SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); 472f92363d1SSreekanth Reddy tmp = (u8 *)&manufacture_reply->component_id; 473f92363d1SSreekanth Reddy edev->component_id = tmp[0] << 8 | tmp[1]; 474f92363d1SSreekanth Reddy edev->component_revision_id = 475f92363d1SSreekanth Reddy manufacture_reply->component_revision_id; 476f92363d1SSreekanth Reddy } 477f92363d1SSreekanth Reddy } else 478919d8a3fSJoe Perches dtransportprintk(ioc, 479919d8a3fSJoe Perches ioc_info(ioc, "report_manufacture - no reply\n")); 480f92363d1SSreekanth Reddy 481f92363d1SSreekanth Reddy issue_host_reset: 482f92363d1SSreekanth Reddy if (issue_reset) 48398c56ad3SCalvin Owens mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); 484f92363d1SSreekanth Reddy out: 485f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_NOT_USED; 486f92363d1SSreekanth Reddy if (data_out) 4871c2048bdSChristoph Hellwig dma_free_coherent(&ioc->pdev->dev, data_out_sz + data_in_sz, 488f92363d1SSreekanth Reddy data_out, data_out_dma); 489f92363d1SSreekanth Reddy 490f92363d1SSreekanth Reddy mutex_unlock(&ioc->transport_cmds.mutex); 491f92363d1SSreekanth Reddy return rc; 492f92363d1SSreekanth Reddy } 493f92363d1SSreekanth Reddy 494f92363d1SSreekanth Reddy 495f92363d1SSreekanth Reddy /** 496f92363d1SSreekanth Reddy * _transport_delete_port - helper function to removing a port 497f92363d1SSreekanth Reddy * @ioc: per adapter object 498f92363d1SSreekanth Reddy * @mpt3sas_port: mpt3sas per port object 499f92363d1SSreekanth Reddy */ 500f92363d1SSreekanth Reddy static void 501f92363d1SSreekanth Reddy _transport_delete_port(struct MPT3SAS_ADAPTER *ioc, 502f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port) 503f92363d1SSreekanth Reddy { 504f92363d1SSreekanth Reddy u64 sas_address = mpt3sas_port->remote_identify.sas_address; 5057d310f24SSreekanth Reddy struct hba_port *port = mpt3sas_port->hba_port; 506f92363d1SSreekanth Reddy enum sas_device_type device_type = 507f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.device_type; 508f92363d1SSreekanth Reddy 509f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &mpt3sas_port->port->dev, 510f92363d1SSreekanth Reddy "remove: sas_addr(0x%016llx)\n", 511f92363d1SSreekanth Reddy (unsigned long long) sas_address); 512f92363d1SSreekanth Reddy 513f92363d1SSreekanth Reddy ioc->logging_level |= MPT_DEBUG_TRANSPORT; 514f92363d1SSreekanth Reddy if (device_type == SAS_END_DEVICE) 5157d310f24SSreekanth Reddy mpt3sas_device_remove_by_sas_address(ioc, 5167d310f24SSreekanth Reddy sas_address, port); 517f92363d1SSreekanth Reddy else if (device_type == SAS_EDGE_EXPANDER_DEVICE || 518f92363d1SSreekanth Reddy device_type == SAS_FANOUT_EXPANDER_DEVICE) 5197d310f24SSreekanth Reddy mpt3sas_expander_remove(ioc, sas_address, port); 520f92363d1SSreekanth Reddy ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; 521f92363d1SSreekanth Reddy } 522f92363d1SSreekanth Reddy 523f92363d1SSreekanth Reddy /** 524f92363d1SSreekanth Reddy * _transport_delete_phy - helper function to removing single phy from port 525f92363d1SSreekanth Reddy * @ioc: per adapter object 526f92363d1SSreekanth Reddy * @mpt3sas_port: mpt3sas per port object 527f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 528f92363d1SSreekanth Reddy */ 529f92363d1SSreekanth Reddy static void 530f92363d1SSreekanth Reddy _transport_delete_phy(struct MPT3SAS_ADAPTER *ioc, 531f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port, struct _sas_phy *mpt3sas_phy) 532f92363d1SSreekanth Reddy { 533f92363d1SSreekanth Reddy u64 sas_address = mpt3sas_port->remote_identify.sas_address; 534f92363d1SSreekanth Reddy 535f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev, 536f92363d1SSreekanth Reddy "remove: sas_addr(0x%016llx), phy(%d)\n", 537f92363d1SSreekanth Reddy (unsigned long long) sas_address, mpt3sas_phy->phy_id); 538f92363d1SSreekanth Reddy 539f92363d1SSreekanth Reddy list_del(&mpt3sas_phy->port_siblings); 540f92363d1SSreekanth Reddy mpt3sas_port->num_phys--; 541f92363d1SSreekanth Reddy sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy); 542f92363d1SSreekanth Reddy mpt3sas_phy->phy_belongs_to_port = 0; 543f92363d1SSreekanth Reddy } 544f92363d1SSreekanth Reddy 545f92363d1SSreekanth Reddy /** 546f92363d1SSreekanth Reddy * _transport_add_phy - helper function to adding single phy to port 547f92363d1SSreekanth Reddy * @ioc: per adapter object 548f92363d1SSreekanth Reddy * @mpt3sas_port: mpt3sas per port object 549f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 550f92363d1SSreekanth Reddy */ 551f92363d1SSreekanth Reddy static void 552f92363d1SSreekanth Reddy _transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port, 553f92363d1SSreekanth Reddy struct _sas_phy *mpt3sas_phy) 554f92363d1SSreekanth Reddy { 555f92363d1SSreekanth Reddy u64 sas_address = mpt3sas_port->remote_identify.sas_address; 556f92363d1SSreekanth Reddy 557f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev, 558f92363d1SSreekanth Reddy "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) 559f92363d1SSreekanth Reddy sas_address, mpt3sas_phy->phy_id); 560f92363d1SSreekanth Reddy 561f92363d1SSreekanth Reddy list_add_tail(&mpt3sas_phy->port_siblings, &mpt3sas_port->phy_list); 562f92363d1SSreekanth Reddy mpt3sas_port->num_phys++; 563f92363d1SSreekanth Reddy sas_port_add_phy(mpt3sas_port->port, mpt3sas_phy->phy); 564f92363d1SSreekanth Reddy mpt3sas_phy->phy_belongs_to_port = 1; 565f92363d1SSreekanth Reddy } 566f92363d1SSreekanth Reddy 567f92363d1SSreekanth Reddy /** 568c71ccf93SSreekanth Reddy * mpt3sas_transport_add_phy_to_an_existing_port - adding new phy to existing port 569f92363d1SSreekanth Reddy * @ioc: per adapter object 570f92363d1SSreekanth Reddy * @sas_node: sas node object (either expander or sas host) 571f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 572f92363d1SSreekanth Reddy * @sas_address: sas address of device/expander were phy needs to be added to 573c71ccf93SSreekanth Reddy * @port: hba port entry 574f92363d1SSreekanth Reddy */ 575c71ccf93SSreekanth Reddy void 576c71ccf93SSreekanth Reddy mpt3sas_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc, 577f92363d1SSreekanth Reddy struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy, 578c71ccf93SSreekanth Reddy u64 sas_address, struct hba_port *port) 579f92363d1SSreekanth Reddy { 580f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port; 581f92363d1SSreekanth Reddy struct _sas_phy *phy_srch; 582f92363d1SSreekanth Reddy 583f92363d1SSreekanth Reddy if (mpt3sas_phy->phy_belongs_to_port == 1) 584f92363d1SSreekanth Reddy return; 585f92363d1SSreekanth Reddy 586c71ccf93SSreekanth Reddy if (!port) 587c71ccf93SSreekanth Reddy return; 588c71ccf93SSreekanth Reddy 589f92363d1SSreekanth Reddy list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list, 590f92363d1SSreekanth Reddy port_list) { 591f92363d1SSreekanth Reddy if (mpt3sas_port->remote_identify.sas_address != 592f92363d1SSreekanth Reddy sas_address) 593f92363d1SSreekanth Reddy continue; 594c71ccf93SSreekanth Reddy if (mpt3sas_port->hba_port != port) 595c71ccf93SSreekanth Reddy continue; 596f92363d1SSreekanth Reddy list_for_each_entry(phy_srch, &mpt3sas_port->phy_list, 597f92363d1SSreekanth Reddy port_siblings) { 598f92363d1SSreekanth Reddy if (phy_srch == mpt3sas_phy) 599f92363d1SSreekanth Reddy return; 600f92363d1SSreekanth Reddy } 601f92363d1SSreekanth Reddy _transport_add_phy(ioc, mpt3sas_port, mpt3sas_phy); 602f92363d1SSreekanth Reddy return; 603f92363d1SSreekanth Reddy } 604f92363d1SSreekanth Reddy 605f92363d1SSreekanth Reddy } 606f92363d1SSreekanth Reddy 607f92363d1SSreekanth Reddy /** 608c71ccf93SSreekanth Reddy * mpt3sas_transport_del_phy_from_an_existing_port - delete phy from existing port 609f92363d1SSreekanth Reddy * @ioc: per adapter object 610f92363d1SSreekanth Reddy * @sas_node: sas node object (either expander or sas host) 611f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 612f92363d1SSreekanth Reddy */ 613c71ccf93SSreekanth Reddy void 614c71ccf93SSreekanth Reddy mpt3sas_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc, 615f92363d1SSreekanth Reddy struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy) 616f92363d1SSreekanth Reddy { 617f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port, *next; 618f92363d1SSreekanth Reddy struct _sas_phy *phy_srch; 619f92363d1SSreekanth Reddy 620f92363d1SSreekanth Reddy if (mpt3sas_phy->phy_belongs_to_port == 0) 621f92363d1SSreekanth Reddy return; 622f92363d1SSreekanth Reddy 623f92363d1SSreekanth Reddy list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list, 624f92363d1SSreekanth Reddy port_list) { 625f92363d1SSreekanth Reddy list_for_each_entry(phy_srch, &mpt3sas_port->phy_list, 626f92363d1SSreekanth Reddy port_siblings) { 627f92363d1SSreekanth Reddy if (phy_srch != mpt3sas_phy) 628f92363d1SSreekanth Reddy continue; 629f92363d1SSreekanth Reddy 630c71ccf93SSreekanth Reddy /* 631c71ccf93SSreekanth Reddy * Don't delete port during host reset, 632c71ccf93SSreekanth Reddy * just delete phy. 633c71ccf93SSreekanth Reddy */ 634c71ccf93SSreekanth Reddy if (mpt3sas_port->num_phys == 1 && !ioc->shost_recovery) 635f92363d1SSreekanth Reddy _transport_delete_port(ioc, mpt3sas_port); 636f92363d1SSreekanth Reddy else 637f92363d1SSreekanth Reddy _transport_delete_phy(ioc, mpt3sas_port, 638f92363d1SSreekanth Reddy mpt3sas_phy); 639f92363d1SSreekanth Reddy return; 640f92363d1SSreekanth Reddy } 641f92363d1SSreekanth Reddy } 642f92363d1SSreekanth Reddy } 643f92363d1SSreekanth Reddy 644f92363d1SSreekanth Reddy /** 645f92363d1SSreekanth Reddy * _transport_sanity_check - sanity check when adding a new port 646f92363d1SSreekanth Reddy * @ioc: per adapter object 647f92363d1SSreekanth Reddy * @sas_node: sas node object (either expander or sas host) 648f92363d1SSreekanth Reddy * @sas_address: sas address of device being added 6497d310f24SSreekanth Reddy * @port: hba port entry 650f92363d1SSreekanth Reddy * 651f92363d1SSreekanth Reddy * See the explanation above from _transport_delete_duplicate_port 652f92363d1SSreekanth Reddy */ 653f92363d1SSreekanth Reddy static void 654f92363d1SSreekanth Reddy _transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node, 6557d310f24SSreekanth Reddy u64 sas_address, struct hba_port *port) 656f92363d1SSreekanth Reddy { 657f92363d1SSreekanth Reddy int i; 658f92363d1SSreekanth Reddy 659f92363d1SSreekanth Reddy for (i = 0; i < sas_node->num_phys; i++) { 660f92363d1SSreekanth Reddy if (sas_node->phy[i].remote_identify.sas_address != sas_address) 661f92363d1SSreekanth Reddy continue; 6627d310f24SSreekanth Reddy if (sas_node->phy[i].port != port) 6637d310f24SSreekanth Reddy continue; 664f92363d1SSreekanth Reddy if (sas_node->phy[i].phy_belongs_to_port == 1) 665c71ccf93SSreekanth Reddy mpt3sas_transport_del_phy_from_an_existing_port(ioc, 666c71ccf93SSreekanth Reddy sas_node, &sas_node->phy[i]); 667f92363d1SSreekanth Reddy } 668f92363d1SSreekanth Reddy } 669f92363d1SSreekanth Reddy 670f92363d1SSreekanth Reddy /** 671f92363d1SSreekanth Reddy * mpt3sas_transport_port_add - insert port to the list 672f92363d1SSreekanth Reddy * @ioc: per adapter object 673f92363d1SSreekanth Reddy * @handle: handle of attached device 674f92363d1SSreekanth Reddy * @sas_address: sas address of parent expander or sas host 675*54cb88dcSLee Jones * @hba_port: hba port entry 676f92363d1SSreekanth Reddy * Context: This function will acquire ioc->sas_node_lock. 677f92363d1SSreekanth Reddy * 678f92363d1SSreekanth Reddy * Adding new port object to the sas_node->sas_port_list. 679f92363d1SSreekanth Reddy * 6804beb4867SBart Van Assche * Return: mpt3sas_port. 681f92363d1SSreekanth Reddy */ 682f92363d1SSreekanth Reddy struct _sas_port * 683f92363d1SSreekanth Reddy mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, 684e2f0cdf7SSreekanth Reddy u64 sas_address, struct hba_port *hba_port) 685f92363d1SSreekanth Reddy { 686f92363d1SSreekanth Reddy struct _sas_phy *mpt3sas_phy, *next; 687f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port; 688f92363d1SSreekanth Reddy unsigned long flags; 689f92363d1SSreekanth Reddy struct _sas_node *sas_node; 690f92363d1SSreekanth Reddy struct sas_rphy *rphy; 691e4bc7f5cSSreekanth Reddy struct _sas_device *sas_device = NULL; 692f92363d1SSreekanth Reddy int i; 693f92363d1SSreekanth Reddy struct sas_port *port; 694ccc59923SSreekanth Reddy struct virtual_phy *vphy = NULL; 695f92363d1SSreekanth Reddy 696e2f0cdf7SSreekanth Reddy if (!hba_port) { 697e2f0cdf7SSreekanth Reddy ioc_err(ioc, "failure at %s:%d/%s()!\n", 698e2f0cdf7SSreekanth Reddy __FILE__, __LINE__, __func__); 699e2f0cdf7SSreekanth Reddy return NULL; 700e2f0cdf7SSreekanth Reddy } 701e2f0cdf7SSreekanth Reddy 702f92363d1SSreekanth Reddy mpt3sas_port = kzalloc(sizeof(struct _sas_port), 703f92363d1SSreekanth Reddy GFP_KERNEL); 704f92363d1SSreekanth Reddy if (!mpt3sas_port) { 705919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 706919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 707f92363d1SSreekanth Reddy return NULL; 708f92363d1SSreekanth Reddy } 709f92363d1SSreekanth Reddy 710f92363d1SSreekanth Reddy INIT_LIST_HEAD(&mpt3sas_port->port_list); 711f92363d1SSreekanth Reddy INIT_LIST_HEAD(&mpt3sas_port->phy_list); 712f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 7137d310f24SSreekanth Reddy sas_node = _transport_sas_node_find_by_sas_address(ioc, 7147d310f24SSreekanth Reddy sas_address, hba_port); 715f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 716f92363d1SSreekanth Reddy 717f92363d1SSreekanth Reddy if (!sas_node) { 718919d8a3fSJoe Perches ioc_err(ioc, "%s: Could not find parent sas_address(0x%016llx)!\n", 719919d8a3fSJoe Perches __func__, (u64)sas_address); 720f92363d1SSreekanth Reddy goto out_fail; 721f92363d1SSreekanth Reddy } 722f92363d1SSreekanth Reddy 723f92363d1SSreekanth Reddy if ((_transport_set_identify(ioc, handle, 724f92363d1SSreekanth Reddy &mpt3sas_port->remote_identify))) { 725919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 726919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 727f92363d1SSreekanth Reddy goto out_fail; 728f92363d1SSreekanth Reddy } 729f92363d1SSreekanth Reddy 730f92363d1SSreekanth Reddy if (mpt3sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { 731919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 732919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 733f92363d1SSreekanth Reddy goto out_fail; 734f92363d1SSreekanth Reddy } 735f92363d1SSreekanth Reddy 736e2f0cdf7SSreekanth Reddy mpt3sas_port->hba_port = hba_port; 737f92363d1SSreekanth Reddy _transport_sanity_check(ioc, sas_node, 7387d310f24SSreekanth Reddy mpt3sas_port->remote_identify.sas_address, hba_port); 739f92363d1SSreekanth Reddy 740f92363d1SSreekanth Reddy for (i = 0; i < sas_node->num_phys; i++) { 741f92363d1SSreekanth Reddy if (sas_node->phy[i].remote_identify.sas_address != 742f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.sas_address) 743f92363d1SSreekanth Reddy continue; 744e2f0cdf7SSreekanth Reddy if (sas_node->phy[i].port != hba_port) 745e2f0cdf7SSreekanth Reddy continue; 746f92363d1SSreekanth Reddy list_add_tail(&sas_node->phy[i].port_siblings, 747f92363d1SSreekanth Reddy &mpt3sas_port->phy_list); 748f92363d1SSreekanth Reddy mpt3sas_port->num_phys++; 749ccc59923SSreekanth Reddy if (sas_node->handle <= ioc->sas_hba.num_phys) { 750ccc59923SSreekanth Reddy if (!sas_node->phy[i].hba_vphy) { 751ccc59923SSreekanth Reddy hba_port->phy_mask |= (1 << i); 752ccc59923SSreekanth Reddy continue; 753ccc59923SSreekanth Reddy } 754ccc59923SSreekanth Reddy 755ccc59923SSreekanth Reddy vphy = mpt3sas_get_vphy_by_phy(ioc, hba_port, i); 756ccc59923SSreekanth Reddy if (!vphy) { 757ccc59923SSreekanth Reddy ioc_err(ioc, "failure at %s:%d/%s()!\n", 758ccc59923SSreekanth Reddy __FILE__, __LINE__, __func__); 759ccc59923SSreekanth Reddy goto out_fail; 760ccc59923SSreekanth Reddy } 761ccc59923SSreekanth Reddy } 762f92363d1SSreekanth Reddy } 763f92363d1SSreekanth Reddy 764f92363d1SSreekanth Reddy if (!mpt3sas_port->num_phys) { 765919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 766919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 767f92363d1SSreekanth Reddy goto out_fail; 768f92363d1SSreekanth Reddy } 769f92363d1SSreekanth Reddy 7707d310f24SSreekanth Reddy if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { 7717d310f24SSreekanth Reddy sas_device = mpt3sas_get_sdev_by_addr(ioc, 7727d310f24SSreekanth Reddy mpt3sas_port->remote_identify.sas_address, 7737d310f24SSreekanth Reddy mpt3sas_port->hba_port); 7747d310f24SSreekanth Reddy if (!sas_device) { 7757d310f24SSreekanth Reddy ioc_err(ioc, "failure at %s:%d/%s()!\n", 7767d310f24SSreekanth Reddy __FILE__, __LINE__, __func__); 7777d310f24SSreekanth Reddy goto out_fail; 7787d310f24SSreekanth Reddy } 7797d310f24SSreekanth Reddy sas_device->pend_sas_rphy_add = 1; 7807d310f24SSreekanth Reddy } 7817d310f24SSreekanth Reddy 782ad2bf165SJoe Lawrence if (!sas_node->parent_dev) { 783919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 784919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 785ad2bf165SJoe Lawrence goto out_fail; 786ad2bf165SJoe Lawrence } 787f92363d1SSreekanth Reddy port = sas_port_alloc_num(sas_node->parent_dev); 788f92363d1SSreekanth Reddy if ((sas_port_add(port))) { 789919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 790919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 791f92363d1SSreekanth Reddy goto out_fail; 792f92363d1SSreekanth Reddy } 793f92363d1SSreekanth Reddy 794f92363d1SSreekanth Reddy list_for_each_entry(mpt3sas_phy, &mpt3sas_port->phy_list, 795f92363d1SSreekanth Reddy port_siblings) { 796f92363d1SSreekanth Reddy if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) 797f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &port->dev, 798f92363d1SSreekanth Reddy "add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n", 799f92363d1SSreekanth Reddy handle, (unsigned long long) 800f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.sas_address, 801f92363d1SSreekanth Reddy mpt3sas_phy->phy_id); 802f92363d1SSreekanth Reddy sas_port_add_phy(port, mpt3sas_phy->phy); 803f92363d1SSreekanth Reddy mpt3sas_phy->phy_belongs_to_port = 1; 804e2f0cdf7SSreekanth Reddy mpt3sas_phy->port = hba_port; 805f92363d1SSreekanth Reddy } 806f92363d1SSreekanth Reddy 807f92363d1SSreekanth Reddy mpt3sas_port->port = port; 808e2f0cdf7SSreekanth Reddy if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { 809f92363d1SSreekanth Reddy rphy = sas_end_device_alloc(port); 8106df6be91SSreekanth Reddy sas_device->rphy = rphy; 811ccc59923SSreekanth Reddy if (sas_node->handle <= ioc->sas_hba.num_phys) { 812ccc59923SSreekanth Reddy if (!vphy) 813ccc59923SSreekanth Reddy hba_port->sas_address = 814ccc59923SSreekanth Reddy sas_device->sas_address; 815ccc59923SSreekanth Reddy else 816ccc59923SSreekanth Reddy vphy->sas_address = 817ccc59923SSreekanth Reddy sas_device->sas_address; 818ccc59923SSreekanth Reddy } 819e2f0cdf7SSreekanth Reddy } else { 820f92363d1SSreekanth Reddy rphy = sas_expander_alloc(port, 821f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.device_type); 822e2f0cdf7SSreekanth Reddy if (sas_node->handle <= ioc->sas_hba.num_phys) 823e2f0cdf7SSreekanth Reddy hba_port->sas_address = 824e2f0cdf7SSreekanth Reddy mpt3sas_port->remote_identify.sas_address; 825e2f0cdf7SSreekanth Reddy } 826f92363d1SSreekanth Reddy 827f92363d1SSreekanth Reddy rphy->identify = mpt3sas_port->remote_identify; 828e4bc7f5cSSreekanth Reddy 829f92363d1SSreekanth Reddy if ((sas_rphy_add(rphy))) { 830919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 831919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 832f92363d1SSreekanth Reddy } 833e4bc7f5cSSreekanth Reddy 834d1cb5e49SSreekanth Reddy if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { 835e4bc7f5cSSreekanth Reddy sas_device->pend_sas_rphy_add = 0; 836d1cb5e49SSreekanth Reddy sas_device_put(sas_device); 837d1cb5e49SSreekanth Reddy } 838e4bc7f5cSSreekanth Reddy 8395b061980SSreekanth Reddy dev_info(&rphy->dev, 8405b061980SSreekanth Reddy "add: handle(0x%04x), sas_addr(0x%016llx)\n", handle, 8415b061980SSreekanth Reddy (unsigned long long)mpt3sas_port->remote_identify.sas_address); 8425b061980SSreekanth Reddy 843f92363d1SSreekanth Reddy mpt3sas_port->rphy = rphy; 844f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 845f92363d1SSreekanth Reddy list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list); 846f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 847f92363d1SSreekanth Reddy 848f92363d1SSreekanth Reddy /* fill in report manufacture */ 849f92363d1SSreekanth Reddy if (mpt3sas_port->remote_identify.device_type == 850f92363d1SSreekanth Reddy MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || 851f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.device_type == 852f92363d1SSreekanth Reddy MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) 853f92363d1SSreekanth Reddy _transport_expander_report_manufacture(ioc, 854f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.sas_address, 8559d0348a9SSreekanth Reddy rphy_to_expander_device(rphy), hba_port->port_id); 856f92363d1SSreekanth Reddy return mpt3sas_port; 857f92363d1SSreekanth Reddy 858f92363d1SSreekanth Reddy out_fail: 859f92363d1SSreekanth Reddy list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list, 860f92363d1SSreekanth Reddy port_siblings) 861f92363d1SSreekanth Reddy list_del(&mpt3sas_phy->port_siblings); 862f92363d1SSreekanth Reddy kfree(mpt3sas_port); 863f92363d1SSreekanth Reddy return NULL; 864f92363d1SSreekanth Reddy } 865f92363d1SSreekanth Reddy 866f92363d1SSreekanth Reddy /** 867f92363d1SSreekanth Reddy * mpt3sas_transport_port_remove - remove port from the list 868f92363d1SSreekanth Reddy * @ioc: per adapter object 869f92363d1SSreekanth Reddy * @sas_address: sas address of attached device 870f92363d1SSreekanth Reddy * @sas_address_parent: sas address of parent expander or sas host 871e2f0cdf7SSreekanth Reddy * @port: hba port entry 872f92363d1SSreekanth Reddy * Context: This function will acquire ioc->sas_node_lock. 873f92363d1SSreekanth Reddy * 874f92363d1SSreekanth Reddy * Removing object and freeing associated memory from the 875f92363d1SSreekanth Reddy * ioc->sas_port_list. 876f92363d1SSreekanth Reddy */ 877f92363d1SSreekanth Reddy void 878f92363d1SSreekanth Reddy mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, 879e2f0cdf7SSreekanth Reddy u64 sas_address_parent, struct hba_port *port) 880f92363d1SSreekanth Reddy { 881f92363d1SSreekanth Reddy int i; 882f92363d1SSreekanth Reddy unsigned long flags; 883f92363d1SSreekanth Reddy struct _sas_port *mpt3sas_port, *next; 884f92363d1SSreekanth Reddy struct _sas_node *sas_node; 885f92363d1SSreekanth Reddy u8 found = 0; 886f92363d1SSreekanth Reddy struct _sas_phy *mpt3sas_phy, *next_phy; 887e2f0cdf7SSreekanth Reddy struct hba_port *hba_port_next, *hba_port = NULL; 888ccc59923SSreekanth Reddy struct virtual_phy *vphy, *vphy_next = NULL; 889e2f0cdf7SSreekanth Reddy 890e2f0cdf7SSreekanth Reddy if (!port) 891e2f0cdf7SSreekanth Reddy return; 892f92363d1SSreekanth Reddy 893f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 894f92363d1SSreekanth Reddy sas_node = _transport_sas_node_find_by_sas_address(ioc, 8957d310f24SSreekanth Reddy sas_address_parent, port); 896f92363d1SSreekanth Reddy if (!sas_node) { 897f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 898f92363d1SSreekanth Reddy return; 899f92363d1SSreekanth Reddy } 900f92363d1SSreekanth Reddy list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list, 901f92363d1SSreekanth Reddy port_list) { 902f92363d1SSreekanth Reddy if (mpt3sas_port->remote_identify.sas_address != sas_address) 903f92363d1SSreekanth Reddy continue; 904e2f0cdf7SSreekanth Reddy if (mpt3sas_port->hba_port != port) 905e2f0cdf7SSreekanth Reddy continue; 906f92363d1SSreekanth Reddy found = 1; 907f92363d1SSreekanth Reddy list_del(&mpt3sas_port->port_list); 908f92363d1SSreekanth Reddy goto out; 909f92363d1SSreekanth Reddy } 910f92363d1SSreekanth Reddy out: 911f92363d1SSreekanth Reddy if (!found) { 912f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 913f92363d1SSreekanth Reddy return; 914f92363d1SSreekanth Reddy } 915f92363d1SSreekanth Reddy 916324c122fSSreekanth Reddy if (sas_node->handle <= ioc->sas_hba.num_phys && 917324c122fSSreekanth Reddy (ioc->multipath_on_hba)) { 918ccc59923SSreekanth Reddy if (port->vphys_mask) { 919ccc59923SSreekanth Reddy list_for_each_entry_safe(vphy, vphy_next, 920ccc59923SSreekanth Reddy &port->vphys_list, list) { 921ccc59923SSreekanth Reddy if (vphy->sas_address != sas_address) 922ccc59923SSreekanth Reddy continue; 923ccc59923SSreekanth Reddy ioc_info(ioc, 924ccc59923SSreekanth Reddy "remove vphy entry: %p of port:%p,from %d port's vphys list\n", 925ccc59923SSreekanth Reddy vphy, port, port->port_id); 926ccc59923SSreekanth Reddy port->vphys_mask &= ~vphy->phy_mask; 927ccc59923SSreekanth Reddy list_del(&vphy->list); 928ccc59923SSreekanth Reddy kfree(vphy); 929ccc59923SSreekanth Reddy } 930ccc59923SSreekanth Reddy } 931ccc59923SSreekanth Reddy 932e2f0cdf7SSreekanth Reddy list_for_each_entry_safe(hba_port, hba_port_next, 933e2f0cdf7SSreekanth Reddy &ioc->port_table_list, list) { 934e2f0cdf7SSreekanth Reddy if (hba_port != port) 935e2f0cdf7SSreekanth Reddy continue; 936ccc59923SSreekanth Reddy /* 937ccc59923SSreekanth Reddy * Delete hba_port object if 938ccc59923SSreekanth Reddy * - hba_port object's sas address matches with current 939ccc59923SSreekanth Reddy * removed device's sas address and no vphy's 940ccc59923SSreekanth Reddy * associated with it. 941ccc59923SSreekanth Reddy * - Current removed device is a vSES device and 942ccc59923SSreekanth Reddy * none of the other direct attached device have 943ccc59923SSreekanth Reddy * this vSES device's port number (hence hba_port 944ccc59923SSreekanth Reddy * object sas_address field will be zero). 945ccc59923SSreekanth Reddy */ 946ccc59923SSreekanth Reddy if ((hba_port->sas_address == sas_address || 947ccc59923SSreekanth Reddy !hba_port->sas_address) && !hba_port->vphys_mask) { 948e2f0cdf7SSreekanth Reddy ioc_info(ioc, 949e2f0cdf7SSreekanth Reddy "remove hba_port entry: %p port: %d from hba_port list\n", 950e2f0cdf7SSreekanth Reddy hba_port, hba_port->port_id); 951e2f0cdf7SSreekanth Reddy list_del(&hba_port->list); 952e2f0cdf7SSreekanth Reddy kfree(hba_port); 953ccc59923SSreekanth Reddy } else if (hba_port->sas_address == sas_address && 954ccc59923SSreekanth Reddy hba_port->vphys_mask) { 955ccc59923SSreekanth Reddy /* 956ccc59923SSreekanth Reddy * Current removed device is a non vSES device 957ccc59923SSreekanth Reddy * and a vSES device has the same port number 958ccc59923SSreekanth Reddy * as of current device's port number. Hence 959ccc59923SSreekanth Reddy * only clear the sas_address filed, don't 960ccc59923SSreekanth Reddy * delete the hba_port object. 961ccc59923SSreekanth Reddy */ 962ccc59923SSreekanth Reddy ioc_info(ioc, 963ccc59923SSreekanth Reddy "clearing sas_address from hba_port entry: %p port: %d from hba_port list\n", 964ccc59923SSreekanth Reddy hba_port, hba_port->port_id); 965ccc59923SSreekanth Reddy port->sas_address = 0; 966ccc59923SSreekanth Reddy } 967ccc59923SSreekanth Reddy break; 968e2f0cdf7SSreekanth Reddy } 969e2f0cdf7SSreekanth Reddy } 970e2f0cdf7SSreekanth Reddy 971f92363d1SSreekanth Reddy for (i = 0; i < sas_node->num_phys; i++) { 972f92363d1SSreekanth Reddy if (sas_node->phy[i].remote_identify.sas_address == sas_address) 973f92363d1SSreekanth Reddy memset(&sas_node->phy[i].remote_identify, 0 , 974f92363d1SSreekanth Reddy sizeof(struct sas_identify)); 975f92363d1SSreekanth Reddy } 976f92363d1SSreekanth Reddy 977f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 978f92363d1SSreekanth Reddy 979f92363d1SSreekanth Reddy list_for_each_entry_safe(mpt3sas_phy, next_phy, 980f92363d1SSreekanth Reddy &mpt3sas_port->phy_list, port_siblings) { 981f92363d1SSreekanth Reddy if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) 982f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &mpt3sas_port->port->dev, 983f92363d1SSreekanth Reddy "remove: sas_addr(0x%016llx), phy(%d)\n", 984f92363d1SSreekanth Reddy (unsigned long long) 985f92363d1SSreekanth Reddy mpt3sas_port->remote_identify.sas_address, 986f92363d1SSreekanth Reddy mpt3sas_phy->phy_id); 987f92363d1SSreekanth Reddy mpt3sas_phy->phy_belongs_to_port = 0; 988dc730212SSuganath Prabu if (!ioc->remove_host) 989dc730212SSuganath Prabu sas_port_delete_phy(mpt3sas_port->port, 990dc730212SSuganath Prabu mpt3sas_phy->phy); 991f92363d1SSreekanth Reddy list_del(&mpt3sas_phy->port_siblings); 992f92363d1SSreekanth Reddy } 993dc730212SSuganath Prabu if (!ioc->remove_host) 994f92363d1SSreekanth Reddy sas_port_delete(mpt3sas_port->port); 9955b061980SSreekanth Reddy ioc_info(ioc, "%s: removed: sas_addr(0x%016llx)\n", 9965b061980SSreekanth Reddy __func__, (unsigned long long)sas_address); 997f92363d1SSreekanth Reddy kfree(mpt3sas_port); 998f92363d1SSreekanth Reddy } 999f92363d1SSreekanth Reddy 1000f92363d1SSreekanth Reddy /** 1001f92363d1SSreekanth Reddy * mpt3sas_transport_add_host_phy - report sas_host phy to transport 1002f92363d1SSreekanth Reddy * @ioc: per adapter object 1003f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 1004f92363d1SSreekanth Reddy * @phy_pg0: sas phy page 0 1005f92363d1SSreekanth Reddy * @parent_dev: parent device class object 1006f92363d1SSreekanth Reddy * 10074beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1008f92363d1SSreekanth Reddy */ 1009f92363d1SSreekanth Reddy int 1010f92363d1SSreekanth Reddy mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy 1011f92363d1SSreekanth Reddy *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev) 1012f92363d1SSreekanth Reddy { 1013f92363d1SSreekanth Reddy struct sas_phy *phy; 1014f92363d1SSreekanth Reddy int phy_index = mpt3sas_phy->phy_id; 1015f92363d1SSreekanth Reddy 1016f92363d1SSreekanth Reddy 1017f92363d1SSreekanth Reddy INIT_LIST_HEAD(&mpt3sas_phy->port_siblings); 1018f92363d1SSreekanth Reddy phy = sas_phy_alloc(parent_dev, phy_index); 1019f92363d1SSreekanth Reddy if (!phy) { 1020919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1021919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1022f92363d1SSreekanth Reddy return -1; 1023f92363d1SSreekanth Reddy } 1024f92363d1SSreekanth Reddy if ((_transport_set_identify(ioc, mpt3sas_phy->handle, 1025f92363d1SSreekanth Reddy &mpt3sas_phy->identify))) { 1026919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1027919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1028f92363d1SSreekanth Reddy sas_phy_free(phy); 1029f92363d1SSreekanth Reddy return -1; 1030f92363d1SSreekanth Reddy } 1031f92363d1SSreekanth Reddy phy->identify = mpt3sas_phy->identify; 1032f92363d1SSreekanth Reddy mpt3sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle); 1033f92363d1SSreekanth Reddy if (mpt3sas_phy->attached_handle) 1034f92363d1SSreekanth Reddy _transport_set_identify(ioc, mpt3sas_phy->attached_handle, 1035f92363d1SSreekanth Reddy &mpt3sas_phy->remote_identify); 1036f92363d1SSreekanth Reddy phy->identify.phy_identifier = mpt3sas_phy->phy_id; 1037f92363d1SSreekanth Reddy phy->negotiated_linkrate = _transport_convert_phy_link_rate( 1038f92363d1SSreekanth Reddy phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); 1039f92363d1SSreekanth Reddy phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( 1040f92363d1SSreekanth Reddy phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); 1041f92363d1SSreekanth Reddy phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( 1042f92363d1SSreekanth Reddy phy_pg0.HwLinkRate >> 4); 1043f92363d1SSreekanth Reddy phy->minimum_linkrate = _transport_convert_phy_link_rate( 1044f92363d1SSreekanth Reddy phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); 1045f92363d1SSreekanth Reddy phy->maximum_linkrate = _transport_convert_phy_link_rate( 1046f92363d1SSreekanth Reddy phy_pg0.ProgrammedLinkRate >> 4); 10479d0348a9SSreekanth Reddy phy->hostdata = mpt3sas_phy->port; 1048f92363d1SSreekanth Reddy 1049f92363d1SSreekanth Reddy if ((sas_phy_add(phy))) { 1050919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1051919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1052f92363d1SSreekanth Reddy sas_phy_free(phy); 1053f92363d1SSreekanth Reddy return -1; 1054f92363d1SSreekanth Reddy } 1055f92363d1SSreekanth Reddy if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) 1056f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &phy->dev, 1057f92363d1SSreekanth Reddy "add: handle(0x%04x), sas_addr(0x%016llx)\n" 1058f92363d1SSreekanth Reddy "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", 1059f92363d1SSreekanth Reddy mpt3sas_phy->handle, (unsigned long long) 1060f92363d1SSreekanth Reddy mpt3sas_phy->identify.sas_address, 1061f92363d1SSreekanth Reddy mpt3sas_phy->attached_handle, 1062f92363d1SSreekanth Reddy (unsigned long long) 1063f92363d1SSreekanth Reddy mpt3sas_phy->remote_identify.sas_address); 1064f92363d1SSreekanth Reddy mpt3sas_phy->phy = phy; 1065f92363d1SSreekanth Reddy return 0; 1066f92363d1SSreekanth Reddy } 1067f92363d1SSreekanth Reddy 1068f92363d1SSreekanth Reddy 1069f92363d1SSreekanth Reddy /** 1070f92363d1SSreekanth Reddy * mpt3sas_transport_add_expander_phy - report expander phy to transport 1071f92363d1SSreekanth Reddy * @ioc: per adapter object 1072f92363d1SSreekanth Reddy * @mpt3sas_phy: mpt3sas per phy object 1073f92363d1SSreekanth Reddy * @expander_pg1: expander page 1 1074f92363d1SSreekanth Reddy * @parent_dev: parent device class object 1075f92363d1SSreekanth Reddy * 10764beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1077f92363d1SSreekanth Reddy */ 1078f92363d1SSreekanth Reddy int 1079f92363d1SSreekanth Reddy mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy 1080f92363d1SSreekanth Reddy *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1, 1081f92363d1SSreekanth Reddy struct device *parent_dev) 1082f92363d1SSreekanth Reddy { 1083f92363d1SSreekanth Reddy struct sas_phy *phy; 1084f92363d1SSreekanth Reddy int phy_index = mpt3sas_phy->phy_id; 1085f92363d1SSreekanth Reddy 1086f92363d1SSreekanth Reddy INIT_LIST_HEAD(&mpt3sas_phy->port_siblings); 1087f92363d1SSreekanth Reddy phy = sas_phy_alloc(parent_dev, phy_index); 1088f92363d1SSreekanth Reddy if (!phy) { 1089919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1090919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1091f92363d1SSreekanth Reddy return -1; 1092f92363d1SSreekanth Reddy } 1093f92363d1SSreekanth Reddy if ((_transport_set_identify(ioc, mpt3sas_phy->handle, 1094f92363d1SSreekanth Reddy &mpt3sas_phy->identify))) { 1095919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1096919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1097f92363d1SSreekanth Reddy sas_phy_free(phy); 1098f92363d1SSreekanth Reddy return -1; 1099f92363d1SSreekanth Reddy } 1100f92363d1SSreekanth Reddy phy->identify = mpt3sas_phy->identify; 1101f92363d1SSreekanth Reddy mpt3sas_phy->attached_handle = 1102f92363d1SSreekanth Reddy le16_to_cpu(expander_pg1.AttachedDevHandle); 1103f92363d1SSreekanth Reddy if (mpt3sas_phy->attached_handle) 1104f92363d1SSreekanth Reddy _transport_set_identify(ioc, mpt3sas_phy->attached_handle, 1105f92363d1SSreekanth Reddy &mpt3sas_phy->remote_identify); 1106f92363d1SSreekanth Reddy phy->identify.phy_identifier = mpt3sas_phy->phy_id; 1107f92363d1SSreekanth Reddy phy->negotiated_linkrate = _transport_convert_phy_link_rate( 1108f92363d1SSreekanth Reddy expander_pg1.NegotiatedLinkRate & 1109f92363d1SSreekanth Reddy MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); 1110f92363d1SSreekanth Reddy phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( 1111f92363d1SSreekanth Reddy expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); 1112f92363d1SSreekanth Reddy phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( 1113f92363d1SSreekanth Reddy expander_pg1.HwLinkRate >> 4); 1114f92363d1SSreekanth Reddy phy->minimum_linkrate = _transport_convert_phy_link_rate( 1115f92363d1SSreekanth Reddy expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); 1116f92363d1SSreekanth Reddy phy->maximum_linkrate = _transport_convert_phy_link_rate( 1117f92363d1SSreekanth Reddy expander_pg1.ProgrammedLinkRate >> 4); 11189d0348a9SSreekanth Reddy phy->hostdata = mpt3sas_phy->port; 1119f92363d1SSreekanth Reddy 1120f92363d1SSreekanth Reddy if ((sas_phy_add(phy))) { 1121919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1122919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1123f92363d1SSreekanth Reddy sas_phy_free(phy); 1124f92363d1SSreekanth Reddy return -1; 1125f92363d1SSreekanth Reddy } 1126f92363d1SSreekanth Reddy if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) 1127f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &phy->dev, 1128f92363d1SSreekanth Reddy "add: handle(0x%04x), sas_addr(0x%016llx)\n" 1129f92363d1SSreekanth Reddy "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", 1130f92363d1SSreekanth Reddy mpt3sas_phy->handle, (unsigned long long) 1131f92363d1SSreekanth Reddy mpt3sas_phy->identify.sas_address, 1132f92363d1SSreekanth Reddy mpt3sas_phy->attached_handle, 1133f92363d1SSreekanth Reddy (unsigned long long) 1134f92363d1SSreekanth Reddy mpt3sas_phy->remote_identify.sas_address); 1135f92363d1SSreekanth Reddy mpt3sas_phy->phy = phy; 1136f92363d1SSreekanth Reddy return 0; 1137f92363d1SSreekanth Reddy } 1138f92363d1SSreekanth Reddy 1139f92363d1SSreekanth Reddy /** 1140f92363d1SSreekanth Reddy * mpt3sas_transport_update_links - refreshing phy link changes 1141f92363d1SSreekanth Reddy * @ioc: per adapter object 1142f92363d1SSreekanth Reddy * @sas_address: sas address of parent expander or sas host 1143f92363d1SSreekanth Reddy * @handle: attached device handle 11444beb4867SBart Van Assche * @phy_number: phy number 1145f92363d1SSreekanth Reddy * @link_rate: new link rate 1146e2f0cdf7SSreekanth Reddy * @port: hba port entry 1147e2f0cdf7SSreekanth Reddy * 1148e2f0cdf7SSreekanth Reddy * Return nothing. 1149f92363d1SSreekanth Reddy */ 1150f92363d1SSreekanth Reddy void 1151f92363d1SSreekanth Reddy mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc, 1152e2f0cdf7SSreekanth Reddy u64 sas_address, u16 handle, u8 phy_number, u8 link_rate, 1153e2f0cdf7SSreekanth Reddy struct hba_port *port) 1154f92363d1SSreekanth Reddy { 1155f92363d1SSreekanth Reddy unsigned long flags; 1156f92363d1SSreekanth Reddy struct _sas_node *sas_node; 1157f92363d1SSreekanth Reddy struct _sas_phy *mpt3sas_phy; 1158e2f0cdf7SSreekanth Reddy struct hba_port *hba_port = NULL; 1159f92363d1SSreekanth Reddy 1160f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) 1161f92363d1SSreekanth Reddy return; 1162f92363d1SSreekanth Reddy 1163f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 11647d310f24SSreekanth Reddy sas_node = _transport_sas_node_find_by_sas_address(ioc, 11657d310f24SSreekanth Reddy sas_address, port); 1166f92363d1SSreekanth Reddy if (!sas_node) { 1167f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1168f92363d1SSreekanth Reddy return; 1169f92363d1SSreekanth Reddy } 1170f92363d1SSreekanth Reddy 1171f92363d1SSreekanth Reddy mpt3sas_phy = &sas_node->phy[phy_number]; 1172f92363d1SSreekanth Reddy mpt3sas_phy->attached_handle = handle; 1173f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1174f92363d1SSreekanth Reddy if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { 1175f92363d1SSreekanth Reddy _transport_set_identify(ioc, handle, 1176f92363d1SSreekanth Reddy &mpt3sas_phy->remote_identify); 1177324c122fSSreekanth Reddy if ((sas_node->handle <= ioc->sas_hba.num_phys) && 1178324c122fSSreekanth Reddy (ioc->multipath_on_hba)) { 1179e2f0cdf7SSreekanth Reddy list_for_each_entry(hba_port, 1180e2f0cdf7SSreekanth Reddy &ioc->port_table_list, list) { 1181e2f0cdf7SSreekanth Reddy if (hba_port->sas_address == sas_address && 1182e2f0cdf7SSreekanth Reddy hba_port == port) 1183e2f0cdf7SSreekanth Reddy hba_port->phy_mask |= 1184e2f0cdf7SSreekanth Reddy (1 << mpt3sas_phy->phy_id); 1185e2f0cdf7SSreekanth Reddy } 1186e2f0cdf7SSreekanth Reddy } 1187c71ccf93SSreekanth Reddy mpt3sas_transport_add_phy_to_an_existing_port(ioc, sas_node, 1188c71ccf93SSreekanth Reddy mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address, 1189c71ccf93SSreekanth Reddy port); 11902311ce4dSSreekanth Reddy } else 1191f92363d1SSreekanth Reddy memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct 1192f92363d1SSreekanth Reddy sas_identify)); 1193f92363d1SSreekanth Reddy 1194f92363d1SSreekanth Reddy if (mpt3sas_phy->phy) 1195f92363d1SSreekanth Reddy mpt3sas_phy->phy->negotiated_linkrate = 1196f92363d1SSreekanth Reddy _transport_convert_phy_link_rate(link_rate); 1197f92363d1SSreekanth Reddy 1198f92363d1SSreekanth Reddy if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) 1199f92363d1SSreekanth Reddy dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev, 1200f92363d1SSreekanth Reddy "refresh: parent sas_addr(0x%016llx),\n" 1201f92363d1SSreekanth Reddy "\tlink_rate(0x%02x), phy(%d)\n" 1202f92363d1SSreekanth Reddy "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", 1203f92363d1SSreekanth Reddy (unsigned long long)sas_address, 1204f92363d1SSreekanth Reddy link_rate, phy_number, handle, (unsigned long long) 1205f92363d1SSreekanth Reddy mpt3sas_phy->remote_identify.sas_address); 1206f92363d1SSreekanth Reddy } 1207f92363d1SSreekanth Reddy 1208f92363d1SSreekanth Reddy static inline void * 1209f92363d1SSreekanth Reddy phy_to_ioc(struct sas_phy *phy) 1210f92363d1SSreekanth Reddy { 1211f92363d1SSreekanth Reddy struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); 1212f92363d1SSreekanth Reddy return shost_priv(shost); 1213f92363d1SSreekanth Reddy } 1214f92363d1SSreekanth Reddy 1215f92363d1SSreekanth Reddy static inline void * 1216f92363d1SSreekanth Reddy rphy_to_ioc(struct sas_rphy *rphy) 1217f92363d1SSreekanth Reddy { 1218f92363d1SSreekanth Reddy struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); 1219f92363d1SSreekanth Reddy return shost_priv(shost); 1220f92363d1SSreekanth Reddy } 1221f92363d1SSreekanth Reddy 1222f92363d1SSreekanth Reddy /* report phy error log structure */ 1223f92363d1SSreekanth Reddy struct phy_error_log_request { 1224f92363d1SSreekanth Reddy u8 smp_frame_type; /* 0x40 */ 1225f92363d1SSreekanth Reddy u8 function; /* 0x11 */ 1226f92363d1SSreekanth Reddy u8 allocated_response_length; 1227f92363d1SSreekanth Reddy u8 request_length; /* 02 */ 1228f92363d1SSreekanth Reddy u8 reserved_1[5]; 1229f92363d1SSreekanth Reddy u8 phy_identifier; 1230f92363d1SSreekanth Reddy u8 reserved_2[2]; 1231f92363d1SSreekanth Reddy }; 1232f92363d1SSreekanth Reddy 1233f92363d1SSreekanth Reddy /* report phy error log reply structure */ 1234f92363d1SSreekanth Reddy struct phy_error_log_reply { 1235f92363d1SSreekanth Reddy u8 smp_frame_type; /* 0x41 */ 1236f92363d1SSreekanth Reddy u8 function; /* 0x11 */ 1237f92363d1SSreekanth Reddy u8 function_result; 1238f92363d1SSreekanth Reddy u8 response_length; 1239f92363d1SSreekanth Reddy __be16 expander_change_count; 1240f92363d1SSreekanth Reddy u8 reserved_1[3]; 1241f92363d1SSreekanth Reddy u8 phy_identifier; 1242f92363d1SSreekanth Reddy u8 reserved_2[2]; 1243f92363d1SSreekanth Reddy __be32 invalid_dword; 1244f92363d1SSreekanth Reddy __be32 running_disparity_error; 1245f92363d1SSreekanth Reddy __be32 loss_of_dword_sync; 1246f92363d1SSreekanth Reddy __be32 phy_reset_problem; 1247f92363d1SSreekanth Reddy }; 1248f92363d1SSreekanth Reddy 1249f92363d1SSreekanth Reddy /** 1250f92363d1SSreekanth Reddy * _transport_get_expander_phy_error_log - return expander counters 1251f92363d1SSreekanth Reddy * @ioc: per adapter object 1252f92363d1SSreekanth Reddy * @phy: The sas phy object 1253f92363d1SSreekanth Reddy * 12544beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1255f92363d1SSreekanth Reddy * 1256f92363d1SSreekanth Reddy */ 1257f92363d1SSreekanth Reddy static int 1258f92363d1SSreekanth Reddy _transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc, 1259f92363d1SSreekanth Reddy struct sas_phy *phy) 1260f92363d1SSreekanth Reddy { 1261f92363d1SSreekanth Reddy Mpi2SmpPassthroughRequest_t *mpi_request; 1262f92363d1SSreekanth Reddy Mpi2SmpPassthroughReply_t *mpi_reply; 1263f92363d1SSreekanth Reddy struct phy_error_log_request *phy_error_log_request; 1264f92363d1SSreekanth Reddy struct phy_error_log_reply *phy_error_log_reply; 1265f92363d1SSreekanth Reddy int rc; 1266f92363d1SSreekanth Reddy u16 smid; 1267f92363d1SSreekanth Reddy void *psge; 1268f92363d1SSreekanth Reddy u8 issue_reset = 0; 1269f92363d1SSreekanth Reddy void *data_out = NULL; 1270f92363d1SSreekanth Reddy dma_addr_t data_out_dma; 1271f92363d1SSreekanth Reddy u32 sz; 1272f92363d1SSreekanth Reddy 1273f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) { 12744dc74b2eSJoe Perches ioc_info(ioc, "%s: host reset in progress!\n", __func__); 1275f92363d1SSreekanth Reddy return -EFAULT; 1276f92363d1SSreekanth Reddy } 1277f92363d1SSreekanth Reddy 1278f92363d1SSreekanth Reddy mutex_lock(&ioc->transport_cmds.mutex); 1279f92363d1SSreekanth Reddy 1280f92363d1SSreekanth Reddy if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { 1281919d8a3fSJoe Perches ioc_err(ioc, "%s: transport_cmds in use\n", __func__); 1282f92363d1SSreekanth Reddy rc = -EAGAIN; 1283f92363d1SSreekanth Reddy goto out; 1284f92363d1SSreekanth Reddy } 1285f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_PENDING; 1286f92363d1SSreekanth Reddy 1287f4305749SSuganath Prabu rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT); 1288f4305749SSuganath Prabu if (rc) 1289f92363d1SSreekanth Reddy goto out; 1290f92363d1SSreekanth Reddy 1291f92363d1SSreekanth Reddy smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx); 1292f92363d1SSreekanth Reddy if (!smid) { 1293919d8a3fSJoe Perches ioc_err(ioc, "%s: failed obtaining a smid\n", __func__); 1294f92363d1SSreekanth Reddy rc = -EAGAIN; 1295f92363d1SSreekanth Reddy goto out; 1296f92363d1SSreekanth Reddy } 1297f92363d1SSreekanth Reddy 1298f92363d1SSreekanth Reddy mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); 1299f92363d1SSreekanth Reddy ioc->transport_cmds.smid = smid; 1300f92363d1SSreekanth Reddy 1301f92363d1SSreekanth Reddy sz = sizeof(struct phy_error_log_request) + 1302f92363d1SSreekanth Reddy sizeof(struct phy_error_log_reply); 13031c2048bdSChristoph Hellwig data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma, 13041c2048bdSChristoph Hellwig GFP_KERNEL); 1305f92363d1SSreekanth Reddy if (!data_out) { 1306f92363d1SSreekanth Reddy pr_err("failure at %s:%d/%s()!\n", __FILE__, 1307f92363d1SSreekanth Reddy __LINE__, __func__); 1308f92363d1SSreekanth Reddy rc = -ENOMEM; 1309f92363d1SSreekanth Reddy mpt3sas_base_free_smid(ioc, smid); 1310f92363d1SSreekanth Reddy goto out; 1311f92363d1SSreekanth Reddy } 1312f92363d1SSreekanth Reddy 1313f92363d1SSreekanth Reddy rc = -EINVAL; 1314f92363d1SSreekanth Reddy memset(data_out, 0, sz); 1315f92363d1SSreekanth Reddy phy_error_log_request = data_out; 1316f92363d1SSreekanth Reddy phy_error_log_request->smp_frame_type = 0x40; 1317f92363d1SSreekanth Reddy phy_error_log_request->function = 0x11; 1318f92363d1SSreekanth Reddy phy_error_log_request->request_length = 2; 1319f92363d1SSreekanth Reddy phy_error_log_request->allocated_response_length = 0; 1320f92363d1SSreekanth Reddy phy_error_log_request->phy_identifier = phy->number; 1321f92363d1SSreekanth Reddy 1322f92363d1SSreekanth Reddy memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); 1323f92363d1SSreekanth Reddy mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 13249d0348a9SSreekanth Reddy mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy); 1325f92363d1SSreekanth Reddy mpi_request->VF_ID = 0; /* TODO */ 1326f92363d1SSreekanth Reddy mpi_request->VP_ID = 0; 1327f92363d1SSreekanth Reddy mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); 1328f92363d1SSreekanth Reddy mpi_request->RequestDataLength = 1329f92363d1SSreekanth Reddy cpu_to_le16(sizeof(struct phy_error_log_request)); 1330f92363d1SSreekanth Reddy psge = &mpi_request->SGL; 1331f92363d1SSreekanth Reddy 1332f92363d1SSreekanth Reddy ioc->build_sg(ioc, psge, data_out_dma, 1333f92363d1SSreekanth Reddy sizeof(struct phy_error_log_request), 1334f92363d1SSreekanth Reddy data_out_dma + sizeof(struct phy_error_log_request), 1335f92363d1SSreekanth Reddy sizeof(struct phy_error_log_reply)); 1336f92363d1SSreekanth Reddy 1337919d8a3fSJoe Perches dtransportprintk(ioc, 1338919d8a3fSJoe Perches ioc_info(ioc, "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n", 1339919d8a3fSJoe Perches (u64)phy->identify.sas_address, 1340f92363d1SSreekanth Reddy phy->number)); 1341f92363d1SSreekanth Reddy init_completion(&ioc->transport_cmds.done); 1342078a4cc1SSuganath Prabu S ioc->put_smid_default(ioc, smid); 13438bbb1cf6SCalvin Owens wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); 1344f92363d1SSreekanth Reddy 1345f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { 1346919d8a3fSJoe Perches ioc_err(ioc, "%s: timeout\n", __func__); 1347f92363d1SSreekanth Reddy _debug_dump_mf(mpi_request, 1348f92363d1SSreekanth Reddy sizeof(Mpi2SmpPassthroughRequest_t)/4); 1349f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) 1350f92363d1SSreekanth Reddy issue_reset = 1; 1351f92363d1SSreekanth Reddy goto issue_host_reset; 1352f92363d1SSreekanth Reddy } 1353f92363d1SSreekanth Reddy 1354919d8a3fSJoe Perches dtransportprintk(ioc, ioc_info(ioc, "phy_error_log - complete\n")); 1355f92363d1SSreekanth Reddy 1356f92363d1SSreekanth Reddy if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { 1357f92363d1SSreekanth Reddy 1358f92363d1SSreekanth Reddy mpi_reply = ioc->transport_cmds.reply; 1359f92363d1SSreekanth Reddy 1360919d8a3fSJoe Perches dtransportprintk(ioc, 1361919d8a3fSJoe Perches ioc_info(ioc, "phy_error_log - reply data transfer size(%d)\n", 1362919d8a3fSJoe Perches le16_to_cpu(mpi_reply->ResponseDataLength))); 1363f92363d1SSreekanth Reddy 1364f92363d1SSreekanth Reddy if (le16_to_cpu(mpi_reply->ResponseDataLength) != 1365f92363d1SSreekanth Reddy sizeof(struct phy_error_log_reply)) 1366f92363d1SSreekanth Reddy goto out; 1367f92363d1SSreekanth Reddy 1368f92363d1SSreekanth Reddy phy_error_log_reply = data_out + 1369f92363d1SSreekanth Reddy sizeof(struct phy_error_log_request); 1370f92363d1SSreekanth Reddy 1371919d8a3fSJoe Perches dtransportprintk(ioc, 1372919d8a3fSJoe Perches ioc_info(ioc, "phy_error_log - function_result(%d)\n", 1373919d8a3fSJoe Perches phy_error_log_reply->function_result)); 1374f92363d1SSreekanth Reddy 1375f92363d1SSreekanth Reddy phy->invalid_dword_count = 1376f92363d1SSreekanth Reddy be32_to_cpu(phy_error_log_reply->invalid_dword); 1377f92363d1SSreekanth Reddy phy->running_disparity_error_count = 1378f92363d1SSreekanth Reddy be32_to_cpu(phy_error_log_reply->running_disparity_error); 1379f92363d1SSreekanth Reddy phy->loss_of_dword_sync_count = 1380f92363d1SSreekanth Reddy be32_to_cpu(phy_error_log_reply->loss_of_dword_sync); 1381f92363d1SSreekanth Reddy phy->phy_reset_problem_count = 1382f92363d1SSreekanth Reddy be32_to_cpu(phy_error_log_reply->phy_reset_problem); 1383f92363d1SSreekanth Reddy rc = 0; 1384f92363d1SSreekanth Reddy } else 1385919d8a3fSJoe Perches dtransportprintk(ioc, 1386919d8a3fSJoe Perches ioc_info(ioc, "phy_error_log - no reply\n")); 1387f92363d1SSreekanth Reddy 1388f92363d1SSreekanth Reddy issue_host_reset: 1389f92363d1SSreekanth Reddy if (issue_reset) 139098c56ad3SCalvin Owens mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); 1391f92363d1SSreekanth Reddy out: 1392f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_NOT_USED; 1393f92363d1SSreekanth Reddy if (data_out) 13941c2048bdSChristoph Hellwig dma_free_coherent(&ioc->pdev->dev, sz, data_out, data_out_dma); 1395f92363d1SSreekanth Reddy 1396f92363d1SSreekanth Reddy mutex_unlock(&ioc->transport_cmds.mutex); 1397f92363d1SSreekanth Reddy return rc; 1398f92363d1SSreekanth Reddy } 1399f92363d1SSreekanth Reddy 1400f92363d1SSreekanth Reddy /** 1401f92363d1SSreekanth Reddy * _transport_get_linkerrors - return phy counters for both hba and expanders 1402f92363d1SSreekanth Reddy * @phy: The sas phy object 1403f92363d1SSreekanth Reddy * 14044beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1405f92363d1SSreekanth Reddy * 1406f92363d1SSreekanth Reddy */ 1407f92363d1SSreekanth Reddy static int 1408f92363d1SSreekanth Reddy _transport_get_linkerrors(struct sas_phy *phy) 1409f92363d1SSreekanth Reddy { 1410f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); 1411f92363d1SSreekanth Reddy unsigned long flags; 1412f92363d1SSreekanth Reddy Mpi2ConfigReply_t mpi_reply; 1413f92363d1SSreekanth Reddy Mpi2SasPhyPage1_t phy_pg1; 14147d310f24SSreekanth Reddy struct hba_port *port = phy->hostdata; 14157d310f24SSreekanth Reddy int port_id = port->port_id; 1416f92363d1SSreekanth Reddy 1417f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 1418f92363d1SSreekanth Reddy if (_transport_sas_node_find_by_sas_address(ioc, 14197d310f24SSreekanth Reddy phy->identify.sas_address, 142034b0a785SSreekanth Reddy mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { 1421f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1422f92363d1SSreekanth Reddy return -EINVAL; 1423f92363d1SSreekanth Reddy } 1424f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1425f92363d1SSreekanth Reddy 1426f92363d1SSreekanth Reddy if (phy->identify.sas_address != ioc->sas_hba.sas_address) 1427f92363d1SSreekanth Reddy return _transport_get_expander_phy_error_log(ioc, phy); 1428f92363d1SSreekanth Reddy 1429f92363d1SSreekanth Reddy /* get hba phy error logs */ 1430f92363d1SSreekanth Reddy if ((mpt3sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, 1431f92363d1SSreekanth Reddy phy->number))) { 1432919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1433919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1434f92363d1SSreekanth Reddy return -ENXIO; 1435f92363d1SSreekanth Reddy } 1436f92363d1SSreekanth Reddy 1437f92363d1SSreekanth Reddy if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) 1438919d8a3fSJoe Perches ioc_info(ioc, "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n", 1439919d8a3fSJoe Perches phy->number, 1440f92363d1SSreekanth Reddy le16_to_cpu(mpi_reply.IOCStatus), 1441f92363d1SSreekanth Reddy le32_to_cpu(mpi_reply.IOCLogInfo)); 1442f92363d1SSreekanth Reddy 1443f92363d1SSreekanth Reddy phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); 1444f92363d1SSreekanth Reddy phy->running_disparity_error_count = 1445f92363d1SSreekanth Reddy le32_to_cpu(phy_pg1.RunningDisparityErrorCount); 1446f92363d1SSreekanth Reddy phy->loss_of_dword_sync_count = 1447f92363d1SSreekanth Reddy le32_to_cpu(phy_pg1.LossDwordSynchCount); 1448f92363d1SSreekanth Reddy phy->phy_reset_problem_count = 1449f92363d1SSreekanth Reddy le32_to_cpu(phy_pg1.PhyResetProblemCount); 1450f92363d1SSreekanth Reddy return 0; 1451f92363d1SSreekanth Reddy } 1452f92363d1SSreekanth Reddy 1453f92363d1SSreekanth Reddy /** 1454f92363d1SSreekanth Reddy * _transport_get_enclosure_identifier - 14554beb4867SBart Van Assche * @rphy: The sas phy object 14564beb4867SBart Van Assche * @identifier: ? 1457f92363d1SSreekanth Reddy * 1458f92363d1SSreekanth Reddy * Obtain the enclosure logical id for an expander. 14594beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1460f92363d1SSreekanth Reddy */ 1461f92363d1SSreekanth Reddy static int 1462f92363d1SSreekanth Reddy _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) 1463f92363d1SSreekanth Reddy { 1464f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); 1465f92363d1SSreekanth Reddy struct _sas_device *sas_device; 1466f92363d1SSreekanth Reddy unsigned long flags; 1467f92363d1SSreekanth Reddy int rc; 1468f92363d1SSreekanth Reddy 1469f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_device_lock, flags); 14706df6be91SSreekanth Reddy sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); 1471f92363d1SSreekanth Reddy if (sas_device) { 1472f92363d1SSreekanth Reddy *identifier = sas_device->enclosure_logical_id; 1473f92363d1SSreekanth Reddy rc = 0; 1474d1cb5e49SSreekanth Reddy sas_device_put(sas_device); 1475f92363d1SSreekanth Reddy } else { 1476f92363d1SSreekanth Reddy *identifier = 0; 1477f92363d1SSreekanth Reddy rc = -ENXIO; 1478f92363d1SSreekanth Reddy } 1479d1cb5e49SSreekanth Reddy 1480f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_device_lock, flags); 1481f92363d1SSreekanth Reddy return rc; 1482f92363d1SSreekanth Reddy } 1483f92363d1SSreekanth Reddy 1484f92363d1SSreekanth Reddy /** 1485f92363d1SSreekanth Reddy * _transport_get_bay_identifier - 14864beb4867SBart Van Assche * @rphy: The sas phy object 1487f92363d1SSreekanth Reddy * 14884beb4867SBart Van Assche * Return: the slot id for a device that resides inside an enclosure. 1489f92363d1SSreekanth Reddy */ 1490f92363d1SSreekanth Reddy static int 1491f92363d1SSreekanth Reddy _transport_get_bay_identifier(struct sas_rphy *rphy) 1492f92363d1SSreekanth Reddy { 1493f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy); 1494f92363d1SSreekanth Reddy struct _sas_device *sas_device; 1495f92363d1SSreekanth Reddy unsigned long flags; 1496f92363d1SSreekanth Reddy int rc; 1497f92363d1SSreekanth Reddy 1498f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_device_lock, flags); 14996df6be91SSreekanth Reddy sas_device = __mpt3sas_get_sdev_by_rphy(ioc, rphy); 1500d1cb5e49SSreekanth Reddy if (sas_device) { 1501f92363d1SSreekanth Reddy rc = sas_device->slot; 1502d1cb5e49SSreekanth Reddy sas_device_put(sas_device); 1503d1cb5e49SSreekanth Reddy } else { 1504f92363d1SSreekanth Reddy rc = -ENXIO; 1505d1cb5e49SSreekanth Reddy } 1506f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_device_lock, flags); 1507f92363d1SSreekanth Reddy return rc; 1508f92363d1SSreekanth Reddy } 1509f92363d1SSreekanth Reddy 1510f92363d1SSreekanth Reddy /* phy control request structure */ 1511f92363d1SSreekanth Reddy struct phy_control_request { 1512f92363d1SSreekanth Reddy u8 smp_frame_type; /* 0x40 */ 1513f92363d1SSreekanth Reddy u8 function; /* 0x91 */ 1514f92363d1SSreekanth Reddy u8 allocated_response_length; 1515f92363d1SSreekanth Reddy u8 request_length; /* 0x09 */ 1516f92363d1SSreekanth Reddy u16 expander_change_count; 1517f92363d1SSreekanth Reddy u8 reserved_1[3]; 1518f92363d1SSreekanth Reddy u8 phy_identifier; 1519f92363d1SSreekanth Reddy u8 phy_operation; 1520f92363d1SSreekanth Reddy u8 reserved_2[13]; 1521f92363d1SSreekanth Reddy u64 attached_device_name; 1522f92363d1SSreekanth Reddy u8 programmed_min_physical_link_rate; 1523f92363d1SSreekanth Reddy u8 programmed_max_physical_link_rate; 1524f92363d1SSreekanth Reddy u8 reserved_3[6]; 1525f92363d1SSreekanth Reddy }; 1526f92363d1SSreekanth Reddy 1527f92363d1SSreekanth Reddy /* phy control reply structure */ 1528f92363d1SSreekanth Reddy struct phy_control_reply { 1529f92363d1SSreekanth Reddy u8 smp_frame_type; /* 0x41 */ 1530f92363d1SSreekanth Reddy u8 function; /* 0x11 */ 1531f92363d1SSreekanth Reddy u8 function_result; 1532f92363d1SSreekanth Reddy u8 response_length; 1533f92363d1SSreekanth Reddy }; 1534f92363d1SSreekanth Reddy 1535f92363d1SSreekanth Reddy #define SMP_PHY_CONTROL_LINK_RESET (0x01) 1536f92363d1SSreekanth Reddy #define SMP_PHY_CONTROL_HARD_RESET (0x02) 1537f92363d1SSreekanth Reddy #define SMP_PHY_CONTROL_DISABLE (0x03) 1538f92363d1SSreekanth Reddy 1539f92363d1SSreekanth Reddy /** 1540f92363d1SSreekanth Reddy * _transport_expander_phy_control - expander phy control 1541f92363d1SSreekanth Reddy * @ioc: per adapter object 1542f92363d1SSreekanth Reddy * @phy: The sas phy object 15434beb4867SBart Van Assche * @phy_operation: ? 1544f92363d1SSreekanth Reddy * 15454beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1546f92363d1SSreekanth Reddy * 1547f92363d1SSreekanth Reddy */ 1548f92363d1SSreekanth Reddy static int 1549f92363d1SSreekanth Reddy _transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc, 1550f92363d1SSreekanth Reddy struct sas_phy *phy, u8 phy_operation) 1551f92363d1SSreekanth Reddy { 1552f92363d1SSreekanth Reddy Mpi2SmpPassthroughRequest_t *mpi_request; 1553f92363d1SSreekanth Reddy Mpi2SmpPassthroughReply_t *mpi_reply; 1554f92363d1SSreekanth Reddy struct phy_control_request *phy_control_request; 1555f92363d1SSreekanth Reddy struct phy_control_reply *phy_control_reply; 1556f92363d1SSreekanth Reddy int rc; 1557f92363d1SSreekanth Reddy u16 smid; 1558f92363d1SSreekanth Reddy void *psge; 1559f92363d1SSreekanth Reddy u8 issue_reset = 0; 1560f92363d1SSreekanth Reddy void *data_out = NULL; 1561f92363d1SSreekanth Reddy dma_addr_t data_out_dma; 1562f92363d1SSreekanth Reddy u32 sz; 1563f92363d1SSreekanth Reddy 1564f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) { 15654dc74b2eSJoe Perches ioc_info(ioc, "%s: host reset in progress!\n", __func__); 1566f92363d1SSreekanth Reddy return -EFAULT; 1567f92363d1SSreekanth Reddy } 1568f92363d1SSreekanth Reddy 1569f92363d1SSreekanth Reddy mutex_lock(&ioc->transport_cmds.mutex); 1570f92363d1SSreekanth Reddy 1571f92363d1SSreekanth Reddy if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { 1572919d8a3fSJoe Perches ioc_err(ioc, "%s: transport_cmds in use\n", __func__); 1573f92363d1SSreekanth Reddy rc = -EAGAIN; 1574f92363d1SSreekanth Reddy goto out; 1575f92363d1SSreekanth Reddy } 1576f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_PENDING; 1577f92363d1SSreekanth Reddy 1578f4305749SSuganath Prabu rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT); 1579f4305749SSuganath Prabu if (rc) 1580f92363d1SSreekanth Reddy goto out; 1581f92363d1SSreekanth Reddy 1582f92363d1SSreekanth Reddy smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx); 1583f92363d1SSreekanth Reddy if (!smid) { 1584919d8a3fSJoe Perches ioc_err(ioc, "%s: failed obtaining a smid\n", __func__); 1585f92363d1SSreekanth Reddy rc = -EAGAIN; 1586f92363d1SSreekanth Reddy goto out; 1587f92363d1SSreekanth Reddy } 1588f92363d1SSreekanth Reddy 1589f92363d1SSreekanth Reddy mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); 1590f92363d1SSreekanth Reddy ioc->transport_cmds.smid = smid; 1591f92363d1SSreekanth Reddy 1592f92363d1SSreekanth Reddy sz = sizeof(struct phy_control_request) + 1593f92363d1SSreekanth Reddy sizeof(struct phy_control_reply); 15941c2048bdSChristoph Hellwig data_out = dma_alloc_coherent(&ioc->pdev->dev, sz, &data_out_dma, 15951c2048bdSChristoph Hellwig GFP_KERNEL); 1596f92363d1SSreekanth Reddy if (!data_out) { 1597f92363d1SSreekanth Reddy pr_err("failure at %s:%d/%s()!\n", __FILE__, 1598f92363d1SSreekanth Reddy __LINE__, __func__); 1599f92363d1SSreekanth Reddy rc = -ENOMEM; 1600f92363d1SSreekanth Reddy mpt3sas_base_free_smid(ioc, smid); 1601f92363d1SSreekanth Reddy goto out; 1602f92363d1SSreekanth Reddy } 1603f92363d1SSreekanth Reddy 1604f92363d1SSreekanth Reddy rc = -EINVAL; 1605f92363d1SSreekanth Reddy memset(data_out, 0, sz); 1606f92363d1SSreekanth Reddy phy_control_request = data_out; 1607f92363d1SSreekanth Reddy phy_control_request->smp_frame_type = 0x40; 1608f92363d1SSreekanth Reddy phy_control_request->function = 0x91; 1609f92363d1SSreekanth Reddy phy_control_request->request_length = 9; 1610f92363d1SSreekanth Reddy phy_control_request->allocated_response_length = 0; 1611f92363d1SSreekanth Reddy phy_control_request->phy_identifier = phy->number; 1612f92363d1SSreekanth Reddy phy_control_request->phy_operation = phy_operation; 1613f92363d1SSreekanth Reddy phy_control_request->programmed_min_physical_link_rate = 1614f92363d1SSreekanth Reddy phy->minimum_linkrate << 4; 1615f92363d1SSreekanth Reddy phy_control_request->programmed_max_physical_link_rate = 1616f92363d1SSreekanth Reddy phy->maximum_linkrate << 4; 1617f92363d1SSreekanth Reddy 1618f92363d1SSreekanth Reddy memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); 1619f92363d1SSreekanth Reddy mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 16209d0348a9SSreekanth Reddy mpi_request->PhysicalPort = _transport_get_port_id_by_sas_phy(phy); 1621f92363d1SSreekanth Reddy mpi_request->VF_ID = 0; /* TODO */ 1622f92363d1SSreekanth Reddy mpi_request->VP_ID = 0; 1623f92363d1SSreekanth Reddy mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); 1624f92363d1SSreekanth Reddy mpi_request->RequestDataLength = 1625f92363d1SSreekanth Reddy cpu_to_le16(sizeof(struct phy_error_log_request)); 1626f92363d1SSreekanth Reddy psge = &mpi_request->SGL; 1627f92363d1SSreekanth Reddy 1628ce61c574SSuganath prabu Subramani ioc->build_sg(ioc, psge, data_out_dma, 1629ce61c574SSuganath prabu Subramani sizeof(struct phy_control_request), 1630ce61c574SSuganath prabu Subramani data_out_dma + sizeof(struct phy_control_request), 1631ce61c574SSuganath prabu Subramani sizeof(struct phy_control_reply)); 1632f92363d1SSreekanth Reddy 1633919d8a3fSJoe Perches dtransportprintk(ioc, 1634919d8a3fSJoe Perches ioc_info(ioc, "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", 1635919d8a3fSJoe Perches (u64)phy->identify.sas_address, 1636f92363d1SSreekanth Reddy phy->number, phy_operation)); 1637f92363d1SSreekanth Reddy init_completion(&ioc->transport_cmds.done); 1638078a4cc1SSuganath Prabu S ioc->put_smid_default(ioc, smid); 16398bbb1cf6SCalvin Owens wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); 1640f92363d1SSreekanth Reddy 1641f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { 1642919d8a3fSJoe Perches ioc_err(ioc, "%s: timeout\n", __func__); 1643f92363d1SSreekanth Reddy _debug_dump_mf(mpi_request, 1644f92363d1SSreekanth Reddy sizeof(Mpi2SmpPassthroughRequest_t)/4); 1645f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) 1646f92363d1SSreekanth Reddy issue_reset = 1; 1647f92363d1SSreekanth Reddy goto issue_host_reset; 1648f92363d1SSreekanth Reddy } 1649f92363d1SSreekanth Reddy 1650919d8a3fSJoe Perches dtransportprintk(ioc, ioc_info(ioc, "phy_control - complete\n")); 1651f92363d1SSreekanth Reddy 1652f92363d1SSreekanth Reddy if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) { 1653f92363d1SSreekanth Reddy 1654f92363d1SSreekanth Reddy mpi_reply = ioc->transport_cmds.reply; 1655f92363d1SSreekanth Reddy 1656919d8a3fSJoe Perches dtransportprintk(ioc, 1657919d8a3fSJoe Perches ioc_info(ioc, "phy_control - reply data transfer size(%d)\n", 1658919d8a3fSJoe Perches le16_to_cpu(mpi_reply->ResponseDataLength))); 1659f92363d1SSreekanth Reddy 1660f92363d1SSreekanth Reddy if (le16_to_cpu(mpi_reply->ResponseDataLength) != 1661f92363d1SSreekanth Reddy sizeof(struct phy_control_reply)) 1662f92363d1SSreekanth Reddy goto out; 1663f92363d1SSreekanth Reddy 1664f92363d1SSreekanth Reddy phy_control_reply = data_out + 1665f92363d1SSreekanth Reddy sizeof(struct phy_control_request); 1666f92363d1SSreekanth Reddy 1667919d8a3fSJoe Perches dtransportprintk(ioc, 1668919d8a3fSJoe Perches ioc_info(ioc, "phy_control - function_result(%d)\n", 1669919d8a3fSJoe Perches phy_control_reply->function_result)); 1670f92363d1SSreekanth Reddy 1671f92363d1SSreekanth Reddy rc = 0; 1672f92363d1SSreekanth Reddy } else 1673919d8a3fSJoe Perches dtransportprintk(ioc, 1674919d8a3fSJoe Perches ioc_info(ioc, "phy_control - no reply\n")); 1675f92363d1SSreekanth Reddy 1676f92363d1SSreekanth Reddy issue_host_reset: 1677f92363d1SSreekanth Reddy if (issue_reset) 167898c56ad3SCalvin Owens mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); 1679f92363d1SSreekanth Reddy out: 1680f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_NOT_USED; 1681f92363d1SSreekanth Reddy if (data_out) 16821c2048bdSChristoph Hellwig dma_free_coherent(&ioc->pdev->dev, sz, data_out, 16831c2048bdSChristoph Hellwig data_out_dma); 1684f92363d1SSreekanth Reddy 1685f92363d1SSreekanth Reddy mutex_unlock(&ioc->transport_cmds.mutex); 1686f92363d1SSreekanth Reddy return rc; 1687f92363d1SSreekanth Reddy } 1688f92363d1SSreekanth Reddy 1689f92363d1SSreekanth Reddy /** 1690f92363d1SSreekanth Reddy * _transport_phy_reset - 1691f92363d1SSreekanth Reddy * @phy: The sas phy object 1692f92363d1SSreekanth Reddy * @hard_reset: 1693f92363d1SSreekanth Reddy * 16944beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1695f92363d1SSreekanth Reddy */ 1696f92363d1SSreekanth Reddy static int 1697f92363d1SSreekanth Reddy _transport_phy_reset(struct sas_phy *phy, int hard_reset) 1698f92363d1SSreekanth Reddy { 1699f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); 1700f92363d1SSreekanth Reddy Mpi2SasIoUnitControlReply_t mpi_reply; 1701f92363d1SSreekanth Reddy Mpi2SasIoUnitControlRequest_t mpi_request; 17027d310f24SSreekanth Reddy struct hba_port *port = phy->hostdata; 17037d310f24SSreekanth Reddy int port_id = port->port_id; 1704f92363d1SSreekanth Reddy unsigned long flags; 1705f92363d1SSreekanth Reddy 1706f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 1707f92363d1SSreekanth Reddy if (_transport_sas_node_find_by_sas_address(ioc, 17087d310f24SSreekanth Reddy phy->identify.sas_address, 170934b0a785SSreekanth Reddy mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { 1710f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1711f92363d1SSreekanth Reddy return -EINVAL; 1712f92363d1SSreekanth Reddy } 1713f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1714f92363d1SSreekanth Reddy 1715f92363d1SSreekanth Reddy /* handle expander phys */ 1716f92363d1SSreekanth Reddy if (phy->identify.sas_address != ioc->sas_hba.sas_address) 1717f92363d1SSreekanth Reddy return _transport_expander_phy_control(ioc, phy, 1718f92363d1SSreekanth Reddy (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET : 1719f92363d1SSreekanth Reddy SMP_PHY_CONTROL_LINK_RESET); 1720f92363d1SSreekanth Reddy 1721f92363d1SSreekanth Reddy /* handle hba phys */ 1722869817f9SSuganath prabu Subramani memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); 1723f92363d1SSreekanth Reddy mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; 1724f92363d1SSreekanth Reddy mpi_request.Operation = hard_reset ? 1725f92363d1SSreekanth Reddy MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; 1726f92363d1SSreekanth Reddy mpi_request.PhyNum = phy->number; 1727f92363d1SSreekanth Reddy 1728f92363d1SSreekanth Reddy if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { 1729919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1730919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1731f92363d1SSreekanth Reddy return -ENXIO; 1732f92363d1SSreekanth Reddy } 1733f92363d1SSreekanth Reddy 1734f92363d1SSreekanth Reddy if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) 1735919d8a3fSJoe Perches ioc_info(ioc, "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", 1736919d8a3fSJoe Perches phy->number, le16_to_cpu(mpi_reply.IOCStatus), 1737f92363d1SSreekanth Reddy le32_to_cpu(mpi_reply.IOCLogInfo)); 1738f92363d1SSreekanth Reddy 1739f92363d1SSreekanth Reddy return 0; 1740f92363d1SSreekanth Reddy } 1741f92363d1SSreekanth Reddy 1742f92363d1SSreekanth Reddy /** 1743f92363d1SSreekanth Reddy * _transport_phy_enable - enable/disable phys 1744f92363d1SSreekanth Reddy * @phy: The sas phy object 1745f92363d1SSreekanth Reddy * @enable: enable phy when true 1746f92363d1SSreekanth Reddy * 1747f92363d1SSreekanth Reddy * Only support sas_host direct attached phys. 17484beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1749f92363d1SSreekanth Reddy */ 1750f92363d1SSreekanth Reddy static int 1751f92363d1SSreekanth Reddy _transport_phy_enable(struct sas_phy *phy, int enable) 1752f92363d1SSreekanth Reddy { 1753f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); 1754f92363d1SSreekanth Reddy Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; 1755f92363d1SSreekanth Reddy Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; 1756f92363d1SSreekanth Reddy Mpi2ConfigReply_t mpi_reply; 1757f92363d1SSreekanth Reddy u16 ioc_status; 1758f92363d1SSreekanth Reddy u16 sz; 1759f92363d1SSreekanth Reddy int rc = 0; 1760f92363d1SSreekanth Reddy unsigned long flags; 1761f92363d1SSreekanth Reddy int i, discovery_active; 17627d310f24SSreekanth Reddy struct hba_port *port = phy->hostdata; 17637d310f24SSreekanth Reddy int port_id = port->port_id; 1764f92363d1SSreekanth Reddy 1765f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 1766f92363d1SSreekanth Reddy if (_transport_sas_node_find_by_sas_address(ioc, 17677d310f24SSreekanth Reddy phy->identify.sas_address, 176834b0a785SSreekanth Reddy mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { 1769f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1770f92363d1SSreekanth Reddy return -EINVAL; 1771f92363d1SSreekanth Reddy } 1772f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1773f92363d1SSreekanth Reddy 1774f92363d1SSreekanth Reddy /* handle expander phys */ 1775f92363d1SSreekanth Reddy if (phy->identify.sas_address != ioc->sas_hba.sas_address) 1776f92363d1SSreekanth Reddy return _transport_expander_phy_control(ioc, phy, 1777f92363d1SSreekanth Reddy (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET : 1778f92363d1SSreekanth Reddy SMP_PHY_CONTROL_DISABLE); 1779f92363d1SSreekanth Reddy 1780f92363d1SSreekanth Reddy /* handle hba phys */ 1781f92363d1SSreekanth Reddy 1782f92363d1SSreekanth Reddy /* read sas_iounit page 0 */ 1783f92363d1SSreekanth Reddy sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * 1784f92363d1SSreekanth Reddy sizeof(Mpi2SasIOUnit0PhyData_t)); 1785f92363d1SSreekanth Reddy sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); 1786f92363d1SSreekanth Reddy if (!sas_iounit_pg0) { 1787919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1788919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1789f92363d1SSreekanth Reddy rc = -ENOMEM; 1790f92363d1SSreekanth Reddy goto out; 1791f92363d1SSreekanth Reddy } 1792f92363d1SSreekanth Reddy if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, 1793f92363d1SSreekanth Reddy sas_iounit_pg0, sz))) { 1794919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1795919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1796f92363d1SSreekanth Reddy rc = -ENXIO; 1797f92363d1SSreekanth Reddy goto out; 1798f92363d1SSreekanth Reddy } 1799f92363d1SSreekanth Reddy ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & 1800f92363d1SSreekanth Reddy MPI2_IOCSTATUS_MASK; 1801f92363d1SSreekanth Reddy if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { 1802919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1803919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1804f92363d1SSreekanth Reddy rc = -EIO; 1805f92363d1SSreekanth Reddy goto out; 1806f92363d1SSreekanth Reddy } 1807f92363d1SSreekanth Reddy 1808f92363d1SSreekanth Reddy /* unable to enable/disable phys when when discovery is active */ 1809f92363d1SSreekanth Reddy for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) { 1810f92363d1SSreekanth Reddy if (sas_iounit_pg0->PhyData[i].PortFlags & 1811f92363d1SSreekanth Reddy MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { 1812919d8a3fSJoe Perches ioc_err(ioc, "discovery is active on port = %d, phy = %d: unable to enable/disable phys, try again later!\n", 1813f92363d1SSreekanth Reddy sas_iounit_pg0->PhyData[i].Port, i); 1814f92363d1SSreekanth Reddy discovery_active = 1; 1815f92363d1SSreekanth Reddy } 1816f92363d1SSreekanth Reddy } 1817f92363d1SSreekanth Reddy 1818f92363d1SSreekanth Reddy if (discovery_active) { 1819f92363d1SSreekanth Reddy rc = -EAGAIN; 1820f92363d1SSreekanth Reddy goto out; 1821f92363d1SSreekanth Reddy } 1822f92363d1SSreekanth Reddy 1823f92363d1SSreekanth Reddy /* read sas_iounit page 1 */ 1824f92363d1SSreekanth Reddy sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * 1825f92363d1SSreekanth Reddy sizeof(Mpi2SasIOUnit1PhyData_t)); 1826f92363d1SSreekanth Reddy sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); 1827f92363d1SSreekanth Reddy if (!sas_iounit_pg1) { 1828919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1829919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1830f92363d1SSreekanth Reddy rc = -ENOMEM; 1831f92363d1SSreekanth Reddy goto out; 1832f92363d1SSreekanth Reddy } 1833f92363d1SSreekanth Reddy if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, 1834f92363d1SSreekanth Reddy sas_iounit_pg1, sz))) { 1835919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1836919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1837f92363d1SSreekanth Reddy rc = -ENXIO; 1838f92363d1SSreekanth Reddy goto out; 1839f92363d1SSreekanth Reddy } 1840f92363d1SSreekanth Reddy ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & 1841f92363d1SSreekanth Reddy MPI2_IOCSTATUS_MASK; 1842f92363d1SSreekanth Reddy if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { 1843919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1844919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1845f92363d1SSreekanth Reddy rc = -EIO; 1846f92363d1SSreekanth Reddy goto out; 1847f92363d1SSreekanth Reddy } 1848f92363d1SSreekanth Reddy 1849f92363d1SSreekanth Reddy /* copy Port/PortFlags/PhyFlags from page 0 */ 1850f92363d1SSreekanth Reddy for (i = 0; i < ioc->sas_hba.num_phys ; i++) { 1851f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[i].Port = 1852f92363d1SSreekanth Reddy sas_iounit_pg0->PhyData[i].Port; 1853f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[i].PortFlags = 1854f92363d1SSreekanth Reddy (sas_iounit_pg0->PhyData[i].PortFlags & 1855f92363d1SSreekanth Reddy MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG); 1856f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[i].PhyFlags = 1857f92363d1SSreekanth Reddy (sas_iounit_pg0->PhyData[i].PhyFlags & 1858f92363d1SSreekanth Reddy (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED + 1859f92363d1SSreekanth Reddy MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)); 1860f92363d1SSreekanth Reddy } 1861f92363d1SSreekanth Reddy 1862f92363d1SSreekanth Reddy if (enable) 1863f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[phy->number].PhyFlags 1864f92363d1SSreekanth Reddy &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; 1865f92363d1SSreekanth Reddy else 1866f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[phy->number].PhyFlags 1867f92363d1SSreekanth Reddy |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; 1868f92363d1SSreekanth Reddy 1869f92363d1SSreekanth Reddy mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz); 1870f92363d1SSreekanth Reddy 1871f92363d1SSreekanth Reddy /* link reset */ 1872f92363d1SSreekanth Reddy if (enable) 1873f92363d1SSreekanth Reddy _transport_phy_reset(phy, 0); 1874f92363d1SSreekanth Reddy 1875f92363d1SSreekanth Reddy out: 1876f92363d1SSreekanth Reddy kfree(sas_iounit_pg1); 1877f92363d1SSreekanth Reddy kfree(sas_iounit_pg0); 1878f92363d1SSreekanth Reddy return rc; 1879f92363d1SSreekanth Reddy } 1880f92363d1SSreekanth Reddy 1881f92363d1SSreekanth Reddy /** 1882f92363d1SSreekanth Reddy * _transport_phy_speed - set phy min/max link rates 1883f92363d1SSreekanth Reddy * @phy: The sas phy object 1884f92363d1SSreekanth Reddy * @rates: rates defined in sas_phy_linkrates 1885f92363d1SSreekanth Reddy * 1886f92363d1SSreekanth Reddy * Only support sas_host direct attached phys. 18874beb4867SBart Van Assche * 18884beb4867SBart Van Assche * Return: 0 for success, non-zero for failure. 1889f92363d1SSreekanth Reddy */ 1890f92363d1SSreekanth Reddy static int 1891f92363d1SSreekanth Reddy _transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) 1892f92363d1SSreekanth Reddy { 1893f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy); 1894f92363d1SSreekanth Reddy Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; 1895f92363d1SSreekanth Reddy Mpi2SasPhyPage0_t phy_pg0; 1896f92363d1SSreekanth Reddy Mpi2ConfigReply_t mpi_reply; 1897f92363d1SSreekanth Reddy u16 ioc_status; 1898f92363d1SSreekanth Reddy u16 sz; 1899f92363d1SSreekanth Reddy int i; 1900f92363d1SSreekanth Reddy int rc = 0; 1901f92363d1SSreekanth Reddy unsigned long flags; 19027d310f24SSreekanth Reddy struct hba_port *port = phy->hostdata; 19037d310f24SSreekanth Reddy int port_id = port->port_id; 1904f92363d1SSreekanth Reddy 1905f92363d1SSreekanth Reddy spin_lock_irqsave(&ioc->sas_node_lock, flags); 1906f92363d1SSreekanth Reddy if (_transport_sas_node_find_by_sas_address(ioc, 19077d310f24SSreekanth Reddy phy->identify.sas_address, 190834b0a785SSreekanth Reddy mpt3sas_get_port_by_id(ioc, port_id, 0)) == NULL) { 1909f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1910f92363d1SSreekanth Reddy return -EINVAL; 1911f92363d1SSreekanth Reddy } 1912f92363d1SSreekanth Reddy spin_unlock_irqrestore(&ioc->sas_node_lock, flags); 1913f92363d1SSreekanth Reddy 1914f92363d1SSreekanth Reddy if (!rates->minimum_linkrate) 1915f92363d1SSreekanth Reddy rates->minimum_linkrate = phy->minimum_linkrate; 1916f92363d1SSreekanth Reddy else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) 1917f92363d1SSreekanth Reddy rates->minimum_linkrate = phy->minimum_linkrate_hw; 1918f92363d1SSreekanth Reddy 1919f92363d1SSreekanth Reddy if (!rates->maximum_linkrate) 1920f92363d1SSreekanth Reddy rates->maximum_linkrate = phy->maximum_linkrate; 1921f92363d1SSreekanth Reddy else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) 1922f92363d1SSreekanth Reddy rates->maximum_linkrate = phy->maximum_linkrate_hw; 1923f92363d1SSreekanth Reddy 1924f92363d1SSreekanth Reddy /* handle expander phys */ 1925f92363d1SSreekanth Reddy if (phy->identify.sas_address != ioc->sas_hba.sas_address) { 1926f92363d1SSreekanth Reddy phy->minimum_linkrate = rates->minimum_linkrate; 1927f92363d1SSreekanth Reddy phy->maximum_linkrate = rates->maximum_linkrate; 1928f92363d1SSreekanth Reddy return _transport_expander_phy_control(ioc, phy, 1929f92363d1SSreekanth Reddy SMP_PHY_CONTROL_LINK_RESET); 1930f92363d1SSreekanth Reddy } 1931f92363d1SSreekanth Reddy 1932f92363d1SSreekanth Reddy /* handle hba phys */ 1933f92363d1SSreekanth Reddy 1934f92363d1SSreekanth Reddy /* sas_iounit page 1 */ 1935f92363d1SSreekanth Reddy sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * 1936f92363d1SSreekanth Reddy sizeof(Mpi2SasIOUnit1PhyData_t)); 1937f92363d1SSreekanth Reddy sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); 1938f92363d1SSreekanth Reddy if (!sas_iounit_pg1) { 1939919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1940919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1941f92363d1SSreekanth Reddy rc = -ENOMEM; 1942f92363d1SSreekanth Reddy goto out; 1943f92363d1SSreekanth Reddy } 1944f92363d1SSreekanth Reddy if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, 1945f92363d1SSreekanth Reddy sas_iounit_pg1, sz))) { 1946919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1947919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1948f92363d1SSreekanth Reddy rc = -ENXIO; 1949f92363d1SSreekanth Reddy goto out; 1950f92363d1SSreekanth Reddy } 1951f92363d1SSreekanth Reddy ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & 1952f92363d1SSreekanth Reddy MPI2_IOCSTATUS_MASK; 1953f92363d1SSreekanth Reddy if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { 1954919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1955919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1956f92363d1SSreekanth Reddy rc = -EIO; 1957f92363d1SSreekanth Reddy goto out; 1958f92363d1SSreekanth Reddy } 1959f92363d1SSreekanth Reddy 1960f92363d1SSreekanth Reddy for (i = 0; i < ioc->sas_hba.num_phys; i++) { 1961f92363d1SSreekanth Reddy if (phy->number != i) { 1962f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[i].MaxMinLinkRate = 1963f92363d1SSreekanth Reddy (ioc->sas_hba.phy[i].phy->minimum_linkrate + 1964f92363d1SSreekanth Reddy (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4)); 1965f92363d1SSreekanth Reddy } else { 1966f92363d1SSreekanth Reddy sas_iounit_pg1->PhyData[i].MaxMinLinkRate = 1967f92363d1SSreekanth Reddy (rates->minimum_linkrate + 1968f92363d1SSreekanth Reddy (rates->maximum_linkrate << 4)); 1969f92363d1SSreekanth Reddy } 1970f92363d1SSreekanth Reddy } 1971f92363d1SSreekanth Reddy 1972f92363d1SSreekanth Reddy if (mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, 1973f92363d1SSreekanth Reddy sz)) { 1974919d8a3fSJoe Perches ioc_err(ioc, "failure at %s:%d/%s()!\n", 1975919d8a3fSJoe Perches __FILE__, __LINE__, __func__); 1976f92363d1SSreekanth Reddy rc = -ENXIO; 1977f92363d1SSreekanth Reddy goto out; 1978f92363d1SSreekanth Reddy } 1979f92363d1SSreekanth Reddy 1980f92363d1SSreekanth Reddy /* link reset */ 1981f92363d1SSreekanth Reddy _transport_phy_reset(phy, 0); 1982f92363d1SSreekanth Reddy 1983f92363d1SSreekanth Reddy /* read phy page 0, then update the rates in the sas transport phy */ 1984f92363d1SSreekanth Reddy if (!mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, 1985f92363d1SSreekanth Reddy phy->number)) { 1986f92363d1SSreekanth Reddy phy->minimum_linkrate = _transport_convert_phy_link_rate( 1987f92363d1SSreekanth Reddy phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); 1988f92363d1SSreekanth Reddy phy->maximum_linkrate = _transport_convert_phy_link_rate( 1989f92363d1SSreekanth Reddy phy_pg0.ProgrammedLinkRate >> 4); 1990f92363d1SSreekanth Reddy phy->negotiated_linkrate = _transport_convert_phy_link_rate( 1991f92363d1SSreekanth Reddy phy_pg0.NegotiatedLinkRate & 1992f92363d1SSreekanth Reddy MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); 1993f92363d1SSreekanth Reddy } 1994f92363d1SSreekanth Reddy 1995f92363d1SSreekanth Reddy out: 1996f92363d1SSreekanth Reddy kfree(sas_iounit_pg1); 1997f92363d1SSreekanth Reddy return rc; 1998f92363d1SSreekanth Reddy } 1999f92363d1SSreekanth Reddy 2000651a0136SChristoph Hellwig static int 2001651a0136SChristoph Hellwig _transport_map_smp_buffer(struct device *dev, struct bsg_buffer *buf, 2002651a0136SChristoph Hellwig dma_addr_t *dma_addr, size_t *dma_len, void **p) 2003651a0136SChristoph Hellwig { 2004651a0136SChristoph Hellwig /* Check if the request is split across multiple segments */ 2005651a0136SChristoph Hellwig if (buf->sg_cnt > 1) { 2006651a0136SChristoph Hellwig *p = dma_alloc_coherent(dev, buf->payload_len, dma_addr, 2007651a0136SChristoph Hellwig GFP_KERNEL); 2008651a0136SChristoph Hellwig if (!*p) 2009651a0136SChristoph Hellwig return -ENOMEM; 2010651a0136SChristoph Hellwig *dma_len = buf->payload_len; 2011651a0136SChristoph Hellwig } else { 2012651a0136SChristoph Hellwig if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL)) 2013651a0136SChristoph Hellwig return -ENOMEM; 2014651a0136SChristoph Hellwig *dma_addr = sg_dma_address(buf->sg_list); 2015651a0136SChristoph Hellwig *dma_len = sg_dma_len(buf->sg_list); 2016651a0136SChristoph Hellwig *p = NULL; 2017651a0136SChristoph Hellwig } 2018651a0136SChristoph Hellwig 2019651a0136SChristoph Hellwig return 0; 2020651a0136SChristoph Hellwig } 2021651a0136SChristoph Hellwig 2022651a0136SChristoph Hellwig static void 2023651a0136SChristoph Hellwig _transport_unmap_smp_buffer(struct device *dev, struct bsg_buffer *buf, 2024651a0136SChristoph Hellwig dma_addr_t dma_addr, void *p) 2025651a0136SChristoph Hellwig { 2026651a0136SChristoph Hellwig if (p) 2027651a0136SChristoph Hellwig dma_free_coherent(dev, buf->payload_len, p, dma_addr); 2028651a0136SChristoph Hellwig else 2029651a0136SChristoph Hellwig dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL); 2030651a0136SChristoph Hellwig } 2031651a0136SChristoph Hellwig 2032f92363d1SSreekanth Reddy /** 2033f92363d1SSreekanth Reddy * _transport_smp_handler - transport portal for smp passthru 20344beb4867SBart Van Assche * @job: ? 2035f92363d1SSreekanth Reddy * @shost: shost object 2036f92363d1SSreekanth Reddy * @rphy: sas transport rphy object 2037f92363d1SSreekanth Reddy * 2038f92363d1SSreekanth Reddy * This used primarily for smp_utils. 2039f92363d1SSreekanth Reddy * Example: 2040f92363d1SSreekanth Reddy * smp_rep_general /sys/class/bsg/expander-5:0 2041f92363d1SSreekanth Reddy */ 2042651a0136SChristoph Hellwig static void 2043651a0136SChristoph Hellwig _transport_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, 2044651a0136SChristoph Hellwig struct sas_rphy *rphy) 2045f92363d1SSreekanth Reddy { 2046f92363d1SSreekanth Reddy struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); 2047f92363d1SSreekanth Reddy Mpi2SmpPassthroughRequest_t *mpi_request; 2048f92363d1SSreekanth Reddy Mpi2SmpPassthroughReply_t *mpi_reply; 20497988613bSKent Overstreet int rc; 2050f92363d1SSreekanth Reddy u16 smid; 2051f92363d1SSreekanth Reddy void *psge; 2052651a0136SChristoph Hellwig dma_addr_t dma_addr_in; 2053651a0136SChristoph Hellwig dma_addr_t dma_addr_out; 2054651a0136SChristoph Hellwig void *addr_in = NULL; 2055651a0136SChristoph Hellwig void *addr_out = NULL; 2056651a0136SChristoph Hellwig size_t dma_len_in; 2057651a0136SChristoph Hellwig size_t dma_len_out; 2058651a0136SChristoph Hellwig unsigned int reslen = 0; 2059f92363d1SSreekanth Reddy 2060f92363d1SSreekanth Reddy if (ioc->shost_recovery || ioc->pci_error_recovery) { 20614dc74b2eSJoe Perches ioc_info(ioc, "%s: host reset in progress!\n", __func__); 2062651a0136SChristoph Hellwig rc = -EFAULT; 206391b7bdb2SBart Van Assche goto job_done; 2064f92363d1SSreekanth Reddy } 2065f92363d1SSreekanth Reddy 2066f92363d1SSreekanth Reddy rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); 2067f92363d1SSreekanth Reddy if (rc) 206891b7bdb2SBart Van Assche goto job_done; 2069f92363d1SSreekanth Reddy 2070f92363d1SSreekanth Reddy if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) { 2071919d8a3fSJoe Perches ioc_err(ioc, "%s: transport_cmds in use\n", 2072f92363d1SSreekanth Reddy __func__); 2073f92363d1SSreekanth Reddy rc = -EAGAIN; 2074f92363d1SSreekanth Reddy goto out; 2075f92363d1SSreekanth Reddy } 2076f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_PENDING; 2077f92363d1SSreekanth Reddy 2078651a0136SChristoph Hellwig rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->request_payload, 2079651a0136SChristoph Hellwig &dma_addr_out, &dma_len_out, &addr_out); 2080651a0136SChristoph Hellwig if (rc) 2081f92363d1SSreekanth Reddy goto out; 2082651a0136SChristoph Hellwig if (addr_out) { 2083651a0136SChristoph Hellwig sg_copy_to_buffer(job->request_payload.sg_list, 2084651a0136SChristoph Hellwig job->request_payload.sg_cnt, addr_out, 2085651a0136SChristoph Hellwig job->request_payload.payload_len); 2086f92363d1SSreekanth Reddy } 2087f92363d1SSreekanth Reddy 2088651a0136SChristoph Hellwig rc = _transport_map_smp_buffer(&ioc->pdev->dev, &job->reply_payload, 2089651a0136SChristoph Hellwig &dma_addr_in, &dma_len_in, &addr_in); 2090651a0136SChristoph Hellwig if (rc) 2091651a0136SChristoph Hellwig goto unmap_out; 2092f92363d1SSreekanth Reddy 2093f4305749SSuganath Prabu rc = mpt3sas_wait_for_ioc(ioc, IOC_OPERATIONAL_WAIT_COUNT); 2094f4305749SSuganath Prabu if (rc) 2095651a0136SChristoph Hellwig goto unmap_in; 2096f92363d1SSreekanth Reddy 2097f92363d1SSreekanth Reddy smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx); 2098f92363d1SSreekanth Reddy if (!smid) { 2099919d8a3fSJoe Perches ioc_err(ioc, "%s: failed obtaining a smid\n", __func__); 2100f92363d1SSreekanth Reddy rc = -EAGAIN; 2101651a0136SChristoph Hellwig goto unmap_in; 2102f92363d1SSreekanth Reddy } 2103f92363d1SSreekanth Reddy 2104f92363d1SSreekanth Reddy rc = 0; 2105f92363d1SSreekanth Reddy mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); 2106f92363d1SSreekanth Reddy ioc->transport_cmds.smid = smid; 2107f92363d1SSreekanth Reddy 2108f92363d1SSreekanth Reddy memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); 2109f92363d1SSreekanth Reddy mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 21109d0348a9SSreekanth Reddy mpi_request->PhysicalPort = _transport_get_port_id_by_rphy(ioc, rphy); 2111f92363d1SSreekanth Reddy mpi_request->SASAddress = (rphy) ? 2112f92363d1SSreekanth Reddy cpu_to_le64(rphy->identify.sas_address) : 2113f92363d1SSreekanth Reddy cpu_to_le64(ioc->sas_hba.sas_address); 2114651a0136SChristoph Hellwig mpi_request->RequestDataLength = cpu_to_le16(dma_len_out - 4); 2115f92363d1SSreekanth Reddy psge = &mpi_request->SGL; 2116f92363d1SSreekanth Reddy 2117651a0136SChristoph Hellwig ioc->build_sg(ioc, psge, dma_addr_out, dma_len_out - 4, dma_addr_in, 2118651a0136SChristoph Hellwig dma_len_in - 4); 2119f92363d1SSreekanth Reddy 2120919d8a3fSJoe Perches dtransportprintk(ioc, 2121919d8a3fSJoe Perches ioc_info(ioc, "%s: sending smp request\n", __func__)); 2122f92363d1SSreekanth Reddy 2123f92363d1SSreekanth Reddy init_completion(&ioc->transport_cmds.done); 2124078a4cc1SSuganath Prabu S ioc->put_smid_default(ioc, smid); 21258bbb1cf6SCalvin Owens wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); 2126f92363d1SSreekanth Reddy 2127f92363d1SSreekanth Reddy if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) { 21284dc74b2eSJoe Perches ioc_err(ioc, "%s: timeout\n", __func__); 2129f92363d1SSreekanth Reddy _debug_dump_mf(mpi_request, 2130f92363d1SSreekanth Reddy sizeof(Mpi2SmpPassthroughRequest_t)/4); 2131651a0136SChristoph Hellwig if (!(ioc->transport_cmds.status & MPT3_CMD_RESET)) { 2132651a0136SChristoph Hellwig mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); 2133651a0136SChristoph Hellwig rc = -ETIMEDOUT; 2134651a0136SChristoph Hellwig goto unmap_in; 2135651a0136SChristoph Hellwig } 2136f92363d1SSreekanth Reddy } 2137f92363d1SSreekanth Reddy 2138919d8a3fSJoe Perches dtransportprintk(ioc, ioc_info(ioc, "%s - complete\n", __func__)); 2139f92363d1SSreekanth Reddy 2140651a0136SChristoph Hellwig if (!(ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID)) { 2141919d8a3fSJoe Perches dtransportprintk(ioc, 2142919d8a3fSJoe Perches ioc_info(ioc, "%s: no reply\n", __func__)); 2143f92363d1SSreekanth Reddy rc = -ENXIO; 2144651a0136SChristoph Hellwig goto unmap_in; 2145f92363d1SSreekanth Reddy } 2146f92363d1SSreekanth Reddy 2147651a0136SChristoph Hellwig mpi_reply = ioc->transport_cmds.reply; 2148651a0136SChristoph Hellwig 2149651a0136SChristoph Hellwig dtransportprintk(ioc, 2150919d8a3fSJoe Perches ioc_info(ioc, "%s: reply data transfer size(%d)\n", 2151919d8a3fSJoe Perches __func__, 2152651a0136SChristoph Hellwig le16_to_cpu(mpi_reply->ResponseDataLength))); 2153651a0136SChristoph Hellwig 2154651a0136SChristoph Hellwig memcpy(job->reply, mpi_reply, sizeof(*mpi_reply)); 2155651a0136SChristoph Hellwig job->reply_len = sizeof(*mpi_reply); 2156651a0136SChristoph Hellwig reslen = le16_to_cpu(mpi_reply->ResponseDataLength); 2157651a0136SChristoph Hellwig 2158651a0136SChristoph Hellwig if (addr_in) { 2159651a0136SChristoph Hellwig sg_copy_to_buffer(job->reply_payload.sg_list, 2160651a0136SChristoph Hellwig job->reply_payload.sg_cnt, addr_in, 2161651a0136SChristoph Hellwig job->reply_payload.payload_len); 2162f92363d1SSreekanth Reddy } 2163f92363d1SSreekanth Reddy 2164651a0136SChristoph Hellwig rc = 0; 2165651a0136SChristoph Hellwig unmap_in: 2166651a0136SChristoph Hellwig _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->reply_payload, 2167651a0136SChristoph Hellwig dma_addr_in, addr_in); 2168651a0136SChristoph Hellwig unmap_out: 2169651a0136SChristoph Hellwig _transport_unmap_smp_buffer(&ioc->pdev->dev, &job->request_payload, 2170651a0136SChristoph Hellwig dma_addr_out, addr_out); 2171f92363d1SSreekanth Reddy out: 2172f92363d1SSreekanth Reddy ioc->transport_cmds.status = MPT3_CMD_NOT_USED; 2173f92363d1SSreekanth Reddy mutex_unlock(&ioc->transport_cmds.mutex); 217491b7bdb2SBart Van Assche job_done: 2175651a0136SChristoph Hellwig bsg_job_done(job, rc, reslen); 2176f92363d1SSreekanth Reddy } 2177f92363d1SSreekanth Reddy 2178f92363d1SSreekanth Reddy struct sas_function_template mpt3sas_transport_functions = { 2179f92363d1SSreekanth Reddy .get_linkerrors = _transport_get_linkerrors, 2180f92363d1SSreekanth Reddy .get_enclosure_identifier = _transport_get_enclosure_identifier, 2181f92363d1SSreekanth Reddy .get_bay_identifier = _transport_get_bay_identifier, 2182f92363d1SSreekanth Reddy .phy_reset = _transport_phy_reset, 2183f92363d1SSreekanth Reddy .phy_enable = _transport_phy_enable, 2184f92363d1SSreekanth Reddy .set_phy_speed = _transport_phy_speed, 2185f92363d1SSreekanth Reddy .smp_handler = _transport_smp_handler, 2186f92363d1SSreekanth Reddy }; 2187f92363d1SSreekanth Reddy 2188f92363d1SSreekanth Reddy struct scsi_transport_template *mpt3sas_transport_template; 2189