18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * driver for Microsemi PQI-based storage controllers 48c2ecf20Sopenharmony_ci * Copyright (c) 2019-2020 Microchip Technology Inc. and its subsidiaries 58c2ecf20Sopenharmony_ci * Copyright (c) 2016-2018 Microsemi Corporation 68c2ecf20Sopenharmony_ci * Copyright (c) 2016 PMC-Sierra, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Questions/Comments/Bugfixes to storagedev@microchip.com 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/bsg-lib.h> 148c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 158c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 168c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_sas.h> 178c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 188c2ecf20Sopenharmony_ci#include "smartpqi.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct pqi_sas_phy *pqi_sas_phy; 238c2ecf20Sopenharmony_ci struct sas_phy *phy; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL); 268c2ecf20Sopenharmony_ci if (!pqi_sas_phy) 278c2ecf20Sopenharmony_ci return NULL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev, 308c2ecf20Sopenharmony_ci pqi_sas_port->next_phy_index); 318c2ecf20Sopenharmony_ci if (!phy) { 328c2ecf20Sopenharmony_ci kfree(pqi_sas_phy); 338c2ecf20Sopenharmony_ci return NULL; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci pqi_sas_port->next_phy_index++; 378c2ecf20Sopenharmony_ci pqi_sas_phy->phy = phy; 388c2ecf20Sopenharmony_ci pqi_sas_phy->parent_port = pqi_sas_port; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return pqi_sas_phy; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct sas_phy *phy = pqi_sas_phy->phy; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy); 488c2ecf20Sopenharmony_ci if (pqi_sas_phy->added_to_port) 498c2ecf20Sopenharmony_ci list_del(&pqi_sas_phy->phy_list_entry); 508c2ecf20Sopenharmony_ci sas_phy_delete(phy); 518c2ecf20Sopenharmony_ci kfree(pqi_sas_phy); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int rc; 578c2ecf20Sopenharmony_ci struct pqi_sas_port *pqi_sas_port; 588c2ecf20Sopenharmony_ci struct sas_phy *phy; 598c2ecf20Sopenharmony_ci struct sas_identify *identify; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci pqi_sas_port = pqi_sas_phy->parent_port; 628c2ecf20Sopenharmony_ci phy = pqi_sas_phy->phy; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci identify = &phy->identify; 658c2ecf20Sopenharmony_ci memset(identify, 0, sizeof(*identify)); 668c2ecf20Sopenharmony_ci identify->sas_address = pqi_sas_port->sas_address; 678c2ecf20Sopenharmony_ci identify->device_type = SAS_END_DEVICE; 688c2ecf20Sopenharmony_ci identify->initiator_port_protocols = SAS_PROTOCOL_STP; 698c2ecf20Sopenharmony_ci identify->target_port_protocols = SAS_PROTOCOL_STP; 708c2ecf20Sopenharmony_ci phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 718c2ecf20Sopenharmony_ci phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 728c2ecf20Sopenharmony_ci phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; 738c2ecf20Sopenharmony_ci phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; 748c2ecf20Sopenharmony_ci phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci rc = sas_phy_add(pqi_sas_phy->phy); 778c2ecf20Sopenharmony_ci if (rc) 788c2ecf20Sopenharmony_ci return rc; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy); 818c2ecf20Sopenharmony_ci list_add_tail(&pqi_sas_phy->phy_list_entry, 828c2ecf20Sopenharmony_ci &pqi_sas_port->phy_list_head); 838c2ecf20Sopenharmony_ci pqi_sas_phy->added_to_port = true; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port, 898c2ecf20Sopenharmony_ci struct sas_rphy *rphy) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct sas_identify *identify; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci identify = &rphy->identify; 948c2ecf20Sopenharmony_ci identify->sas_address = pqi_sas_port->sas_address; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (pqi_sas_port->device && 978c2ecf20Sopenharmony_ci pqi_sas_port->device->is_expander_smp_device) { 988c2ecf20Sopenharmony_ci identify->initiator_port_protocols = SAS_PROTOCOL_SMP; 998c2ecf20Sopenharmony_ci identify->target_port_protocols = SAS_PROTOCOL_SMP; 1008c2ecf20Sopenharmony_ci } else { 1018c2ecf20Sopenharmony_ci identify->initiator_port_protocols = SAS_PROTOCOL_STP; 1028c2ecf20Sopenharmony_ci identify->target_port_protocols = SAS_PROTOCOL_STP; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return sas_rphy_add(rphy); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic struct sas_rphy *pqi_sas_rphy_alloc(struct pqi_sas_port *pqi_sas_port) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (pqi_sas_port->device && 1118c2ecf20Sopenharmony_ci pqi_sas_port->device->is_expander_smp_device) 1128c2ecf20Sopenharmony_ci return sas_expander_alloc(pqi_sas_port->port, 1138c2ecf20Sopenharmony_ci SAS_FANOUT_EXPANDER_DEVICE); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return sas_end_device_alloc(pqi_sas_port->port); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct pqi_sas_port *pqi_alloc_sas_port( 1198c2ecf20Sopenharmony_ci struct pqi_sas_node *pqi_sas_node, u64 sas_address, 1208c2ecf20Sopenharmony_ci struct pqi_scsi_dev *device) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int rc; 1238c2ecf20Sopenharmony_ci struct pqi_sas_port *pqi_sas_port; 1248c2ecf20Sopenharmony_ci struct sas_port *port; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL); 1278c2ecf20Sopenharmony_ci if (!pqi_sas_port) 1288c2ecf20Sopenharmony_ci return NULL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pqi_sas_port->phy_list_head); 1318c2ecf20Sopenharmony_ci pqi_sas_port->parent_node = pqi_sas_node; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci port = sas_port_alloc_num(pqi_sas_node->parent_dev); 1348c2ecf20Sopenharmony_ci if (!port) 1358c2ecf20Sopenharmony_ci goto free_pqi_port; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rc = sas_port_add(port); 1388c2ecf20Sopenharmony_ci if (rc) 1398c2ecf20Sopenharmony_ci goto free_sas_port; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci pqi_sas_port->port = port; 1428c2ecf20Sopenharmony_ci pqi_sas_port->sas_address = sas_address; 1438c2ecf20Sopenharmony_ci pqi_sas_port->device = device; 1448c2ecf20Sopenharmony_ci list_add_tail(&pqi_sas_port->port_list_entry, 1458c2ecf20Sopenharmony_ci &pqi_sas_node->port_list_head); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return pqi_sas_port; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cifree_sas_port: 1508c2ecf20Sopenharmony_ci sas_port_free(port); 1518c2ecf20Sopenharmony_cifree_pqi_port: 1528c2ecf20Sopenharmony_ci kfree(pqi_sas_port); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return NULL; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct pqi_sas_phy *pqi_sas_phy; 1608c2ecf20Sopenharmony_ci struct pqi_sas_phy *next; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci list_for_each_entry_safe(pqi_sas_phy, next, 1638c2ecf20Sopenharmony_ci &pqi_sas_port->phy_list_head, phy_list_entry) 1648c2ecf20Sopenharmony_ci pqi_free_sas_phy(pqi_sas_phy); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci sas_port_delete(pqi_sas_port->port); 1678c2ecf20Sopenharmony_ci list_del(&pqi_sas_port->port_list_entry); 1688c2ecf20Sopenharmony_ci kfree(pqi_sas_port); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct pqi_sas_node *pqi_sas_node; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (pqi_sas_node) { 1778c2ecf20Sopenharmony_ci pqi_sas_node->parent_dev = parent_dev; 1788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pqi_sas_node->port_list_head); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return pqi_sas_node; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct pqi_sas_port *pqi_sas_port; 1878c2ecf20Sopenharmony_ci struct pqi_sas_port *next; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!pqi_sas_node) 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci list_for_each_entry_safe(pqi_sas_port, next, 1938c2ecf20Sopenharmony_ci &pqi_sas_node->port_list_head, port_list_entry) 1948c2ecf20Sopenharmony_ci pqi_free_sas_port(pqi_sas_port); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci kfree(pqi_sas_node); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistruct pqi_scsi_dev *pqi_find_device_by_sas_rphy( 2008c2ecf20Sopenharmony_ci struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct pqi_scsi_dev *device; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci list_for_each_entry(device, &ctrl_info->scsi_device_list, 2058c2ecf20Sopenharmony_ci scsi_device_list_entry) { 2068c2ecf20Sopenharmony_ci if (!device->sas_port) 2078c2ecf20Sopenharmony_ci continue; 2088c2ecf20Sopenharmony_ci if (device->sas_port->rphy == rphy) 2098c2ecf20Sopenharmony_ci return device; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return NULL; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciint pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int rc; 2188c2ecf20Sopenharmony_ci struct device *parent_dev; 2198c2ecf20Sopenharmony_ci struct pqi_sas_node *pqi_sas_node; 2208c2ecf20Sopenharmony_ci struct pqi_sas_port *pqi_sas_port; 2218c2ecf20Sopenharmony_ci struct pqi_sas_phy *pqi_sas_phy; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci parent_dev = &shost->shost_dev; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci pqi_sas_node = pqi_alloc_sas_node(parent_dev); 2268c2ecf20Sopenharmony_ci if (!pqi_sas_node) 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, 2308c2ecf20Sopenharmony_ci ctrl_info->sas_address, NULL); 2318c2ecf20Sopenharmony_ci if (!pqi_sas_port) { 2328c2ecf20Sopenharmony_ci rc = -ENODEV; 2338c2ecf20Sopenharmony_ci goto free_sas_node; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port); 2378c2ecf20Sopenharmony_ci if (!pqi_sas_phy) { 2388c2ecf20Sopenharmony_ci rc = -ENODEV; 2398c2ecf20Sopenharmony_ci goto free_sas_port; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci rc = pqi_sas_port_add_phy(pqi_sas_phy); 2438c2ecf20Sopenharmony_ci if (rc) 2448c2ecf20Sopenharmony_ci goto free_sas_phy; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ctrl_info->sas_host = pqi_sas_node; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cifree_sas_phy: 2518c2ecf20Sopenharmony_ci pqi_free_sas_phy(pqi_sas_phy); 2528c2ecf20Sopenharmony_cifree_sas_port: 2538c2ecf20Sopenharmony_ci pqi_free_sas_port(pqi_sas_port); 2548c2ecf20Sopenharmony_cifree_sas_node: 2558c2ecf20Sopenharmony_ci pqi_free_sas_node(pqi_sas_node); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return rc; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_civoid pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci pqi_free_sas_node(ctrl_info->sas_host); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, 2668c2ecf20Sopenharmony_ci struct pqi_scsi_dev *device) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int rc; 2698c2ecf20Sopenharmony_ci struct pqi_sas_port *pqi_sas_port; 2708c2ecf20Sopenharmony_ci struct sas_rphy *rphy; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, 2738c2ecf20Sopenharmony_ci device->sas_address, device); 2748c2ecf20Sopenharmony_ci if (!pqi_sas_port) 2758c2ecf20Sopenharmony_ci return -ENOMEM; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci rphy = pqi_sas_rphy_alloc(pqi_sas_port); 2788c2ecf20Sopenharmony_ci if (!rphy) { 2798c2ecf20Sopenharmony_ci rc = -ENODEV; 2808c2ecf20Sopenharmony_ci goto free_sas_port; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci pqi_sas_port->rphy = rphy; 2848c2ecf20Sopenharmony_ci device->sas_port = pqi_sas_port; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy); 2878c2ecf20Sopenharmony_ci if (rc) 2888c2ecf20Sopenharmony_ci goto free_sas_port; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cifree_sas_port: 2938c2ecf20Sopenharmony_ci pqi_free_sas_port(pqi_sas_port); 2948c2ecf20Sopenharmony_ci device->sas_port = NULL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return rc; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid pqi_remove_sas_device(struct pqi_scsi_dev *device) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci if (device->sas_port) { 3028c2ecf20Sopenharmony_ci pqi_free_sas_port(device->sas_port); 3038c2ecf20Sopenharmony_ci device->sas_port = NULL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int pqi_sas_get_linkerrors(struct sas_phy *phy) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy, 3138c2ecf20Sopenharmony_ci u64 *identifier) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int rc; 3168c2ecf20Sopenharmony_ci unsigned long flags; 3178c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 3188c2ecf20Sopenharmony_ci struct pqi_ctrl_info *ctrl_info; 3198c2ecf20Sopenharmony_ci struct pqi_scsi_dev *found_device; 3208c2ecf20Sopenharmony_ci struct pqi_scsi_dev *device; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!rphy) 3238c2ecf20Sopenharmony_ci return -ENODEV; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci shost = rphy_to_shost(rphy); 3268c2ecf20Sopenharmony_ci ctrl_info = shost_to_hba(shost); 3278c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); 3288c2ecf20Sopenharmony_ci found_device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!found_device) { 3318c2ecf20Sopenharmony_ci rc = -ENODEV; 3328c2ecf20Sopenharmony_ci goto out; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (found_device->devtype == TYPE_ENCLOSURE) { 3368c2ecf20Sopenharmony_ci *identifier = get_unaligned_be64(&found_device->wwid); 3378c2ecf20Sopenharmony_ci rc = 0; 3388c2ecf20Sopenharmony_ci goto out; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (found_device->box_index == 0xff || 3428c2ecf20Sopenharmony_ci found_device->phys_box_on_bus == 0 || 3438c2ecf20Sopenharmony_ci found_device->bay == 0xff) { 3448c2ecf20Sopenharmony_ci rc = -EINVAL; 3458c2ecf20Sopenharmony_ci goto out; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci list_for_each_entry(device, &ctrl_info->scsi_device_list, 3498c2ecf20Sopenharmony_ci scsi_device_list_entry) { 3508c2ecf20Sopenharmony_ci if (device->devtype == TYPE_ENCLOSURE && 3518c2ecf20Sopenharmony_ci device->box_index == found_device->box_index && 3528c2ecf20Sopenharmony_ci device->phys_box_on_bus == 3538c2ecf20Sopenharmony_ci found_device->phys_box_on_bus && 3548c2ecf20Sopenharmony_ci memcmp(device->phys_connector, 3558c2ecf20Sopenharmony_ci found_device->phys_connector, 2) == 0) { 3568c2ecf20Sopenharmony_ci *identifier = 3578c2ecf20Sopenharmony_ci get_unaligned_be64(&device->wwid); 3588c2ecf20Sopenharmony_ci rc = 0; 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (found_device->phy_connected_dev_type != SA_DEVICE_TYPE_CONTROLLER) { 3648c2ecf20Sopenharmony_ci rc = -EINVAL; 3658c2ecf20Sopenharmony_ci goto out; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci list_for_each_entry(device, &ctrl_info->scsi_device_list, 3698c2ecf20Sopenharmony_ci scsi_device_list_entry) { 3708c2ecf20Sopenharmony_ci if (device->devtype == TYPE_ENCLOSURE && 3718c2ecf20Sopenharmony_ci CISS_GET_DRIVE_NUMBER(device->scsi3addr) == 3728c2ecf20Sopenharmony_ci PQI_VSEP_CISS_BTL) { 3738c2ecf20Sopenharmony_ci *identifier = get_unaligned_be64(&device->wwid); 3748c2ecf20Sopenharmony_ci rc = 0; 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci rc = -EINVAL; 3808c2ecf20Sopenharmony_ciout: 3818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return rc; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci int rc; 3898c2ecf20Sopenharmony_ci unsigned long flags; 3908c2ecf20Sopenharmony_ci struct pqi_ctrl_info *ctrl_info; 3918c2ecf20Sopenharmony_ci struct pqi_scsi_dev *device; 3928c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (!rphy) 3958c2ecf20Sopenharmony_ci return -ENODEV; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci shost = rphy_to_shost(rphy); 3988c2ecf20Sopenharmony_ci ctrl_info = shost_to_hba(shost); 3998c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); 4008c2ecf20Sopenharmony_ci device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!device) { 4038c2ecf20Sopenharmony_ci rc = -ENODEV; 4048c2ecf20Sopenharmony_ci goto out; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (device->bay == 0xff) 4088c2ecf20Sopenharmony_ci rc = -EINVAL; 4098c2ecf20Sopenharmony_ci else 4108c2ecf20Sopenharmony_ci rc = device->bay; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciout: 4138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return rc; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int pqi_sas_phy_enable(struct sas_phy *phy, int enable) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int pqi_sas_phy_setup(struct sas_phy *phy) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void pqi_sas_phy_release(struct sas_phy *phy) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int pqi_sas_phy_speed(struct sas_phy *phy, 4388c2ecf20Sopenharmony_ci struct sas_phy_linkrates *rates) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci#define CSMI_IOCTL_TIMEOUT 60 4448c2ecf20Sopenharmony_ci#define SMP_CRC_FIELD_LENGTH 4 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic struct bmic_csmi_smp_passthru_buffer * 4478c2ecf20Sopenharmony_cipqi_build_csmi_smp_passthru_buffer(struct sas_rphy *rphy, 4488c2ecf20Sopenharmony_ci struct bsg_job *job) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct bmic_csmi_smp_passthru_buffer *smp_buf; 4518c2ecf20Sopenharmony_ci struct bmic_csmi_ioctl_header *ioctl_header; 4528c2ecf20Sopenharmony_ci struct bmic_csmi_smp_passthru *parameters; 4538c2ecf20Sopenharmony_ci u32 req_size; 4548c2ecf20Sopenharmony_ci u32 resp_size; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci smp_buf = kzalloc(sizeof(*smp_buf), GFP_KERNEL); 4578c2ecf20Sopenharmony_ci if (!smp_buf) 4588c2ecf20Sopenharmony_ci return NULL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci req_size = job->request_payload.payload_len; 4618c2ecf20Sopenharmony_ci resp_size = job->reply_payload.payload_len; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ioctl_header = &smp_buf->ioctl_header; 4648c2ecf20Sopenharmony_ci put_unaligned_le32(sizeof(smp_buf->ioctl_header), 4658c2ecf20Sopenharmony_ci &ioctl_header->header_length); 4668c2ecf20Sopenharmony_ci put_unaligned_le32(CSMI_IOCTL_TIMEOUT, &ioctl_header->timeout); 4678c2ecf20Sopenharmony_ci put_unaligned_le32(CSMI_CC_SAS_SMP_PASSTHRU, 4688c2ecf20Sopenharmony_ci &ioctl_header->control_code); 4698c2ecf20Sopenharmony_ci put_unaligned_le32(sizeof(smp_buf->parameters), &ioctl_header->length); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci parameters = &smp_buf->parameters; 4728c2ecf20Sopenharmony_ci parameters->phy_identifier = rphy->identify.phy_identifier; 4738c2ecf20Sopenharmony_ci parameters->port_identifier = 0; 4748c2ecf20Sopenharmony_ci parameters->connection_rate = 0; 4758c2ecf20Sopenharmony_ci put_unaligned_be64(rphy->identify.sas_address, 4768c2ecf20Sopenharmony_ci ¶meters->destination_sas_address); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (req_size > SMP_CRC_FIELD_LENGTH) 4798c2ecf20Sopenharmony_ci req_size -= SMP_CRC_FIELD_LENGTH; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci put_unaligned_le32(req_size, ¶meters->request_length); 4828c2ecf20Sopenharmony_ci put_unaligned_le32(resp_size, ¶meters->response_length); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci sg_copy_to_buffer(job->request_payload.sg_list, 4858c2ecf20Sopenharmony_ci job->reply_payload.sg_cnt, ¶meters->request, 4868c2ecf20Sopenharmony_ci req_size); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return smp_buf; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic unsigned int pqi_build_sas_smp_handler_reply( 4928c2ecf20Sopenharmony_ci struct bmic_csmi_smp_passthru_buffer *smp_buf, struct bsg_job *job, 4938c2ecf20Sopenharmony_ci struct pqi_raid_error_info *error_info) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci sg_copy_from_buffer(job->reply_payload.sg_list, 4968c2ecf20Sopenharmony_ci job->reply_payload.sg_cnt, &smp_buf->parameters.response, 4978c2ecf20Sopenharmony_ci le32_to_cpu(smp_buf->parameters.response_length)); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci job->reply_len = le16_to_cpu(error_info->sense_data_length); 5008c2ecf20Sopenharmony_ci memcpy(job->reply, error_info->data, 5018c2ecf20Sopenharmony_ci le16_to_cpu(error_info->sense_data_length)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return job->reply_payload.payload_len - 5048c2ecf20Sopenharmony_ci get_unaligned_le32(&error_info->data_in_transferred); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_civoid pqi_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, 5088c2ecf20Sopenharmony_ci struct sas_rphy *rphy) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int rc; 5118c2ecf20Sopenharmony_ci struct pqi_ctrl_info *ctrl_info; 5128c2ecf20Sopenharmony_ci struct bmic_csmi_smp_passthru_buffer *smp_buf; 5138c2ecf20Sopenharmony_ci struct pqi_raid_error_info error_info; 5148c2ecf20Sopenharmony_ci unsigned int reslen = 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ctrl_info = shost_to_hba(shost); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (job->reply_payload.payload_len == 0) { 5198c2ecf20Sopenharmony_ci rc = -ENOMEM; 5208c2ecf20Sopenharmony_ci goto out; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (!rphy) { 5248c2ecf20Sopenharmony_ci rc = -EINVAL; 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (rphy->identify.device_type != SAS_FANOUT_EXPANDER_DEVICE) { 5298c2ecf20Sopenharmony_ci rc = -EINVAL; 5308c2ecf20Sopenharmony_ci goto out; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (job->request_payload.sg_cnt > 1 || job->reply_payload.sg_cnt > 1) { 5348c2ecf20Sopenharmony_ci rc = -EINVAL; 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci smp_buf = pqi_build_csmi_smp_passthru_buffer(rphy, job); 5398c2ecf20Sopenharmony_ci if (!smp_buf) { 5408c2ecf20Sopenharmony_ci rc = -ENOMEM; 5418c2ecf20Sopenharmony_ci goto out; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci rc = pqi_csmi_smp_passthru(ctrl_info, smp_buf, sizeof(*smp_buf), 5458c2ecf20Sopenharmony_ci &error_info); 5468c2ecf20Sopenharmony_ci if (rc) 5478c2ecf20Sopenharmony_ci goto out; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci reslen = pqi_build_sas_smp_handler_reply(smp_buf, job, &error_info); 5508c2ecf20Sopenharmony_ciout: 5518c2ecf20Sopenharmony_ci bsg_job_done(job, rc, reslen); 5528c2ecf20Sopenharmony_ci pqi_ctrl_unbusy(ctrl_info); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_cistruct sas_function_template pqi_sas_transport_functions = { 5558c2ecf20Sopenharmony_ci .get_linkerrors = pqi_sas_get_linkerrors, 5568c2ecf20Sopenharmony_ci .get_enclosure_identifier = pqi_sas_get_enclosure_identifier, 5578c2ecf20Sopenharmony_ci .get_bay_identifier = pqi_sas_get_bay_identifier, 5588c2ecf20Sopenharmony_ci .phy_reset = pqi_sas_phy_reset, 5598c2ecf20Sopenharmony_ci .phy_enable = pqi_sas_phy_enable, 5608c2ecf20Sopenharmony_ci .phy_setup = pqi_sas_phy_setup, 5618c2ecf20Sopenharmony_ci .phy_release = pqi_sas_phy_release, 5628c2ecf20Sopenharmony_ci .set_phy_speed = pqi_sas_phy_speed, 5638c2ecf20Sopenharmony_ci .smp_handler = pqi_sas_smp_handler, 5648c2ecf20Sopenharmony_ci}; 565