diff options
-rw-r--r-- | drivers/scsi/isci/port.c | 115 | ||||
-rw-r--r-- | drivers/scsi/isci/port.h | 5 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 259 |
3 files changed, 338 insertions, 41 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, | |||
152 | return SCI_SUCCESS; | 152 | return SCI_SUCCESS; |
153 | } | 153 | } |
154 | 154 | ||
155 | static void scic_port_bcn_enable(struct scic_sds_port *sci_port) | ||
156 | { | ||
157 | struct scic_sds_phy *sci_phy; | ||
158 | u32 val; | ||
159 | int i; | ||
160 | |||
161 | for (i = 0; i < ARRAY_SIZE(sci_port->phy_table); i++) { | ||
162 | sci_phy = sci_port->phy_table[i]; | ||
163 | if (!sci_phy) | ||
164 | continue; | ||
165 | val = readl(&sci_phy->link_layer_registers->link_layer_control); | ||
166 | /* clear the bit by writing 1. */ | ||
167 | writel(val, &sci_phy->link_layer_registers->link_layer_control); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /* called under scic_lock to stabilize phy:port associations */ | ||
172 | void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport) | ||
173 | { | ||
174 | int i; | ||
175 | |||
176 | clear_bit(IPORT_BCN_BLOCKED, &iport->flags); | ||
177 | wake_up(&ihost->eventq); | ||
178 | |||
179 | if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags)) | ||
180 | return; | ||
181 | |||
182 | for (i = 0; i < ARRAY_SIZE(iport->sci.phy_table); i++) { | ||
183 | struct scic_sds_phy *sci_phy = iport->sci.phy_table[i]; | ||
184 | struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); | ||
185 | |||
186 | if (!sci_phy) | ||
187 | continue; | ||
188 | |||
189 | ihost->sas_ha.notify_port_event(&iphy->sas_phy, | ||
190 | PORTE_BROADCAST_RCVD); | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | void isci_port_bc_change_received(struct isci_host *ihost, | ||
196 | struct scic_sds_port *sci_port, | ||
197 | struct scic_sds_phy *sci_phy) | ||
198 | { | ||
199 | struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); | ||
200 | struct isci_port *iport = iphy->isci_port; | ||
201 | |||
202 | if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) { | ||
203 | dev_dbg(&ihost->pdev->dev, | ||
204 | "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n", | ||
205 | __func__, iphy, &iphy->sas_phy); | ||
206 | set_bit(IPORT_BCN_PENDING, &iport->flags); | ||
207 | atomic_inc(&iport->event); | ||
208 | wake_up(&ihost->eventq); | ||
209 | } else { | ||
210 | dev_dbg(&ihost->pdev->dev, | ||
211 | "%s: isci_phy = %p, sas_phy = %p\n", | ||
212 | __func__, iphy, &iphy->sas_phy); | ||
213 | |||
214 | ihost->sas_ha.notify_port_event(&iphy->sas_phy, | ||
215 | PORTE_BROADCAST_RCVD); | ||
216 | } | ||
217 | scic_port_bcn_enable(sci_port); | ||
218 | } | ||
219 | |||
155 | static void isci_port_link_up(struct isci_host *isci_host, | 220 | static void isci_port_link_up(struct isci_host *isci_host, |
156 | struct scic_sds_port *port, | 221 | struct scic_sds_port *port, |
157 | struct scic_sds_phy *phy) | 222 | struct scic_sds_phy *phy) |
@@ -240,13 +305,15 @@ static void isci_port_link_down(struct isci_host *isci_host, | |||
240 | if (isci_port) { | 305 | if (isci_port) { |
241 | 306 | ||
242 | /* check to see if this is the last phy on this port. */ | 307 | /* check to see if this is the last phy on this port. */ |
243 | if (isci_phy->sas_phy.port | 308 | if (isci_phy->sas_phy.port && |
244 | && isci_phy->sas_phy.port->num_phys == 1) { | 309 | isci_phy->sas_phy.port->num_phys == 1) { |
245 | 310 | atomic_inc(&isci_port->event); | |
246 | /* change the state for all devices on this port. | 311 | isci_port_bcn_enable(isci_host, isci_port); |
247 | * The next task sent to this device will be returned | 312 | |
248 | * as SAS_TASK_UNDELIVERED, and the scsi mid layer | 313 | /* change the state for all devices on this port. The |
249 | * will remove the target | 314 | * next task sent to this device will be returned as |
315 | * SAS_TASK_UNDELIVERED, and the scsi mid layer will | ||
316 | * remove the target | ||
250 | */ | 317 | */ |
251 | list_for_each_entry(isci_device, | 318 | list_for_each_entry(isci_device, |
252 | &isci_port->remote_dev_list, | 319 | &isci_port->remote_dev_list, |
@@ -1033,26 +1100,6 @@ enum sas_linkrate scic_sds_port_get_max_allowed_speed( | |||
1033 | return max_allowed_speed; | 1100 | return max_allowed_speed; |
1034 | } | 1101 | } |
1035 | 1102 | ||
1036 | static void scic_port_enable_broadcast_change_notification(struct scic_sds_port *port) | ||
1037 | { | ||
1038 | struct scic_sds_phy *phy; | ||
1039 | u32 register_value; | ||
1040 | u8 index; | ||
1041 | |||
1042 | /* Loop through all of the phys to enable BCN. */ | ||
1043 | for (index = 0; index < SCI_MAX_PHYS; index++) { | ||
1044 | phy = port->phy_table[index]; | ||
1045 | if (phy != NULL) { | ||
1046 | register_value = | ||
1047 | readl(&phy->link_layer_registers->link_layer_control); | ||
1048 | |||
1049 | /* clear the bit by writing 1. */ | ||
1050 | writel(register_value, | ||
1051 | &phy->link_layer_registers->link_layer_control); | ||
1052 | } | ||
1053 | } | ||
1054 | } | ||
1055 | |||
1056 | /** | 1103 | /** |
1057 | * | 1104 | * |
1058 | * @sci_port: This is the struct scic_sds_port object to suspend. | 1105 | * @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) | |||
1838 | init_completion(&iport->start_complete); | 1885 | init_completion(&iport->start_complete); |
1839 | iport->isci_host = ihost; | 1886 | iport->isci_host = ihost; |
1840 | isci_port_change_state(iport, isci_freed); | 1887 | isci_port_change_state(iport, isci_freed); |
1888 | atomic_set(&iport->event, 0); | ||
1841 | } | 1889 | } |
1842 | 1890 | ||
1843 | /** | 1891 | /** |
@@ -1852,19 +1900,6 @@ enum isci_status isci_port_get_state( | |||
1852 | return isci_port->status; | 1900 | return isci_port->status; |
1853 | } | 1901 | } |
1854 | 1902 | ||
1855 | static void isci_port_bc_change_received(struct isci_host *ihost, | ||
1856 | struct scic_sds_port *sci_port, | ||
1857 | struct scic_sds_phy *sci_phy) | ||
1858 | { | ||
1859 | struct isci_phy *iphy = sci_phy_to_iphy(sci_phy); | ||
1860 | |||
1861 | dev_dbg(&ihost->pdev->dev, "%s: iphy = %p, sas_phy = %p\n", | ||
1862 | __func__, iphy, &iphy->sas_phy); | ||
1863 | |||
1864 | ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD); | ||
1865 | scic_port_enable_broadcast_change_notification(sci_port); | ||
1866 | } | ||
1867 | |||
1868 | void scic_sds_port_broadcast_change_received( | 1903 | void scic_sds_port_broadcast_change_received( |
1869 | struct scic_sds_port *sci_port, | 1904 | struct scic_sds_port *sci_port, |
1870 | struct scic_sds_phy *sci_phy) | 1905 | struct scic_sds_phy *sci_phy) |
diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index fee6d80df769..45c01f80bf83 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h | |||
@@ -173,6 +173,10 @@ struct scic_sds_port { | |||
173 | */ | 173 | */ |
174 | struct isci_port { | 174 | struct isci_port { |
175 | enum isci_status status; | 175 | enum isci_status status; |
176 | #define IPORT_BCN_BLOCKED 0 | ||
177 | #define IPORT_BCN_PENDING 1 | ||
178 | unsigned long flags; | ||
179 | atomic_t event; | ||
176 | struct isci_host *isci_host; | 180 | struct isci_host *isci_host; |
177 | struct asd_sas_port sas_port; | 181 | struct asd_sas_port sas_port; |
178 | struct list_head remote_dev_list; | 182 | struct list_head remote_dev_list; |
@@ -334,6 +338,7 @@ void scic_sds_port_setup_transports( | |||
334 | struct scic_sds_port *sci_port, | 338 | struct scic_sds_port *sci_port, |
335 | u32 device_id); | 339 | u32 device_id); |
336 | 340 | ||
341 | void isci_port_bcn_enable(struct isci_host *, struct isci_port *); | ||
337 | 342 | ||
338 | void scic_sds_port_deactivate_phy( | 343 | void scic_sds_port_deactivate_phy( |
339 | struct scic_sds_port *sci_port, | 344 | struct scic_sds_port *sci_port, |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 69f17b98e4fa..709c08171743 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/completion.h> | 56 | #include <linux/completion.h> |
57 | #include <linux/irqflags.h> | 57 | #include <linux/irqflags.h> |
58 | #include "sas.h" | 58 | #include "sas.h" |
59 | #include <scsi/libsas.h> | ||
59 | #include "remote_device.h" | 60 | #include "remote_device.h" |
60 | #include "remote_node_context.h" | 61 | #include "remote_node_context.h" |
61 | #include "isci.h" | 62 | #include "isci.h" |
@@ -1397,11 +1398,250 @@ isci_task_request_complete(struct isci_host *ihost, | |||
1397 | complete(tmf_complete); | 1398 | complete(tmf_complete); |
1398 | } | 1399 | } |
1399 | 1400 | ||
1401 | static void isci_smp_task_timedout(unsigned long _task) | ||
1402 | { | ||
1403 | struct sas_task *task = (void *) _task; | ||
1404 | unsigned long flags; | ||
1405 | |||
1406 | spin_lock_irqsave(&task->task_state_lock, flags); | ||
1407 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) | ||
1408 | task->task_state_flags |= SAS_TASK_STATE_ABORTED; | ||
1409 | spin_unlock_irqrestore(&task->task_state_lock, flags); | ||
1410 | |||
1411 | complete(&task->completion); | ||
1412 | } | ||
1413 | |||
1414 | static void isci_smp_task_done(struct sas_task *task) | ||
1415 | { | ||
1416 | if (!del_timer(&task->timer)) | ||
1417 | return; | ||
1418 | complete(&task->completion); | ||
1419 | } | ||
1420 | |||
1421 | static struct sas_task *isci_alloc_task(void) | ||
1422 | { | ||
1423 | struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL); | ||
1424 | |||
1425 | if (task) { | ||
1426 | INIT_LIST_HEAD(&task->list); | ||
1427 | spin_lock_init(&task->task_state_lock); | ||
1428 | task->task_state_flags = SAS_TASK_STATE_PENDING; | ||
1429 | init_timer(&task->timer); | ||
1430 | init_completion(&task->completion); | ||
1431 | } | ||
1432 | |||
1433 | return task; | ||
1434 | } | ||
1435 | |||
1436 | static void isci_free_task(struct isci_host *ihost, struct sas_task *task) | ||
1437 | { | ||
1438 | if (task) { | ||
1439 | BUG_ON(!list_empty(&task->list)); | ||
1440 | kfree(task); | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1444 | static int isci_smp_execute_task(struct isci_host *ihost, | ||
1445 | struct domain_device *dev, void *req, | ||
1446 | int req_size, void *resp, int resp_size) | ||
1447 | { | ||
1448 | int res, retry; | ||
1449 | struct sas_task *task = NULL; | ||
1450 | |||
1451 | for (retry = 0; retry < 3; retry++) { | ||
1452 | task = isci_alloc_task(); | ||
1453 | if (!task) | ||
1454 | return -ENOMEM; | ||
1455 | |||
1456 | task->dev = dev; | ||
1457 | task->task_proto = dev->tproto; | ||
1458 | sg_init_one(&task->smp_task.smp_req, req, req_size); | ||
1459 | sg_init_one(&task->smp_task.smp_resp, resp, resp_size); | ||
1460 | |||
1461 | task->task_done = isci_smp_task_done; | ||
1462 | |||
1463 | task->timer.data = (unsigned long) task; | ||
1464 | task->timer.function = isci_smp_task_timedout; | ||
1465 | task->timer.expires = jiffies + 10*HZ; | ||
1466 | add_timer(&task->timer); | ||
1467 | |||
1468 | res = isci_task_execute_task(task, 1, GFP_KERNEL); | ||
1469 | |||
1470 | if (res) { | ||
1471 | del_timer(&task->timer); | ||
1472 | dev_err(&ihost->pdev->dev, | ||
1473 | "%s: executing SMP task failed:%d\n", | ||
1474 | __func__, res); | ||
1475 | goto ex_err; | ||
1476 | } | ||
1477 | |||
1478 | wait_for_completion(&task->completion); | ||
1479 | res = -ECOMM; | ||
1480 | if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { | ||
1481 | dev_err(&ihost->pdev->dev, | ||
1482 | "%s: smp task timed out or aborted\n", | ||
1483 | __func__); | ||
1484 | isci_task_abort_task(task); | ||
1485 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { | ||
1486 | dev_err(&ihost->pdev->dev, | ||
1487 | "%s: SMP task aborted and not done\n", | ||
1488 | __func__); | ||
1489 | goto ex_err; | ||
1490 | } | ||
1491 | } | ||
1492 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1493 | task->task_status.stat == SAM_STAT_GOOD) { | ||
1494 | res = 0; | ||
1495 | break; | ||
1496 | } | ||
1497 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1498 | task->task_status.stat == SAS_DATA_UNDERRUN) { | ||
1499 | /* no error, but return the number of bytes of | ||
1500 | * underrun */ | ||
1501 | res = task->task_status.residual; | ||
1502 | break; | ||
1503 | } | ||
1504 | if (task->task_status.resp == SAS_TASK_COMPLETE && | ||
1505 | task->task_status.stat == SAS_DATA_OVERRUN) { | ||
1506 | res = -EMSGSIZE; | ||
1507 | break; | ||
1508 | } else { | ||
1509 | dev_err(&ihost->pdev->dev, | ||
1510 | "%s: task to dev %016llx response: 0x%x " | ||
1511 | "status 0x%x\n", __func__, | ||
1512 | SAS_ADDR(dev->sas_addr), | ||
1513 | task->task_status.resp, | ||
1514 | task->task_status.stat); | ||
1515 | isci_free_task(ihost, task); | ||
1516 | task = NULL; | ||
1517 | } | ||
1518 | } | ||
1519 | ex_err: | ||
1520 | BUG_ON(retry == 3 && task != NULL); | ||
1521 | isci_free_task(ihost, task); | ||
1522 | return res; | ||
1523 | } | ||
1524 | |||
1525 | #define DISCOVER_REQ_SIZE 16 | ||
1526 | #define DISCOVER_RESP_SIZE 56 | ||
1527 | |||
1528 | int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost, | ||
1529 | struct domain_device *dev, | ||
1530 | int phy_id, int *adt) | ||
1531 | { | ||
1532 | struct smp_resp *disc_resp; | ||
1533 | u8 *disc_req; | ||
1534 | int res; | ||
1535 | |||
1536 | disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL); | ||
1537 | if (!disc_resp) | ||
1538 | return -ENOMEM; | ||
1539 | |||
1540 | disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL); | ||
1541 | if (disc_req) { | ||
1542 | disc_req[0] = SMP_REQUEST; | ||
1543 | disc_req[1] = SMP_DISCOVER; | ||
1544 | disc_req[9] = phy_id; | ||
1545 | } else { | ||
1546 | kfree(disc_resp); | ||
1547 | return -ENOMEM; | ||
1548 | } | ||
1549 | res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE, | ||
1550 | disc_resp, DISCOVER_RESP_SIZE); | ||
1551 | if (!res) { | ||
1552 | if (disc_resp->result != SMP_RESP_FUNC_ACC) | ||
1553 | res = disc_resp->result; | ||
1554 | else | ||
1555 | *adt = disc_resp->disc.attached_dev_type; | ||
1556 | } | ||
1557 | kfree(disc_req); | ||
1558 | kfree(disc_resp); | ||
1559 | |||
1560 | return res; | ||
1561 | } | ||
1562 | |||
1563 | static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num) | ||
1564 | { | ||
1565 | struct domain_device *dev = idev->domain_dev; | ||
1566 | struct isci_port *iport = idev->isci_port; | ||
1567 | struct isci_host *ihost = iport->isci_host; | ||
1568 | int res, iteration = 0, attached_device_type; | ||
1569 | #define STP_WAIT_MSECS 25000 | ||
1570 | unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS); | ||
1571 | unsigned long deadline = jiffies + tmo; | ||
1572 | enum { | ||
1573 | SMP_PHYWAIT_PHYDOWN, | ||
1574 | SMP_PHYWAIT_PHYUP, | ||
1575 | SMP_PHYWAIT_DONE | ||
1576 | } phy_state = SMP_PHYWAIT_PHYDOWN; | ||
1577 | |||
1578 | /* While there is time, wait for the phy to go away and come back */ | ||
1579 | while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) { | ||
1580 | int event = atomic_read(&iport->event); | ||
1581 | |||
1582 | ++iteration; | ||
1583 | |||
1584 | tmo = wait_event_timeout(ihost->eventq, | ||
1585 | event != atomic_read(&iport->event) || | ||
1586 | !test_bit(IPORT_BCN_BLOCKED, &iport->flags), | ||
1587 | tmo); | ||
1588 | /* link down, stop polling */ | ||
1589 | if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags)) | ||
1590 | break; | ||
1591 | |||
1592 | dev_dbg(&ihost->pdev->dev, | ||
1593 | "%s: iport %p, iteration %d," | ||
1594 | " phase %d: time_remaining %lu, bcns = %d\n", | ||
1595 | __func__, iport, iteration, phy_state, | ||
1596 | tmo, test_bit(IPORT_BCN_PENDING, &iport->flags)); | ||
1597 | |||
1598 | res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num, | ||
1599 | &attached_device_type); | ||
1600 | tmo = deadline - jiffies; | ||
1601 | |||
1602 | if (res) { | ||
1603 | dev_warn(&ihost->pdev->dev, | ||
1604 | "%s: iteration %d, phase %d:" | ||
1605 | " SMP error=%d, time_remaining=%lu\n", | ||
1606 | __func__, iteration, phy_state, res, tmo); | ||
1607 | break; | ||
1608 | } | ||
1609 | dev_dbg(&ihost->pdev->dev, | ||
1610 | "%s: iport %p, iteration %d," | ||
1611 | " phase %d: time_remaining %lu, bcns = %d, " | ||
1612 | "attdevtype = %x\n", | ||
1613 | __func__, iport, iteration, phy_state, | ||
1614 | tmo, test_bit(IPORT_BCN_PENDING, &iport->flags), | ||
1615 | attached_device_type); | ||
1616 | |||
1617 | switch (phy_state) { | ||
1618 | case SMP_PHYWAIT_PHYDOWN: | ||
1619 | /* Has the device gone away? */ | ||
1620 | if (!attached_device_type) | ||
1621 | phy_state = SMP_PHYWAIT_PHYUP; | ||
1622 | |||
1623 | break; | ||
1624 | |||
1625 | case SMP_PHYWAIT_PHYUP: | ||
1626 | /* Has the device come back? */ | ||
1627 | if (attached_device_type) | ||
1628 | phy_state = SMP_PHYWAIT_DONE; | ||
1629 | break; | ||
1630 | |||
1631 | case SMP_PHYWAIT_DONE: | ||
1632 | break; | ||
1633 | } | ||
1634 | |||
1635 | } | ||
1636 | dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__); | ||
1637 | } | ||
1638 | |||
1400 | static int isci_reset_device(struct domain_device *dev, int hard_reset) | 1639 | static int isci_reset_device(struct domain_device *dev, int hard_reset) |
1401 | { | 1640 | { |
1402 | struct isci_remote_device *idev = dev->lldd_dev; | 1641 | struct isci_remote_device *idev = dev->lldd_dev; |
1403 | struct sas_phy *phy = sas_find_local_phy(dev); | 1642 | struct sas_phy *phy = sas_find_local_phy(dev); |
1404 | struct isci_host *ihost = dev_to_ihost(dev); | 1643 | struct isci_host *ihost = dev_to_ihost(dev); |
1644 | struct isci_port *iport = idev->isci_port; | ||
1405 | enum sci_status status; | 1645 | enum sci_status status; |
1406 | unsigned long flags; | 1646 | unsigned long flags; |
1407 | int rc; | 1647 | int rc; |
@@ -1432,6 +1672,10 @@ static int isci_reset_device(struct domain_device *dev, int hard_reset) | |||
1432 | /* Make sure all pending requests are able to be fully terminated. */ | 1672 | /* Make sure all pending requests are able to be fully terminated. */ |
1433 | isci_device_clear_reset_pending(ihost, idev); | 1673 | isci_device_clear_reset_pending(ihost, idev); |
1434 | 1674 | ||
1675 | /* If this is a device on an expander, disable BCN processing. */ | ||
1676 | if (!scsi_is_sas_phy_local(phy)) | ||
1677 | set_bit(IPORT_BCN_BLOCKED, &iport->flags); | ||
1678 | |||
1435 | rc = sas_phy_reset(phy, hard_reset); | 1679 | rc = sas_phy_reset(phy, hard_reset); |
1436 | 1680 | ||
1437 | /* Terminate in-progress I/O now. */ | 1681 | /* Terminate in-progress I/O now. */ |
@@ -1442,7 +1686,20 @@ static int isci_reset_device(struct domain_device *dev, int hard_reset) | |||
1442 | status = scic_remote_device_reset_complete(&idev->sci); | 1686 | status = scic_remote_device_reset_complete(&idev->sci); |
1443 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 1687 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
1444 | 1688 | ||
1445 | msleep(2000); /* just like mvsas */ | 1689 | /* If this is a device on an expander, bring the phy back up. */ |
1690 | if (!scsi_is_sas_phy_local(phy)) { | ||
1691 | /* A phy reset will cause the device to go away then reappear. | ||
1692 | * Since libsas will take action on incoming BCNs (eg. remove | ||
1693 | * a device going through an SMP phy-control driven reset), | ||
1694 | * we need to wait until the phy comes back up before letting | ||
1695 | * discovery proceed in libsas. | ||
1696 | */ | ||
1697 | isci_wait_for_smp_phy_reset(idev, phy->number); | ||
1698 | |||
1699 | spin_lock_irqsave(&ihost->scic_lock, flags); | ||
1700 | isci_port_bcn_enable(ihost, idev->isci_port); | ||
1701 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | ||
1702 | } | ||
1446 | 1703 | ||
1447 | if (status != SCI_SUCCESS) { | 1704 | if (status != SCI_SUCCESS) { |
1448 | dev_warn(&ihost->pdev->dev, | 1705 | dev_warn(&ihost->pdev->dev, |