static struct isci_port *sci_port_configuration_agent_find_port(
	struct isci_host *ihost,
	struct isci_phy *iphy)
{
	u8 i;
	struct sci_sas_address port_sas_address;
	struct sci_sas_address port_attached_device_address;
	struct sci_sas_address phy_sas_address;
	struct sci_sas_address phy_attached_device_address;

	sci_phy_get_sas_address(iphy, &phy_sas_address);
	sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);

	for (i = 0; i < ihost->logical_port_entries; i++) {
		struct isci_port *iport = &ihost->ports[i];

		sci_port_get_sas_address(iport, &port_sas_address);
		sci_port_get_attached_sas_address(iport, &port_attached_device_address);

		if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
		    sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
			return iport;
	}

	return NULL;
}
/**
 *
 * @controller: The controller object used for the port search.
 * @phy: The phy object to match.
 *
 * This routine will find a matching port for the phy.  This means that the
 * port and phy both have the same broadcast sas address and same received sas
 * address. The port address or the NULL if there is no matching
 * port. port address if the port can be found to match the phy.
 * NULL if there is no matching port for the phy.
 */
static struct isci_port *sci_port_configuration_agent_find_port(
	struct isci_host *ihost,
	struct isci_phy *iphy)
{
	u8 i;
	struct sci_sas_address port_sas_address;
	struct sci_sas_address port_attached_device_address;
	struct sci_sas_address phy_sas_address;
	struct sci_sas_address phy_attached_device_address;

	/*
	 * Since this phy can be a member of a wide port check to see if one or
	 * more phys match the sent and received SAS address as this phy in which
	 * case it should participate in the same port.
	 */
	sci_phy_get_sas_address(iphy, &phy_sas_address);
	sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);

	for (i = 0; i < ihost->logical_port_entries; i++) {
		struct isci_port *iport = &ihost->ports[i];

		sci_port_get_sas_address(iport, &port_sas_address);
		sci_port_get_attached_sas_address(iport, &port_attached_device_address);

		if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
		    sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
			return iport;
	}

	return NULL;
}
static enum sci_status
sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
					      struct sci_port_configuration_agent *port_agent)
{
	u8 phy_index;
	u8 port_index;
	struct sci_sas_address sas_address;
	struct sci_sas_address phy_assigned_address;

	phy_index = 0;

	while (phy_index < SCI_MAX_PHYS) {
		port_index = phy_index;

		
		sci_phy_get_sas_address(&ihost->phys[phy_index],
					    &sas_address);

		while (++phy_index < SCI_MAX_PHYS) {
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &phy_assigned_address);

			
			if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
				port_agent->phy_valid_port_range[phy_index].min_index = port_index;
				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
			} else {
				port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
				break;
			}
		}
	}

	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
/* verify phys are assigned a valid SAS address for automatic port
 * configuration mode.
 */
static enum sci_status
sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
					      struct sci_port_configuration_agent *port_agent)
{
	u8 phy_index;
	u8 port_index;
	struct sci_sas_address sas_address;
	struct sci_sas_address phy_assigned_address;

	phy_index = 0;

	while (phy_index < SCI_MAX_PHYS) {
		port_index = phy_index;

		/* Get the assigned SAS Address for the first PHY on the controller. */
		sci_phy_get_sas_address(&ihost->phys[phy_index],
					    &sas_address);

		while (++phy_index < SCI_MAX_PHYS) {
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &phy_assigned_address);

			/* Verify each of the SAS address are all the same for every PHY */
			if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
				port_agent->phy_valid_port_range[phy_index].min_index = port_index;
				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
			} else {
				port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
				port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
				break;
			}
		}
	}

	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
static enum sci_status sci_port_configuration_agent_validate_ports(
	struct isci_host *ihost,
	struct sci_port_configuration_agent *port_agent)
{
	struct sci_sas_address first_address;
	struct sci_sas_address second_address;

