1ba6d10abSLinus Torvalds // SPDX-License-Identifier: GPL-2.0-only
22908d778SJames Bottomley /*
32908d778SJames Bottomley * Serial Attached SCSI (SAS) Transport Layer initialization
42908d778SJames Bottomley *
52908d778SJames Bottomley * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
62908d778SJames Bottomley * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
72908d778SJames Bottomley */
82908d778SJames Bottomley
92908d778SJames Bottomley #include <linux/module.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
112908d778SJames Bottomley #include <linux/init.h>
122908d778SJames Bottomley #include <linux/device.h>
132908d778SJames Bottomley #include <linux/spinlock.h>
1481c757bcSDan Williams #include <scsi/sas_ata.h>
152908d778SJames Bottomley #include <scsi/scsi_host.h>
162908d778SJames Bottomley #include <scsi/scsi_device.h>
172908d778SJames Bottomley #include <scsi/scsi_transport.h>
182908d778SJames Bottomley #include <scsi/scsi_transport_sas.h>
192908d778SJames Bottomley
202908d778SJames Bottomley #include "sas_internal.h"
212908d778SJames Bottomley
22e15f669cSJason Yan #include "scsi_sas_internal.h"
232908d778SJames Bottomley
244fcf812cSDan Williams static struct kmem_cache *sas_task_cache;
251c393b97SJason Yan static struct kmem_cache *sas_event_cache;
264fcf812cSDan Williams
sas_alloc_task(gfp_t flags)274fcf812cSDan Williams struct sas_task *sas_alloc_task(gfp_t flags)
284fcf812cSDan Williams {
294fcf812cSDan Williams struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags);
304fcf812cSDan Williams
314fcf812cSDan Williams if (task) {
324fcf812cSDan Williams spin_lock_init(&task->task_state_lock);
334fcf812cSDan Williams task->task_state_flags = SAS_TASK_STATE_PENDING;
344fcf812cSDan Williams }
354fcf812cSDan Williams
364fcf812cSDan Williams return task;
374fcf812cSDan Williams }
384fcf812cSDan Williams
sas_alloc_slow_task(gfp_t flags)39f0bf750cSDan Williams struct sas_task *sas_alloc_slow_task(gfp_t flags)
40f0bf750cSDan Williams {
41f0bf750cSDan Williams struct sas_task *task = sas_alloc_task(flags);
42f0bf750cSDan Williams struct sas_task_slow *slow = kmalloc(sizeof(*slow), flags);
43f0bf750cSDan Williams
44f0bf750cSDan Williams if (!task || !slow) {
45f0bf750cSDan Williams if (task)
46f0bf750cSDan Williams kmem_cache_free(sas_task_cache, task);
47f0bf750cSDan Williams kfree(slow);
48f0bf750cSDan Williams return NULL;
49f0bf750cSDan Williams }
50f0bf750cSDan Williams
51f0bf750cSDan Williams task->slow_task = slow;
5277570eedSKees Cook slow->task = task;
5377570eedSKees Cook timer_setup(&slow->timer, NULL, 0);
54f0bf750cSDan Williams init_completion(&slow->completion);
55f0bf750cSDan Williams
56f0bf750cSDan Williams return task;
57f0bf750cSDan Williams }
58f0bf750cSDan Williams
sas_free_task(struct sas_task * task)594fcf812cSDan Williams void sas_free_task(struct sas_task *task)
604fcf812cSDan Williams {
614fcf812cSDan Williams if (task) {
62f0bf750cSDan Williams kfree(task->slow_task);
634fcf812cSDan Williams kmem_cache_free(sas_task_cache, task);
644fcf812cSDan Williams }
654fcf812cSDan Williams }
662908d778SJames Bottomley
672908d778SJames Bottomley /*------------ SAS addr hash -----------*/
sas_hash_addr(u8 * hashed,const u8 * sas_addr)682908d778SJames Bottomley void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
692908d778SJames Bottomley {
702908d778SJames Bottomley const u32 poly = 0x00DB2777;
712908d778SJames Bottomley u32 r = 0;
722908d778SJames Bottomley int i;
732908d778SJames Bottomley
747b27c5feSJohn Garry for (i = 0; i < SAS_ADDR_SIZE; i++) {
752908d778SJames Bottomley int b;
767b27c5feSJohn Garry
777b27c5feSJohn Garry for (b = (SAS_ADDR_SIZE - 1); b >= 0; b--) {
782908d778SJames Bottomley r <<= 1;
792908d778SJames Bottomley if ((1 << b) & sas_addr[i]) {
802908d778SJames Bottomley if (!(r & 0x01000000))
812908d778SJames Bottomley r ^= poly;
827b27c5feSJohn Garry } else if (r & 0x01000000) {
832908d778SJames Bottomley r ^= poly;
842908d778SJames Bottomley }
852908d778SJames Bottomley }
867b27c5feSJohn Garry }
872908d778SJames Bottomley
882908d778SJames Bottomley hashed[0] = (r >> 16) & 0xFF;
892908d778SJames Bottomley hashed[1] = (r >> 8) & 0xFF;
902908d778SJames Bottomley hashed[2] = r & 0xFF;
912908d778SJames Bottomley }
922908d778SJames Bottomley
sas_register_ha(struct sas_ha_struct * sas_ha)932908d778SJames Bottomley int sas_register_ha(struct sas_ha_struct *sas_ha)
942908d778SJames Bottomley {
9593bdbd06SJason Yan char name[64];
962908d778SJames Bottomley int error = 0;
972908d778SJames Bottomley
9887c8331fSDan Williams mutex_init(&sas_ha->disco_mutex);
992908d778SJames Bottomley spin_lock_init(&sas_ha->phy_port_lock);
1002908d778SJames Bottomley sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
1012908d778SJames Bottomley
102f8daa6e6SDan Williams set_bit(SAS_HA_REGISTERED, &sas_ha->state);
103e4a9c373SDan Williams spin_lock_init(&sas_ha->lock);
104b1124cd3SDan Williams mutex_init(&sas_ha->drain_mutex);
1055db45bdcSDan Williams init_waitqueue_head(&sas_ha->eh_wait_q);
106b1124cd3SDan Williams INIT_LIST_HEAD(&sas_ha->defer_q);
1075db45bdcSDan Williams INIT_LIST_HEAD(&sas_ha->eh_dev_q);
1086b0efb85SDarrick J. Wong
109f12486e0SJason Yan sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES;
110f12486e0SJason Yan
1112908d778SJames Bottomley error = sas_register_phys(sas_ha);
1122908d778SJames Bottomley if (error) {
11315ba7806SJohn Garry pr_notice("couldn't register sas phys:%d\n", error);
1142908d778SJames Bottomley return error;
1152908d778SJames Bottomley }
1162908d778SJames Bottomley
1172908d778SJames Bottomley error = sas_register_ports(sas_ha);
1182908d778SJames Bottomley if (error) {
11915ba7806SJohn Garry pr_notice("couldn't register sas ports:%d\n", error);
1202908d778SJames Bottomley goto Undo_phys;
1212908d778SJames Bottomley }
1222908d778SJames Bottomley
12393bdbd06SJason Yan error = -ENOMEM;
12493bdbd06SJason Yan snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev));
12593bdbd06SJason Yan sas_ha->event_q = create_singlethread_workqueue(name);
12693bdbd06SJason Yan if (!sas_ha->event_q)
12793bdbd06SJason Yan goto Undo_ports;
12893bdbd06SJason Yan
12993bdbd06SJason Yan snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev));
13093bdbd06SJason Yan sas_ha->disco_q = create_singlethread_workqueue(name);
13193bdbd06SJason Yan if (!sas_ha->disco_q)
13293bdbd06SJason Yan goto Undo_event_q;
13393bdbd06SJason Yan
134f456393eSDarrick J. Wong INIT_LIST_HEAD(&sas_ha->eh_done_q);
1353944f509SDan Williams INIT_LIST_HEAD(&sas_ha->eh_ata_q);
136f456393eSDarrick J. Wong
1372908d778SJames Bottomley return 0;
13893bdbd06SJason Yan
13993bdbd06SJason Yan Undo_event_q:
14093bdbd06SJason Yan destroy_workqueue(sas_ha->event_q);
1412908d778SJames Bottomley Undo_ports:
1422908d778SJames Bottomley sas_unregister_ports(sas_ha);
1432908d778SJames Bottomley Undo_phys:
1442908d778SJames Bottomley
1452908d778SJames Bottomley return error;
1462908d778SJames Bottomley }
147ce4fc333SJohn Garry EXPORT_SYMBOL_GPL(sas_register_ha);
1482908d778SJames Bottomley
sas_disable_events(struct sas_ha_struct * sas_ha)149303694eeSDan Williams static void sas_disable_events(struct sas_ha_struct *sas_ha)
1502908d778SJames Bottomley {
151b1124cd3SDan Williams /* Set the state to unregistered to avoid further unchained
1525d7f6d10SDan Williams * events to be queued, and flush any in-progress drainers
153b1124cd3SDan Williams */
1545d7f6d10SDan Williams mutex_lock(&sas_ha->drain_mutex);
155e4a9c373SDan Williams spin_lock_irq(&sas_ha->lock);
156f8daa6e6SDan Williams clear_bit(SAS_HA_REGISTERED, &sas_ha->state);
157e4a9c373SDan Williams spin_unlock_irq(&sas_ha->lock);
1585d7f6d10SDan Williams __sas_drain_work(sas_ha);
1595d7f6d10SDan Williams mutex_unlock(&sas_ha->drain_mutex);
160303694eeSDan Williams }
1616b0efb85SDarrick J. Wong
sas_unregister_ha(struct sas_ha_struct * sas_ha)162303694eeSDan Williams int sas_unregister_ha(struct sas_ha_struct *sas_ha)
163303694eeSDan Williams {
164303694eeSDan Williams sas_disable_events(sas_ha);
165cde3f74bSDarrick J. Wong sas_unregister_ports(sas_ha);
1665d7f6d10SDan Williams
1675d7f6d10SDan Williams /* flush unregistration work */
1685d7f6d10SDan Williams mutex_lock(&sas_ha->drain_mutex);
1695d7f6d10SDan Williams __sas_drain_work(sas_ha);
1705d7f6d10SDan Williams mutex_unlock(&sas_ha->drain_mutex);
171cde3f74bSDarrick J. Wong
17293bdbd06SJason Yan destroy_workqueue(sas_ha->disco_q);
17393bdbd06SJason Yan destroy_workqueue(sas_ha->event_q);
17493bdbd06SJason Yan
1752908d778SJames Bottomley return 0;
1762908d778SJames Bottomley }
177ce4fc333SJohn Garry EXPORT_SYMBOL_GPL(sas_unregister_ha);
1782908d778SJames Bottomley
sas_get_linkerrors(struct sas_phy * phy)1792908d778SJames Bottomley static int sas_get_linkerrors(struct sas_phy *phy)
1802908d778SJames Bottomley {
181ac013ed1SDan Williams if (scsi_is_sas_phy_local(phy)) {
182ac013ed1SDan Williams struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
183ac013ed1SDan Williams struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
184ac013ed1SDan Williams struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
185ac013ed1SDan Williams struct sas_internal *i =
186*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
187ac013ed1SDan Williams
188ac013ed1SDan Williams return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL);
189ac013ed1SDan Williams }
1902908d778SJames Bottomley
1912908d778SJames Bottomley return sas_smp_get_phy_events(phy);
1922908d778SJames Bottomley }
1932908d778SJames Bottomley
sas_try_ata_reset(struct asd_sas_phy * asd_phy)194ab526633SDan Williams int sas_try_ata_reset(struct asd_sas_phy *asd_phy)
195ab526633SDan Williams {
196ab526633SDan Williams struct domain_device *dev = NULL;
197ab526633SDan Williams
198ab526633SDan Williams /* try to route user requested link resets through libata */
199ab526633SDan Williams if (asd_phy->port)
200ab526633SDan Williams dev = asd_phy->port->port_dev;
201ab526633SDan Williams
202ab526633SDan Williams /* validate that dev has been probed */
203ab526633SDan Williams if (dev)
204ab526633SDan Williams dev = sas_find_dev_by_rphy(dev->rphy);
205ab526633SDan Williams
206ab526633SDan Williams if (dev && dev_is_sata(dev)) {
207ab526633SDan Williams sas_ata_schedule_reset(dev);
208ab526633SDan Williams sas_ata_wait_eh(dev);
209ab526633SDan Williams return 0;
210ab526633SDan Williams }
211ab526633SDan Williams
212ab526633SDan Williams return -ENODEV;
213ab526633SDan Williams }
214ab526633SDan Williams
215121246aeSBart Van Assche /*
21681c757bcSDan Williams * transport_sas_phy_reset - reset a phy and permit libata to manage the link
21781c757bcSDan Williams *
21881c757bcSDan Williams * phy reset request via sysfs in host workqueue context so we know we
21981c757bcSDan Williams * can block on eh and safely traverse the domain_device topology
22081c757bcSDan Williams */
transport_sas_phy_reset(struct sas_phy * phy,int hard_reset)22181c757bcSDan Williams static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
22281c757bcSDan Williams {
22381c757bcSDan Williams enum phy_func reset_type;
22481c757bcSDan Williams
22581c757bcSDan Williams if (hard_reset)
22681c757bcSDan Williams reset_type = PHY_FUNC_HARD_RESET;
22781c757bcSDan Williams else
22881c757bcSDan Williams reset_type = PHY_FUNC_LINK_RESET;
22981c757bcSDan Williams
23081c757bcSDan Williams if (scsi_is_sas_phy_local(phy)) {
23181c757bcSDan Williams struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
23281c757bcSDan Williams struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
23381c757bcSDan Williams struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
23481c757bcSDan Williams struct sas_internal *i =
235*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
23681c757bcSDan Williams
237ab526633SDan Williams if (!hard_reset && sas_try_ata_reset(asd_phy) == 0)
238ab526633SDan Williams return 0;
239ab526633SDan Williams return i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
24081c757bcSDan Williams } else {
24181c757bcSDan Williams struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
24281c757bcSDan Williams struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
24381c757bcSDan Williams struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
24481c757bcSDan Williams
24581c757bcSDan Williams if (ata_dev && !hard_reset) {
24681c757bcSDan Williams sas_ata_schedule_reset(ata_dev);
24781c757bcSDan Williams sas_ata_wait_eh(ata_dev);
248ab526633SDan Williams return 0;
24981c757bcSDan Williams } else
250ab526633SDan Williams return sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
25181c757bcSDan Williams }
25281c757bcSDan Williams }
25381c757bcSDan Williams
sas_phy_enable(struct sas_phy * phy,int enable)25400aeaf32SLuo Jiaxing int sas_phy_enable(struct sas_phy *phy, int enable)
255acbf167dSDarrick J. Wong {
256acbf167dSDarrick J. Wong int ret;
2572a559f4bSDan Williams enum phy_func cmd;
258acbf167dSDarrick J. Wong
259acbf167dSDarrick J. Wong if (enable)
2602a559f4bSDan Williams cmd = PHY_FUNC_LINK_RESET;
261acbf167dSDarrick J. Wong else
2622a559f4bSDan Williams cmd = PHY_FUNC_DISABLE;
263acbf167dSDarrick J. Wong
264acbf167dSDarrick J. Wong if (scsi_is_sas_phy_local(phy)) {
265acbf167dSDarrick J. Wong struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
266acbf167dSDarrick J. Wong struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
267acbf167dSDarrick J. Wong struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
268acbf167dSDarrick J. Wong struct sas_internal *i =
269*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
270acbf167dSDarrick J. Wong
2712a559f4bSDan Williams if (enable)
2722a559f4bSDan Williams ret = transport_sas_phy_reset(phy, 0);
2731f4fe89cSJeff Skirvin else
2742a559f4bSDan Williams ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL);
275acbf167dSDarrick J. Wong } else {
276acbf167dSDarrick J. Wong struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
277acbf167dSDarrick J. Wong struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
2782a559f4bSDan Williams
2792a559f4bSDan Williams if (enable)
2802a559f4bSDan Williams ret = transport_sas_phy_reset(phy, 0);
2812a559f4bSDan Williams else
2822a559f4bSDan Williams ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL);
283acbf167dSDarrick J. Wong }
284acbf167dSDarrick J. Wong return ret;
285acbf167dSDarrick J. Wong }
28600aeaf32SLuo Jiaxing EXPORT_SYMBOL_GPL(sas_phy_enable);
287acbf167dSDarrick J. Wong
sas_phy_reset(struct sas_phy * phy,int hard_reset)288dea22214SDarrick J. Wong int sas_phy_reset(struct sas_phy *phy, int hard_reset)
2892908d778SJames Bottomley {
2902908d778SJames Bottomley int ret;
2912908d778SJames Bottomley enum phy_func reset_type;
2922908d778SJames Bottomley
29326a2e68fSDan Williams if (!phy->enabled)
29426a2e68fSDan Williams return -ENODEV;
29526a2e68fSDan Williams
2962908d778SJames Bottomley if (hard_reset)
2972908d778SJames Bottomley reset_type = PHY_FUNC_HARD_RESET;
2982908d778SJames Bottomley else
2992908d778SJames Bottomley reset_type = PHY_FUNC_LINK_RESET;
3002908d778SJames Bottomley
3012908d778SJames Bottomley if (scsi_is_sas_phy_local(phy)) {
3022908d778SJames Bottomley struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
3032908d778SJames Bottomley struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
3042908d778SJames Bottomley struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
3052908d778SJames Bottomley struct sas_internal *i =
306*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
3072908d778SJames Bottomley
308a01e70e5SJames Bottomley ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
3092908d778SJames Bottomley } else {
3102908d778SJames Bottomley struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
3112908d778SJames Bottomley struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
312a01e70e5SJames Bottomley ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
3132908d778SJames Bottomley }
3142908d778SJames Bottomley return ret;
3152908d778SJames Bottomley }
316ce4fc333SJohn Garry EXPORT_SYMBOL_GPL(sas_phy_reset);
3172908d778SJames Bottomley
sas_set_phy_speed(struct sas_phy * phy,struct sas_phy_linkrates * rates)318acbf167dSDarrick J. Wong int sas_set_phy_speed(struct sas_phy *phy,
319a01e70e5SJames Bottomley struct sas_phy_linkrates *rates)
320a01e70e5SJames Bottomley {
321a01e70e5SJames Bottomley int ret;
322a01e70e5SJames Bottomley
323a01e70e5SJames Bottomley if ((rates->minimum_linkrate &&
324a01e70e5SJames Bottomley rates->minimum_linkrate > phy->maximum_linkrate) ||
325a01e70e5SJames Bottomley (rates->maximum_linkrate &&
326a01e70e5SJames Bottomley rates->maximum_linkrate < phy->minimum_linkrate))
327a01e70e5SJames Bottomley return -EINVAL;
328a01e70e5SJames Bottomley
329a01e70e5SJames Bottomley if (rates->minimum_linkrate &&
330a01e70e5SJames Bottomley rates->minimum_linkrate < phy->minimum_linkrate_hw)
331a01e70e5SJames Bottomley rates->minimum_linkrate = phy->minimum_linkrate_hw;
332a01e70e5SJames Bottomley
333a01e70e5SJames Bottomley if (rates->maximum_linkrate &&
334a01e70e5SJames Bottomley rates->maximum_linkrate > phy->maximum_linkrate_hw)
335a01e70e5SJames Bottomley rates->maximum_linkrate = phy->maximum_linkrate_hw;
336a01e70e5SJames Bottomley
337a01e70e5SJames Bottomley if (scsi_is_sas_phy_local(phy)) {
338a01e70e5SJames Bottomley struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
339a01e70e5SJames Bottomley struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
340a01e70e5SJames Bottomley struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
341a01e70e5SJames Bottomley struct sas_internal *i =
342*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
343a01e70e5SJames Bottomley
344a01e70e5SJames Bottomley ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
345a01e70e5SJames Bottomley rates);
346a01e70e5SJames Bottomley } else {
347a01e70e5SJames Bottomley struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
348a01e70e5SJames Bottomley struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
349a01e70e5SJames Bottomley ret = sas_smp_phy_control(ddev, phy->number,
350a01e70e5SJames Bottomley PHY_FUNC_LINK_RESET, rates);
351a01e70e5SJames Bottomley
352a01e70e5SJames Bottomley }
353a01e70e5SJames Bottomley
354a01e70e5SJames Bottomley return ret;
355a01e70e5SJames Bottomley }
356a01e70e5SJames Bottomley
sas_prep_resume_ha(struct sas_ha_struct * ha)357303694eeSDan Williams void sas_prep_resume_ha(struct sas_ha_struct *ha)
358303694eeSDan Williams {
359303694eeSDan Williams int i;
360303694eeSDan Williams
361303694eeSDan Williams set_bit(SAS_HA_REGISTERED, &ha->state);
3624ea775abSXiang Chen set_bit(SAS_HA_RESUMING, &ha->state);
363303694eeSDan Williams
364303694eeSDan Williams /* clear out any stale link events/data from the suspension path */
365303694eeSDan Williams for (i = 0; i < ha->num_phys; i++) {
366303694eeSDan Williams struct asd_sas_phy *phy = ha->sas_phy[i];
367303694eeSDan Williams
368303694eeSDan Williams memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
369303694eeSDan Williams phy->frame_rcvd_size = 0;
370303694eeSDan Williams }
371303694eeSDan Williams }
372303694eeSDan Williams EXPORT_SYMBOL(sas_prep_resume_ha);
373303694eeSDan Williams
phys_suspended(struct sas_ha_struct * ha)374303694eeSDan Williams static int phys_suspended(struct sas_ha_struct *ha)
375303694eeSDan Williams {
376303694eeSDan Williams int i, rc = 0;
377303694eeSDan Williams
378303694eeSDan Williams for (i = 0; i < ha->num_phys; i++) {
379303694eeSDan Williams struct asd_sas_phy *phy = ha->sas_phy[i];
380303694eeSDan Williams
381303694eeSDan Williams if (phy->suspended)
382303694eeSDan Williams rc++;
383303694eeSDan Williams }
384303694eeSDan Williams
385303694eeSDan Williams return rc;
386303694eeSDan Williams }
387303694eeSDan Williams
sas_resume_insert_broadcast_ha(struct sas_ha_struct * ha)388e31e1812SXiang Chen static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
389e31e1812SXiang Chen {
390e31e1812SXiang Chen int i;
391e31e1812SXiang Chen
392e31e1812SXiang Chen for (i = 0; i < ha->num_phys; i++) {
393e31e1812SXiang Chen struct asd_sas_port *port = ha->sas_port[i];
394e31e1812SXiang Chen struct domain_device *dev = port->port_dev;
395e31e1812SXiang Chen
396e31e1812SXiang Chen if (dev && dev_is_expander(dev->dev_type)) {
397e31e1812SXiang Chen struct asd_sas_phy *first_phy;
398e31e1812SXiang Chen
399e31e1812SXiang Chen spin_lock(&port->phy_list_lock);
400e31e1812SXiang Chen first_phy = list_first_entry_or_null(
401e31e1812SXiang Chen &port->phy_list, struct asd_sas_phy,
402e31e1812SXiang Chen port_phy_el);
403e31e1812SXiang Chen spin_unlock(&port->phy_list_lock);
404e31e1812SXiang Chen
405e31e1812SXiang Chen if (first_phy)
406e31e1812SXiang Chen sas_notify_port_event(first_phy,
407e31e1812SXiang Chen PORTE_BROADCAST_RCVD, GFP_KERNEL);
408e31e1812SXiang Chen }
409e31e1812SXiang Chen }
410e31e1812SXiang Chen }
411e31e1812SXiang Chen
_sas_resume_ha(struct sas_ha_struct * ha,bool drain)412fbefe228SJohn Garry static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
413303694eeSDan Williams {
414303694eeSDan Williams const unsigned long tmo = msecs_to_jiffies(25000);
415303694eeSDan Williams int i;
416303694eeSDan Williams
417303694eeSDan Williams /* deform ports on phys that did not resume
418303694eeSDan Williams * at this point we may be racing the phy coming back (as posted
419303694eeSDan Williams * by the lldd). So we post the event and once we are in the
420303694eeSDan Williams * libsas context check that the phy remains suspended before
421303694eeSDan Williams * tearing it down.
422303694eeSDan Williams */
423303694eeSDan Williams i = phys_suspended(ha);
424303694eeSDan Williams if (i)
425303694eeSDan Williams dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
426303694eeSDan Williams i, i > 1 ? "s" : "");
427303694eeSDan Williams wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
428303694eeSDan Williams for (i = 0; i < ha->num_phys; i++) {
429303694eeSDan Williams struct asd_sas_phy *phy = ha->sas_phy[i];
430303694eeSDan Williams
431303694eeSDan Williams if (phy->suspended) {
432303694eeSDan Williams dev_warn(&phy->phy->dev, "resume timeout\n");
433f76d9f1aSAhmed S. Darwish sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT,
43419a39831SAhmed S. Darwish GFP_KERNEL);
435303694eeSDan Williams }
436303694eeSDan Williams }
437303694eeSDan Williams
438303694eeSDan Williams /* all phys are back up or timed out, turn on i/o so we can
439303694eeSDan Williams * flush out disks that did not return
440303694eeSDan Williams */
441*1136a022SJohn Garry scsi_unblock_requests(ha->shost);
442fbefe228SJohn Garry if (drain)
443303694eeSDan Williams sas_drain_work(ha);
4444ea775abSXiang Chen clear_bit(SAS_HA_RESUMING, &ha->state);
445e31e1812SXiang Chen
446bf19aea4SXiang Chen sas_queue_deferred_work(ha);
447e31e1812SXiang Chen /* send event PORTE_BROADCAST_RCVD to identify some new inserted
448e31e1812SXiang Chen * disks for expander
449e31e1812SXiang Chen */
450e31e1812SXiang Chen sas_resume_insert_broadcast_ha(ha);
451303694eeSDan Williams }
452fbefe228SJohn Garry
sas_resume_ha(struct sas_ha_struct * ha)453fbefe228SJohn Garry void sas_resume_ha(struct sas_ha_struct *ha)
454fbefe228SJohn Garry {
455fbefe228SJohn Garry _sas_resume_ha(ha, true);
456fbefe228SJohn Garry }
457303694eeSDan Williams EXPORT_SYMBOL(sas_resume_ha);
458303694eeSDan Williams
459fbefe228SJohn Garry /* A no-sync variant, which does not call sas_drain_ha(). */
sas_resume_ha_no_sync(struct sas_ha_struct * ha)460fbefe228SJohn Garry void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
461fbefe228SJohn Garry {
462fbefe228SJohn Garry _sas_resume_ha(ha, false);
463fbefe228SJohn Garry }
464fbefe228SJohn Garry EXPORT_SYMBOL(sas_resume_ha_no_sync);
465fbefe228SJohn Garry
sas_suspend_ha(struct sas_ha_struct * ha)466303694eeSDan Williams void sas_suspend_ha(struct sas_ha_struct *ha)
467303694eeSDan Williams {
468303694eeSDan Williams int i;
469303694eeSDan Williams
470303694eeSDan Williams sas_disable_events(ha);
471*1136a022SJohn Garry scsi_block_requests(ha->shost);
472303694eeSDan Williams for (i = 0; i < ha->num_phys; i++) {
473303694eeSDan Williams struct asd_sas_port *port = ha->sas_port[i];
474303694eeSDan Williams
475303694eeSDan Williams sas_discover_event(port, DISCE_SUSPEND);
476303694eeSDan Williams }
477303694eeSDan Williams
478303694eeSDan Williams /* flush suspend events while unregistered */
479303694eeSDan Williams mutex_lock(&ha->drain_mutex);
480303694eeSDan Williams __sas_drain_work(ha);
481303694eeSDan Williams mutex_unlock(&ha->drain_mutex);
482303694eeSDan Williams }
483303694eeSDan Williams EXPORT_SYMBOL(sas_suspend_ha);
484303694eeSDan Williams
sas_phy_release(struct sas_phy * phy)4850b3e09daSDan Williams static void sas_phy_release(struct sas_phy *phy)
4860b3e09daSDan Williams {
4870b3e09daSDan Williams kfree(phy->hostdata);
4880b3e09daSDan Williams phy->hostdata = NULL;
4890b3e09daSDan Williams }
4900b3e09daSDan Williams
phy_reset_work(struct work_struct * work)4910b3e09daSDan Williams static void phy_reset_work(struct work_struct *work)
4920b3e09daSDan Williams {
49322b9153fSDan Williams struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work);
4940b3e09daSDan Williams
49581c757bcSDan Williams d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
4960b3e09daSDan Williams }
4970b3e09daSDan Williams
phy_enable_work(struct work_struct * work)4982a559f4bSDan Williams static void phy_enable_work(struct work_struct *work)
4992a559f4bSDan Williams {
50022b9153fSDan Williams struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work);
5012a559f4bSDan Williams
5022a559f4bSDan Williams d->enable_result = sas_phy_enable(d->phy, d->enable);
5032a559f4bSDan Williams }
5042a559f4bSDan Williams
sas_phy_setup(struct sas_phy * phy)5050b3e09daSDan Williams static int sas_phy_setup(struct sas_phy *phy)
5060b3e09daSDan Williams {
5070b3e09daSDan Williams struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
5080b3e09daSDan Williams
5090b3e09daSDan Williams if (!d)
5100b3e09daSDan Williams return -ENOMEM;
5110b3e09daSDan Williams
5120b3e09daSDan Williams mutex_init(&d->event_lock);
51322b9153fSDan Williams INIT_SAS_WORK(&d->reset_work, phy_reset_work);
51422b9153fSDan Williams INIT_SAS_WORK(&d->enable_work, phy_enable_work);
5150b3e09daSDan Williams d->phy = phy;
5160b3e09daSDan Williams phy->hostdata = d;
5170b3e09daSDan Williams
5180b3e09daSDan Williams return 0;
5190b3e09daSDan Williams }
5200b3e09daSDan Williams
queue_phy_reset(struct sas_phy * phy,int hard_reset)5210b3e09daSDan Williams static int queue_phy_reset(struct sas_phy *phy, int hard_reset)
5220b3e09daSDan Williams {
5230b3e09daSDan Williams struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
5240b3e09daSDan Williams struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
5250b3e09daSDan Williams struct sas_phy_data *d = phy->hostdata;
5260b3e09daSDan Williams int rc;
5270b3e09daSDan Williams
5280b3e09daSDan Williams if (!d)
5290b3e09daSDan Williams return -ENOMEM;
5300b3e09daSDan Williams
5311e82e462SXiang Chen pm_runtime_get_sync(ha->dev);
5320b3e09daSDan Williams /* libsas workqueue coordinates ata-eh reset with discovery */
5330b3e09daSDan Williams mutex_lock(&d->event_lock);
5340b3e09daSDan Williams d->reset_result = 0;
5350b3e09daSDan Williams d->hard_reset = hard_reset;
5360b3e09daSDan Williams
537e4a9c373SDan Williams spin_lock_irq(&ha->lock);
5380b3e09daSDan Williams sas_queue_work(ha, &d->reset_work);
539e4a9c373SDan Williams spin_unlock_irq(&ha->lock);
5400b3e09daSDan Williams
5410b3e09daSDan Williams rc = sas_drain_work(ha);
5420b3e09daSDan Williams if (rc == 0)
5430b3e09daSDan Williams rc = d->reset_result;
5440b3e09daSDan Williams mutex_unlock(&d->event_lock);
5451e82e462SXiang Chen pm_runtime_put_sync(ha->dev);
5460b3e09daSDan Williams
5470b3e09daSDan Williams return rc;
5480b3e09daSDan Williams }
5490b3e09daSDan Williams
queue_phy_enable(struct sas_phy * phy,int enable)5502a559f4bSDan Williams static int queue_phy_enable(struct sas_phy *phy, int enable)
5512a559f4bSDan Williams {
5522a559f4bSDan Williams struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
5532a559f4bSDan Williams struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
5542a559f4bSDan Williams struct sas_phy_data *d = phy->hostdata;
5552a559f4bSDan Williams int rc;
5562a559f4bSDan Williams
5572a559f4bSDan Williams if (!d)
5582a559f4bSDan Williams return -ENOMEM;
5592a559f4bSDan Williams
5601e82e462SXiang Chen pm_runtime_get_sync(ha->dev);
5612a559f4bSDan Williams /* libsas workqueue coordinates ata-eh reset with discovery */
5622a559f4bSDan Williams mutex_lock(&d->event_lock);
5632a559f4bSDan Williams d->enable_result = 0;
5642a559f4bSDan Williams d->enable = enable;
5652a559f4bSDan Williams
566e4a9c373SDan Williams spin_lock_irq(&ha->lock);
5672a559f4bSDan Williams sas_queue_work(ha, &d->enable_work);
568e4a9c373SDan Williams spin_unlock_irq(&ha->lock);
5692a559f4bSDan Williams
5702a559f4bSDan Williams rc = sas_drain_work(ha);
5712a559f4bSDan Williams if (rc == 0)
5722a559f4bSDan Williams rc = d->enable_result;
5732a559f4bSDan Williams mutex_unlock(&d->event_lock);
5741e82e462SXiang Chen pm_runtime_put_sync(ha->dev);
5752a559f4bSDan Williams
5762a559f4bSDan Williams return rc;
5772a559f4bSDan Williams }
5782a559f4bSDan Williams
5792908d778SJames Bottomley static struct sas_function_template sft = {
5802a559f4bSDan Williams .phy_enable = queue_phy_enable,
5810b3e09daSDan Williams .phy_reset = queue_phy_reset,
5820b3e09daSDan Williams .phy_setup = sas_phy_setup,
5830b3e09daSDan Williams .phy_release = sas_phy_release,
584a01e70e5SJames Bottomley .set_phy_speed = sas_set_phy_speed,
5852908d778SJames Bottomley .get_linkerrors = sas_get_linkerrors,
586ba1fc175SFUJITA Tomonori .smp_handler = sas_smp_handler,
5872908d778SJames Bottomley };
5882908d778SJames Bottomley
phy_event_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)5898eea9dd8SJason Yan static inline ssize_t phy_event_threshold_show(struct device *dev,
5908eea9dd8SJason Yan struct device_attribute *attr, char *buf)
5918eea9dd8SJason Yan {
5928eea9dd8SJason Yan struct Scsi_Host *shost = class_to_shost(dev);
5938eea9dd8SJason Yan struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
5948eea9dd8SJason Yan
5958eea9dd8SJason Yan return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres);
5968eea9dd8SJason Yan }
5978eea9dd8SJason Yan
phy_event_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)5988eea9dd8SJason Yan static inline ssize_t phy_event_threshold_store(struct device *dev,
5998eea9dd8SJason Yan struct device_attribute *attr,
6008eea9dd8SJason Yan const char *buf, size_t count)
6018eea9dd8SJason Yan {
6028eea9dd8SJason Yan struct Scsi_Host *shost = class_to_shost(dev);
6038eea9dd8SJason Yan struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
6048eea9dd8SJason Yan
6058eea9dd8SJason Yan sha->event_thres = simple_strtol(buf, NULL, 10);
6068eea9dd8SJason Yan
6078eea9dd8SJason Yan /* threshold cannot be set too small */
6088eea9dd8SJason Yan if (sha->event_thres < 32)
6098eea9dd8SJason Yan sha->event_thres = 32;
6108eea9dd8SJason Yan
6118eea9dd8SJason Yan return count;
6128eea9dd8SJason Yan }
6138eea9dd8SJason Yan
6148eea9dd8SJason Yan DEVICE_ATTR(phy_event_threshold,
6158eea9dd8SJason Yan S_IRUGO|S_IWUSR,
6168eea9dd8SJason Yan phy_event_threshold_show,
6178eea9dd8SJason Yan phy_event_threshold_store);
6188eea9dd8SJason Yan EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold);
6198eea9dd8SJason Yan
6202908d778SJames Bottomley struct scsi_transport_template *
sas_domain_attach_transport(struct sas_domain_function_template * dft)6212908d778SJames Bottomley sas_domain_attach_transport(struct sas_domain_function_template *dft)
6222908d778SJames Bottomley {
6232908d778SJames Bottomley struct scsi_transport_template *stt = sas_attach_transport(&sft);
6242908d778SJames Bottomley struct sas_internal *i;
6252908d778SJames Bottomley
6262908d778SJames Bottomley if (!stt)
6272908d778SJames Bottomley return stt;
6282908d778SJames Bottomley
6292908d778SJames Bottomley i = to_sas_internal(stt);
6302908d778SJames Bottomley i->dft = dft;
6312908d778SJames Bottomley stt->create_work_queue = 1;
6322908d778SJames Bottomley stt->eh_strategy_handler = sas_scsi_recover_host;
6332908d778SJames Bottomley
6342908d778SJames Bottomley return stt;
6352908d778SJames Bottomley }
6362908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_domain_attach_transport);
6372908d778SJames Bottomley
sas_alloc_event(struct asd_sas_phy * phy,gfp_t gfp_flags)6385d6a75a1SAhmed S. Darwish struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,
639c2d0f1a6SAhmed S. Darwish gfp_t gfp_flags)
6401c393b97SJason Yan {
641f12486e0SJason Yan struct asd_sas_event *event;
642f12486e0SJason Yan struct sas_ha_struct *sas_ha = phy->ha;
643f12486e0SJason Yan struct sas_internal *i =
644*1136a022SJohn Garry to_sas_internal(sas_ha->shost->transportt);
6451c393b97SJason Yan
646c2d0f1a6SAhmed S. Darwish event = kmem_cache_zalloc(sas_event_cache, gfp_flags);
647f12486e0SJason Yan if (!event)
648f12486e0SJason Yan return NULL;
649f12486e0SJason Yan
650f12486e0SJason Yan atomic_inc(&phy->event_nr);
651f12486e0SJason Yan
652f12486e0SJason Yan if (atomic_read(&phy->event_nr) > phy->ha->event_thres) {
653f12486e0SJason Yan if (i->dft->lldd_control_phy) {
654f12486e0SJason Yan if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) {
6553c236f8cSJohn Garry pr_notice("The phy%d bursting events, shut it down.\n",
656f12486e0SJason Yan phy->id);
657f76d9f1aSAhmed S. Darwish sas_notify_phy_event(phy, PHYE_SHUTDOWN,
658c2d0f1a6SAhmed S. Darwish gfp_flags);
659f12486e0SJason Yan }
660f12486e0SJason Yan } else {
661f12486e0SJason Yan /* Do not support PHY control, stop allocating events */
662f12486e0SJason Yan WARN_ONCE(1, "PHY control not supported.\n");
663f12486e0SJason Yan kmem_cache_free(sas_event_cache, event);
664f12486e0SJason Yan atomic_dec(&phy->event_nr);
665f12486e0SJason Yan event = NULL;
666f12486e0SJason Yan }
667f12486e0SJason Yan }
668f12486e0SJason Yan
669f12486e0SJason Yan return event;
6701c393b97SJason Yan }
6711c393b97SJason Yan
sas_free_event(struct asd_sas_event * event)6721c393b97SJason Yan void sas_free_event(struct asd_sas_event *event)
6731c393b97SJason Yan {
674f12486e0SJason Yan struct asd_sas_phy *phy = event->phy;
675f12486e0SJason Yan
6761c393b97SJason Yan kmem_cache_free(sas_event_cache, event);
677f12486e0SJason Yan atomic_dec(&phy->event_nr);
6781c393b97SJason Yan }
6791c393b97SJason Yan
6802908d778SJames Bottomley /* ---------- SAS Class register/unregister ---------- */
6812908d778SJames Bottomley
sas_class_init(void)6822908d778SJames Bottomley static int __init sas_class_init(void)
6832908d778SJames Bottomley {
6844fcf812cSDan Williams sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN);
6852908d778SJames Bottomley if (!sas_task_cache)
6861c393b97SJason Yan goto out;
6871c393b97SJason Yan
6881c393b97SJason Yan sas_event_cache = KMEM_CACHE(asd_sas_event, SLAB_HWCACHE_ALIGN);
6891c393b97SJason Yan if (!sas_event_cache)
6901c393b97SJason Yan goto free_task_kmem;
6912908d778SJames Bottomley
6922908d778SJames Bottomley return 0;
6931c393b97SJason Yan free_task_kmem:
6941c393b97SJason Yan kmem_cache_destroy(sas_task_cache);
6951c393b97SJason Yan out:
6961c393b97SJason Yan return -ENOMEM;
6972908d778SJames Bottomley }
6982908d778SJames Bottomley
sas_class_exit(void)6992908d778SJames Bottomley static void __exit sas_class_exit(void)
7002908d778SJames Bottomley {
7012908d778SJames Bottomley kmem_cache_destroy(sas_task_cache);
7021c393b97SJason Yan kmem_cache_destroy(sas_event_cache);
7032908d778SJames Bottomley }
7042908d778SJames Bottomley
7052908d778SJames Bottomley MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
7062908d778SJames Bottomley MODULE_DESCRIPTION("SAS Transport Layer");
7072908d778SJames Bottomley MODULE_LICENSE("GPL v2");
7082908d778SJames Bottomley
7092908d778SJames Bottomley module_init(sas_class_init);
7102908d778SJames Bottomley module_exit(sas_class_exit);
7112908d778SJames Bottomley
712