xref: /openbmc/linux/drivers/scsi/libsas/sas_init.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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