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		&parameters->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, &parameters->request_length);
4828c2ecf20Sopenharmony_ci	put_unaligned_le32(resp_size, &parameters->response_length);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	sg_copy_to_buffer(job->request_payload.sg_list,
4858c2ecf20Sopenharmony_ci		job->reply_payload.sg_cnt, &parameters->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