/* * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * Copyright (C) 2012-2016 LSI Corporation * Copyright (C) 2013-2016 Avago Technologies * (mailto: MPT-FusionLinux.pdl@avagotech.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * NO WARRANTY * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is * solely responsible for determining the appropriateness of using and * distributing the Program and assumes all risks associated with its * exercise of rights under this Agreement, including but not limited to * the risks and costs of program errors, damage to or loss of data, * programs or equipment, and unavailability or interruption of operations. * DISCLAIMER OF LIABILITY * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include "csmi/csmisas.h" static int _csmisas_get_driver_info(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_DRIVER_INFO_BUFFER *karg); static int _csmisas_get_cntlr_status(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_CNTLR_STATUS_BUFFER *karg); static int _csmisas_get_cntlr_config(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_CNTLR_CONFIG_BUFFER *karg); static int _csmisas_get_phy_info(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_PHY_INFO_BUFFER *karg); static int _csmisas_get_scsi_address(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_GET_SCSI_ADDRESS_BUFFER *karg); static int _csmisas_get_link_errors(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_LINK_ERRORS_BUFFER *karg); static int _csmisas_smp_passthru(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_SMP_PASSTHRU_BUFFER *karg); static int _csmisas_firmware_download(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_FIRMWARE_DOWNLOAD_BUFFER *karg); static int _csmisas_get_raid_info(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_INFO_BUFFER *karg); static int _csmisas_get_raid_config(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_CONFIG_BUFFER *karg); static int _csmisas_get_raid_features(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_FEATURES_BUFFER *karg); static int _csmisas_set_raid_control(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_CONTROL_BUFFER *karg); static int _csmisas_get_raid_element(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_ELEMENT_BUFFER *karg); static int _csmisas_set_raid_operation(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_RAID_SET_OPERATION_BUFFER *karg); static int _csmisas_set_phy_info(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_SET_PHY_INFO_BUFFER *karg); static int _csmisas_ssp_passthru(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_SSP_PASSTHRU_BUFFER *karg); static int _csmisas_stp_passthru(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_STP_PASSTHRU_BUFFER *karg); static int _csmisas_get_sata_signature(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_SATA_SIGNATURE_BUFFER *karg); static int _csmisas_get_device_address(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_GET_DEVICE_ADDRESS_BUFFER *karg); static int _csmisas_task_managment(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_SSP_TASK_IU_BUFFER *karg); static int _csmisas_phy_control(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_PHY_CONTROL_BUFFER *karg); static int _csmisas_get_connector_info(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_CONNECTOR_INFO_BUFFER *karg); static int _csmisas_get_location(struct MPT3SAS_ADAPTER *ioc, CSMI_SAS_GET_LOCATION_BUFFER *karg); #define MPT2SAS_HP_3PAR_SSVID 0x1590 #define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 #define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 #define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 #define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 #define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 /** * _ctl_check_for_hp_branded_controllers - customer branding check * @ioc: per adapter object * * HP controllers are only allowed to do CSMI IOCTL's * * Returns; "1" if HP controller, else "0" */ static int _ctl_check_for_hp_branded_controllers(struct MPT3SAS_ADAPTER *ioc) { int rc = 0; if (ioc->pdev->subsystem_vendor != MPT2SAS_HP_3PAR_SSVID) goto out; switch (ioc->pdev->device) { case MPI2_MFGPAGE_DEVID_SAS2004: switch (ioc->pdev->subsystem_device) { case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: rc = 1; break; default: break; } break; case MPI2_MFGPAGE_DEVID_SAS2308_2: switch (ioc->pdev->subsystem_device) { case MPT2SAS_HP_2_4_INTERNAL_SSDID: case MPT2SAS_HP_2_4_EXTERNAL_SSDID: case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: rc = 1; break; default: break; } break; default: break; } out: return rc; } long _ctl_ioctl_csmi(struct MPT3SAS_ADAPTER *ioc, unsigned int cmd, void __user *arg) { IOCTL_HEADER karg; void *payload; u32 payload_sz; int payload_pages; long ret = -EINVAL; if (copy_from_user(&karg, arg, sizeof(IOCTL_HEADER))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } if (_ctl_check_for_hp_branded_controllers(ioc) != 1) return -EPERM; payload_sz = karg.Length + sizeof(IOCTL_HEADER); payload_pages = get_order(payload_sz); payload = (void *)__get_free_pages(GFP_KERNEL, payload_pages); if (!payload) goto out; if (copy_from_user(payload, arg, payload_sz)) { printk(KERN_ERR "%s():%d: failure\n", __func__, __LINE__); ret = -EFAULT; goto out_free_pages; } switch (cmd) { case CC_CSMI_SAS_GET_DRIVER_INFO: ret = _csmisas_get_driver_info(ioc, payload); break; case CC_CSMI_SAS_GET_CNTLR_STATUS: ret = _csmisas_get_cntlr_status(ioc, payload); break; case CC_CSMI_SAS_GET_SCSI_ADDRESS: ret = _csmisas_get_scsi_address(ioc, payload); break; case CC_CSMI_SAS_GET_DEVICE_ADDRESS: ret = _csmisas_get_device_address(ioc, payload); break; case CC_CSMI_SAS_GET_CNTLR_CONFIG: ret = _csmisas_get_cntlr_config(ioc, payload); break; case CC_CSMI_SAS_GET_PHY_INFO: ret = _csmisas_get_phy_info(ioc, payload); break; case CC_CSMI_SAS_GET_SATA_SIGNATURE: ret = _csmisas_get_sata_signature(ioc, payload); break; case CC_CSMI_SAS_GET_LINK_ERRORS: ret = _csmisas_get_link_errors(ioc, payload); break; case CC_CSMI_SAS_SMP_PASSTHRU: ret = _csmisas_smp_passthru(ioc, payload); break; case CC_CSMI_SAS_SSP_PASSTHRU: ret = _csmisas_ssp_passthru(ioc, payload); break; case CC_CSMI_SAS_FIRMWARE_DOWNLOAD: ret = _csmisas_firmware_download(ioc, payload); break; case CC_CSMI_SAS_GET_RAID_INFO: ret = _csmisas_get_raid_info(ioc, payload); break; case CC_CSMI_SAS_GET_RAID_CONFIG: ret = _csmisas_get_raid_config(ioc, payload); break; case CC_CSMI_SAS_GET_RAID_FEATURES: ret = _csmisas_get_raid_features(ioc, payload); break; case CC_CSMI_SAS_SET_RAID_CONTROL: ret = _csmisas_set_raid_control(ioc, payload); break; case CC_CSMI_SAS_GET_RAID_ELEMENT: ret = _csmisas_get_raid_element(ioc, payload); break; case CC_CSMI_SAS_SET_RAID_OPERATION: ret = _csmisas_set_raid_operation(ioc, payload); break; case CC_CSMI_SAS_SET_PHY_INFO: ret = _csmisas_set_phy_info(ioc, payload); break; case CC_CSMI_SAS_STP_PASSTHRU: ret = _csmisas_stp_passthru(ioc, payload); break; case CC_CSMI_SAS_TASK_MANAGEMENT: ret = _csmisas_task_managment(ioc, payload); break; case CC_CSMI_SAS_PHY_CONTROL: ret = _csmisas_phy_control(ioc, payload); break; case CC_CSMI_SAS_GET_CONNECTOR_INFO: ret = _csmisas_get_connector_info(ioc, payload); break; case CC_CSMI_SAS_GET_LOCATION: ret = _csmisas_get_location(ioc, payload); break; } if (copy_to_user(arg, payload, payload_sz)) { printk(KERN_ERR "%s():%d: failure\n", __func__, __LINE__); ret = -EFAULT; } out_free_pages: free_pages((unsigned long)payload, payload_pages); out: return ret; } /** * _ctl_do_fw_download - Download fw to HBA * @ioc - pointer to ioc structure * @fwbuf - the fw buffer to flash * @fwlen - the size of the firmware * * This is the fw download engine for ioctls */ static int _ctl_do_fw_download(struct MPT3SAS_ADAPTER *ioc, char *fwbuf, size_t fwlen) { MPI2RequestHeader_t *mpi_request = NULL; MPI2DefaultReply_t *mpi_reply; Mpi2FWDownloadRequest *dlmsg; Mpi2FWDownloadTCSGE_t *tcsge; u32 ioc_state; u16 ioc_status; u16 smid; unsigned long timeout, timeleft; u8 issue_reset; void *psge; void *data_out = NULL; dma_addr_t data_out_dma = 0; size_t data_out_sz = 0; u32 sgl_flags; long ret; u16 wait_state_count; u32 chunk_sz = 0; u32 cur_offset = 0; u32 remaining_bytes = (u32)fwlen; issue_reset = 0; if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { printk(MPT3SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); ret = -EAGAIN; goto out; } wait_state_count = 0; ioc_state = mpt3sas_base_get_iocstate(ioc, 1); while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { if (wait_state_count++ == 10) { printk(MPT3SAS_ERR_FMT "%s: failed due to ioc not operational\n", ioc->name, __func__); ret = -EFAULT; goto out; } ssleep(1); ioc_state = mpt3sas_base_get_iocstate(ioc, 1); printk(MPT3SAS_INFO_FMT "%s: waiting for " "operational state(count=%d)\n", ioc->name, __func__, wait_state_count); } if (wait_state_count) printk(MPT3SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); again: if (remaining_bytes > FW_DL_CHUNK_SIZE) chunk_sz = FW_DL_CHUNK_SIZE; else chunk_sz = remaining_bytes; smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); ret = -EAGAIN; goto out; } mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(*mpi_request)); /* * Construct f/w download request */ dlmsg = (Mpi2FWDownloadRequest *)mpi_request; dlmsg->ImageType = MPI2_FW_DOWNLOAD_ITYPE_FW; dlmsg->Function = MPI2_FUNCTION_FW_DOWNLOAD; dlmsg->TotalImageSize = cpu_to_le32(fwlen); if (remaining_bytes == chunk_sz) dlmsg->MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT; /* Construct TrasactionContext Element */ tcsge = (Mpi2FWDownloadTCSGE_t *)&dlmsg->SGL; memset(tcsge, 0, sizeof(Mpi2FWDownloadTCSGE_t)); /* spec defines 12 or size of element. which is better */ tcsge->DetailsLength = offsetof(Mpi2FWDownloadTCSGE_t, ImageSize); tcsge->Flags = MPI2_SGE_FLAGS_TRANSACTION_ELEMENT; tcsge->ImageSize = cpu_to_le32(chunk_sz); tcsge->ImageOffset = cpu_to_le32(cur_offset); ret = 0; ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); ioc->ctl_cmds.smid = smid; data_out_sz = chunk_sz; /* obtain dma-able memory for data transfer */ if (!data_out) { data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, &data_out_dma); } if (!data_out) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENOMEM; mpt3sas_base_free_smid(ioc, smid); goto out; } memcpy(data_out, fwbuf+cur_offset, data_out_sz); /* add scatter gather elements */ psge = (void *)mpi_request + sizeof(Mpi2FWDownloadRequest) - sizeof(MPI2_MPI_SGE_UNION) + sizeof(Mpi2FWDownloadTCSGE_t); sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(psge, sgl_flags | data_out_sz, data_out_dma); /* send command to firmware */ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); init_completion(&ioc->ctl_cmds.done); ioc->put_smid_default(ioc, smid); timeout = MPT3_IOCTL_DEFAULT_TIMEOUT; timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { printk(MPT3SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, 0); if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) issue_reset = 1; goto issue_host_reset; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; cur_offset += chunk_sz; remaining_bytes -= chunk_sz; if (remaining_bytes > 0) goto again; goto out; issue_host_reset: /* Reset is only here for error conditions */ mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); out: /* free memory associated with sg buffers */ if (data_out) { pci_free_consistent(ioc->pdev, data_out_sz, data_out, data_out_dma); data_out = NULL; } ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; return ret; } #include "csmi/csmisas.c"