	if (port_agent->phy_valid_port_range[0].max_index != 0 ||
	    port_agent->phy_valid_port_range[1].max_index != 1 ||
	    port_agent->phy_valid_port_range[2].max_index != 2 ||
	    port_agent->phy_valid_port_range[3].max_index != 3)
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;

	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
	    port_agent->phy_valid_port_range[1].min_index == 0 &&
	    port_agent->phy_valid_port_range[2].min_index == 0 &&
	    port_agent->phy_valid_port_range[3].min_index == 0)
		return SCI_SUCCESS;

	if (port_agent->phy_valid_port_range[2].min_index == 1) {
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
	}

	sci_phy_get_sas_address(&ihost->phys[0], &first_address);
	sci_phy_get_sas_address(&ihost->phys[3], &second_address);

	if (sci_sas_address_compare(first_address, second_address) == 0) {
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
	}

	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
	    port_agent->phy_valid_port_range[1].min_index == 1) {
		sci_phy_get_sas_address(&ihost->phys[0], &first_address);
		sci_phy_get_sas_address(&ihost->phys[2], &second_address);

		if (sci_sas_address_compare(first_address, second_address) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}
	}

	if (port_agent->phy_valid_port_range[2].min_index == 2 &&
	    port_agent->phy_valid_port_range[3].min_index == 3) {
		sci_phy_get_sas_address(&ihost->phys[1], &first_address);
		sci_phy_get_sas_address(&ihost->phys[3], &second_address);

		if (sci_sas_address_compare(first_address, second_address) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}
	}

	return SCI_SUCCESS;
}
static enum sci_status
sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
					      struct sci_port_configuration_agent *port_agent)
{
	u32 phy_mask;
	u32 assigned_phy_mask;
	struct sci_sas_address sas_address;
	struct sci_sas_address phy_assigned_address;
	u8 port_index;
	u8 phy_index;

	assigned_phy_mask = 0;
	sas_address.high = 0;
	sas_address.low = 0;

	for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
		phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;

		if (!phy_mask)
			continue;
		if ((phy_mask & ~assigned_phy_mask) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}

		
		for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
			if ((phy_mask & (1 << phy_index)) == 0)
				continue;
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &sas_address);

			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;

			if (phy_index != port_index) {
				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
			}

			break;
		}

		while (phy_index < SCI_MAX_PHYS) {
			if ((phy_mask & (1 << phy_index)) == 0)
				continue;
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &phy_assigned_address);

			if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
			}

			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;

			sci_port_add_phy(&ihost->ports[port_index],
					      &ihost->phys[phy_index]);

			assigned_phy_mask |= (1 << phy_index);
		}

		phy_index++;
	}

	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
/* verify all of the phys in the same port are using the same SAS address */
static enum sci_status
sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
					      struct sci_port_configuration_agent *port_agent)
{
	u32 phy_mask;
	u32 assigned_phy_mask;
	struct sci_sas_address sas_address;
	struct sci_sas_address phy_assigned_address;
	u8 port_index;
	u8 phy_index;

	assigned_phy_mask = 0;
	sas_address.high = 0;
	sas_address.low = 0;

	for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
		phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;

		if (!phy_mask)
			continue;
		/*
		 * Make sure that one or more of the phys were not already assinged to
		 * a different port. */
		if ((phy_mask & ~assigned_phy_mask) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}

