diff options
Diffstat (limited to 'drivers/scsi/isci/port.c')
-rw-r--r-- | drivers/scsi/isci/port.c | 115 |
1 files changed, 75 insertions, 40 deletions
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 74f06f3c0735..2946eee8e702 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -152,6 +152,71 @@ static enum sci_status scic_port_get_properties(struct scic_sds_port *port, return SCI_SUCCESS; } +static void scic_port_bcn_enable(struct scic_sds_port *sci_port) +{ + struct scic_sds_phy *sci_phy; + u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(sci_port->phy_table); i++) { + sci_phy = sci_port->phy_table[i]; + if (!sci_phy) + continue; + val = readl(&sci_phy->link_layer_registers->link_layer_control); + /* clear the bit by writing 1. */ + writel(val, &sci_phy->link_layer_registers->link_layer_control); + } +} + +/* called under scic_lock to stabilize phy:port associations */ +void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport) +{ + int i; + + clear_bit(IPORT_BCN_BLOCKED, &iport->flags); + wake_up(&ihost->eventq); + + if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags)) + return; + + for (i = 0; i < ARRAY_SIZE(iport->sci.phy_table); i++) { + struct scic_sds_phy *sci_phy = iport->sci.phy_table[i]; + struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); + + if (!sci_phy) + continue; + + ihost->sas_ha.notify_port_event(&iphy->sas_phy, + PORTE_BROADCAST_RCVD); + break; + } +} + +void isci_port_bc_change_received(struct isci_host *ihost, + struct scic_sds_port *sci_port, + struct scic_sds_phy *sci_phy) +{ + struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); + struct isci_port *iport = iphy->isci_port; + + if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) { + dev_dbg(&ihost->pdev->dev, + "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n", + __func__, iphy, &iphy->sas_phy); + set_bit(IPORT_BCN_PENDING, &iport->flags); + atomic_inc(&iport->event); + wake_up(&ihost->eventq); + } else { + dev_dbg(&ihost->pdev->dev, + "%s: isci_phy = %p, sas_phy = %p\n", + __func__, iphy, &iphy->sas_phy); + + ihost->sas_ha.notify_port_event(&iphy->sas_phy, + PORTE_BROADCAST_RCVD); + } + scic_port_bcn_enable(sci_port); +} + static void isci_port_link_up(struct isci_host *isci_host, struct scic_sds_port *port, struct scic_sds_phy *phy) @@ -240,13 +305,15 @@ static void isci_port_link_down(struct isci_host *isci_host, if (isci_port) { /* check to see if this is the last phy on this port. */ - if (isci_phy->sas_phy.port - && isci_phy->sas_phy.port->num_phys == 1) { - - /* change the state for all devices on this port. - * The next task sent to this device will be returned - * as SAS_TASK_UNDELIVERED, and the scsi mid layer - * will remove the target + if (isci_phy->sas_phy.port && + isci_phy->sas_phy.port->num_phys == 1) { + atomic_inc(&isci_port->event); + isci_port_bcn_enable(isci_host, isci_port); + + /* change the state for all devices on this port. The + * next task sent to this device will be returned as + * SAS_TASK_UNDELIVERED, and the scsi mid layer will + * remove the target */ list_for_each_entry(isci_device, &isci_port->remote_dev_list, @@ -1033,26 +1100,6 @@ enum sas_linkrate scic_sds_port_get_max_allowed_speed( return max_allowed_speed; } -static void scic_port_enable_broadcast_change_notification(struct scic_sds_port *port) -{ - struct scic_sds_phy *phy; - u32 register_value; - u8 index; - - /* Loop through all of the phys to enable BCN. */ - for (index = 0; index < SCI_MAX_PHYS; index++) { - phy = port->phy_table[index]; - if (phy != NULL) { - register_value = - readl(&phy->link_layer_registers->link_layer_control); - - /* clear the bit by writing 1. */ - writel(register_value, - &phy->link_layer_registers->link_layer_control); - } - } -} - /** * * @sci_port: This is the struct scic_sds_port object to suspend. @@ -1838,6 +1885,7 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index) init_completion(&iport->start_complete); iport->isci_host = ihost; isci_port_change_state(iport, isci_freed); + atomic_set(&iport->event, 0); } /** @@ -1852,19 +1900,6 @@ enum isci_status isci_port_get_state( return isci_port->status; } -static void isci_port_bc_change_received(struct isci_host *ihost, - struct scic_sds_port *sci_port, - struct scic_sds_phy *sci_phy) -{ - struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); - - dev_dbg(&ihost->pdev->dev, "%s: iphy = %p, sas_phy = %p\n", - __func__, iphy, &iphy->sas_phy); - - ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD); - scic_port_enable_broadcast_change_notification(sci_port); -} - void scic_sds_port_broadcast_change_received( struct scic_sds_port *sci_port, struct scic_sds_phy *sci_phy) |