162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Serial Attached SCSI (SAS) Expander discovery and configuration 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 662306a36Sopenharmony_ci * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is licensed under GPLv2. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/scatterlist.h> 1262306a36Sopenharmony_ci#include <linux/blkdev.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <asm/unaligned.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "sas_internal.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <scsi/sas_ata.h> 1962306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 2062306a36Sopenharmony_ci#include <scsi/scsi_transport_sas.h> 2162306a36Sopenharmony_ci#include "scsi_sas_internal.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int sas_discover_expander(struct domain_device *dev); 2462306a36Sopenharmony_cistatic int sas_configure_routing(struct domain_device *dev, u8 *sas_addr); 2562306a36Sopenharmony_cistatic int sas_configure_phy(struct domain_device *dev, int phy_id, 2662306a36Sopenharmony_ci u8 *sas_addr, int include); 2762306a36Sopenharmony_cistatic int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* ---------- SMP task management ---------- */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Give it some long enough timeout. In seconds. */ 3262306a36Sopenharmony_ci#define SMP_TIMEOUT 10 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int smp_execute_task_sg(struct domain_device *dev, 3562306a36Sopenharmony_ci struct scatterlist *req, struct scatterlist *resp) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int res, retry; 3862306a36Sopenharmony_ci struct sas_task *task = NULL; 3962306a36Sopenharmony_ci struct sas_internal *i = 4062306a36Sopenharmony_ci to_sas_internal(dev->port->ha->shost->transportt); 4162306a36Sopenharmony_ci struct sas_ha_struct *ha = dev->port->ha; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pm_runtime_get_sync(ha->dev); 4462306a36Sopenharmony_ci mutex_lock(&dev->ex_dev.cmd_mutex); 4562306a36Sopenharmony_ci for (retry = 0; retry < 3; retry++) { 4662306a36Sopenharmony_ci if (test_bit(SAS_DEV_GONE, &dev->state)) { 4762306a36Sopenharmony_ci res = -ECOMM; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci task = sas_alloc_slow_task(GFP_KERNEL); 5262306a36Sopenharmony_ci if (!task) { 5362306a36Sopenharmony_ci res = -ENOMEM; 5462306a36Sopenharmony_ci break; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci task->dev = dev; 5762306a36Sopenharmony_ci task->task_proto = dev->tproto; 5862306a36Sopenharmony_ci task->smp_task.smp_req = *req; 5962306a36Sopenharmony_ci task->smp_task.smp_resp = *resp; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci task->task_done = sas_task_internal_done; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci task->slow_task->timer.function = sas_task_internal_timedout; 6462306a36Sopenharmony_ci task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; 6562306a36Sopenharmony_ci add_timer(&task->slow_task->timer); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci res = i->dft->lldd_execute_task(task, GFP_KERNEL); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (res) { 7062306a36Sopenharmony_ci del_timer_sync(&task->slow_task->timer); 7162306a36Sopenharmony_ci pr_notice("executing SMP task failed:%d\n", res); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci wait_for_completion(&task->slow_task->completion); 7662306a36Sopenharmony_ci res = -ECOMM; 7762306a36Sopenharmony_ci if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { 7862306a36Sopenharmony_ci pr_notice("smp task timed out or aborted\n"); 7962306a36Sopenharmony_ci i->dft->lldd_abort_task(task); 8062306a36Sopenharmony_ci if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { 8162306a36Sopenharmony_ci pr_notice("SMP task aborted and not done\n"); 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci if (task->task_status.resp == SAS_TASK_COMPLETE && 8662306a36Sopenharmony_ci task->task_status.stat == SAS_SAM_STAT_GOOD) { 8762306a36Sopenharmony_ci res = 0; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci if (task->task_status.resp == SAS_TASK_COMPLETE && 9162306a36Sopenharmony_ci task->task_status.stat == SAS_DATA_UNDERRUN) { 9262306a36Sopenharmony_ci /* no error, but return the number of bytes of 9362306a36Sopenharmony_ci * underrun */ 9462306a36Sopenharmony_ci res = task->task_status.residual; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if (task->task_status.resp == SAS_TASK_COMPLETE && 9862306a36Sopenharmony_ci task->task_status.stat == SAS_DATA_OVERRUN) { 9962306a36Sopenharmony_ci res = -EMSGSIZE; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci if (task->task_status.resp == SAS_TASK_UNDELIVERED && 10362306a36Sopenharmony_ci task->task_status.stat == SAS_DEVICE_UNKNOWN) 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci else { 10662306a36Sopenharmony_ci pr_notice("%s: task to dev %016llx response: 0x%x status 0x%x\n", 10762306a36Sopenharmony_ci __func__, 10862306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), 10962306a36Sopenharmony_ci task->task_status.resp, 11062306a36Sopenharmony_ci task->task_status.stat); 11162306a36Sopenharmony_ci sas_free_task(task); 11262306a36Sopenharmony_ci task = NULL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci mutex_unlock(&dev->ex_dev.cmd_mutex); 11662306a36Sopenharmony_ci pm_runtime_put_sync(ha->dev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci BUG_ON(retry == 3 && task != NULL); 11962306a36Sopenharmony_ci sas_free_task(task); 12062306a36Sopenharmony_ci return res; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int smp_execute_task(struct domain_device *dev, void *req, int req_size, 12462306a36Sopenharmony_ci void *resp, int resp_size) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct scatterlist req_sg; 12762306a36Sopenharmony_ci struct scatterlist resp_sg; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci sg_init_one(&req_sg, req, req_size); 13062306a36Sopenharmony_ci sg_init_one(&resp_sg, resp, resp_size); 13162306a36Sopenharmony_ci return smp_execute_task_sg(dev, &req_sg, &resp_sg); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* ---------- Allocations ---------- */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void *alloc_smp_req(int size) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u8 *p = kzalloc(size, GFP_KERNEL); 13962306a36Sopenharmony_ci if (p) 14062306a36Sopenharmony_ci p[0] = SMP_REQUEST; 14162306a36Sopenharmony_ci return p; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic inline void *alloc_smp_resp(int size) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci return kzalloc(size, GFP_KERNEL); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic char sas_route_char(struct domain_device *dev, struct ex_phy *phy) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci switch (phy->routing_attr) { 15262306a36Sopenharmony_ci case TABLE_ROUTING: 15362306a36Sopenharmony_ci if (dev->ex_dev.t2t_supp) 15462306a36Sopenharmony_ci return 'U'; 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci return 'T'; 15762306a36Sopenharmony_ci case DIRECT_ROUTING: 15862306a36Sopenharmony_ci return 'D'; 15962306a36Sopenharmony_ci case SUBTRACTIVE_ROUTING: 16062306a36Sopenharmony_ci return 'S'; 16162306a36Sopenharmony_ci default: 16262306a36Sopenharmony_ci return '?'; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic enum sas_device_type to_dev_type(struct discover_resp *dr) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci /* This is detecting a failure to transmit initial dev to host 16962306a36Sopenharmony_ci * FIS as described in section J.5 of sas-2 r16 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev && 17262306a36Sopenharmony_ci dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) 17362306a36Sopenharmony_ci return SAS_SATA_PENDING; 17462306a36Sopenharmony_ci else 17562306a36Sopenharmony_ci return dr->attached_dev_type; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void sas_set_ex_phy(struct domain_device *dev, int phy_id, 17962306a36Sopenharmony_ci struct smp_disc_resp *disc_resp) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci enum sas_device_type dev_type; 18262306a36Sopenharmony_ci enum sas_linkrate linkrate; 18362306a36Sopenharmony_ci u8 sas_addr[SAS_ADDR_SIZE]; 18462306a36Sopenharmony_ci struct discover_resp *dr = &disc_resp->disc; 18562306a36Sopenharmony_ci struct sas_ha_struct *ha = dev->port->ha; 18662306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 18762306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[phy_id]; 18862306a36Sopenharmony_ci struct sas_rphy *rphy = dev->rphy; 18962306a36Sopenharmony_ci bool new_phy = !phy->phy; 19062306a36Sopenharmony_ci char *type; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (new_phy) { 19362306a36Sopenharmony_ci if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) 19462306a36Sopenharmony_ci return; 19562306a36Sopenharmony_ci phy->phy = sas_phy_alloc(&rphy->dev, phy_id); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* FIXME: error_handling */ 19862306a36Sopenharmony_ci BUG_ON(!phy->phy); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci switch (disc_resp->result) { 20262306a36Sopenharmony_ci case SMP_RESP_PHY_VACANT: 20362306a36Sopenharmony_ci phy->phy_state = PHY_VACANT; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci phy->phy_state = PHY_NOT_PRESENT; 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci case SMP_RESP_FUNC_ACC: 20962306a36Sopenharmony_ci phy->phy_state = PHY_EMPTY; /* do not know yet */ 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* check if anything important changed to squelch debug */ 21462306a36Sopenharmony_ci dev_type = phy->attached_dev_type; 21562306a36Sopenharmony_ci linkrate = phy->linkrate; 21662306a36Sopenharmony_ci memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Handle vacant phy - rest of dr data is not valid so skip it */ 21962306a36Sopenharmony_ci if (phy->phy_state == PHY_VACANT) { 22062306a36Sopenharmony_ci memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); 22162306a36Sopenharmony_ci phy->attached_dev_type = SAS_PHY_UNUSED; 22262306a36Sopenharmony_ci if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { 22362306a36Sopenharmony_ci phy->phy_id = phy_id; 22462306a36Sopenharmony_ci goto skip; 22562306a36Sopenharmony_ci } else 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci phy->attached_dev_type = to_dev_type(dr); 23062306a36Sopenharmony_ci if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci phy->phy_id = phy_id; 23362306a36Sopenharmony_ci phy->linkrate = dr->linkrate; 23462306a36Sopenharmony_ci phy->attached_sata_host = dr->attached_sata_host; 23562306a36Sopenharmony_ci phy->attached_sata_dev = dr->attached_sata_dev; 23662306a36Sopenharmony_ci phy->attached_sata_ps = dr->attached_sata_ps; 23762306a36Sopenharmony_ci phy->attached_iproto = dr->iproto << 1; 23862306a36Sopenharmony_ci phy->attached_tproto = dr->tproto << 1; 23962306a36Sopenharmony_ci /* help some expanders that fail to zero sas_address in the 'no 24062306a36Sopenharmony_ci * device' case 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci if (phy->attached_dev_type == SAS_PHY_UNUSED || 24362306a36Sopenharmony_ci phy->linkrate < SAS_LINK_RATE_1_5_GBPS) 24462306a36Sopenharmony_ci memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); 24762306a36Sopenharmony_ci phy->attached_phy_id = dr->attached_phy_id; 24862306a36Sopenharmony_ci phy->phy_change_count = dr->change_count; 24962306a36Sopenharmony_ci phy->routing_attr = dr->routing_attr; 25062306a36Sopenharmony_ci phy->virtual = dr->virtual; 25162306a36Sopenharmony_ci phy->last_da_index = -1; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); 25462306a36Sopenharmony_ci phy->phy->identify.device_type = dr->attached_dev_type; 25562306a36Sopenharmony_ci phy->phy->identify.initiator_port_protocols = phy->attached_iproto; 25662306a36Sopenharmony_ci phy->phy->identify.target_port_protocols = phy->attached_tproto; 25762306a36Sopenharmony_ci if (!phy->attached_tproto && dr->attached_sata_dev) 25862306a36Sopenharmony_ci phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; 25962306a36Sopenharmony_ci phy->phy->identify.phy_identifier = phy_id; 26062306a36Sopenharmony_ci phy->phy->minimum_linkrate_hw = dr->hmin_linkrate; 26162306a36Sopenharmony_ci phy->phy->maximum_linkrate_hw = dr->hmax_linkrate; 26262306a36Sopenharmony_ci phy->phy->minimum_linkrate = dr->pmin_linkrate; 26362306a36Sopenharmony_ci phy->phy->maximum_linkrate = dr->pmax_linkrate; 26462306a36Sopenharmony_ci phy->phy->negotiated_linkrate = phy->linkrate; 26562306a36Sopenharmony_ci phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci skip: 26862306a36Sopenharmony_ci if (new_phy) 26962306a36Sopenharmony_ci if (sas_phy_add(phy->phy)) { 27062306a36Sopenharmony_ci sas_phy_free(phy->phy); 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci out: 27562306a36Sopenharmony_ci switch (phy->attached_dev_type) { 27662306a36Sopenharmony_ci case SAS_SATA_PENDING: 27762306a36Sopenharmony_ci type = "stp pending"; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case SAS_PHY_UNUSED: 28062306a36Sopenharmony_ci type = "no device"; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case SAS_END_DEVICE: 28362306a36Sopenharmony_ci if (phy->attached_iproto) { 28462306a36Sopenharmony_ci if (phy->attached_tproto) 28562306a36Sopenharmony_ci type = "host+target"; 28662306a36Sopenharmony_ci else 28762306a36Sopenharmony_ci type = "host"; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci if (dr->attached_sata_dev) 29062306a36Sopenharmony_ci type = "stp"; 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci type = "ssp"; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case SAS_EDGE_EXPANDER_DEVICE: 29662306a36Sopenharmony_ci case SAS_FANOUT_EXPANDER_DEVICE: 29762306a36Sopenharmony_ci type = "smp"; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci default: 30062306a36Sopenharmony_ci type = "unknown"; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* this routine is polled by libata error recovery so filter 30462306a36Sopenharmony_ci * unimportant messages 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (new_phy || phy->attached_dev_type != dev_type || 30762306a36Sopenharmony_ci phy->linkrate != linkrate || 30862306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) 30962306a36Sopenharmony_ci /* pass */; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci return; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* if the attached device type changed and ata_eh is active, 31462306a36Sopenharmony_ci * make sure we run revalidation when eh completes (see: 31562306a36Sopenharmony_ci * sas_enable_revalidation) 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) 31862306a36Sopenharmony_ci set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci pr_debug("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", 32162306a36Sopenharmony_ci test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", 32262306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy->phy_id, 32362306a36Sopenharmony_ci sas_route_char(dev, phy), phy->linkrate, 32462306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr), type); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* check if we have an existing attached ata device on this expander phy */ 32862306a36Sopenharmony_cistruct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; 33162306a36Sopenharmony_ci struct domain_device *dev; 33262306a36Sopenharmony_ci struct sas_rphy *rphy; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!ex_phy->port) 33562306a36Sopenharmony_ci return NULL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci rphy = ex_phy->port->rphy; 33862306a36Sopenharmony_ci if (!rphy) 33962306a36Sopenharmony_ci return NULL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci dev = sas_find_dev_by_rphy(rphy); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (dev && dev_is_sata(dev)) 34462306a36Sopenharmony_ci return dev; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return NULL; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci#define DISCOVER_REQ_SIZE 16 35062306a36Sopenharmony_ci#define DISCOVER_RESP_SIZE sizeof(struct smp_disc_resp) 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, 35362306a36Sopenharmony_ci struct smp_disc_resp *disc_resp, 35462306a36Sopenharmony_ci int single) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct discover_resp *dr = &disc_resp->disc; 35762306a36Sopenharmony_ci int res; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci disc_req[9] = single; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, 36262306a36Sopenharmony_ci disc_resp, DISCOVER_RESP_SIZE); 36362306a36Sopenharmony_ci if (res) 36462306a36Sopenharmony_ci return res; 36562306a36Sopenharmony_ci if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { 36662306a36Sopenharmony_ci pr_notice("Found loopback topology, just ignore it!\n"); 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci sas_set_ex_phy(dev, single, disc_resp); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciint sas_ex_phy_discover(struct domain_device *dev, int single) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 37662306a36Sopenharmony_ci int res = 0; 37762306a36Sopenharmony_ci u8 *disc_req; 37862306a36Sopenharmony_ci struct smp_disc_resp *disc_resp; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); 38162306a36Sopenharmony_ci if (!disc_req) 38262306a36Sopenharmony_ci return -ENOMEM; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); 38562306a36Sopenharmony_ci if (!disc_resp) { 38662306a36Sopenharmony_ci kfree(disc_req); 38762306a36Sopenharmony_ci return -ENOMEM; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci disc_req[1] = SMP_DISCOVER; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (0 <= single && single < ex->num_phys) { 39362306a36Sopenharmony_ci res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci int i; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci for (i = 0; i < ex->num_phys; i++) { 39862306a36Sopenharmony_ci res = sas_ex_phy_discover_helper(dev, disc_req, 39962306a36Sopenharmony_ci disc_resp, i); 40062306a36Sopenharmony_ci if (res) 40162306a36Sopenharmony_ci goto out_err; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ciout_err: 40562306a36Sopenharmony_ci kfree(disc_resp); 40662306a36Sopenharmony_ci kfree(disc_req); 40762306a36Sopenharmony_ci return res; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int sas_expander_discover(struct domain_device *dev) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 41362306a36Sopenharmony_ci int res; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ex->ex_phy = kcalloc(ex->num_phys, sizeof(*ex->ex_phy), GFP_KERNEL); 41662306a36Sopenharmony_ci if (!ex->ex_phy) 41762306a36Sopenharmony_ci return -ENOMEM; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci res = sas_ex_phy_discover(dev, -1); 42062306a36Sopenharmony_ci if (res) 42162306a36Sopenharmony_ci goto out_err; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci out_err: 42562306a36Sopenharmony_ci kfree(ex->ex_phy); 42662306a36Sopenharmony_ci ex->ex_phy = NULL; 42762306a36Sopenharmony_ci return res; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci#define MAX_EXPANDER_PHYS 128 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci#define RG_REQ_SIZE 8 43362306a36Sopenharmony_ci#define RG_RESP_SIZE sizeof(struct smp_rg_resp) 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int sas_ex_general(struct domain_device *dev) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci u8 *rg_req; 43862306a36Sopenharmony_ci struct smp_rg_resp *rg_resp; 43962306a36Sopenharmony_ci struct report_general_resp *rg; 44062306a36Sopenharmony_ci int res; 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci rg_req = alloc_smp_req(RG_REQ_SIZE); 44462306a36Sopenharmony_ci if (!rg_req) 44562306a36Sopenharmony_ci return -ENOMEM; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci rg_resp = alloc_smp_resp(RG_RESP_SIZE); 44862306a36Sopenharmony_ci if (!rg_resp) { 44962306a36Sopenharmony_ci kfree(rg_req); 45062306a36Sopenharmony_ci return -ENOMEM; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rg_req[1] = SMP_REPORT_GENERAL; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 45662306a36Sopenharmony_ci res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, 45762306a36Sopenharmony_ci RG_RESP_SIZE); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (res) { 46062306a36Sopenharmony_ci pr_notice("RG to ex %016llx failed:0x%x\n", 46162306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), res); 46262306a36Sopenharmony_ci goto out; 46362306a36Sopenharmony_ci } else if (rg_resp->result != SMP_RESP_FUNC_ACC) { 46462306a36Sopenharmony_ci pr_debug("RG:ex %016llx returned SMP result:0x%x\n", 46562306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), rg_resp->result); 46662306a36Sopenharmony_ci res = rg_resp->result; 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci rg = &rg_resp->rg; 47162306a36Sopenharmony_ci dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); 47262306a36Sopenharmony_ci dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); 47362306a36Sopenharmony_ci dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); 47462306a36Sopenharmony_ci dev->ex_dev.t2t_supp = rg->t2t_supp; 47562306a36Sopenharmony_ci dev->ex_dev.conf_route_table = rg->conf_route_table; 47662306a36Sopenharmony_ci dev->ex_dev.configuring = rg->configuring; 47762306a36Sopenharmony_ci memcpy(dev->ex_dev.enclosure_logical_id, 47862306a36Sopenharmony_ci rg->enclosure_logical_id, 8); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (dev->ex_dev.configuring) { 48162306a36Sopenharmony_ci pr_debug("RG: ex %016llx self-configuring...\n", 48262306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr)); 48362306a36Sopenharmony_ci schedule_timeout_interruptible(5*HZ); 48462306a36Sopenharmony_ci } else 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ciout: 48862306a36Sopenharmony_ci kfree(rg_req); 48962306a36Sopenharmony_ci kfree(rg_resp); 49062306a36Sopenharmony_ci return res; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic void ex_assign_manuf_info(struct domain_device *dev, void 49462306a36Sopenharmony_ci *_mi_resp) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci u8 *mi_resp = _mi_resp; 49762306a36Sopenharmony_ci struct sas_rphy *rphy = dev->rphy; 49862306a36Sopenharmony_ci struct sas_expander_device *edev = rphy_to_expander_device(rphy); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci memcpy(edev->vendor_id, mi_resp + 12, SAS_EXPANDER_VENDOR_ID_LEN); 50162306a36Sopenharmony_ci memcpy(edev->product_id, mi_resp + 20, SAS_EXPANDER_PRODUCT_ID_LEN); 50262306a36Sopenharmony_ci memcpy(edev->product_rev, mi_resp + 36, 50362306a36Sopenharmony_ci SAS_EXPANDER_PRODUCT_REV_LEN); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (mi_resp[8] & 1) { 50662306a36Sopenharmony_ci memcpy(edev->component_vendor_id, mi_resp + 40, 50762306a36Sopenharmony_ci SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); 50862306a36Sopenharmony_ci edev->component_id = mi_resp[48] << 8 | mi_resp[49]; 50962306a36Sopenharmony_ci edev->component_revision_id = mi_resp[50]; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci#define MI_REQ_SIZE 8 51462306a36Sopenharmony_ci#define MI_RESP_SIZE 64 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int sas_ex_manuf_info(struct domain_device *dev) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci u8 *mi_req; 51962306a36Sopenharmony_ci u8 *mi_resp; 52062306a36Sopenharmony_ci int res; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci mi_req = alloc_smp_req(MI_REQ_SIZE); 52362306a36Sopenharmony_ci if (!mi_req) 52462306a36Sopenharmony_ci return -ENOMEM; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci mi_resp = alloc_smp_resp(MI_RESP_SIZE); 52762306a36Sopenharmony_ci if (!mi_resp) { 52862306a36Sopenharmony_ci kfree(mi_req); 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci mi_req[1] = SMP_REPORT_MANUF_INFO; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp, MI_RESP_SIZE); 53562306a36Sopenharmony_ci if (res) { 53662306a36Sopenharmony_ci pr_notice("MI: ex %016llx failed:0x%x\n", 53762306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), res); 53862306a36Sopenharmony_ci goto out; 53962306a36Sopenharmony_ci } else if (mi_resp[2] != SMP_RESP_FUNC_ACC) { 54062306a36Sopenharmony_ci pr_debug("MI ex %016llx returned SMP result:0x%x\n", 54162306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), mi_resp[2]); 54262306a36Sopenharmony_ci goto out; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ex_assign_manuf_info(dev, mi_resp); 54662306a36Sopenharmony_ciout: 54762306a36Sopenharmony_ci kfree(mi_req); 54862306a36Sopenharmony_ci kfree(mi_resp); 54962306a36Sopenharmony_ci return res; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci#define PC_REQ_SIZE 44 55362306a36Sopenharmony_ci#define PC_RESP_SIZE 8 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ciint sas_smp_phy_control(struct domain_device *dev, int phy_id, 55662306a36Sopenharmony_ci enum phy_func phy_func, 55762306a36Sopenharmony_ci struct sas_phy_linkrates *rates) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci u8 *pc_req; 56062306a36Sopenharmony_ci u8 *pc_resp; 56162306a36Sopenharmony_ci int res; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci pc_req = alloc_smp_req(PC_REQ_SIZE); 56462306a36Sopenharmony_ci if (!pc_req) 56562306a36Sopenharmony_ci return -ENOMEM; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci pc_resp = alloc_smp_resp(PC_RESP_SIZE); 56862306a36Sopenharmony_ci if (!pc_resp) { 56962306a36Sopenharmony_ci kfree(pc_req); 57062306a36Sopenharmony_ci return -ENOMEM; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci pc_req[1] = SMP_PHY_CONTROL; 57462306a36Sopenharmony_ci pc_req[9] = phy_id; 57562306a36Sopenharmony_ci pc_req[10] = phy_func; 57662306a36Sopenharmony_ci if (rates) { 57762306a36Sopenharmony_ci pc_req[32] = rates->minimum_linkrate << 4; 57862306a36Sopenharmony_ci pc_req[33] = rates->maximum_linkrate << 4; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp, PC_RESP_SIZE); 58262306a36Sopenharmony_ci if (res) { 58362306a36Sopenharmony_ci pr_err("ex %016llx phy%02d PHY control failed: %d\n", 58462306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, res); 58562306a36Sopenharmony_ci } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) { 58662306a36Sopenharmony_ci pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n", 58762306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]); 58862306a36Sopenharmony_ci res = pc_resp[2]; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci kfree(pc_resp); 59162306a36Sopenharmony_ci kfree(pc_req); 59262306a36Sopenharmony_ci return res; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void sas_ex_disable_phy(struct domain_device *dev, int phy_id) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 59862306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[phy_id]; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE, NULL); 60162306a36Sopenharmony_ci phy->linkrate = SAS_PHY_DISABLED; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void sas_ex_disable_port(struct domain_device *dev, u8 *sas_addr) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 60762306a36Sopenharmony_ci int i; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci for (i = 0; i < ex->num_phys; i++) { 61062306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[i]; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (phy->phy_state == PHY_VACANT || 61362306a36Sopenharmony_ci phy->phy_state == PHY_NOT_PRESENT) 61462306a36Sopenharmony_ci continue; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (SAS_ADDR(phy->attached_sas_addr) == SAS_ADDR(sas_addr)) 61762306a36Sopenharmony_ci sas_ex_disable_phy(dev, i); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int sas_dev_present_in_domain(struct asd_sas_port *port, 62262306a36Sopenharmony_ci u8 *sas_addr) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct domain_device *dev; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (SAS_ADDR(port->sas_addr) == SAS_ADDR(sas_addr)) 62762306a36Sopenharmony_ci return 1; 62862306a36Sopenharmony_ci list_for_each_entry(dev, &port->dev_list, dev_list_node) { 62962306a36Sopenharmony_ci if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr)) 63062306a36Sopenharmony_ci return 1; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci#define RPEL_REQ_SIZE 16 63662306a36Sopenharmony_ci#define RPEL_RESP_SIZE 32 63762306a36Sopenharmony_ciint sas_smp_get_phy_events(struct sas_phy *phy) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci int res; 64062306a36Sopenharmony_ci u8 *req; 64162306a36Sopenharmony_ci u8 *resp; 64262306a36Sopenharmony_ci struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); 64362306a36Sopenharmony_ci struct domain_device *dev = sas_find_dev_by_rphy(rphy); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci req = alloc_smp_req(RPEL_REQ_SIZE); 64662306a36Sopenharmony_ci if (!req) 64762306a36Sopenharmony_ci return -ENOMEM; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci resp = alloc_smp_resp(RPEL_RESP_SIZE); 65062306a36Sopenharmony_ci if (!resp) { 65162306a36Sopenharmony_ci kfree(req); 65262306a36Sopenharmony_ci return -ENOMEM; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci req[1] = SMP_REPORT_PHY_ERR_LOG; 65662306a36Sopenharmony_ci req[9] = phy->number; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci res = smp_execute_task(dev, req, RPEL_REQ_SIZE, 65962306a36Sopenharmony_ci resp, RPEL_RESP_SIZE); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (res) 66262306a36Sopenharmony_ci goto out; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci phy->invalid_dword_count = get_unaligned_be32(&resp[12]); 66562306a36Sopenharmony_ci phy->running_disparity_error_count = get_unaligned_be32(&resp[16]); 66662306a36Sopenharmony_ci phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]); 66762306a36Sopenharmony_ci phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci out: 67062306a36Sopenharmony_ci kfree(req); 67162306a36Sopenharmony_ci kfree(resp); 67262306a36Sopenharmony_ci return res; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci#ifdef CONFIG_SCSI_SAS_ATA 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci#define RPS_REQ_SIZE 16 67962306a36Sopenharmony_ci#define RPS_RESP_SIZE sizeof(struct smp_rps_resp) 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciint sas_get_report_phy_sata(struct domain_device *dev, int phy_id, 68262306a36Sopenharmony_ci struct smp_rps_resp *rps_resp) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int res; 68562306a36Sopenharmony_ci u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); 68662306a36Sopenharmony_ci u8 *resp = (u8 *)rps_resp; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (!rps_req) 68962306a36Sopenharmony_ci return -ENOMEM; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci rps_req[1] = SMP_REPORT_PHY_SATA; 69262306a36Sopenharmony_ci rps_req[9] = phy_id; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, 69562306a36Sopenharmony_ci rps_resp, RPS_RESP_SIZE); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 0x34 is the FIS type for the D2H fis. There's a potential 69862306a36Sopenharmony_ci * standards cockup here. sas-2 explicitly specifies the FIS 69962306a36Sopenharmony_ci * should be encoded so that FIS type is in resp[24]. 70062306a36Sopenharmony_ci * However, some expanders endian reverse this. Undo the 70162306a36Sopenharmony_ci * reversal here */ 70262306a36Sopenharmony_ci if (!res && resp[27] == 0x34 && resp[24] != 0x34) { 70362306a36Sopenharmony_ci int i; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 70662306a36Sopenharmony_ci int j = 24 + (i*4); 70762306a36Sopenharmony_ci u8 a, b; 70862306a36Sopenharmony_ci a = resp[j + 0]; 70962306a36Sopenharmony_ci b = resp[j + 1]; 71062306a36Sopenharmony_ci resp[j + 0] = resp[j + 3]; 71162306a36Sopenharmony_ci resp[j + 1] = resp[j + 2]; 71262306a36Sopenharmony_ci resp[j + 2] = b; 71362306a36Sopenharmony_ci resp[j + 3] = a; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci kfree(rps_req); 71862306a36Sopenharmony_ci return res; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci#endif 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic void sas_ex_get_linkrate(struct domain_device *parent, 72362306a36Sopenharmony_ci struct domain_device *child, 72462306a36Sopenharmony_ci struct ex_phy *parent_phy) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct expander_device *parent_ex = &parent->ex_dev; 72762306a36Sopenharmony_ci struct sas_port *port; 72862306a36Sopenharmony_ci int i; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci child->pathways = 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci port = parent_phy->port; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci for (i = 0; i < parent_ex->num_phys; i++) { 73562306a36Sopenharmony_ci struct ex_phy *phy = &parent_ex->ex_phy[i]; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (phy->phy_state == PHY_VACANT || 73862306a36Sopenharmony_ci phy->phy_state == PHY_NOT_PRESENT) 73962306a36Sopenharmony_ci continue; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (sas_phy_match_dev_addr(child, phy)) { 74262306a36Sopenharmony_ci child->min_linkrate = min(parent->min_linkrate, 74362306a36Sopenharmony_ci phy->linkrate); 74462306a36Sopenharmony_ci child->max_linkrate = max(parent->max_linkrate, 74562306a36Sopenharmony_ci phy->linkrate); 74662306a36Sopenharmony_ci child->pathways++; 74762306a36Sopenharmony_ci sas_port_add_phy(port, phy->phy); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci child->linkrate = min(parent_phy->linkrate, child->max_linkrate); 75162306a36Sopenharmony_ci child->pathways = min(child->pathways, parent->pathways); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int sas_ex_add_dev(struct domain_device *parent, struct ex_phy *phy, 75562306a36Sopenharmony_ci struct domain_device *child, int phy_id) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct sas_rphy *rphy; 75862306a36Sopenharmony_ci int res; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci child->dev_type = SAS_END_DEVICE; 76162306a36Sopenharmony_ci rphy = sas_end_device_alloc(phy->port); 76262306a36Sopenharmony_ci if (!rphy) 76362306a36Sopenharmony_ci return -ENOMEM; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci child->tproto = phy->attached_tproto; 76662306a36Sopenharmony_ci sas_init_dev(child); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci child->rphy = rphy; 76962306a36Sopenharmony_ci get_device(&rphy->dev); 77062306a36Sopenharmony_ci rphy->identify.phy_identifier = phy_id; 77162306a36Sopenharmony_ci sas_fill_in_rphy(child, rphy); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci list_add_tail(&child->disco_list_node, &parent->port->disco_list); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci res = sas_notify_lldd_dev_found(child); 77662306a36Sopenharmony_ci if (res) { 77762306a36Sopenharmony_ci pr_notice("notify lldd for device %016llx at %016llx:%02d returned 0x%x\n", 77862306a36Sopenharmony_ci SAS_ADDR(child->sas_addr), 77962306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr), phy_id, res); 78062306a36Sopenharmony_ci sas_rphy_free(child->rphy); 78162306a36Sopenharmony_ci list_del(&child->disco_list_node); 78262306a36Sopenharmony_ci return res; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic struct domain_device *sas_ex_discover_end_dev( 78962306a36Sopenharmony_ci struct domain_device *parent, int phy_id) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct expander_device *parent_ex = &parent->ex_dev; 79262306a36Sopenharmony_ci struct ex_phy *phy = &parent_ex->ex_phy[phy_id]; 79362306a36Sopenharmony_ci struct domain_device *child = NULL; 79462306a36Sopenharmony_ci int res; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (phy->attached_sata_host || phy->attached_sata_ps) 79762306a36Sopenharmony_ci return NULL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci child = sas_alloc_device(); 80062306a36Sopenharmony_ci if (!child) 80162306a36Sopenharmony_ci return NULL; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci kref_get(&parent->kref); 80462306a36Sopenharmony_ci child->parent = parent; 80562306a36Sopenharmony_ci child->port = parent->port; 80662306a36Sopenharmony_ci child->iproto = phy->attached_iproto; 80762306a36Sopenharmony_ci memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); 80862306a36Sopenharmony_ci sas_hash_addr(child->hashed_sas_addr, child->sas_addr); 80962306a36Sopenharmony_ci if (!phy->port) { 81062306a36Sopenharmony_ci phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); 81162306a36Sopenharmony_ci if (unlikely(!phy->port)) 81262306a36Sopenharmony_ci goto out_err; 81362306a36Sopenharmony_ci if (unlikely(sas_port_add(phy->port) != 0)) { 81462306a36Sopenharmony_ci sas_port_free(phy->port); 81562306a36Sopenharmony_ci goto out_err; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci sas_ex_get_linkrate(parent, child, phy); 81962306a36Sopenharmony_ci sas_device_set_phy(child, phy->port); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { 82262306a36Sopenharmony_ci res = sas_ata_add_dev(parent, phy, child, phy_id); 82362306a36Sopenharmony_ci } else if (phy->attached_tproto & SAS_PROTOCOL_SSP) { 82462306a36Sopenharmony_ci res = sas_ex_add_dev(parent, phy, child, phy_id); 82562306a36Sopenharmony_ci } else { 82662306a36Sopenharmony_ci pr_notice("target proto 0x%x at %016llx:0x%x not handled\n", 82762306a36Sopenharmony_ci phy->attached_tproto, SAS_ADDR(parent->sas_addr), 82862306a36Sopenharmony_ci phy_id); 82962306a36Sopenharmony_ci res = -ENODEV; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (res) 83362306a36Sopenharmony_ci goto out_free; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci list_add_tail(&child->siblings, &parent_ex->children); 83662306a36Sopenharmony_ci return child; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci out_free: 83962306a36Sopenharmony_ci sas_port_delete(phy->port); 84062306a36Sopenharmony_ci out_err: 84162306a36Sopenharmony_ci phy->port = NULL; 84262306a36Sopenharmony_ci sas_put_device(child); 84362306a36Sopenharmony_ci return NULL; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci/* See if this phy is part of a wide port */ 84762306a36Sopenharmony_cistatic bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; 85062306a36Sopenharmony_ci int i; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci for (i = 0; i < parent->ex_dev.num_phys; i++) { 85362306a36Sopenharmony_ci struct ex_phy *ephy = &parent->ex_dev.ex_phy[i]; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (ephy == phy) 85662306a36Sopenharmony_ci continue; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr, 85962306a36Sopenharmony_ci SAS_ADDR_SIZE) && ephy->port) { 86062306a36Sopenharmony_ci sas_port_add_phy(ephy->port, phy->phy); 86162306a36Sopenharmony_ci phy->port = ephy->port; 86262306a36Sopenharmony_ci phy->phy_state = PHY_DEVICE_DISCOVERED; 86362306a36Sopenharmony_ci return true; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return false; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic struct domain_device *sas_ex_discover_expander( 87162306a36Sopenharmony_ci struct domain_device *parent, int phy_id) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct sas_expander_device *parent_ex = rphy_to_expander_device(parent->rphy); 87462306a36Sopenharmony_ci struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; 87562306a36Sopenharmony_ci struct domain_device *child = NULL; 87662306a36Sopenharmony_ci struct sas_rphy *rphy; 87762306a36Sopenharmony_ci struct sas_expander_device *edev; 87862306a36Sopenharmony_ci struct asd_sas_port *port; 87962306a36Sopenharmony_ci int res; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (phy->routing_attr == DIRECT_ROUTING) { 88262306a36Sopenharmony_ci pr_warn("ex %016llx:%02d:D <--> ex %016llx:0x%x is not allowed\n", 88362306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr), phy_id, 88462306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr), 88562306a36Sopenharmony_ci phy->attached_phy_id); 88662306a36Sopenharmony_ci return NULL; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci child = sas_alloc_device(); 88962306a36Sopenharmony_ci if (!child) 89062306a36Sopenharmony_ci return NULL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); 89362306a36Sopenharmony_ci /* FIXME: better error handling */ 89462306a36Sopenharmony_ci BUG_ON(sas_port_add(phy->port) != 0); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci switch (phy->attached_dev_type) { 89862306a36Sopenharmony_ci case SAS_EDGE_EXPANDER_DEVICE: 89962306a36Sopenharmony_ci rphy = sas_expander_alloc(phy->port, 90062306a36Sopenharmony_ci SAS_EDGE_EXPANDER_DEVICE); 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci case SAS_FANOUT_EXPANDER_DEVICE: 90362306a36Sopenharmony_ci rphy = sas_expander_alloc(phy->port, 90462306a36Sopenharmony_ci SAS_FANOUT_EXPANDER_DEVICE); 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci default: 90762306a36Sopenharmony_ci rphy = NULL; /* shut gcc up */ 90862306a36Sopenharmony_ci BUG(); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci port = parent->port; 91162306a36Sopenharmony_ci child->rphy = rphy; 91262306a36Sopenharmony_ci get_device(&rphy->dev); 91362306a36Sopenharmony_ci edev = rphy_to_expander_device(rphy); 91462306a36Sopenharmony_ci child->dev_type = phy->attached_dev_type; 91562306a36Sopenharmony_ci kref_get(&parent->kref); 91662306a36Sopenharmony_ci child->parent = parent; 91762306a36Sopenharmony_ci child->port = port; 91862306a36Sopenharmony_ci child->iproto = phy->attached_iproto; 91962306a36Sopenharmony_ci child->tproto = phy->attached_tproto; 92062306a36Sopenharmony_ci memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); 92162306a36Sopenharmony_ci sas_hash_addr(child->hashed_sas_addr, child->sas_addr); 92262306a36Sopenharmony_ci sas_ex_get_linkrate(parent, child, phy); 92362306a36Sopenharmony_ci edev->level = parent_ex->level + 1; 92462306a36Sopenharmony_ci parent->port->disc.max_level = max(parent->port->disc.max_level, 92562306a36Sopenharmony_ci edev->level); 92662306a36Sopenharmony_ci sas_init_dev(child); 92762306a36Sopenharmony_ci sas_fill_in_rphy(child, rphy); 92862306a36Sopenharmony_ci sas_rphy_add(rphy); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci spin_lock_irq(&parent->port->dev_list_lock); 93162306a36Sopenharmony_ci list_add_tail(&child->dev_list_node, &parent->port->dev_list); 93262306a36Sopenharmony_ci spin_unlock_irq(&parent->port->dev_list_lock); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci res = sas_discover_expander(child); 93562306a36Sopenharmony_ci if (res) { 93662306a36Sopenharmony_ci sas_rphy_delete(rphy); 93762306a36Sopenharmony_ci spin_lock_irq(&parent->port->dev_list_lock); 93862306a36Sopenharmony_ci list_del(&child->dev_list_node); 93962306a36Sopenharmony_ci spin_unlock_irq(&parent->port->dev_list_lock); 94062306a36Sopenharmony_ci sas_put_device(child); 94162306a36Sopenharmony_ci sas_port_delete(phy->port); 94262306a36Sopenharmony_ci phy->port = NULL; 94362306a36Sopenharmony_ci return NULL; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci list_add_tail(&child->siblings, &parent->ex_dev.children); 94662306a36Sopenharmony_ci return child; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic int sas_ex_discover_dev(struct domain_device *dev, int phy_id) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 95262306a36Sopenharmony_ci struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; 95362306a36Sopenharmony_ci struct domain_device *child = NULL; 95462306a36Sopenharmony_ci int res = 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Phy state */ 95762306a36Sopenharmony_ci if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) { 95862306a36Sopenharmony_ci if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET, NULL)) 95962306a36Sopenharmony_ci res = sas_ex_phy_discover(dev, phy_id); 96062306a36Sopenharmony_ci if (res) 96162306a36Sopenharmony_ci return res; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Parent and domain coherency */ 96562306a36Sopenharmony_ci if (!dev->parent && sas_phy_match_port_addr(dev->port, ex_phy)) { 96662306a36Sopenharmony_ci sas_add_parent_port(dev, phy_id); 96762306a36Sopenharmony_ci return 0; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci if (dev->parent && sas_phy_match_dev_addr(dev->parent, ex_phy)) { 97062306a36Sopenharmony_ci sas_add_parent_port(dev, phy_id); 97162306a36Sopenharmony_ci if (ex_phy->routing_attr == TABLE_ROUTING) 97262306a36Sopenharmony_ci sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1); 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr)) 97762306a36Sopenharmony_ci sas_ex_disable_port(dev, ex_phy->attached_sas_addr); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (ex_phy->attached_dev_type == SAS_PHY_UNUSED) { 98062306a36Sopenharmony_ci if (ex_phy->routing_attr == DIRECT_ROUTING) { 98162306a36Sopenharmony_ci memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); 98262306a36Sopenharmony_ci sas_configure_routing(dev, ex_phy->attached_sas_addr); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci } else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN) 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (ex_phy->attached_dev_type != SAS_END_DEVICE && 98962306a36Sopenharmony_ci ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE && 99062306a36Sopenharmony_ci ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE && 99162306a36Sopenharmony_ci ex_phy->attached_dev_type != SAS_SATA_PENDING) { 99262306a36Sopenharmony_ci pr_warn("unknown device type(0x%x) attached to ex %016llx phy%02d\n", 99362306a36Sopenharmony_ci ex_phy->attached_dev_type, 99462306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), 99562306a36Sopenharmony_ci phy_id); 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci res = sas_configure_routing(dev, ex_phy->attached_sas_addr); 100062306a36Sopenharmony_ci if (res) { 100162306a36Sopenharmony_ci pr_notice("configure routing for dev %016llx reported 0x%x. Forgotten\n", 100262306a36Sopenharmony_ci SAS_ADDR(ex_phy->attached_sas_addr), res); 100362306a36Sopenharmony_ci sas_disable_routing(dev, ex_phy->attached_sas_addr); 100462306a36Sopenharmony_ci return res; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (sas_ex_join_wide_port(dev, phy_id)) { 100862306a36Sopenharmony_ci pr_debug("Attaching ex phy%02d to wide port %016llx\n", 100962306a36Sopenharmony_ci phy_id, SAS_ADDR(ex_phy->attached_sas_addr)); 101062306a36Sopenharmony_ci return res; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci switch (ex_phy->attached_dev_type) { 101462306a36Sopenharmony_ci case SAS_END_DEVICE: 101562306a36Sopenharmony_ci case SAS_SATA_PENDING: 101662306a36Sopenharmony_ci child = sas_ex_discover_end_dev(dev, phy_id); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case SAS_FANOUT_EXPANDER_DEVICE: 101962306a36Sopenharmony_ci if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) { 102062306a36Sopenharmony_ci pr_debug("second fanout expander %016llx phy%02d attached to ex %016llx phy%02d\n", 102162306a36Sopenharmony_ci SAS_ADDR(ex_phy->attached_sas_addr), 102262306a36Sopenharmony_ci ex_phy->attached_phy_id, 102362306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), 102462306a36Sopenharmony_ci phy_id); 102562306a36Sopenharmony_ci sas_ex_disable_phy(dev, phy_id); 102662306a36Sopenharmony_ci return res; 102762306a36Sopenharmony_ci } else 102862306a36Sopenharmony_ci memcpy(dev->port->disc.fanout_sas_addr, 102962306a36Sopenharmony_ci ex_phy->attached_sas_addr, SAS_ADDR_SIZE); 103062306a36Sopenharmony_ci fallthrough; 103162306a36Sopenharmony_ci case SAS_EDGE_EXPANDER_DEVICE: 103262306a36Sopenharmony_ci child = sas_ex_discover_expander(dev, phy_id); 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci default: 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (!child) 103962306a36Sopenharmony_ci pr_notice("ex %016llx phy%02d failed to discover\n", 104062306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id); 104162306a36Sopenharmony_ci return res; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 104762306a36Sopenharmony_ci int i; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci for (i = 0; i < ex->num_phys; i++) { 105062306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[i]; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (phy->phy_state == PHY_VACANT || 105362306a36Sopenharmony_ci phy->phy_state == PHY_NOT_PRESENT) 105462306a36Sopenharmony_ci continue; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (dev_is_expander(phy->attached_dev_type) && 105762306a36Sopenharmony_ci phy->routing_attr == SUBTRACTIVE_ROUTING) { 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci memcpy(sub_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return 1; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic int sas_check_level_subtractive_boundary(struct domain_device *dev) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 107062306a36Sopenharmony_ci struct domain_device *child; 107162306a36Sopenharmony_ci u8 sub_addr[SAS_ADDR_SIZE] = {0, }; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci list_for_each_entry(child, &ex->children, siblings) { 107462306a36Sopenharmony_ci if (!dev_is_expander(child->dev_type)) 107562306a36Sopenharmony_ci continue; 107662306a36Sopenharmony_ci if (sub_addr[0] == 0) { 107762306a36Sopenharmony_ci sas_find_sub_addr(child, sub_addr); 107862306a36Sopenharmony_ci continue; 107962306a36Sopenharmony_ci } else { 108062306a36Sopenharmony_ci u8 s2[SAS_ADDR_SIZE]; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (sas_find_sub_addr(child, s2) && 108362306a36Sopenharmony_ci (SAS_ADDR(sub_addr) != SAS_ADDR(s2))) { 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci pr_notice("ex %016llx->%016llx-?->%016llx diverges from subtractive boundary %016llx\n", 108662306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), 108762306a36Sopenharmony_ci SAS_ADDR(child->sas_addr), 108862306a36Sopenharmony_ci SAS_ADDR(s2), 108962306a36Sopenharmony_ci SAS_ADDR(sub_addr)); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci sas_ex_disable_port(child, s2); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci return 0; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci/** 109862306a36Sopenharmony_ci * sas_ex_discover_devices - discover devices attached to this expander 109962306a36Sopenharmony_ci * @dev: pointer to the expander domain device 110062306a36Sopenharmony_ci * @single: if you want to do a single phy, else set to -1; 110162306a36Sopenharmony_ci * 110262306a36Sopenharmony_ci * Configure this expander for use with its devices and register the 110362306a36Sopenharmony_ci * devices of this expander. 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_cistatic int sas_ex_discover_devices(struct domain_device *dev, int single) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 110862306a36Sopenharmony_ci int i = 0, end = ex->num_phys; 110962306a36Sopenharmony_ci int res = 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (0 <= single && single < end) { 111262306a36Sopenharmony_ci i = single; 111362306a36Sopenharmony_ci end = i+1; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci for ( ; i < end; i++) { 111762306a36Sopenharmony_ci struct ex_phy *ex_phy = &ex->ex_phy[i]; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (ex_phy->phy_state == PHY_VACANT || 112062306a36Sopenharmony_ci ex_phy->phy_state == PHY_NOT_PRESENT || 112162306a36Sopenharmony_ci ex_phy->phy_state == PHY_DEVICE_DISCOVERED) 112262306a36Sopenharmony_ci continue; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci switch (ex_phy->linkrate) { 112562306a36Sopenharmony_ci case SAS_PHY_DISABLED: 112662306a36Sopenharmony_ci case SAS_PHY_RESET_PROBLEM: 112762306a36Sopenharmony_ci case SAS_SATA_PORT_SELECTOR: 112862306a36Sopenharmony_ci continue; 112962306a36Sopenharmony_ci default: 113062306a36Sopenharmony_ci res = sas_ex_discover_dev(dev, i); 113162306a36Sopenharmony_ci if (res) 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci continue; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!res) 113862306a36Sopenharmony_ci sas_check_level_subtractive_boundary(dev); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci return res; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int sas_check_ex_subtractive_boundary(struct domain_device *dev) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 114662306a36Sopenharmony_ci int i; 114762306a36Sopenharmony_ci u8 *sub_sas_addr = NULL; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (dev->dev_type != SAS_EDGE_EXPANDER_DEVICE) 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci for (i = 0; i < ex->num_phys; i++) { 115362306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[i]; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (phy->phy_state == PHY_VACANT || 115662306a36Sopenharmony_ci phy->phy_state == PHY_NOT_PRESENT) 115762306a36Sopenharmony_ci continue; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (dev_is_expander(phy->attached_dev_type) && 116062306a36Sopenharmony_ci phy->routing_attr == SUBTRACTIVE_ROUTING) { 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (!sub_sas_addr) 116362306a36Sopenharmony_ci sub_sas_addr = &phy->attached_sas_addr[0]; 116462306a36Sopenharmony_ci else if (SAS_ADDR(sub_sas_addr) != 116562306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr)) { 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci pr_notice("ex %016llx phy%02d diverges(%016llx) on subtractive boundary(%016llx). Disabled\n", 116862306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), i, 116962306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr), 117062306a36Sopenharmony_ci SAS_ADDR(sub_sas_addr)); 117162306a36Sopenharmony_ci sas_ex_disable_phy(dev, i); 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci return 0; 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic void sas_print_parent_topology_bug(struct domain_device *child, 117962306a36Sopenharmony_ci struct ex_phy *parent_phy, 118062306a36Sopenharmony_ci struct ex_phy *child_phy) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci static const char *ex_type[] = { 118362306a36Sopenharmony_ci [SAS_EDGE_EXPANDER_DEVICE] = "edge", 118462306a36Sopenharmony_ci [SAS_FANOUT_EXPANDER_DEVICE] = "fanout", 118562306a36Sopenharmony_ci }; 118662306a36Sopenharmony_ci struct domain_device *parent = child->parent; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci pr_notice("%s ex %016llx phy%02d <--> %s ex %016llx phy%02d has %c:%c routing link!\n", 118962306a36Sopenharmony_ci ex_type[parent->dev_type], 119062306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr), 119162306a36Sopenharmony_ci parent_phy->phy_id, 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci ex_type[child->dev_type], 119462306a36Sopenharmony_ci SAS_ADDR(child->sas_addr), 119562306a36Sopenharmony_ci child_phy->phy_id, 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci sas_route_char(parent, parent_phy), 119862306a36Sopenharmony_ci sas_route_char(child, child_phy)); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic bool sas_eeds_valid(struct domain_device *parent, 120262306a36Sopenharmony_ci struct domain_device *child) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct sas_discovery *disc = &parent->port->disc; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci return (SAS_ADDR(disc->eeds_a) == SAS_ADDR(parent->sas_addr) || 120762306a36Sopenharmony_ci SAS_ADDR(disc->eeds_a) == SAS_ADDR(child->sas_addr)) && 120862306a36Sopenharmony_ci (SAS_ADDR(disc->eeds_b) == SAS_ADDR(parent->sas_addr) || 120962306a36Sopenharmony_ci SAS_ADDR(disc->eeds_b) == SAS_ADDR(child->sas_addr)); 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic int sas_check_eeds(struct domain_device *child, 121362306a36Sopenharmony_ci struct ex_phy *parent_phy, 121462306a36Sopenharmony_ci struct ex_phy *child_phy) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci int res = 0; 121762306a36Sopenharmony_ci struct domain_device *parent = child->parent; 121862306a36Sopenharmony_ci struct sas_discovery *disc = &parent->port->disc; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (SAS_ADDR(disc->fanout_sas_addr) != 0) { 122162306a36Sopenharmony_ci res = -ENODEV; 122262306a36Sopenharmony_ci pr_warn("edge ex %016llx phy S:%02d <--> edge ex %016llx phy S:%02d, while there is a fanout ex %016llx\n", 122362306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr), 122462306a36Sopenharmony_ci parent_phy->phy_id, 122562306a36Sopenharmony_ci SAS_ADDR(child->sas_addr), 122662306a36Sopenharmony_ci child_phy->phy_id, 122762306a36Sopenharmony_ci SAS_ADDR(disc->fanout_sas_addr)); 122862306a36Sopenharmony_ci } else if (SAS_ADDR(disc->eeds_a) == 0) { 122962306a36Sopenharmony_ci memcpy(disc->eeds_a, parent->sas_addr, SAS_ADDR_SIZE); 123062306a36Sopenharmony_ci memcpy(disc->eeds_b, child->sas_addr, SAS_ADDR_SIZE); 123162306a36Sopenharmony_ci } else if (!sas_eeds_valid(parent, child)) { 123262306a36Sopenharmony_ci res = -ENODEV; 123362306a36Sopenharmony_ci pr_warn("edge ex %016llx phy%02d <--> edge ex %016llx phy%02d link forms a third EEDS!\n", 123462306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr), 123562306a36Sopenharmony_ci parent_phy->phy_id, 123662306a36Sopenharmony_ci SAS_ADDR(child->sas_addr), 123762306a36Sopenharmony_ci child_phy->phy_id); 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return res; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int sas_check_edge_expander_topo(struct domain_device *child, 124462306a36Sopenharmony_ci struct ex_phy *parent_phy) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct expander_device *child_ex = &child->ex_dev; 124762306a36Sopenharmony_ci struct expander_device *parent_ex = &child->parent->ex_dev; 124862306a36Sopenharmony_ci struct ex_phy *child_phy; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { 125362306a36Sopenharmony_ci if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || 125462306a36Sopenharmony_ci child_phy->routing_attr != TABLE_ROUTING) 125562306a36Sopenharmony_ci goto error; 125662306a36Sopenharmony_ci } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { 125762306a36Sopenharmony_ci if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) 125862306a36Sopenharmony_ci return sas_check_eeds(child, parent_phy, child_phy); 125962306a36Sopenharmony_ci else if (child_phy->routing_attr != TABLE_ROUTING) 126062306a36Sopenharmony_ci goto error; 126162306a36Sopenharmony_ci } else if (parent_phy->routing_attr == TABLE_ROUTING) { 126262306a36Sopenharmony_ci if (child_phy->routing_attr != SUBTRACTIVE_ROUTING && 126362306a36Sopenharmony_ci (child_phy->routing_attr != TABLE_ROUTING || 126462306a36Sopenharmony_ci !child_ex->t2t_supp || !parent_ex->t2t_supp)) 126562306a36Sopenharmony_ci goto error; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci return 0; 126962306a36Sopenharmony_cierror: 127062306a36Sopenharmony_ci sas_print_parent_topology_bug(child, parent_phy, child_phy); 127162306a36Sopenharmony_ci return -ENODEV; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int sas_check_fanout_expander_topo(struct domain_device *child, 127562306a36Sopenharmony_ci struct ex_phy *parent_phy) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct expander_device *child_ex = &child->ex_dev; 127862306a36Sopenharmony_ci struct ex_phy *child_phy; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (parent_phy->routing_attr == TABLE_ROUTING && 128362306a36Sopenharmony_ci child_phy->routing_attr == SUBTRACTIVE_ROUTING) 128462306a36Sopenharmony_ci return 0; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci sas_print_parent_topology_bug(child, parent_phy, child_phy); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci return -ENODEV; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic int sas_check_parent_topology(struct domain_device *child) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct expander_device *parent_ex; 129462306a36Sopenharmony_ci int i; 129562306a36Sopenharmony_ci int res = 0; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (!child->parent) 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (!dev_is_expander(child->parent->dev_type)) 130162306a36Sopenharmony_ci return 0; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci parent_ex = &child->parent->ex_dev; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci for (i = 0; i < parent_ex->num_phys; i++) { 130662306a36Sopenharmony_ci struct ex_phy *parent_phy = &parent_ex->ex_phy[i]; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (parent_phy->phy_state == PHY_VACANT || 130962306a36Sopenharmony_ci parent_phy->phy_state == PHY_NOT_PRESENT) 131062306a36Sopenharmony_ci continue; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (!sas_phy_match_dev_addr(child, parent_phy)) 131362306a36Sopenharmony_ci continue; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci switch (child->parent->dev_type) { 131662306a36Sopenharmony_ci case SAS_EDGE_EXPANDER_DEVICE: 131762306a36Sopenharmony_ci if (sas_check_edge_expander_topo(child, parent_phy)) 131862306a36Sopenharmony_ci res = -ENODEV; 131962306a36Sopenharmony_ci break; 132062306a36Sopenharmony_ci case SAS_FANOUT_EXPANDER_DEVICE: 132162306a36Sopenharmony_ci if (sas_check_fanout_expander_topo(child, parent_phy)) 132262306a36Sopenharmony_ci res = -ENODEV; 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci default: 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return res; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci#define RRI_REQ_SIZE 16 133362306a36Sopenharmony_ci#define RRI_RESP_SIZE 44 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic int sas_configure_present(struct domain_device *dev, int phy_id, 133662306a36Sopenharmony_ci u8 *sas_addr, int *index, int *present) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci int i, res = 0; 133962306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 134062306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[phy_id]; 134162306a36Sopenharmony_ci u8 *rri_req; 134262306a36Sopenharmony_ci u8 *rri_resp; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci *present = 0; 134562306a36Sopenharmony_ci *index = 0; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci rri_req = alloc_smp_req(RRI_REQ_SIZE); 134862306a36Sopenharmony_ci if (!rri_req) 134962306a36Sopenharmony_ci return -ENOMEM; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci rri_resp = alloc_smp_resp(RRI_RESP_SIZE); 135262306a36Sopenharmony_ci if (!rri_resp) { 135362306a36Sopenharmony_ci kfree(rri_req); 135462306a36Sopenharmony_ci return -ENOMEM; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci rri_req[1] = SMP_REPORT_ROUTE_INFO; 135862306a36Sopenharmony_ci rri_req[9] = phy_id; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci for (i = 0; i < ex->max_route_indexes ; i++) { 136162306a36Sopenharmony_ci *(__be16 *)(rri_req+6) = cpu_to_be16(i); 136262306a36Sopenharmony_ci res = smp_execute_task(dev, rri_req, RRI_REQ_SIZE, rri_resp, 136362306a36Sopenharmony_ci RRI_RESP_SIZE); 136462306a36Sopenharmony_ci if (res) 136562306a36Sopenharmony_ci goto out; 136662306a36Sopenharmony_ci res = rri_resp[2]; 136762306a36Sopenharmony_ci if (res == SMP_RESP_NO_INDEX) { 136862306a36Sopenharmony_ci pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", 136962306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, i); 137062306a36Sopenharmony_ci goto out; 137162306a36Sopenharmony_ci } else if (res != SMP_RESP_FUNC_ACC) { 137262306a36Sopenharmony_ci pr_notice("%s: dev %016llx phy%02d index 0x%x result 0x%x\n", 137362306a36Sopenharmony_ci __func__, SAS_ADDR(dev->sas_addr), phy_id, 137462306a36Sopenharmony_ci i, res); 137562306a36Sopenharmony_ci goto out; 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci if (SAS_ADDR(sas_addr) != 0) { 137862306a36Sopenharmony_ci if (SAS_ADDR(rri_resp+16) == SAS_ADDR(sas_addr)) { 137962306a36Sopenharmony_ci *index = i; 138062306a36Sopenharmony_ci if ((rri_resp[12] & 0x80) == 0x80) 138162306a36Sopenharmony_ci *present = 0; 138262306a36Sopenharmony_ci else 138362306a36Sopenharmony_ci *present = 1; 138462306a36Sopenharmony_ci goto out; 138562306a36Sopenharmony_ci } else if (SAS_ADDR(rri_resp+16) == 0) { 138662306a36Sopenharmony_ci *index = i; 138762306a36Sopenharmony_ci *present = 0; 138862306a36Sopenharmony_ci goto out; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci } else if (SAS_ADDR(rri_resp+16) == 0 && 139162306a36Sopenharmony_ci phy->last_da_index < i) { 139262306a36Sopenharmony_ci phy->last_da_index = i; 139362306a36Sopenharmony_ci *index = i; 139462306a36Sopenharmony_ci *present = 0; 139562306a36Sopenharmony_ci goto out; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci res = -1; 139962306a36Sopenharmony_ciout: 140062306a36Sopenharmony_ci kfree(rri_req); 140162306a36Sopenharmony_ci kfree(rri_resp); 140262306a36Sopenharmony_ci return res; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci#define CRI_REQ_SIZE 44 140662306a36Sopenharmony_ci#define CRI_RESP_SIZE 8 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_cistatic int sas_configure_set(struct domain_device *dev, int phy_id, 140962306a36Sopenharmony_ci u8 *sas_addr, int index, int include) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci int res; 141262306a36Sopenharmony_ci u8 *cri_req; 141362306a36Sopenharmony_ci u8 *cri_resp; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci cri_req = alloc_smp_req(CRI_REQ_SIZE); 141662306a36Sopenharmony_ci if (!cri_req) 141762306a36Sopenharmony_ci return -ENOMEM; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci cri_resp = alloc_smp_resp(CRI_RESP_SIZE); 142062306a36Sopenharmony_ci if (!cri_resp) { 142162306a36Sopenharmony_ci kfree(cri_req); 142262306a36Sopenharmony_ci return -ENOMEM; 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci cri_req[1] = SMP_CONF_ROUTE_INFO; 142662306a36Sopenharmony_ci *(__be16 *)(cri_req+6) = cpu_to_be16(index); 142762306a36Sopenharmony_ci cri_req[9] = phy_id; 142862306a36Sopenharmony_ci if (SAS_ADDR(sas_addr) == 0 || !include) 142962306a36Sopenharmony_ci cri_req[12] |= 0x80; 143062306a36Sopenharmony_ci memcpy(cri_req+16, sas_addr, SAS_ADDR_SIZE); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci res = smp_execute_task(dev, cri_req, CRI_REQ_SIZE, cri_resp, 143362306a36Sopenharmony_ci CRI_RESP_SIZE); 143462306a36Sopenharmony_ci if (res) 143562306a36Sopenharmony_ci goto out; 143662306a36Sopenharmony_ci res = cri_resp[2]; 143762306a36Sopenharmony_ci if (res == SMP_RESP_NO_INDEX) { 143862306a36Sopenharmony_ci pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", 143962306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, index); 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ciout: 144262306a36Sopenharmony_ci kfree(cri_req); 144362306a36Sopenharmony_ci kfree(cri_resp); 144462306a36Sopenharmony_ci return res; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int sas_configure_phy(struct domain_device *dev, int phy_id, 144862306a36Sopenharmony_ci u8 *sas_addr, int include) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci int index; 145162306a36Sopenharmony_ci int present; 145262306a36Sopenharmony_ci int res; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci res = sas_configure_present(dev, phy_id, sas_addr, &index, &present); 145562306a36Sopenharmony_ci if (res) 145662306a36Sopenharmony_ci return res; 145762306a36Sopenharmony_ci if (include ^ present) 145862306a36Sopenharmony_ci return sas_configure_set(dev, phy_id, sas_addr, index, 145962306a36Sopenharmony_ci include); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci return res; 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci/** 146562306a36Sopenharmony_ci * sas_configure_parent - configure routing table of parent 146662306a36Sopenharmony_ci * @parent: parent expander 146762306a36Sopenharmony_ci * @child: child expander 146862306a36Sopenharmony_ci * @sas_addr: SAS port identifier of device directly attached to child 146962306a36Sopenharmony_ci * @include: whether or not to include @child in the expander routing table 147062306a36Sopenharmony_ci */ 147162306a36Sopenharmony_cistatic int sas_configure_parent(struct domain_device *parent, 147262306a36Sopenharmony_ci struct domain_device *child, 147362306a36Sopenharmony_ci u8 *sas_addr, int include) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct expander_device *ex_parent = &parent->ex_dev; 147662306a36Sopenharmony_ci int res = 0; 147762306a36Sopenharmony_ci int i; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (parent->parent) { 148062306a36Sopenharmony_ci res = sas_configure_parent(parent->parent, parent, sas_addr, 148162306a36Sopenharmony_ci include); 148262306a36Sopenharmony_ci if (res) 148362306a36Sopenharmony_ci return res; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (ex_parent->conf_route_table == 0) { 148762306a36Sopenharmony_ci pr_debug("ex %016llx has self-configuring routing table\n", 148862306a36Sopenharmony_ci SAS_ADDR(parent->sas_addr)); 148962306a36Sopenharmony_ci return 0; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci for (i = 0; i < ex_parent->num_phys; i++) { 149362306a36Sopenharmony_ci struct ex_phy *phy = &ex_parent->ex_phy[i]; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if ((phy->routing_attr == TABLE_ROUTING) && 149662306a36Sopenharmony_ci sas_phy_match_dev_addr(child, phy)) { 149762306a36Sopenharmony_ci res = sas_configure_phy(parent, i, sas_addr, include); 149862306a36Sopenharmony_ci if (res) 149962306a36Sopenharmony_ci return res; 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci return res; 150462306a36Sopenharmony_ci} 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci/** 150762306a36Sopenharmony_ci * sas_configure_routing - configure routing 150862306a36Sopenharmony_ci * @dev: expander device 150962306a36Sopenharmony_ci * @sas_addr: port identifier of device directly attached to the expander device 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_cistatic int sas_configure_routing(struct domain_device *dev, u8 *sas_addr) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci if (dev->parent) 151462306a36Sopenharmony_ci return sas_configure_parent(dev->parent, dev, sas_addr, 1); 151562306a36Sopenharmony_ci return 0; 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) 151962306a36Sopenharmony_ci{ 152062306a36Sopenharmony_ci if (dev->parent) 152162306a36Sopenharmony_ci return sas_configure_parent(dev->parent, dev, sas_addr, 0); 152262306a36Sopenharmony_ci return 0; 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci/** 152662306a36Sopenharmony_ci * sas_discover_expander - expander discovery 152762306a36Sopenharmony_ci * @dev: pointer to expander domain device 152862306a36Sopenharmony_ci * 152962306a36Sopenharmony_ci * See comment in sas_discover_sata(). 153062306a36Sopenharmony_ci */ 153162306a36Sopenharmony_cistatic int sas_discover_expander(struct domain_device *dev) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci int res; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci res = sas_notify_lldd_dev_found(dev); 153662306a36Sopenharmony_ci if (res) 153762306a36Sopenharmony_ci return res; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci res = sas_ex_general(dev); 154062306a36Sopenharmony_ci if (res) 154162306a36Sopenharmony_ci goto out_err; 154262306a36Sopenharmony_ci res = sas_ex_manuf_info(dev); 154362306a36Sopenharmony_ci if (res) 154462306a36Sopenharmony_ci goto out_err; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci res = sas_expander_discover(dev); 154762306a36Sopenharmony_ci if (res) { 154862306a36Sopenharmony_ci pr_warn("expander %016llx discovery failed(0x%x)\n", 154962306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), res); 155062306a36Sopenharmony_ci goto out_err; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci sas_check_ex_subtractive_boundary(dev); 155462306a36Sopenharmony_ci res = sas_check_parent_topology(dev); 155562306a36Sopenharmony_ci if (res) 155662306a36Sopenharmony_ci goto out_err; 155762306a36Sopenharmony_ci return 0; 155862306a36Sopenharmony_ciout_err: 155962306a36Sopenharmony_ci sas_notify_lldd_dev_gone(dev); 156062306a36Sopenharmony_ci return res; 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic int sas_ex_level_discovery(struct asd_sas_port *port, const int level) 156462306a36Sopenharmony_ci{ 156562306a36Sopenharmony_ci int res = 0; 156662306a36Sopenharmony_ci struct domain_device *dev; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci list_for_each_entry(dev, &port->dev_list, dev_list_node) { 156962306a36Sopenharmony_ci if (dev_is_expander(dev->dev_type)) { 157062306a36Sopenharmony_ci struct sas_expander_device *ex = 157162306a36Sopenharmony_ci rphy_to_expander_device(dev->rphy); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (level == ex->level) 157462306a36Sopenharmony_ci res = sas_ex_discover_devices(dev, -1); 157562306a36Sopenharmony_ci else if (level > 0) 157662306a36Sopenharmony_ci res = sas_ex_discover_devices(port->port_dev, -1); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci return res; 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic int sas_ex_bfs_disc(struct asd_sas_port *port) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci int res; 158762306a36Sopenharmony_ci int level; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci do { 159062306a36Sopenharmony_ci level = port->disc.max_level; 159162306a36Sopenharmony_ci res = sas_ex_level_discovery(port, level); 159262306a36Sopenharmony_ci mb(); 159362306a36Sopenharmony_ci } while (level < port->disc.max_level); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return res; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ciint sas_discover_root_expander(struct domain_device *dev) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci int res; 160162306a36Sopenharmony_ci struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci res = sas_rphy_add(dev->rphy); 160462306a36Sopenharmony_ci if (res) 160562306a36Sopenharmony_ci goto out_err; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci ex->level = dev->port->disc.max_level; /* 0 */ 160862306a36Sopenharmony_ci res = sas_discover_expander(dev); 160962306a36Sopenharmony_ci if (res) 161062306a36Sopenharmony_ci goto out_err2; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci sas_ex_bfs_disc(dev->port); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci return res; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ciout_err2: 161762306a36Sopenharmony_ci sas_rphy_remove(dev->rphy); 161862306a36Sopenharmony_ciout_err: 161962306a36Sopenharmony_ci return res; 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci/* ---------- Domain revalidation ---------- */ 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic int sas_get_phy_discover(struct domain_device *dev, 162562306a36Sopenharmony_ci int phy_id, struct smp_disc_resp *disc_resp) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci int res; 162862306a36Sopenharmony_ci u8 *disc_req; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); 163162306a36Sopenharmony_ci if (!disc_req) 163262306a36Sopenharmony_ci return -ENOMEM; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci disc_req[1] = SMP_DISCOVER; 163562306a36Sopenharmony_ci disc_req[9] = phy_id; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, 163862306a36Sopenharmony_ci disc_resp, DISCOVER_RESP_SIZE); 163962306a36Sopenharmony_ci if (res) 164062306a36Sopenharmony_ci goto out; 164162306a36Sopenharmony_ci if (disc_resp->result != SMP_RESP_FUNC_ACC) 164262306a36Sopenharmony_ci res = disc_resp->result; 164362306a36Sopenharmony_ciout: 164462306a36Sopenharmony_ci kfree(disc_req); 164562306a36Sopenharmony_ci return res; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic int sas_get_phy_change_count(struct domain_device *dev, 164962306a36Sopenharmony_ci int phy_id, int *pcc) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci int res; 165262306a36Sopenharmony_ci struct smp_disc_resp *disc_resp; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); 165562306a36Sopenharmony_ci if (!disc_resp) 165662306a36Sopenharmony_ci return -ENOMEM; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci res = sas_get_phy_discover(dev, phy_id, disc_resp); 165962306a36Sopenharmony_ci if (!res) 166062306a36Sopenharmony_ci *pcc = disc_resp->disc.change_count; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci kfree(disc_resp); 166362306a36Sopenharmony_ci return res; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ciint sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, 166762306a36Sopenharmony_ci u8 *sas_addr, enum sas_device_type *type) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci int res; 167062306a36Sopenharmony_ci struct smp_disc_resp *disc_resp; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); 167362306a36Sopenharmony_ci if (!disc_resp) 167462306a36Sopenharmony_ci return -ENOMEM; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci res = sas_get_phy_discover(dev, phy_id, disc_resp); 167762306a36Sopenharmony_ci if (res == 0) { 167862306a36Sopenharmony_ci memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 167962306a36Sopenharmony_ci SAS_ADDR_SIZE); 168062306a36Sopenharmony_ci *type = to_dev_type(&disc_resp->disc); 168162306a36Sopenharmony_ci if (*type == 0) 168262306a36Sopenharmony_ci memset(sas_addr, 0, SAS_ADDR_SIZE); 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci kfree(disc_resp); 168562306a36Sopenharmony_ci return res; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, 168962306a36Sopenharmony_ci int from_phy, bool update) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 169262306a36Sopenharmony_ci int res = 0; 169362306a36Sopenharmony_ci int i; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci for (i = from_phy; i < ex->num_phys; i++) { 169662306a36Sopenharmony_ci int phy_change_count = 0; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci res = sas_get_phy_change_count(dev, i, &phy_change_count); 169962306a36Sopenharmony_ci switch (res) { 170062306a36Sopenharmony_ci case SMP_RESP_PHY_VACANT: 170162306a36Sopenharmony_ci case SMP_RESP_NO_PHY: 170262306a36Sopenharmony_ci continue; 170362306a36Sopenharmony_ci case SMP_RESP_FUNC_ACC: 170462306a36Sopenharmony_ci break; 170562306a36Sopenharmony_ci default: 170662306a36Sopenharmony_ci return res; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (phy_change_count != ex->ex_phy[i].phy_change_count) { 171062306a36Sopenharmony_ci if (update) 171162306a36Sopenharmony_ci ex->ex_phy[i].phy_change_count = 171262306a36Sopenharmony_ci phy_change_count; 171362306a36Sopenharmony_ci *phy_id = i; 171462306a36Sopenharmony_ci return 0; 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int sas_get_ex_change_count(struct domain_device *dev, int *ecc) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci int res; 172362306a36Sopenharmony_ci u8 *rg_req; 172462306a36Sopenharmony_ci struct smp_rg_resp *rg_resp; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci rg_req = alloc_smp_req(RG_REQ_SIZE); 172762306a36Sopenharmony_ci if (!rg_req) 172862306a36Sopenharmony_ci return -ENOMEM; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci rg_resp = alloc_smp_resp(RG_RESP_SIZE); 173162306a36Sopenharmony_ci if (!rg_resp) { 173262306a36Sopenharmony_ci kfree(rg_req); 173362306a36Sopenharmony_ci return -ENOMEM; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci rg_req[1] = SMP_REPORT_GENERAL; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, 173962306a36Sopenharmony_ci RG_RESP_SIZE); 174062306a36Sopenharmony_ci if (res) 174162306a36Sopenharmony_ci goto out; 174262306a36Sopenharmony_ci if (rg_resp->result != SMP_RESP_FUNC_ACC) { 174362306a36Sopenharmony_ci res = rg_resp->result; 174462306a36Sopenharmony_ci goto out; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci *ecc = be16_to_cpu(rg_resp->rg.change_count); 174862306a36Sopenharmony_ciout: 174962306a36Sopenharmony_ci kfree(rg_resp); 175062306a36Sopenharmony_ci kfree(rg_req); 175162306a36Sopenharmony_ci return res; 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci/** 175462306a36Sopenharmony_ci * sas_find_bcast_dev - find the device issue BROADCAST(CHANGE). 175562306a36Sopenharmony_ci * @dev:domain device to be detect. 175662306a36Sopenharmony_ci * @src_dev: the device which originated BROADCAST(CHANGE). 175762306a36Sopenharmony_ci * 175862306a36Sopenharmony_ci * Add self-configuration expander support. Suppose two expander cascading, 175962306a36Sopenharmony_ci * when the first level expander is self-configuring, hotplug the disks in 176062306a36Sopenharmony_ci * second level expander, BROADCAST(CHANGE) will not only be originated 176162306a36Sopenharmony_ci * in the second level expander, but also be originated in the first level 176262306a36Sopenharmony_ci * expander (see SAS protocol SAS 2r-14, 7.11 for detail), it is to say, 176362306a36Sopenharmony_ci * expander changed count in two level expanders will all increment at least 176462306a36Sopenharmony_ci * once, but the phy which chang count has changed is the source device which 176562306a36Sopenharmony_ci * we concerned. 176662306a36Sopenharmony_ci */ 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic int sas_find_bcast_dev(struct domain_device *dev, 176962306a36Sopenharmony_ci struct domain_device **src_dev) 177062306a36Sopenharmony_ci{ 177162306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 177262306a36Sopenharmony_ci int ex_change_count = -1; 177362306a36Sopenharmony_ci int phy_id = -1; 177462306a36Sopenharmony_ci int res; 177562306a36Sopenharmony_ci struct domain_device *ch; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci res = sas_get_ex_change_count(dev, &ex_change_count); 177862306a36Sopenharmony_ci if (res) 177962306a36Sopenharmony_ci goto out; 178062306a36Sopenharmony_ci if (ex_change_count != -1 && ex_change_count != ex->ex_change_count) { 178162306a36Sopenharmony_ci /* Just detect if this expander phys phy change count changed, 178262306a36Sopenharmony_ci * in order to determine if this expander originate BROADCAST, 178362306a36Sopenharmony_ci * and do not update phy change count field in our structure. 178462306a36Sopenharmony_ci */ 178562306a36Sopenharmony_ci res = sas_find_bcast_phy(dev, &phy_id, 0, false); 178662306a36Sopenharmony_ci if (phy_id != -1) { 178762306a36Sopenharmony_ci *src_dev = dev; 178862306a36Sopenharmony_ci ex->ex_change_count = ex_change_count; 178962306a36Sopenharmony_ci pr_info("ex %016llx phy%02d change count has changed\n", 179062306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id); 179162306a36Sopenharmony_ci return res; 179262306a36Sopenharmony_ci } else 179362306a36Sopenharmony_ci pr_info("ex %016llx phys DID NOT change\n", 179462306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr)); 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci list_for_each_entry(ch, &ex->children, siblings) { 179762306a36Sopenharmony_ci if (dev_is_expander(ch->dev_type)) { 179862306a36Sopenharmony_ci res = sas_find_bcast_dev(ch, src_dev); 179962306a36Sopenharmony_ci if (*src_dev) 180062306a36Sopenharmony_ci return res; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci } 180362306a36Sopenharmony_ciout: 180462306a36Sopenharmony_ci return res; 180562306a36Sopenharmony_ci} 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_cistatic void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev) 180862306a36Sopenharmony_ci{ 180962306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 181062306a36Sopenharmony_ci struct domain_device *child, *n; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci list_for_each_entry_safe(child, n, &ex->children, siblings) { 181362306a36Sopenharmony_ci set_bit(SAS_DEV_GONE, &child->state); 181462306a36Sopenharmony_ci if (dev_is_expander(child->dev_type)) 181562306a36Sopenharmony_ci sas_unregister_ex_tree(port, child); 181662306a36Sopenharmony_ci else 181762306a36Sopenharmony_ci sas_unregister_dev(port, child); 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci sas_unregister_dev(port, dev); 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic void sas_unregister_devs_sas_addr(struct domain_device *parent, 182362306a36Sopenharmony_ci int phy_id, bool last) 182462306a36Sopenharmony_ci{ 182562306a36Sopenharmony_ci struct expander_device *ex_dev = &parent->ex_dev; 182662306a36Sopenharmony_ci struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; 182762306a36Sopenharmony_ci struct domain_device *child, *n, *found = NULL; 182862306a36Sopenharmony_ci if (last) { 182962306a36Sopenharmony_ci list_for_each_entry_safe(child, n, 183062306a36Sopenharmony_ci &ex_dev->children, siblings) { 183162306a36Sopenharmony_ci if (sas_phy_match_dev_addr(child, phy)) { 183262306a36Sopenharmony_ci set_bit(SAS_DEV_GONE, &child->state); 183362306a36Sopenharmony_ci if (dev_is_expander(child->dev_type)) 183462306a36Sopenharmony_ci sas_unregister_ex_tree(parent->port, child); 183562306a36Sopenharmony_ci else 183662306a36Sopenharmony_ci sas_unregister_dev(parent->port, child); 183762306a36Sopenharmony_ci found = child; 183862306a36Sopenharmony_ci break; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci sas_disable_routing(parent, phy->attached_sas_addr); 184262306a36Sopenharmony_ci } 184362306a36Sopenharmony_ci memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); 184462306a36Sopenharmony_ci if (phy->port) { 184562306a36Sopenharmony_ci sas_port_delete_phy(phy->port, phy->phy); 184662306a36Sopenharmony_ci sas_device_set_phy(found, phy->port); 184762306a36Sopenharmony_ci if (phy->port->num_phys == 0) 184862306a36Sopenharmony_ci list_add_tail(&phy->port->del_list, 184962306a36Sopenharmony_ci &parent->port->sas_port_del_list); 185062306a36Sopenharmony_ci phy->port = NULL; 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci} 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_cistatic int sas_discover_bfs_by_root_level(struct domain_device *root, 185562306a36Sopenharmony_ci const int level) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci struct expander_device *ex_root = &root->ex_dev; 185862306a36Sopenharmony_ci struct domain_device *child; 185962306a36Sopenharmony_ci int res = 0; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci list_for_each_entry(child, &ex_root->children, siblings) { 186262306a36Sopenharmony_ci if (dev_is_expander(child->dev_type)) { 186362306a36Sopenharmony_ci struct sas_expander_device *ex = 186462306a36Sopenharmony_ci rphy_to_expander_device(child->rphy); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci if (level > ex->level) 186762306a36Sopenharmony_ci res = sas_discover_bfs_by_root_level(child, 186862306a36Sopenharmony_ci level); 186962306a36Sopenharmony_ci else if (level == ex->level) 187062306a36Sopenharmony_ci res = sas_ex_discover_devices(child, -1); 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci return res; 187462306a36Sopenharmony_ci} 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_cistatic int sas_discover_bfs_by_root(struct domain_device *dev) 187762306a36Sopenharmony_ci{ 187862306a36Sopenharmony_ci int res; 187962306a36Sopenharmony_ci struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); 188062306a36Sopenharmony_ci int level = ex->level+1; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci res = sas_ex_discover_devices(dev, -1); 188362306a36Sopenharmony_ci if (res) 188462306a36Sopenharmony_ci goto out; 188562306a36Sopenharmony_ci do { 188662306a36Sopenharmony_ci res = sas_discover_bfs_by_root_level(dev, level); 188762306a36Sopenharmony_ci mb(); 188862306a36Sopenharmony_ci level += 1; 188962306a36Sopenharmony_ci } while (level <= dev->port->disc.max_level); 189062306a36Sopenharmony_ciout: 189162306a36Sopenharmony_ci return res; 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cistatic int sas_discover_new(struct domain_device *dev, int phy_id) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id]; 189762306a36Sopenharmony_ci struct domain_device *child; 189862306a36Sopenharmony_ci int res; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci pr_debug("ex %016llx phy%02d new device attached\n", 190162306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id); 190262306a36Sopenharmony_ci res = sas_ex_phy_discover(dev, phy_id); 190362306a36Sopenharmony_ci if (res) 190462306a36Sopenharmony_ci return res; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (sas_ex_join_wide_port(dev, phy_id)) 190762306a36Sopenharmony_ci return 0; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci res = sas_ex_discover_devices(dev, phy_id); 191062306a36Sopenharmony_ci if (res) 191162306a36Sopenharmony_ci return res; 191262306a36Sopenharmony_ci list_for_each_entry(child, &dev->ex_dev.children, siblings) { 191362306a36Sopenharmony_ci if (sas_phy_match_dev_addr(child, ex_phy)) { 191462306a36Sopenharmony_ci if (dev_is_expander(child->dev_type)) 191562306a36Sopenharmony_ci res = sas_discover_bfs_by_root(child); 191662306a36Sopenharmony_ci break; 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci return res; 192062306a36Sopenharmony_ci} 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci if (old == new) 192562306a36Sopenharmony_ci return true; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci /* treat device directed resets as flutter, if we went 192862306a36Sopenharmony_ci * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery 192962306a36Sopenharmony_ci */ 193062306a36Sopenharmony_ci if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) || 193162306a36Sopenharmony_ci (old == SAS_END_DEVICE && new == SAS_SATA_PENDING)) 193262306a36Sopenharmony_ci return true; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return false; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic int sas_rediscover_dev(struct domain_device *dev, int phy_id, 193862306a36Sopenharmony_ci bool last, int sibling) 193962306a36Sopenharmony_ci{ 194062306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 194162306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[phy_id]; 194262306a36Sopenharmony_ci enum sas_device_type type = SAS_PHY_UNUSED; 194362306a36Sopenharmony_ci u8 sas_addr[SAS_ADDR_SIZE]; 194462306a36Sopenharmony_ci char msg[80] = ""; 194562306a36Sopenharmony_ci int res; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (!last) 194862306a36Sopenharmony_ci sprintf(msg, ", part of a wide port with phy%02d", sibling); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci pr_debug("ex %016llx rediscovering phy%02d%s\n", 195162306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, msg); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci memset(sas_addr, 0, SAS_ADDR_SIZE); 195462306a36Sopenharmony_ci res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); 195562306a36Sopenharmony_ci switch (res) { 195662306a36Sopenharmony_ci case SMP_RESP_NO_PHY: 195762306a36Sopenharmony_ci phy->phy_state = PHY_NOT_PRESENT; 195862306a36Sopenharmony_ci sas_unregister_devs_sas_addr(dev, phy_id, last); 195962306a36Sopenharmony_ci return res; 196062306a36Sopenharmony_ci case SMP_RESP_PHY_VACANT: 196162306a36Sopenharmony_ci phy->phy_state = PHY_VACANT; 196262306a36Sopenharmony_ci sas_unregister_devs_sas_addr(dev, phy_id, last); 196362306a36Sopenharmony_ci return res; 196462306a36Sopenharmony_ci case SMP_RESP_FUNC_ACC: 196562306a36Sopenharmony_ci break; 196662306a36Sopenharmony_ci case -ECOMM: 196762306a36Sopenharmony_ci break; 196862306a36Sopenharmony_ci default: 196962306a36Sopenharmony_ci return res; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { 197362306a36Sopenharmony_ci phy->phy_state = PHY_EMPTY; 197462306a36Sopenharmony_ci sas_unregister_devs_sas_addr(dev, phy_id, last); 197562306a36Sopenharmony_ci /* 197662306a36Sopenharmony_ci * Even though the PHY is empty, for convenience we discover 197762306a36Sopenharmony_ci * the PHY to update the PHY info, like negotiated linkrate. 197862306a36Sopenharmony_ci */ 197962306a36Sopenharmony_ci sas_ex_phy_discover(dev, phy_id); 198062306a36Sopenharmony_ci return res; 198162306a36Sopenharmony_ci } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && 198262306a36Sopenharmony_ci dev_type_flutter(type, phy->attached_dev_type)) { 198362306a36Sopenharmony_ci struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); 198462306a36Sopenharmony_ci char *action = ""; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci sas_ex_phy_discover(dev, phy_id); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) 198962306a36Sopenharmony_ci action = ", needs recovery"; 199062306a36Sopenharmony_ci pr_debug("ex %016llx phy%02d broadcast flutter%s\n", 199162306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, action); 199262306a36Sopenharmony_ci return res; 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* we always have to delete the old device when we went here */ 199662306a36Sopenharmony_ci pr_info("ex %016llx phy%02d replace %016llx\n", 199762306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id, 199862306a36Sopenharmony_ci SAS_ADDR(phy->attached_sas_addr)); 199962306a36Sopenharmony_ci sas_unregister_devs_sas_addr(dev, phy_id, last); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci return sas_discover_new(dev, phy_id); 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci/** 200562306a36Sopenharmony_ci * sas_rediscover - revalidate the domain. 200662306a36Sopenharmony_ci * @dev:domain device to be detect. 200762306a36Sopenharmony_ci * @phy_id: the phy id will be detected. 200862306a36Sopenharmony_ci * 200962306a36Sopenharmony_ci * NOTE: this process _must_ quit (return) as soon as any connection 201062306a36Sopenharmony_ci * errors are encountered. Connection recovery is done elsewhere. 201162306a36Sopenharmony_ci * Discover process only interrogates devices in order to discover the 201262306a36Sopenharmony_ci * domain.For plugging out, we un-register the device only when it is 201362306a36Sopenharmony_ci * the last phy in the port, for other phys in this port, we just delete it 201462306a36Sopenharmony_ci * from the port.For inserting, we do discovery when it is the 201562306a36Sopenharmony_ci * first phy,for other phys in this port, we add it to the port to 201662306a36Sopenharmony_ci * forming the wide-port. 201762306a36Sopenharmony_ci */ 201862306a36Sopenharmony_cistatic int sas_rediscover(struct domain_device *dev, const int phy_id) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 202162306a36Sopenharmony_ci struct ex_phy *changed_phy = &ex->ex_phy[phy_id]; 202262306a36Sopenharmony_ci int res = 0; 202362306a36Sopenharmony_ci int i; 202462306a36Sopenharmony_ci bool last = true; /* is this the last phy of the port */ 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci pr_debug("ex %016llx phy%02d originated BROADCAST(CHANGE)\n", 202762306a36Sopenharmony_ci SAS_ADDR(dev->sas_addr), phy_id); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) { 203062306a36Sopenharmony_ci for (i = 0; i < ex->num_phys; i++) { 203162306a36Sopenharmony_ci struct ex_phy *phy = &ex->ex_phy[i]; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci if (i == phy_id) 203462306a36Sopenharmony_ci continue; 203562306a36Sopenharmony_ci if (sas_phy_addr_match(phy, changed_phy)) { 203662306a36Sopenharmony_ci last = false; 203762306a36Sopenharmony_ci break; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci res = sas_rediscover_dev(dev, phy_id, last, i); 204162306a36Sopenharmony_ci } else 204262306a36Sopenharmony_ci res = sas_discover_new(dev, phy_id); 204362306a36Sopenharmony_ci return res; 204462306a36Sopenharmony_ci} 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci/** 204762306a36Sopenharmony_ci * sas_ex_revalidate_domain - revalidate the domain 204862306a36Sopenharmony_ci * @port_dev: port domain device. 204962306a36Sopenharmony_ci * 205062306a36Sopenharmony_ci * NOTE: this process _must_ quit (return) as soon as any connection 205162306a36Sopenharmony_ci * errors are encountered. Connection recovery is done elsewhere. 205262306a36Sopenharmony_ci * Discover process only interrogates devices in order to discover the 205362306a36Sopenharmony_ci * domain. 205462306a36Sopenharmony_ci */ 205562306a36Sopenharmony_ciint sas_ex_revalidate_domain(struct domain_device *port_dev) 205662306a36Sopenharmony_ci{ 205762306a36Sopenharmony_ci int res; 205862306a36Sopenharmony_ci struct domain_device *dev = NULL; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci res = sas_find_bcast_dev(port_dev, &dev); 206162306a36Sopenharmony_ci if (res == 0 && dev) { 206262306a36Sopenharmony_ci struct expander_device *ex = &dev->ex_dev; 206362306a36Sopenharmony_ci int i = 0, phy_id; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci do { 206662306a36Sopenharmony_ci phy_id = -1; 206762306a36Sopenharmony_ci res = sas_find_bcast_phy(dev, &phy_id, i, true); 206862306a36Sopenharmony_ci if (phy_id == -1) 206962306a36Sopenharmony_ci break; 207062306a36Sopenharmony_ci res = sas_rediscover(dev, phy_id); 207162306a36Sopenharmony_ci i = phy_id + 1; 207262306a36Sopenharmony_ci } while (i < ex->num_phys); 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci return res; 207562306a36Sopenharmony_ci} 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ciint sas_find_attached_phy_id(struct expander_device *ex_dev, 207862306a36Sopenharmony_ci struct domain_device *dev) 207962306a36Sopenharmony_ci{ 208062306a36Sopenharmony_ci struct ex_phy *phy; 208162306a36Sopenharmony_ci int phy_id; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci for (phy_id = 0; phy_id < ex_dev->num_phys; phy_id++) { 208462306a36Sopenharmony_ci phy = &ex_dev->ex_phy[phy_id]; 208562306a36Sopenharmony_ci if (sas_phy_match_dev_addr(dev, phy)) 208662306a36Sopenharmony_ci return phy_id; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci return -ENODEV; 209062306a36Sopenharmony_ci} 209162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sas_find_attached_phy_id); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_civoid sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, 209462306a36Sopenharmony_ci struct sas_rphy *rphy) 209562306a36Sopenharmony_ci{ 209662306a36Sopenharmony_ci struct domain_device *dev; 209762306a36Sopenharmony_ci unsigned int rcvlen = 0; 209862306a36Sopenharmony_ci int ret = -EINVAL; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* no rphy means no smp target support (ie aic94xx host) */ 210162306a36Sopenharmony_ci if (!rphy) 210262306a36Sopenharmony_ci return sas_smp_host_handler(job, shost); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci switch (rphy->identify.device_type) { 210562306a36Sopenharmony_ci case SAS_EDGE_EXPANDER_DEVICE: 210662306a36Sopenharmony_ci case SAS_FANOUT_EXPANDER_DEVICE: 210762306a36Sopenharmony_ci break; 210862306a36Sopenharmony_ci default: 210962306a36Sopenharmony_ci pr_err("%s: can we send a smp request to a device?\n", 211062306a36Sopenharmony_ci __func__); 211162306a36Sopenharmony_ci goto out; 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci dev = sas_find_dev_by_rphy(rphy); 211562306a36Sopenharmony_ci if (!dev) { 211662306a36Sopenharmony_ci pr_err("%s: fail to find a domain_device?\n", __func__); 211762306a36Sopenharmony_ci goto out; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci /* do we need to support multiple segments? */ 212162306a36Sopenharmony_ci if (job->request_payload.sg_cnt > 1 || 212262306a36Sopenharmony_ci job->reply_payload.sg_cnt > 1) { 212362306a36Sopenharmony_ci pr_info("%s: multiple segments req %u, rsp %u\n", 212462306a36Sopenharmony_ci __func__, job->request_payload.payload_len, 212562306a36Sopenharmony_ci job->reply_payload.payload_len); 212662306a36Sopenharmony_ci goto out; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci ret = smp_execute_task_sg(dev, job->request_payload.sg_list, 213062306a36Sopenharmony_ci job->reply_payload.sg_list); 213162306a36Sopenharmony_ci if (ret >= 0) { 213262306a36Sopenharmony_ci /* bsg_job_done() requires the length received */ 213362306a36Sopenharmony_ci rcvlen = job->reply_payload.payload_len - ret; 213462306a36Sopenharmony_ci ret = 0; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ciout: 213862306a36Sopenharmony_ci bsg_job_done(job, ret, rcvlen); 213962306a36Sopenharmony_ci} 2140