		/* Find the starting phy index for this round through the loop */
		for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
			if ((phy_mask & (1 << phy_index)) == 0)
				continue;
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &sas_address);

			/*
			 * The phy_index can be used as the starting point for the
			 * port range since the hardware starts all logical ports
			 * the same as the PE index. */
			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;

			if (phy_index != port_index) {
				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
			}

			break;
		}

		/*
		 * See how many additional phys are being added to this logical port.
		 * Note: We have not moved the current phy_index so we will actually
		 *       compare the startting phy with itself.
		 *       This is expected and required to add the phy to the port. */
		while (phy_index < SCI_MAX_PHYS) {
			if ((phy_mask & (1 << phy_index)) == 0)
				continue;
			sci_phy_get_sas_address(&ihost->phys[phy_index],
						     &phy_assigned_address);

			if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
				/*
				 * The phy mask specified that this phy is part of the same port
				 * as the starting phy and it is not so fail this configuration */
				return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
			}

			port_agent->phy_valid_port_range[phy_index].min_index = port_index;
			port_agent->phy_valid_port_range[phy_index].max_index = phy_index;

			sci_port_add_phy(&ihost->ports[port_index],
					      &ihost->phys[phy_index]);

			assigned_phy_mask |= (1 << phy_index);
		}

		phy_index++;
	}

	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
}
/**
 *
 * @controller: This is the controller object that contains the port agent
 * @port_agent: This is the port configruation agent for the controller.
 *
 * This routine will validate the port configuration is correct for the SCU
 * hardware.  The SCU hardware allows for port configurations as follows. LP0
 * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,
 * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for
 * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
 * the port configuration is not valid for this port configuration agent.
 */
static enum sci_status sci_port_configuration_agent_validate_ports(
	struct isci_host *ihost,
	struct sci_port_configuration_agent *port_agent)
{
	struct sci_sas_address first_address;
	struct sci_sas_address second_address;

	/*
	 * Sanity check the max ranges for all the phys the max index
	 * is always equal to the port range index */
	if (port_agent->phy_valid_port_range[0].max_index != 0 ||
	    port_agent->phy_valid_port_range[1].max_index != 1 ||
	    port_agent->phy_valid_port_range[2].max_index != 2 ||
	    port_agent->phy_valid_port_range[3].max_index != 3)
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;

	/*
	 * This is a request to configure a single x4 port or at least attempt
	 * to make all the phys into a single port */
	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
	    port_agent->phy_valid_port_range[1].min_index == 0 &&
	    port_agent->phy_valid_port_range[2].min_index == 0 &&
	    port_agent->phy_valid_port_range[3].min_index == 0)
		return SCI_SUCCESS;

	/*
	 * This is a degenerate case where phy 1 and phy 2 are assigned
	 * to the same port this is explicitly disallowed by the hardware
	 * unless they are part of the same x4 port and this condition was
	 * already checked above. */
	if (port_agent->phy_valid_port_range[2].min_index == 1) {
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
	}

	/*
	 * PE0 and PE3 can never have the same SAS Address unless they
	 * are part of the same x4 wide port and we have already checked
	 * for this condition. */
	sci_phy_get_sas_address(&ihost->phys[0], &first_address);
	sci_phy_get_sas_address(&ihost->phys[3], &second_address);

	if (sci_sas_address_compare(first_address, second_address) == 0) {
		return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
	}

	/*
	 * PE0 and PE1 are configured into a 2x1 ports make sure that the
	 * SAS Address for PE0 and PE2 are different since they can not be
	 * part of the same port. */
	if (port_agent->phy_valid_port_range[0].min_index == 0 &&
	    port_agent->phy_valid_port_range[1].min_index == 1) {
		sci_phy_get_sas_address(&ihost->phys[0], &first_address);
		sci_phy_get_sas_address(&ihost->phys[2], &second_address);

		if (sci_sas_address_compare(first_address, second_address) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}
	}

	/*
	 * PE2 and PE3 are configured into a 2x1 ports make sure that the
	 * SAS Address for PE1 and PE3 are different since they can not be
	 * part of the same port. */
	if (port_agent->phy_valid_port_range[2].min_index == 2 &&
	    port_agent->phy_valid_port_range[3].min_index == 3) {
		sci_phy_get_sas_address(&ihost->phys[1], &first_address);
		sci_phy_get_sas_address(&ihost->phys[3], &second_address);

		if (sci_sas_address_compare(first_address, second_address) == 0) {
			return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
		}
	}

	return SCI_SUCCESS;
}