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_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;
}
/**
 * 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.
 *
 * @param[in] controller The controller object used for the port search.
 * @param[in] phy The phy object to match.
 *
 * @return The port address or the SCI_INVALID_HANDLE if there is no matching
 *         port.
 *
 * @retvalue port address if the port can be found to match the phy.
 * @retvalue SCI_INVALID_HANDLE if there is no matching port for the phy.
 */
static
SCIC_SDS_PORT_T * scic_sds_port_configuration_agent_find_port(
   SCIC_SDS_CONTROLLER_T * controller,
   SCIC_SDS_PHY_T        * phy
)
{
   U8 port_index;
   SCI_PORT_HANDLE_T port_handle;
   SCI_SAS_ADDRESS_T port_sas_address;
   SCI_SAS_ADDRESS_T port_attached_device_address;
   SCI_SAS_ADDRESS_T phy_sas_address;
   SCI_SAS_ADDRESS_T phy_attached_device_address;

   SCIC_LOG_TRACE((
      sci_base_object_get_logger(controller),
      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
      "scic_sds_port_confgiruation_agent_find_port(0x%08x, 0x%08x) enter\n",
      controller, phy
   ));

   // 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.
   scic_sds_phy_get_sas_address(phy, &phy_sas_address);
   scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address);

   for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++)
   {
      if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS)
      {
         SCIC_SDS_PORT_T * port = (SCIC_SDS_PORT_T *)port_handle;

         scic_sds_port_get_sas_address(port, &port_sas_address);
         scic_sds_port_get_attached_sas_address(port, &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 port;
         }
      }
   }

   return SCI_INVALID_HANDLE;
}
/**
 * This routine will verify that the phys are assigned a valid SAS address for
 * automatic port configuration mode.
 */
static
SCI_STATUS scic_sds_apc_agent_validate_phy_configuration(
   SCIC_SDS_CONTROLLER_T               * controller,
   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
   U8 phy_index;
   U8 port_index;
   SCI_SAS_ADDRESS_T sas_address;
   SCI_SAS_ADDRESS_T phy_assigned_address;

   SCIC_LOG_TRACE((
      sci_base_object_get_logger(controller),
      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
      "scic_sds_apc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
      controller, port_agent
   ));

   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.
      scic_sds_phy_get_sas_address(
         &controller->phy_table[phy_index], &sas_address
      );

      while (++phy_index < SCI_MAX_PHYS)
      {
         scic_sds_phy_get_sas_address(
            &controller->phy_table[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 scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
}
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);
}
/**
 * This routine will verify that all of the phys in the same port are using
 * the same SAS address.
 *
 * @param[in] controller This is the controller that contains the PHYs to
 *            be verified.
 */
static
SCI_STATUS scic_sds_mpc_agent_validate_phy_configuration(
   SCIC_SDS_CONTROLLER_T               * controller,
   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
   U32 phy_mask;
   U32 assigned_phy_mask;
   SCI_SAS_ADDRESS_T sas_address;
   SCI_SAS_ADDRESS_T phy_assigned_address;
   U8 port_index;
   U8 phy_index;

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

   SCIC_LOG_TRACE((
      sci_base_object_get_logger(controller),
      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
      "scic_sds_mpc_agent_validate_phy_configuration(0x%08x, 0x%08x) enter\n",
      controller, port_agent
   ));

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

      if (phy_mask != 0)
      {
         // 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 ((1 << phy_index) & phy_mask)
            {
               scic_sds_phy_get_sas_address(
                  &controller->phy_table[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 ((1 << phy_index) & phy_mask)
            {
               scic_sds_phy_get_sas_address(
                  &controller->phy_table[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;

               scic_sds_port_add_phy(
                  &controller->port_table[port_index],
                  &controller->phy_table[phy_index]
               );

               assigned_phy_mask |= (1 << phy_index);
            }

            phy_index++;
         }
      }
   }

   return scic_sds_port_configuration_agent_validate_ports(controller, port_agent);
}
/**
 * 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)
 *
 * @param[in] controller This is the controller object that contains the
 *            port agent
 * @param[in] port_agent This is the port configruation agent for
 *            the controller.
 *
 * @return SCI_STATUS
 * @retval SCI_SUCCESS the port configuration is valid for this
 *         port configuration agent.
 * @retval SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION the port configuration
 *         is not valid for this port configuration agent.
 */
static
SCI_STATUS scic_sds_port_configuration_agent_validate_ports(
   SCIC_SDS_CONTROLLER_T               * controller,
   SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
#if !defined(ARLINGTON_BUILD)
   SCI_SAS_ADDRESS_T first_address;
   SCI_SAS_ADDRESS_T second_address;

   SCIC_LOG_TRACE((
      sci_base_object_get_logger(controller),
      SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
      "scic_sds_port_configuration_agent_validate_ports(0x%08x, 0x%08x) enter\n",
      controller, port_agent
   ));

   // 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.
   scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
   scic_sds_phy_get_sas_address(&controller->phy_table[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)
      )
   {
      scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address);
      scic_sds_phy_get_sas_address(&controller->phy_table[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)
      )
   {
      scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address);
      scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address);

      if (sci_sas_address_compare(first_address, second_address) == 0)
      {
         return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
      }
   }
#endif // !defined(ARLINGTON_BUILD)

   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;
}