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 ¶meters->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, ¶meters->request_length);492 put_unaligned_le32(resp_size, ¶meters->response_length);493 494 sg_copy_to_buffer(job->request_payload.sg_list,495 job->reply_payload.sg_cnt, ¶meters->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