#include <sys/cdefs.h>
#include <dev/isci/scil/scic_controller.h>
#include <dev/isci/scil/scic_sds_logger.h>
#include <dev/isci/scil/scic_sds_controller.h>
#include <dev/isci/scil/scic_sds_port_configuration_agent.h>
#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)
#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)
#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (250)
enum SCIC_SDS_APC_ACTIVITY
{
SCIC_SDS_APC_SKIP_PHY,
SCIC_SDS_APC_ADD_PHY,
SCIC_SDS_APC_START_TIMER,
SCIC_SDS_APC_ACTIVITY_MAX
};
static
S32 sci_sas_address_compare(
SCI_SAS_ADDRESS_T address_one,
SCI_SAS_ADDRESS_T address_two
)
{
if (address_one.high > address_two.high)
{
return 1;
}
else if (address_one.high < address_two.high)
{
return -1;
}
else if (address_one.low > address_two.low)
{
return 1;
}
else if (address_one.low < address_two.low)
{
return -1;
}
return 0;
}
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
));
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;
}
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
));
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;
}
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;
}
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;
}
}
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
return SCI_SUCCESS;
}
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)
{
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 ((1 << phy_index) & phy_mask)
{
scic_sds_phy_get_sas_address(
&controller->phy_table[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 ((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)
{
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);
}
static
void scic_sds_mpc_agent_timeout_handler(
void * object
)
{
U8 index;
SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent = &controller->port_agent;
U16 configure_phy_mask;
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
"scic_sds_mpc_agent_timeout_handler(0x%08x) enter\n",
controller
));
port_agent->timer_pending = FALSE;
configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
for (index = 0; index < SCI_MAX_PHYS; index++)
{
if (configure_phy_mask & (1 << index))
{
port_agent->link_up_handler(
controller,
port_agent,
scic_sds_phy_get_port(&controller->phy_table[index]),
&controller->phy_table[index]
);
}
}
}
static
void scic_sds_mpc_agent_link_up(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PORT_T * port,
SCIC_SDS_PHY_T * phy
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_mpc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
controller, port_agent, port, phy
));
if (port != SCI_INVALID_HANDLE)
{
port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
scic_sds_port_link_up(port, phy);
if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0)
{
port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy));
}
}
}
static
void scic_sds_mpc_agent_link_down(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PORT_T * port,
SCIC_SDS_PHY_T * phy
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_mpc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
controller, port_agent, port, phy
));
if (port != SCI_INVALID_HANDLE)
{
port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy));
if (
(port_agent->phy_configured_mask == 0x0000)
&& (port_agent->phy_ready_mask != 0x0000)
&& !port_agent->timer_pending
)
{
port_agent->timer_pending = TRUE;
scic_cb_timer_start(
controller,
port_agent->timer,
SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT
);
}
scic_sds_port_link_down(port, phy);
}
}
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;
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
);
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
void scic_sds_apc_agent_start_timer(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PHY_T * phy,
U32 timeout
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_apc_agent_start_timer(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
controller, port_agent, phy, timeout
));
if (port_agent->timer_pending)
{
scic_cb_timer_stop(controller, port_agent->timer);
}
port_agent->timer_pending = TRUE;
scic_cb_timer_start(controller, port_agent->timer, timeout);
}
static
void scic_sds_apc_agent_configure_ports(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PHY_T * phy,
BOOL start_timer
)
{
U8 port_index;
SCI_STATUS status;
SCIC_SDS_PORT_T * port;
SCI_PORT_HANDLE_T port_handle;
enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_apc_agent_configure_ports(0x%08x, 0x%08x, 0x%08x, %d) enter\n",
controller, port_agent, phy, start_timer
));
port = scic_sds_port_configuration_agent_find_port(controller, phy);
if (port != SCI_INVALID_HANDLE)
{
if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
apc_activity = SCIC_SDS_APC_ADD_PHY;
else
apc_activity = SCIC_SDS_APC_SKIP_PHY;
}
else
{
for (
port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index;
port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index;
port_index++
)
{
scic_controller_get_port_handle(controller, port_index, &port_handle);
port = (SCIC_SDS_PORT_T *)port_handle;
if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index))
{
if (port->active_phy_mask > (1 << phy->phy_index))
{
apc_activity = SCIC_SDS_APC_SKIP_PHY;
break;
}
if (port->physical_port_index == phy->phy_index)
{
if (apc_activity != SCIC_SDS_APC_START_TIMER)
{
apc_activity = SCIC_SDS_APC_ADD_PHY;
}
break;
}
if (port->active_phy_mask == 0)
{
apc_activity = SCIC_SDS_APC_START_TIMER;
}
}
else if (port->active_phy_mask != 0)
{
apc_activity = SCIC_SDS_APC_SKIP_PHY;
}
}
}
if (
(start_timer == FALSE)
&& (apc_activity == SCIC_SDS_APC_START_TIMER)
)
{
apc_activity = SCIC_SDS_APC_ADD_PHY;
}
switch (apc_activity)
{
case SCIC_SDS_APC_ADD_PHY:
status = scic_sds_port_add_phy(port, phy);
if (status == SCI_SUCCESS)
{
port_agent->phy_configured_mask |= (1 << phy->phy_index);
}
break;
case SCIC_SDS_APC_START_TIMER:
scic_sds_apc_agent_start_timer(
controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
);
break;
case SCIC_SDS_APC_SKIP_PHY:
default:
break;
}
}
static
void scic_sds_apc_agent_link_up(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PORT_T * port,
SCIC_SDS_PHY_T * phy
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_apc_agent_link_up(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
controller, port_agent, port, phy
));
if (port == SCI_INVALID_HANDLE)
{
port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
scic_sds_apc_agent_start_timer(
controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION
);
}
else
{
if ( SCI_BASE_PORT_STATE_RESETTING
== port->parent.state_machine.current_state_id
)
{
port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy));
scic_sds_port_link_up(port, phy);
}
else
{
ASSERT (0);
}
}
}
static
void scic_sds_apc_agent_link_down(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent,
SCIC_SDS_PORT_T * port,
SCIC_SDS_PHY_T * phy
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT | SCIC_LOG_OBJECT_PHY,
"scic_sds_apc_agent_link_down(0x%08x, 0x%08x, 0x%08x, 0x%08x) enter\n",
controller, port_agent, port, phy
));
port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy));
if (port != SCI_INVALID_HANDLE)
{
if (port_agent->phy_configured_mask & (1 << phy->phy_index))
{
SCI_STATUS status;
status = scic_sds_port_remove_phy(port, phy);
if (status == SCI_SUCCESS)
{
port_agent->phy_configured_mask &= ~(1 << phy->phy_index);
}
}
}
}
static
void scic_sds_apc_agent_timeout_handler(
void * object
)
{
U32 index;
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent;
SCIC_SDS_CONTROLLER_T * controller = (SCIC_SDS_CONTROLLER_T *)object;
U16 configure_phy_mask;
port_agent = scic_sds_controller_get_port_configuration_agent(controller);
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
"scic_sds_apc_agent_timeout_handler(0x%08x) enter\n",
controller
));
port_agent->timer_pending = FALSE;
configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
if (configure_phy_mask != 0x00)
{
for (index = 0; index < SCI_MAX_PHYS; index++)
{
if (configure_phy_mask & (1 << index))
{
scic_sds_apc_agent_configure_ports(
controller, port_agent, &controller->phy_table[index], FALSE
);
}
}
if (
(port_agent->phy_ready_mask == port_agent->phy_configured_mask) &&
(controller->next_phy_to_start == SCI_MAX_PHYS) &&
(controller->phy_startup_timer_pending == FALSE)
)
{
if (scic_sds_controller_is_start_complete(controller) == TRUE)
{
scic_sds_controller_port_agent_configured_ports(controller);
}
}
}
}
void scic_sds_port_configuration_agent_construct(
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
U32 index;
port_agent->phy_configured_mask = 0x00;
port_agent->phy_ready_mask = 0x00;
port_agent->link_up_handler = NULL;
port_agent->link_down_handler = NULL;
port_agent->timer_pending = FALSE;
port_agent->timer = NULL;
for (index = 0; index < SCI_MAX_PORTS; index++)
{
port_agent->phy_valid_port_range[index].min_index = 0;
port_agent->phy_valid_port_range[index].max_index = 0;
}
}
SCI_STATUS scic_sds_port_configuration_agent_initialize(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
SCI_STATUS status = SCI_SUCCESS;
enum SCIC_PORT_CONFIGURATION_MODE mode;
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER | SCIC_LOG_OBJECT_PORT,
"scic_sds_port_configuration_agent_initialize(0x%08x, 0x%08x) enter\n",
controller, port_agent
));
mode = controller->oem_parameters.sds1.controller.mode_type;
if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE)
{
status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent);
port_agent->link_up_handler = scic_sds_mpc_agent_link_up;
port_agent->link_down_handler = scic_sds_mpc_agent_link_down;
port_agent->timer = scic_cb_timer_create(
controller,
scic_sds_mpc_agent_timeout_handler,
controller
);
}
else
{
status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent);
port_agent->link_up_handler = scic_sds_apc_agent_link_up;
port_agent->link_down_handler = scic_sds_apc_agent_link_down;
port_agent->timer = scic_cb_timer_create(
controller,
scic_sds_apc_agent_timeout_handler,
controller
);
}
if (status == SCI_SUCCESS && port_agent->timer == NULL)
{
SCIC_LOG_ERROR((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_CONTROLLER,
"Controller 0x%x automatic port configuration agent could not get timer.\n",
controller
));
status = SCI_FAILURE;
}
return status;
}
void scic_sds_port_configuration_agent_destroy(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
if (port_agent->timer_pending == TRUE)
{
scic_cb_timer_stop(controller, port_agent->timer);
}
scic_cb_timer_destroy(controller, port_agent->timer);
port_agent->timer_pending = FALSE;
port_agent->timer = NULL;
}
void scic_sds_port_configuration_agent_release_resource(
SCIC_SDS_CONTROLLER_T * controller,
SCIC_SDS_PORT_CONFIGURATION_AGENT_T * port_agent
)
{
SCIC_LOG_TRACE((
sci_base_object_get_logger(controller),
SCIC_LOG_OBJECT_PORT,
"scic_sds_port_configuration_agent_release_resource(0x%x, 0x%x)\n",
controller, port_agent
));
if (port_agent->timer != NULL)
{
scic_cb_timer_destroy(controller, port_agent->timer);
port_agent->timer = NULL;
}
}