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