12cc37b15SDon Brace // SPDX-License-Identifier: GPL-2.0
26c223761SKevin Barnett /*
3889653ecSKevin Barnett  *    driver for Microchip PQI-based storage controllers
4*49fd52d4SDon Brace  *    Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries
52f4c4b92SDon Brace  *    Copyright (c) 2016-2018 Microsemi Corporation
66c223761SKevin Barnett  *    Copyright (c) 2016 PMC-Sierra, Inc.
76c223761SKevin Barnett  *
82f4c4b92SDon Brace  *    Questions/Comments/Bugfixes to storagedev@microchip.com
96c223761SKevin Barnett  *
106c223761SKevin Barnett  */
116c223761SKevin Barnett 
126c223761SKevin Barnett #include <linux/kernel.h>
133d46a59aSDon Brace #include <linux/bsg-lib.h>
146c223761SKevin Barnett #include <scsi/scsi_host.h>
156c223761SKevin Barnett #include <scsi/scsi_cmnd.h>
166c223761SKevin Barnett #include <scsi/scsi_transport_sas.h>
173d46a59aSDon Brace #include <asm/unaligned.h>
186c223761SKevin Barnett #include "smartpqi.h"
196c223761SKevin Barnett 
pqi_alloc_sas_phy(struct pqi_sas_port * pqi_sas_port)206c223761SKevin Barnett static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port)
216c223761SKevin Barnett {
226c223761SKevin Barnett 	struct pqi_sas_phy *pqi_sas_phy;
236c223761SKevin Barnett 	struct sas_phy *phy;
246c223761SKevin Barnett 
256c223761SKevin Barnett 	pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL);
266c223761SKevin Barnett 	if (!pqi_sas_phy)
276c223761SKevin Barnett 		return NULL;
286c223761SKevin Barnett 
296c223761SKevin Barnett 	phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev,
306c223761SKevin Barnett 		pqi_sas_port->next_phy_index);
316c223761SKevin Barnett 	if (!phy) {
326c223761SKevin Barnett 		kfree(pqi_sas_phy);
336c223761SKevin Barnett 		return NULL;
346c223761SKevin Barnett 	}
356c223761SKevin Barnett 
366c223761SKevin Barnett 	pqi_sas_port->next_phy_index++;
376c223761SKevin Barnett 	pqi_sas_phy->phy = phy;
386c223761SKevin Barnett 	pqi_sas_phy->parent_port = pqi_sas_port;
396c223761SKevin Barnett 
406c223761SKevin Barnett 	return pqi_sas_phy;
416c223761SKevin Barnett }
426c223761SKevin Barnett 
pqi_free_sas_phy(struct pqi_sas_phy * pqi_sas_phy)436c223761SKevin Barnett static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy)
446c223761SKevin Barnett {
456c223761SKevin Barnett 	struct sas_phy *phy = pqi_sas_phy->phy;
466c223761SKevin Barnett 
476c223761SKevin Barnett 	sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy);
486c223761SKevin Barnett 	if (pqi_sas_phy->added_to_port)
496c223761SKevin Barnett 		list_del(&pqi_sas_phy->phy_list_entry);
50b9692611SMurthy Bhat 	sas_phy_delete(phy);
516c223761SKevin Barnett 	kfree(pqi_sas_phy);
526c223761SKevin Barnett }
536c223761SKevin Barnett 
pqi_sas_port_add_phy(struct pqi_sas_phy * pqi_sas_phy)546c223761SKevin Barnett static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy)
556c223761SKevin Barnett {
566c223761SKevin Barnett 	int rc;
576c223761SKevin Barnett 	struct pqi_sas_port *pqi_sas_port;
586c223761SKevin Barnett 	struct sas_phy *phy;
596c223761SKevin Barnett 	struct sas_identify *identify;
606c223761SKevin Barnett 
616c223761SKevin Barnett 	pqi_sas_port = pqi_sas_phy->parent_port;
626c223761SKevin Barnett 	phy = pqi_sas_phy->phy;
636c223761SKevin Barnett 
646c223761SKevin Barnett 	identify = &phy->identify;
656c223761SKevin Barnett 	memset(identify, 0, sizeof(*identify));
666c223761SKevin Barnett 	identify->sas_address = pqi_sas_port->sas_address;
676c223761SKevin Barnett 	identify->device_type = SAS_END_DEVICE;
6855732a46SMurthy Bhat 	identify->initiator_port_protocols = SAS_PROTOCOL_ALL;
6955732a46SMurthy Bhat 	identify->target_port_protocols = SAS_PROTOCOL_ALL;
706c223761SKevin Barnett 	phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
716c223761SKevin Barnett 	phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
726c223761SKevin Barnett 	phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
736c223761SKevin Barnett 	phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
746c223761SKevin Barnett 	phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
756c223761SKevin Barnett 
766c223761SKevin Barnett 	rc = sas_phy_add(pqi_sas_phy->phy);
776c223761SKevin Barnett 	if (rc)
786c223761SKevin Barnett 		return rc;
796c223761SKevin Barnett 
806c223761SKevin Barnett 	sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy);
816c223761SKevin Barnett 	list_add_tail(&pqi_sas_phy->phy_list_entry,
826c223761SKevin Barnett 		&pqi_sas_port->phy_list_head);
836c223761SKevin Barnett 	pqi_sas_phy->added_to_port = true;
846c223761SKevin Barnett 
856c223761SKevin Barnett 	return 0;
866c223761SKevin Barnett }
876c223761SKevin Barnett 
pqi_sas_port_add_rphy(struct pqi_sas_port * pqi_sas_port,struct sas_rphy * rphy)886c223761SKevin Barnett static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port,
896c223761SKevin Barnett 	struct sas_rphy *rphy)
906c223761SKevin Barnett {
916c223761SKevin Barnett 	struct sas_identify *identify;
926c223761SKevin Barnett 
936c223761SKevin Barnett 	identify = &rphy->identify;
946c223761SKevin Barnett 	identify->sas_address = pqi_sas_port->sas_address;
95889cda36SKevin Barnett 	identify->phy_identifier = pqi_sas_port->device->phy_id;
963d46a59aSDon Brace 
9755732a46SMurthy Bhat 	identify->initiator_port_protocols = SAS_PROTOCOL_ALL;
986c223761SKevin Barnett 	identify->target_port_protocols = SAS_PROTOCOL_STP;
9955732a46SMurthy Bhat 
10055732a46SMurthy Bhat 	switch (pqi_sas_port->device->device_type) {
10155732a46SMurthy Bhat 	case SA_DEVICE_TYPE_SAS:
10255732a46SMurthy Bhat 	case SA_DEVICE_TYPE_SES:
10355732a46SMurthy Bhat 	case SA_DEVICE_TYPE_NVME:
10455732a46SMurthy Bhat 		identify->target_port_protocols = SAS_PROTOCOL_SSP;
10555732a46SMurthy Bhat 		break;
10655732a46SMurthy Bhat 	case SA_DEVICE_TYPE_EXPANDER_SMP:
10755732a46SMurthy Bhat 		identify->target_port_protocols = SAS_PROTOCOL_SMP;
10855732a46SMurthy Bhat 		break;
10955732a46SMurthy Bhat 	case SA_DEVICE_TYPE_SATA:
11055732a46SMurthy Bhat 	default:
11155732a46SMurthy Bhat 		break;
11255732a46SMurthy Bhat 	}
1136c223761SKevin Barnett 
1146c223761SKevin Barnett 	return sas_rphy_add(rphy);
1156c223761SKevin Barnett }
1166c223761SKevin Barnett 
pqi_sas_rphy_alloc(struct pqi_sas_port * pqi_sas_port)1173d46a59aSDon Brace static struct sas_rphy *pqi_sas_rphy_alloc(struct pqi_sas_port *pqi_sas_port)
1183d46a59aSDon Brace {
119583891c9SKevin Barnett 	if (pqi_sas_port->device && pqi_sas_port->device->is_expander_smp_device)
1203d46a59aSDon Brace 		return sas_expander_alloc(pqi_sas_port->port,
1213d46a59aSDon Brace 				SAS_FANOUT_EXPANDER_DEVICE);
1223d46a59aSDon Brace 
1233d46a59aSDon Brace 	return sas_end_device_alloc(pqi_sas_port->port);
1243d46a59aSDon Brace }
1253d46a59aSDon Brace 
pqi_alloc_sas_port(struct pqi_sas_node * pqi_sas_node,u64 sas_address,struct pqi_scsi_dev * device)1266c223761SKevin Barnett static struct pqi_sas_port *pqi_alloc_sas_port(
1273d46a59aSDon Brace 	struct pqi_sas_node *pqi_sas_node, u64 sas_address,
1283d46a59aSDon Brace 	struct pqi_scsi_dev *device)
1296c223761SKevin Barnett {
1306c223761SKevin Barnett 	int rc;
1316c223761SKevin Barnett 	struct pqi_sas_port *pqi_sas_port;
1326c223761SKevin Barnett 	struct sas_port *port;
1336c223761SKevin Barnett 
1346c223761SKevin Barnett 	pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL);
1356c223761SKevin Barnett 	if (!pqi_sas_port)
1366c223761SKevin Barnett 		return NULL;
1376c223761SKevin Barnett 
1386c223761SKevin Barnett 	INIT_LIST_HEAD(&pqi_sas_port->phy_list_head);
1396c223761SKevin Barnett 	pqi_sas_port->parent_node = pqi_sas_node;
1406c223761SKevin Barnett 
1416c223761SKevin Barnett 	port = sas_port_alloc_num(pqi_sas_node->parent_dev);
1426c223761SKevin Barnett 	if (!port)
1436c223761SKevin Barnett 		goto free_pqi_port;
1446c223761SKevin Barnett 
1456c223761SKevin Barnett 	rc = sas_port_add(port);
1466c223761SKevin Barnett 	if (rc)
1476c223761SKevin Barnett 		goto free_sas_port;
1486c223761SKevin Barnett 
1496c223761SKevin Barnett 	pqi_sas_port->port = port;
1506c223761SKevin Barnett 	pqi_sas_port->sas_address = sas_address;
1513d46a59aSDon Brace 	pqi_sas_port->device = device;
1526c223761SKevin Barnett 	list_add_tail(&pqi_sas_port->port_list_entry,
1536c223761SKevin Barnett 		&pqi_sas_node->port_list_head);
1546c223761SKevin Barnett 
1556c223761SKevin Barnett 	return pqi_sas_port;
1566c223761SKevin Barnett 
1576c223761SKevin Barnett free_sas_port:
1586c223761SKevin Barnett 	sas_port_free(port);
1596c223761SKevin Barnett free_pqi_port:
1606c223761SKevin Barnett 	kfree(pqi_sas_port);
1616c223761SKevin Barnett 
1626c223761SKevin Barnett 	return NULL;
1636c223761SKevin Barnett }
1646c223761SKevin Barnett 
pqi_free_sas_port(struct pqi_sas_port * pqi_sas_port)1656c223761SKevin Barnett static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port)
1666c223761SKevin Barnett {
1676c223761SKevin Barnett 	struct pqi_sas_phy *pqi_sas_phy;
1686c223761SKevin Barnett 	struct pqi_sas_phy *next;
1696c223761SKevin Barnett 
1706c223761SKevin Barnett 	list_for_each_entry_safe(pqi_sas_phy, next,
1716c223761SKevin Barnett 		&pqi_sas_port->phy_list_head, phy_list_entry)
1726c223761SKevin Barnett 			pqi_free_sas_phy(pqi_sas_phy);
1736c223761SKevin Barnett 
1746c223761SKevin Barnett 	sas_port_delete(pqi_sas_port->port);
1756c223761SKevin Barnett 	list_del(&pqi_sas_port->port_list_entry);
1766c223761SKevin Barnett 	kfree(pqi_sas_port);
1776c223761SKevin Barnett }
1786c223761SKevin Barnett 
pqi_alloc_sas_node(struct device * parent_dev)1796c223761SKevin Barnett static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev)
1806c223761SKevin Barnett {
1816c223761SKevin Barnett 	struct pqi_sas_node *pqi_sas_node;
1826c223761SKevin Barnett 
1836c223761SKevin Barnett 	pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL);
1846c223761SKevin Barnett 	if (pqi_sas_node) {
1856c223761SKevin Barnett 		pqi_sas_node->parent_dev = parent_dev;
1866c223761SKevin Barnett 		INIT_LIST_HEAD(&pqi_sas_node->port_list_head);
1876c223761SKevin Barnett 	}
1886c223761SKevin Barnett 
1896c223761SKevin Barnett 	return pqi_sas_node;
1906c223761SKevin Barnett }
1916c223761SKevin Barnett 
pqi_free_sas_node(struct pqi_sas_node * pqi_sas_node)1926c223761SKevin Barnett static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node)
1936c223761SKevin Barnett {
1946c223761SKevin Barnett 	struct pqi_sas_port *pqi_sas_port;
1956c223761SKevin Barnett 	struct pqi_sas_port *next;
1966c223761SKevin Barnett 
1976c223761SKevin Barnett 	if (!pqi_sas_node)
1986c223761SKevin Barnett 		return;
1996c223761SKevin Barnett 
2006c223761SKevin Barnett 	list_for_each_entry_safe(pqi_sas_port, next,
2016c223761SKevin Barnett 		&pqi_sas_node->port_list_head, port_list_entry)
2026c223761SKevin Barnett 			pqi_free_sas_port(pqi_sas_port);
2036c223761SKevin Barnett 
2046c223761SKevin Barnett 	kfree(pqi_sas_node);
2056c223761SKevin Barnett }
2066c223761SKevin Barnett 
pqi_find_device_by_sas_rphy(struct pqi_ctrl_info * ctrl_info,struct sas_rphy * rphy)2076c223761SKevin Barnett struct pqi_scsi_dev *pqi_find_device_by_sas_rphy(
2086c223761SKevin Barnett 	struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy)
2096c223761SKevin Barnett {
2106c223761SKevin Barnett 	struct pqi_scsi_dev *device;
2116c223761SKevin Barnett 
2126c223761SKevin Barnett 	list_for_each_entry(device, &ctrl_info->scsi_device_list,
2136c223761SKevin Barnett 		scsi_device_list_entry) {
2146c223761SKevin Barnett 		if (!device->sas_port)
2156c223761SKevin Barnett 			continue;
2166c223761SKevin Barnett 		if (device->sas_port->rphy == rphy)
2176c223761SKevin Barnett 			return device;
2186c223761SKevin Barnett 	}
2196c223761SKevin Barnett 
2206c223761SKevin Barnett 	return NULL;
2216c223761SKevin Barnett }
2226c223761SKevin Barnett 
pqi_add_sas_host(struct Scsi_Host * shost,struct pqi_ctrl_info * ctrl_info)2236c223761SKevin Barnett int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info)
2246c223761SKevin Barnett {
2256c223761SKevin Barnett 	int rc;
2266c223761SKevin Barnett 	struct device *parent_dev;
2276c223761SKevin Barnett 	struct pqi_sas_node *pqi_sas_node;
2286c223761SKevin Barnett 	struct pqi_sas_port *pqi_sas_port;
2296c223761SKevin Barnett 	struct pqi_sas_phy *pqi_sas_phy;
2306c223761SKevin Barnett 
2313d46a59aSDon Brace 	parent_dev = &shost->shost_dev;
2326c223761SKevin Barnett 
2336c223761SKevin Barnett 	pqi_sas_node = pqi_alloc_sas_node(parent_dev);
2346c223761SKevin Barnett 	if (!pqi_sas_node)
2356c223761SKevin Barnett 		return -ENOMEM;
2366c223761SKevin Barnett 
2373d46a59aSDon Brace 	pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node,
2383d46a59aSDon Brace 		ctrl_info->sas_address, NULL);
2396c223761SKevin Barnett 	if (!pqi_sas_port) {
2406c223761SKevin Barnett 		rc = -ENODEV;
2416c223761SKevin Barnett 		goto free_sas_node;
2426c223761SKevin Barnett 	}
2436c223761SKevin Barnett 
2446c223761SKevin Barnett 	pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port);
2456c223761SKevin Barnett 	if (!pqi_sas_phy) {
2466c223761SKevin Barnett 		rc = -ENODEV;
2476c223761SKevin Barnett 		goto free_sas_port;
2486c223761SKevin Barnett 	}
2496c223761SKevin Barnett 
2506c223761SKevin Barnett 	rc = pqi_sas_port_add_phy(pqi_sas_phy);
2516c223761SKevin Barnett 	if (rc)
2526c223761SKevin Barnett 		goto free_sas_phy;
2536c223761SKevin Barnett 
2546c223761SKevin Barnett 	ctrl_info->sas_host = pqi_sas_node;
2556c223761SKevin Barnett 
2566c223761SKevin Barnett 	return 0;
2576c223761SKevin Barnett 
2586c223761SKevin Barnett free_sas_phy:
2596c223761SKevin Barnett 	pqi_free_sas_phy(pqi_sas_phy);
2606c223761SKevin Barnett free_sas_port:
2616c223761SKevin Barnett 	pqi_free_sas_port(pqi_sas_port);
2626c223761SKevin Barnett free_sas_node:
2636c223761SKevin Barnett 	pqi_free_sas_node(pqi_sas_node);
2646c223761SKevin Barnett 
2656c223761SKevin Barnett 	return rc;
2666c223761SKevin Barnett }
2676c223761SKevin Barnett 
pqi_delete_sas_host(struct pqi_ctrl_info * ctrl_info)2686c223761SKevin Barnett void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info)
2696c223761SKevin Barnett {
2706c223761SKevin Barnett 	pqi_free_sas_node(ctrl_info->sas_host);
2716c223761SKevin Barnett }
2726c223761SKevin Barnett 
pqi_add_sas_device(struct pqi_sas_node * pqi_sas_node,struct pqi_scsi_dev * device)2736c223761SKevin Barnett int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node,
2746c223761SKevin Barnett 	struct pqi_scsi_dev *device)
2756c223761SKevin Barnett {
2766c223761SKevin Barnett 	int rc;
2776c223761SKevin Barnett 	struct pqi_sas_port *pqi_sas_port;
2786c223761SKevin Barnett 	struct sas_rphy *rphy;
2796c223761SKevin Barnett 
2803d46a59aSDon Brace 	pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node,
2813d46a59aSDon Brace 		device->sas_address, device);
2826c223761SKevin Barnett 	if (!pqi_sas_port)
2836c223761SKevin Barnett 		return -ENOMEM;
2846c223761SKevin Barnett 
2853d46a59aSDon Brace 	rphy = pqi_sas_rphy_alloc(pqi_sas_port);
2866c223761SKevin Barnett 	if (!rphy) {
2876c223761SKevin Barnett 		rc = -ENODEV;
2886c223761SKevin Barnett 		goto free_sas_port;
2896c223761SKevin Barnett 	}
2906c223761SKevin Barnett 
2916c223761SKevin Barnett 	pqi_sas_port->rphy = rphy;
2926c223761SKevin Barnett 	device->sas_port = pqi_sas_port;
2936c223761SKevin Barnett 
2946c223761SKevin Barnett 	rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy);
2956c223761SKevin Barnett 	if (rc)
2962312e844SDon Brace 		goto free_sas_rphy;
2976c223761SKevin Barnett 
2986c223761SKevin Barnett 	return 0;
2996c223761SKevin Barnett 
3002312e844SDon Brace free_sas_rphy:
3012312e844SDon Brace 	sas_rphy_free(rphy);
3026c223761SKevin Barnett free_sas_port:
3036c223761SKevin Barnett 	pqi_free_sas_port(pqi_sas_port);
3046c223761SKevin Barnett 	device->sas_port = NULL;
3056c223761SKevin Barnett 
3066c223761SKevin Barnett 	return rc;
3076c223761SKevin Barnett }
3086c223761SKevin Barnett 
pqi_remove_sas_device(struct pqi_scsi_dev * device)3096c223761SKevin Barnett void pqi_remove_sas_device(struct pqi_scsi_dev *device)
3106c223761SKevin Barnett {
3116c223761SKevin Barnett 	if (device->sas_port) {
3126c223761SKevin Barnett 		pqi_free_sas_port(device->sas_port);
3136c223761SKevin Barnett 		device->sas_port = NULL;
3146c223761SKevin Barnett 	}
3156c223761SKevin Barnett }
3166c223761SKevin Barnett 
pqi_sas_get_linkerrors(struct sas_phy * phy)3176c223761SKevin Barnett static int pqi_sas_get_linkerrors(struct sas_phy *phy)
3186c223761SKevin Barnett {
3196c223761SKevin Barnett 	return 0;
3206c223761SKevin Barnett }
3216c223761SKevin Barnett 
pqi_sas_get_enclosure_identifier(struct sas_rphy * rphy,u64 * identifier)3226c223761SKevin Barnett static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy,
3236c223761SKevin Barnett 	u64 *identifier)
3246c223761SKevin Barnett {
3252d2ad4bcSGilbert Wu 	int rc;
3262d2ad4bcSGilbert Wu 	unsigned long flags;
3272d2ad4bcSGilbert Wu 	struct Scsi_Host *shost;
3282d2ad4bcSGilbert Wu 	struct pqi_ctrl_info *ctrl_info;
3292d2ad4bcSGilbert Wu 	struct pqi_scsi_dev *found_device;
3302d2ad4bcSGilbert Wu 	struct pqi_scsi_dev *device;
3312d2ad4bcSGilbert Wu 
3322d2ad4bcSGilbert Wu 	if (!rphy)
3332d2ad4bcSGilbert Wu 		return -ENODEV;
3342d2ad4bcSGilbert Wu 
3352d2ad4bcSGilbert Wu 	shost = rphy_to_shost(rphy);
3362d2ad4bcSGilbert Wu 	ctrl_info = shost_to_hba(shost);
3372d2ad4bcSGilbert Wu 	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
3382d2ad4bcSGilbert Wu 	found_device = pqi_find_device_by_sas_rphy(ctrl_info, rphy);
3392d2ad4bcSGilbert Wu 
3402d2ad4bcSGilbert Wu 	if (!found_device) {
3412d2ad4bcSGilbert Wu 		rc = -ENODEV;
3422d2ad4bcSGilbert Wu 		goto out;
3432d2ad4bcSGilbert Wu 	}
3442d2ad4bcSGilbert Wu 
3452d2ad4bcSGilbert Wu 	if (found_device->devtype == TYPE_ENCLOSURE) {
34628ca6d87SMike McGowen 		*identifier = get_unaligned_be64(&found_device->wwid[8]);
3472d2ad4bcSGilbert Wu 		rc = 0;
3482d2ad4bcSGilbert Wu 		goto out;
3492d2ad4bcSGilbert Wu 	}
3502d2ad4bcSGilbert Wu 
3512d2ad4bcSGilbert Wu 	if (found_device->box_index == 0xff ||
3522d2ad4bcSGilbert Wu 		found_device->phys_box_on_bus == 0 ||
3532d2ad4bcSGilbert Wu 		found_device->bay == 0xff) {
3542d2ad4bcSGilbert Wu 		rc = -EINVAL;
3552d2ad4bcSGilbert Wu 		goto out;
3562d2ad4bcSGilbert Wu 	}
3572d2ad4bcSGilbert Wu 
3582d2ad4bcSGilbert Wu 	list_for_each_entry(device, &ctrl_info->scsi_device_list,
3592d2ad4bcSGilbert Wu 		scsi_device_list_entry) {
3602d2ad4bcSGilbert Wu 		if (device->devtype == TYPE_ENCLOSURE &&
3612d2ad4bcSGilbert Wu 			device->box_index == found_device->box_index &&
3622d2ad4bcSGilbert Wu 			device->phys_box_on_bus ==
3632d2ad4bcSGilbert Wu 				found_device->phys_box_on_bus &&
3642d2ad4bcSGilbert Wu 			memcmp(device->phys_connector,
3652d2ad4bcSGilbert Wu 				found_device->phys_connector, 2) == 0) {
3662d2ad4bcSGilbert Wu 			*identifier =
36728ca6d87SMike McGowen 				get_unaligned_be64(&device->wwid[8]);
3682d2ad4bcSGilbert Wu 			rc = 0;
3692d2ad4bcSGilbert Wu 			goto out;
3702d2ad4bcSGilbert Wu 		}
3712d2ad4bcSGilbert Wu 	}
3722d2ad4bcSGilbert Wu 
373694c5d5bSKevin Barnett 	if (found_device->phy_connected_dev_type != SA_DEVICE_TYPE_CONTROLLER) {
3742d2ad4bcSGilbert Wu 		rc = -EINVAL;
3752d2ad4bcSGilbert Wu 		goto out;
3762d2ad4bcSGilbert Wu 	}
3772d2ad4bcSGilbert Wu 
3782d2ad4bcSGilbert Wu 	list_for_each_entry(device, &ctrl_info->scsi_device_list,
3792d2ad4bcSGilbert Wu 		scsi_device_list_entry) {
3802d2ad4bcSGilbert Wu 		if (device->devtype == TYPE_ENCLOSURE &&
3812d2ad4bcSGilbert Wu 			CISS_GET_DRIVE_NUMBER(device->scsi3addr) ==
3822d2ad4bcSGilbert Wu 				PQI_VSEP_CISS_BTL) {
38328ca6d87SMike McGowen 			*identifier = get_unaligned_be64(&device->wwid[8]);
3842d2ad4bcSGilbert Wu 			rc = 0;
3852d2ad4bcSGilbert Wu 			goto out;
3862d2ad4bcSGilbert Wu 		}
3872d2ad4bcSGilbert Wu 	}
3882d2ad4bcSGilbert Wu 
3892d2ad4bcSGilbert Wu 	rc = -EINVAL;
3902d2ad4bcSGilbert Wu out:
3912d2ad4bcSGilbert Wu 	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
3922d2ad4bcSGilbert Wu 
3932d2ad4bcSGilbert Wu 	return rc;
3946c223761SKevin Barnett }
3956c223761SKevin Barnett 
pqi_sas_get_bay_identifier(struct sas_rphy * rphy)3966c223761SKevin Barnett static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy)
3976c223761SKevin Barnett {
3982d2ad4bcSGilbert Wu 	int rc;
3992d2ad4bcSGilbert Wu 	unsigned long flags;
4002d2ad4bcSGilbert Wu 	struct pqi_ctrl_info *ctrl_info;
4012d2ad4bcSGilbert Wu 	struct pqi_scsi_dev *device;
4022d2ad4bcSGilbert Wu 	struct Scsi_Host *shost;
4032d2ad4bcSGilbert Wu 
4042d2ad4bcSGilbert Wu 	if (!rphy)
4052d2ad4bcSGilbert Wu 		return -ENODEV;
4062d2ad4bcSGilbert Wu 
4072d2ad4bcSGilbert Wu 	shost = rphy_to_shost(rphy);
4082d2ad4bcSGilbert Wu 	ctrl_info = shost_to_hba(shost);
4092d2ad4bcSGilbert Wu 	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);
4102d2ad4bcSGilbert Wu 	device = pqi_find_device_by_sas_rphy(ctrl_info, rphy);
4112d2ad4bcSGilbert Wu 
4122d2ad4bcSGilbert Wu 	if (!device) {
4132d2ad4bcSGilbert Wu 		rc = -ENODEV;
4142d2ad4bcSGilbert Wu 		goto out;
4152d2ad4bcSGilbert Wu 	}
4162d2ad4bcSGilbert Wu 
4172d2ad4bcSGilbert Wu 	if (device->bay == 0xff)
4182d2ad4bcSGilbert Wu 		rc = -EINVAL;
4192d2ad4bcSGilbert Wu 	else
4202d2ad4bcSGilbert Wu 		rc = device->bay;
4212d2ad4bcSGilbert Wu 
4222d2ad4bcSGilbert Wu out:
4232d2ad4bcSGilbert Wu 	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);
4242d2ad4bcSGilbert Wu 
4252d2ad4bcSGilbert Wu 	return rc;
4266c223761SKevin Barnett }
4276c223761SKevin Barnett 
pqi_sas_phy_reset(struct sas_phy * phy,int hard_reset)4286c223761SKevin Barnett static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset)
4296c223761SKevin Barnett {
4306c223761SKevin Barnett 	return 0;
4316c223761SKevin Barnett }
4326c223761SKevin Barnett 
pqi_sas_phy_enable(struct sas_phy * phy,int enable)4336c223761SKevin Barnett static int pqi_sas_phy_enable(struct sas_phy *phy, int enable)
4346c223761SKevin Barnett {
4356c223761SKevin Barnett 	return 0;
4366c223761SKevin Barnett }
4376c223761SKevin Barnett 
pqi_sas_phy_setup(struct sas_phy * phy)4386c223761SKevin Barnett static int pqi_sas_phy_setup(struct sas_phy *phy)
4396c223761SKevin Barnett {
4406c223761SKevin Barnett 	return 0;
4416c223761SKevin Barnett }
4426c223761SKevin Barnett 
pqi_sas_phy_release(struct sas_phy * phy)4436c223761SKevin Barnett static void pqi_sas_phy_release(struct sas_phy *phy)
4446c223761SKevin Barnett {
4456c223761SKevin Barnett }
4466c223761SKevin Barnett 
pqi_sas_phy_speed(struct sas_phy * phy,struct sas_phy_linkrates * rates)4476c223761SKevin Barnett static int pqi_sas_phy_speed(struct sas_phy *phy,
4486c223761SKevin Barnett 	struct sas_phy_linkrates *rates)
4496c223761SKevin Barnett {
4506c223761SKevin Barnett 	return -EINVAL;
4516c223761SKevin Barnett }
4526c223761SKevin Barnett 
4533d46a59aSDon Brace #define CSMI_IOCTL_TIMEOUT	60
4543d46a59aSDon Brace #define SMP_CRC_FIELD_LENGTH	4
4553d46a59aSDon Brace 
4563d46a59aSDon Brace static struct bmic_csmi_smp_passthru_buffer *
pqi_build_csmi_smp_passthru_buffer(struct sas_rphy * rphy,struct bsg_job * job)4573d46a59aSDon Brace pqi_build_csmi_smp_passthru_buffer(struct sas_rphy *rphy,
4583d46a59aSDon Brace 	struct bsg_job *job)
4593d46a59aSDon Brace {
4603d46a59aSDon Brace 	struct bmic_csmi_smp_passthru_buffer *smp_buf;
4613d46a59aSDon Brace 	struct bmic_csmi_ioctl_header *ioctl_header;
4623d46a59aSDon Brace 	struct bmic_csmi_smp_passthru *parameters;
4633d46a59aSDon Brace 	u32 req_size;
4643d46a59aSDon Brace 	u32 resp_size;
4653d46a59aSDon Brace 
4663d46a59aSDon Brace 	smp_buf = kzalloc(sizeof(*smp_buf), GFP_KERNEL);
4673d46a59aSDon Brace 	if (!smp_buf)
4683d46a59aSDon Brace 		return NULL;
4693d46a59aSDon Brace 
4703d46a59aSDon Brace 	req_size = job->request_payload.payload_len;
4713d46a59aSDon Brace 	resp_size = job->reply_payload.payload_len;
4723d46a59aSDon Brace 
4733d46a59aSDon Brace 	ioctl_header = &smp_buf->ioctl_header;
4743d46a59aSDon Brace 	put_unaligned_le32(sizeof(smp_buf->ioctl_header),
4753d46a59aSDon Brace 		&ioctl_header->header_length);
4763d46a59aSDon Brace 	put_unaligned_le32(CSMI_IOCTL_TIMEOUT, &ioctl_header->timeout);
4773d46a59aSDon Brace 	put_unaligned_le32(CSMI_CC_SAS_SMP_PASSTHRU,
4783d46a59aSDon Brace 		&ioctl_header->control_code);
4793d46a59aSDon Brace 	put_unaligned_le32(sizeof(smp_buf->parameters), &ioctl_header->length);
4803d46a59aSDon Brace 
4813d46a59aSDon Brace 	parameters = &smp_buf->parameters;
4823d46a59aSDon Brace 	parameters->phy_identifier = rphy->identify.phy_identifier;
4833d46a59aSDon Brace 	parameters->port_identifier = 0;
4843d46a59aSDon Brace 	parameters->connection_rate = 0;
4853d46a59aSDon Brace 	put_unaligned_be64(rphy->identify.sas_address,
4863d46a59aSDon Brace 		&parameters->destination_sas_address);
4873d46a59aSDon Brace 
4883d46a59aSDon Brace 	if (req_size > SMP_CRC_FIELD_LENGTH)
4893d46a59aSDon Brace 		req_size -= SMP_CRC_FIELD_LENGTH;
4903d46a59aSDon Brace 
4913d46a59aSDon Brace 	put_unaligned_le32(req_size, &parameters->request_length);
4923d46a59aSDon Brace 	put_unaligned_le32(resp_size, &parameters->response_length);
4933d46a59aSDon Brace 
4943d46a59aSDon Brace 	sg_copy_to_buffer(job->request_payload.sg_list,
4953d46a59aSDon Brace 		job->reply_payload.sg_cnt, &parameters->request,
4963d46a59aSDon Brace 		req_size);
4973d46a59aSDon Brace 
4983d46a59aSDon Brace 	return smp_buf;
4993d46a59aSDon Brace }
5003d46a59aSDon Brace 
pqi_build_sas_smp_handler_reply(struct bmic_csmi_smp_passthru_buffer * smp_buf,struct bsg_job * job,struct pqi_raid_error_info * error_info)5013d46a59aSDon Brace static unsigned int pqi_build_sas_smp_handler_reply(
5023d46a59aSDon Brace 	struct bmic_csmi_smp_passthru_buffer *smp_buf, struct bsg_job *job,
5033d46a59aSDon Brace 	struct pqi_raid_error_info *error_info)
5043d46a59aSDon Brace {
5053d46a59aSDon Brace 	sg_copy_from_buffer(job->reply_payload.sg_list,
5063d46a59aSDon Brace 		job->reply_payload.sg_cnt, &smp_buf->parameters.response,
5073d46a59aSDon Brace 		le32_to_cpu(smp_buf->parameters.response_length));
5083d46a59aSDon Brace 
5093d46a59aSDon Brace 	job->reply_len = le16_to_cpu(error_info->sense_data_length);
5103d46a59aSDon Brace 	memcpy(job->reply, error_info->data,
5113d46a59aSDon Brace 		le16_to_cpu(error_info->sense_data_length));
5123d46a59aSDon Brace 
5133d46a59aSDon Brace 	return job->reply_payload.payload_len -
5143d46a59aSDon Brace 		get_unaligned_le32(&error_info->data_in_transferred);
5153d46a59aSDon Brace }
5163d46a59aSDon Brace 
pqi_sas_smp_handler(struct bsg_job * job,struct Scsi_Host * shost,struct sas_rphy * rphy)5173d46a59aSDon Brace void pqi_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,
5183d46a59aSDon Brace 	struct sas_rphy *rphy)
5193d46a59aSDon Brace {
5203d46a59aSDon Brace 	int rc;
521694c5d5bSKevin Barnett 	struct pqi_ctrl_info *ctrl_info;
5223d46a59aSDon Brace 	struct bmic_csmi_smp_passthru_buffer *smp_buf;
5233d46a59aSDon Brace 	struct pqi_raid_error_info error_info;
5243d46a59aSDon Brace 	unsigned int reslen = 0;
5253d46a59aSDon Brace 
526694c5d5bSKevin Barnett 	ctrl_info = shost_to_hba(shost);
5273d46a59aSDon Brace 
5283d46a59aSDon Brace 	if (job->reply_payload.payload_len == 0) {
5293d46a59aSDon Brace 		rc = -ENOMEM;
5303d46a59aSDon Brace 		goto out;
5313d46a59aSDon Brace 	}
5323d46a59aSDon Brace 
5333d46a59aSDon Brace 	if (!rphy) {
5343d46a59aSDon Brace 		rc = -EINVAL;
5353d46a59aSDon Brace 		goto out;
5363d46a59aSDon Brace 	}
5373d46a59aSDon Brace 
5383d46a59aSDon Brace 	if (rphy->identify.device_type != SAS_FANOUT_EXPANDER_DEVICE) {
5393d46a59aSDon Brace 		rc = -EINVAL;
5403d46a59aSDon Brace 		goto out;
5413d46a59aSDon Brace 	}
5423d46a59aSDon Brace 
5433d46a59aSDon Brace 	if (job->request_payload.sg_cnt > 1 || job->reply_payload.sg_cnt > 1) {
5443d46a59aSDon Brace 		rc = -EINVAL;
5453d46a59aSDon Brace 		goto out;
5463d46a59aSDon Brace 	}
5473d46a59aSDon Brace 
5483d46a59aSDon Brace 	smp_buf = pqi_build_csmi_smp_passthru_buffer(rphy, job);
5493d46a59aSDon Brace 	if (!smp_buf) {
5503d46a59aSDon Brace 		rc = -ENOMEM;
5513d46a59aSDon Brace 		goto out;
5523d46a59aSDon Brace 	}
5533d46a59aSDon Brace 
5543d46a59aSDon Brace 	rc = pqi_csmi_smp_passthru(ctrl_info, smp_buf, sizeof(*smp_buf),
5553d46a59aSDon Brace 		&error_info);
5563d46a59aSDon Brace 	if (rc)
5573d46a59aSDon Brace 		goto out;
5583d46a59aSDon Brace 
5593d46a59aSDon Brace 	reslen = pqi_build_sas_smp_handler_reply(smp_buf, job, &error_info);
560583891c9SKevin Barnett 
5613d46a59aSDon Brace out:
5623d46a59aSDon Brace 	bsg_job_done(job, rc, reslen);
5633d46a59aSDon Brace }
5646c223761SKevin Barnett struct sas_function_template pqi_sas_transport_functions = {
5656c223761SKevin Barnett 	.get_linkerrors = pqi_sas_get_linkerrors,
5666c223761SKevin Barnett 	.get_enclosure_identifier = pqi_sas_get_enclosure_identifier,
5676c223761SKevin Barnett 	.get_bay_identifier = pqi_sas_get_bay_identifier,
5686c223761SKevin Barnett 	.phy_reset = pqi_sas_phy_reset,
5696c223761SKevin Barnett 	.phy_enable = pqi_sas_phy_enable,
5706c223761SKevin Barnett 	.phy_setup = pqi_sas_phy_setup,
5716c223761SKevin Barnett 	.phy_release = pqi_sas_phy_release,
5726c223761SKevin Barnett 	.set_phy_speed = pqi_sas_phy_speed,
5733d46a59aSDon Brace 	.smp_handler = pqi_sas_smp_handler,
5746c223761SKevin Barnett };
575