brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · 93e9670 Raw
575 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *    driver for Microchip PQI-based storage controllers4 *    Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries5 *    Copyright (c) 2016-2018 Microsemi Corporation6 *    Copyright (c) 2016 PMC-Sierra, Inc.7 *8 *    Questions/Comments/Bugfixes to storagedev@microchip.com9 *10 */11 12#include <linux/kernel.h>13#include <linux/bsg-lib.h>14#include <scsi/scsi_host.h>15#include <scsi/scsi_cmnd.h>16#include <scsi/scsi_transport_sas.h>17#include <linux/unaligned.h>18#include "smartpqi.h"19 20static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port)21{22	struct pqi_sas_phy *pqi_sas_phy;23	struct sas_phy *phy;24 25	pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL);26	if (!pqi_sas_phy)27		return NULL;28 29	phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev,30		pqi_sas_port->next_phy_index);31	if (!phy) {32		kfree(pqi_sas_phy);33		return NULL;34	}35 36	pqi_sas_port->next_phy_index++;37	pqi_sas_phy->phy = phy;38	pqi_sas_phy->parent_port = pqi_sas_port;39 40	return pqi_sas_phy;41}42 43static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy)44{45	struct sas_phy *phy = pqi_sas_phy->phy;46 47	sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy);48	if (pqi_sas_phy->added_to_port)49		list_del(&pqi_sas_phy->phy_list_entry);50	sas_phy_delete(phy);51	kfree(pqi_sas_phy);52}53 54static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy)55{56	int rc;57	struct pqi_sas_port *pqi_sas_port;58	struct sas_phy *phy;59	struct sas_identify *identify;60 61	pqi_sas_port = pqi_sas_phy->parent_port;62	phy = pqi_sas_phy->phy;63 64	identify = &phy->identify;65	memset(identify, 0, sizeof(*identify));66	identify->sas_address = pqi_sas_port->sas_address;67	identify->device_type = SAS_END_DEVICE;68	identify->initiator_port_protocols = SAS_PROTOCOL_ALL;69	identify->target_port_protocols = SAS_PROTOCOL_ALL;70	phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;71	phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;72	phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;73	phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;74	phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;75 76	rc = sas_phy_add(pqi_sas_phy->phy);77	if (rc)78		return rc;79 80	sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy);81	list_add_tail(&pqi_sas_phy->phy_list_entry,82		&pqi_sas_port->phy_list_head);83	pqi_sas_phy->added_to_port = true;84 85	return 0;86}87 88static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port,89	struct sas_rphy *rphy)90{91	struct sas_identify *identify;92 93	identify = &rphy->identify;94	identify->sas_address = pqi_sas_port->sas_address;95	identify->phy_identifier = pqi_sas_port->device->phy_id;96 97	identify->initiator_port_protocols = SAS_PROTOCOL_ALL;98	identify->target_port_protocols = SAS_PROTOCOL_STP;99 100	switch (pqi_sas_port->device->device_type) {101	case SA_DEVICE_TYPE_SAS:102	case SA_DEVICE_TYPE_SES:103	case SA_DEVICE_TYPE_NVME:104		identify->target_port_protocols = SAS_PROTOCOL_SSP;105		break;106	case SA_DEVICE_TYPE_EXPANDER_SMP:107		identify->target_port_protocols = SAS_PROTOCOL_SMP;108		break;109	case SA_DEVICE_TYPE_SATA:110	default:111		break;112	}113 114	return sas_rphy_add(rphy);115}116 117static struct sas_rphy *pqi_sas_rphy_alloc(struct pqi_sas_port *pqi_sas_port)118{119	if (pqi_sas_port->device && pqi_sas_port->device->is_expander_smp_device)120		return sas_expander_alloc(pqi_sas_port->port,121				SAS_FANOUT_EXPANDER_DEVICE);122 123	return sas_end_device_alloc(pqi_sas_port->port);124}125 126static struct pqi_sas_port *pqi_alloc_sas_port(127	struct pqi_sas_node *pqi_sas_node, u64 sas_address,128	struct pqi_scsi_dev *device)129{130	int rc;131	struct pqi_sas_port *pqi_sas_port;132	struct sas_port *port;133 134	pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL);135	if (!pqi_sas_port)136		return NULL;137 138	INIT_LIST_HEAD(&pqi_sas_port->phy_list_head);139	pqi_sas_port->parent_node = pqi_sas_node;140 141	port = sas_port_alloc_num(pqi_sas_node->parent_dev);142	if (!port)143		goto free_pqi_port;144 145	rc = sas_port_add(port);146	if (rc)147		goto free_sas_port;148 149	pqi_sas_port->port = port;150	pqi_sas_port->sas_address = sas_address;151	pqi_sas_port->device = device;152	list_add_tail(&pqi_sas_port->port_list_entry,153		&pqi_sas_node->port_list_head);154 155	return pqi_sas_port;156 157free_sas_port:158	sas_port_free(port);159free_pqi_port:160	kfree(pqi_sas_port);161 162	return NULL;163}164 165static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port)166{167	struct pqi_sas_phy *pqi_sas_phy;168	struct pqi_sas_phy *next;169 170	list_for_each_entry_safe(pqi_sas_phy, next,171		&pqi_sas_port->phy_list_head, phy_list_entry)172			pqi_free_sas_phy(pqi_sas_phy);173 174	sas_port_delete(pqi_sas_port->port);175	list_del(&pqi_sas_port->port_list_entry);176	kfree(pqi_sas_port);177}178 179static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev)180{181	struct pqi_sas_node *pqi_sas_node;182 183	pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL);184	if (pqi_sas_node) {185		pqi_sas_node->parent_dev = parent_dev;186		INIT_LIST_HEAD(&pqi_sas_node->port_list_head);187	}188 189	return pqi_sas_node;190}191 192static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node)193{194	struct pqi_sas_port *pqi_sas_port;195	struct pqi_sas_port *next;196 197	if (!pqi_sas_node)198		return;199 200	list_for_each_entry_safe(pqi_sas_port, next,201		&pqi_sas_node->port_list_head, port_list_entry)202			pqi_free_sas_port(pqi_sas_port);203 204	kfree(pqi_sas_node);205}206 207struct pqi_scsi_dev *pqi_find_device_by_sas_rphy(208	struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy)209{210	struct pqi_scsi_dev *device;211 212	list_for_each_entry(device, &ctrl_info->scsi_device_list,213		scsi_device_list_entry) {214		if (!device->sas_port)215			continue;216		if (device->sas_port->rphy == rphy)217			return device;218	}219 220	return NULL;221}222 223int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info)224{225	int rc;226	struct device *parent_dev;227	struct pqi_sas_node *pqi_sas_node;228	struct pqi_sas_port *pqi_sas_port;229	struct pqi_sas_phy *pqi_sas_phy;230 231	parent_dev = &shost->shost_dev;232 233	pqi_sas_node = pqi_alloc_sas_node(parent_dev);234	if (!pqi_sas_node)235		return -ENOMEM;236 237	pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node,238		ctrl_info->sas_address, NULL);239	if (!pqi_sas_port) {240		rc = -ENODEV;241		goto free_sas_node;242	}243 244	pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port);245	if (!pqi_sas_phy) {246		rc = -ENODEV;247		goto free_sas_port;248	}249 250	rc = pqi_sas_port_add_phy(pqi_sas_phy);251	if (rc)252		goto free_sas_phy;253 254	ctrl_info->sas_host = pqi_sas_node;255 256	return 0;257 258free_sas_phy:259	pqi_free_sas_phy(pqi_sas_phy);260free_sas_port:261	pqi_free_sas_port(pqi_sas_port);262free_sas_node:263	pqi_free_sas_node(pqi_sas_node);264 265	return rc;266}267 268void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info)269{270	pqi_free_sas_node(ctrl_info->sas_host);271}272 273int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node,274	struct pqi_scsi_dev *device)275{276	int rc;277	struct pqi_sas_port *pqi_sas_port;278	struct sas_rphy *rphy;279 280	pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node,281		device->sas_address, device);282	if (!pqi_sas_port)283		return -ENOMEM;284 285	rphy = pqi_sas_rphy_alloc(pqi_sas_port);286	if (!rphy) {287		rc = -ENODEV;288		goto free_sas_port;289	}290 291	pqi_sas_port->rphy = rphy;292	device->sas_port = pqi_sas_port;293 294	rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy);295	if (rc)296		goto free_sas_rphy;297 298	return 0;299 300free_sas_rphy:301	sas_rphy_free(rphy);302free_sas_port:303	pqi_free_sas_port(pqi_sas_port);304	device->sas_port = NULL;305 306	return rc;307}308 309void pqi_remove_sas_device(struct pqi_scsi_dev *device)310{311	if (device->sas_port) {312		pqi_free_sas_port(device->sas_port);313		device->sas_port = NULL;314	}315}316 317static int pqi_sas_get_linkerrors(struct sas_phy *phy)318{319	return 0;320}321 322static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy,323	u64 *identifier)324{325	int rc;326	unsigned long flags;327	struct Scsi_Host *shost;328	struct pqi_ctrl_info *ctrl_info;329	struct pqi_scsi_dev *found_device;330	struct pqi_scsi_dev *device;331 332	if (!rphy)333		return -ENODEV;334 335	shost = rphy_to_shost(rphy);336	ctrl_info = shost_to_hba(shost);337	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);338	found_device = pqi_find_device_by_sas_rphy(ctrl_info, rphy);339 340	if (!found_device) {341		rc = -ENODEV;342		goto out;343	}344 345	if (found_device->devtype == TYPE_ENCLOSURE) {346		*identifier = get_unaligned_be64(&found_device->wwid[8]);347		rc = 0;348		goto out;349	}350 351	if (found_device->box_index == 0xff ||352		found_device->phys_box_on_bus == 0 ||353		found_device->bay == 0xff) {354		rc = -EINVAL;355		goto out;356	}357 358	list_for_each_entry(device, &ctrl_info->scsi_device_list,359		scsi_device_list_entry) {360		if (device->devtype == TYPE_ENCLOSURE &&361			device->box_index == found_device->box_index &&362			device->phys_box_on_bus ==363				found_device->phys_box_on_bus &&364			memcmp(device->phys_connector,365				found_device->phys_connector, 2) == 0) {366			*identifier =367				get_unaligned_be64(&device->wwid[8]);368			rc = 0;369			goto out;370		}371	}372 373	if (found_device->phy_connected_dev_type != SA_DEVICE_TYPE_CONTROLLER) {374		rc = -EINVAL;375		goto out;376	}377 378	list_for_each_entry(device, &ctrl_info->scsi_device_list,379		scsi_device_list_entry) {380		if (device->devtype == TYPE_ENCLOSURE &&381			CISS_GET_DRIVE_NUMBER(device->scsi3addr) ==382				PQI_VSEP_CISS_BTL) {383			*identifier = get_unaligned_be64(&device->wwid[8]);384			rc = 0;385			goto out;386		}387	}388 389	rc = -EINVAL;390out:391	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);392 393	return rc;394}395 396static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy)397{398	int rc;399	unsigned long flags;400	struct pqi_ctrl_info *ctrl_info;401	struct pqi_scsi_dev *device;402	struct Scsi_Host *shost;403 404	if (!rphy)405		return -ENODEV;406 407	shost = rphy_to_shost(rphy);408	ctrl_info = shost_to_hba(shost);409	spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags);410	device = pqi_find_device_by_sas_rphy(ctrl_info, rphy);411 412	if (!device) {413		rc = -ENODEV;414		goto out;415	}416 417	if (device->bay == 0xff)418		rc = -EINVAL;419	else420		rc = device->bay;421 422out:423	spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags);424 425	return rc;426}427 428static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset)429{430	return 0;431}432 433static int pqi_sas_phy_enable(struct sas_phy *phy, int enable)434{435	return 0;436}437 438static int pqi_sas_phy_setup(struct sas_phy *phy)439{440	return 0;441}442 443static void pqi_sas_phy_release(struct sas_phy *phy)444{445}446 447static int pqi_sas_phy_speed(struct sas_phy *phy,448	struct sas_phy_linkrates *rates)449{450	return -EINVAL;451}452 453#define CSMI_IOCTL_TIMEOUT	60454#define SMP_CRC_FIELD_LENGTH	4455 456static struct bmic_csmi_smp_passthru_buffer *457pqi_build_csmi_smp_passthru_buffer(struct sas_rphy *rphy,458	struct bsg_job *job)459{460	struct bmic_csmi_smp_passthru_buffer *smp_buf;461	struct bmic_csmi_ioctl_header *ioctl_header;462	struct bmic_csmi_smp_passthru *parameters;463	u32 req_size;464	u32 resp_size;465 466	smp_buf = kzalloc(sizeof(*smp_buf), GFP_KERNEL);467	if (!smp_buf)468		return NULL;469 470	req_size = job->request_payload.payload_len;471	resp_size = job->reply_payload.payload_len;472 473	ioctl_header = &smp_buf->ioctl_header;474	put_unaligned_le32(sizeof(smp_buf->ioctl_header),475		&ioctl_header->header_length);476	put_unaligned_le32(CSMI_IOCTL_TIMEOUT, &ioctl_header->timeout);477	put_unaligned_le32(CSMI_CC_SAS_SMP_PASSTHRU,478		&ioctl_header->control_code);479	put_unaligned_le32(sizeof(smp_buf->parameters), &ioctl_header->length);480 481	parameters = &smp_buf->parameters;482	parameters->phy_identifier = rphy->identify.phy_identifier;483	parameters->port_identifier = 0;484	parameters->connection_rate = 0;485	put_unaligned_be64(rphy->identify.sas_address,486		&parameters->destination_sas_address);487 488	if (req_size > SMP_CRC_FIELD_LENGTH)489		req_size -= SMP_CRC_FIELD_LENGTH;490 491	put_unaligned_le32(req_size, &parameters->request_length);492	put_unaligned_le32(resp_size, &parameters->response_length);493 494	sg_copy_to_buffer(job->request_payload.sg_list,495		job->reply_payload.sg_cnt, &parameters->request,496		req_size);497 498	return smp_buf;499}500 501static unsigned int pqi_build_sas_smp_handler_reply(502	struct bmic_csmi_smp_passthru_buffer *smp_buf, struct bsg_job *job,503	struct pqi_raid_error_info *error_info)504{505	sg_copy_from_buffer(job->reply_payload.sg_list,506		job->reply_payload.sg_cnt, &smp_buf->parameters.response,507		le32_to_cpu(smp_buf->parameters.response_length));508 509	job->reply_len = le16_to_cpu(error_info->sense_data_length);510	memcpy(job->reply, error_info->data,511		le16_to_cpu(error_info->sense_data_length));512 513	return job->reply_payload.payload_len -514		get_unaligned_le32(&error_info->data_in_transferred);515}516 517void pqi_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost,518	struct sas_rphy *rphy)519{520	int rc;521	struct pqi_ctrl_info *ctrl_info;522	struct bmic_csmi_smp_passthru_buffer *smp_buf;523	struct pqi_raid_error_info error_info;524	unsigned int reslen = 0;525 526	ctrl_info = shost_to_hba(shost);527 528	if (job->reply_payload.payload_len == 0) {529		rc = -ENOMEM;530		goto out;531	}532 533	if (!rphy) {534		rc = -EINVAL;535		goto out;536	}537 538	if (rphy->identify.device_type != SAS_FANOUT_EXPANDER_DEVICE) {539		rc = -EINVAL;540		goto out;541	}542 543	if (job->request_payload.sg_cnt > 1 || job->reply_payload.sg_cnt > 1) {544		rc = -EINVAL;545		goto out;546	}547 548	smp_buf = pqi_build_csmi_smp_passthru_buffer(rphy, job);549	if (!smp_buf) {550		rc = -ENOMEM;551		goto out;552	}553 554	rc = pqi_csmi_smp_passthru(ctrl_info, smp_buf, sizeof(*smp_buf),555		&error_info);556	if (rc)557		goto out;558 559	reslen = pqi_build_sas_smp_handler_reply(smp_buf, job, &error_info);560 561out:562	bsg_job_done(job, rc, reslen);563}564struct sas_function_template pqi_sas_transport_functions = {565	.get_linkerrors = pqi_sas_get_linkerrors,566	.get_enclosure_identifier = pqi_sas_get_enclosure_identifier,567	.get_bay_identifier = pqi_sas_get_bay_identifier,568	.phy_reset = pqi_sas_phy_reset,569	.phy_enable = pqi_sas_phy_enable,570	.phy_setup = pqi_sas_phy_setup,571	.phy_release = pqi_sas_phy_release,572	.set_phy_speed = pqi_sas_phy_speed,573	.smp_handler = pqi_sas_smp_handler,574};575