xref: /linux/drivers/s390/cio/device_fsm.c (revision c13aca79ff3c4af5fd31a5b2743a90eba6e36a26)
1724117b7SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * finite state machine for device handling
41da177e4SLinus Torvalds  *
5c820de39SCornelia Huck  *    Copyright IBM Corp. 2002, 2008
64ce3b30cSCornelia Huck  *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
71da177e4SLinus Torvalds  *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/module.h>
111da177e4SLinus Torvalds #include <linux/init.h>
124e57b681STim Schmielau #include <linux/jiffies.h>
134e57b681STim Schmielau #include <linux/string.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <asm/ccwdev.h>
164c24da79SCornelia Huck #include <asm/cio.h>
17e5854a58SPeter Oberparleiter #include <asm/chpid.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include "cio.h"
201da177e4SLinus Torvalds #include "cio_debug.h"
211da177e4SLinus Torvalds #include "css.h"
221da177e4SLinus Torvalds #include "device.h"
231da177e4SLinus Torvalds #include "chsc.h"
241da177e4SLinus Torvalds #include "ioasm.h"
25e6b6e10aSPeter Oberparleiter #include "chp.h"
261da177e4SLinus Torvalds 
2714ff56bbSSebastian Ott static int timeout_log_enabled;
2814ff56bbSSebastian Ott 
2914ff56bbSSebastian Ott static int __init ccw_timeout_log_setup(char *unused)
3014ff56bbSSebastian Ott {
3114ff56bbSSebastian Ott 	timeout_log_enabled = 1;
3214ff56bbSSebastian Ott 	return 1;
3314ff56bbSSebastian Ott }
3414ff56bbSSebastian Ott 
3514ff56bbSSebastian Ott __setup("ccw_timeout_log", ccw_timeout_log_setup);
3614ff56bbSSebastian Ott 
3714ff56bbSSebastian Ott static void ccw_timeout_log(struct ccw_device *cdev)
3814ff56bbSSebastian Ott {
3914ff56bbSSebastian Ott 	struct schib schib;
4014ff56bbSSebastian Ott 	struct subchannel *sch;
41cd6b4f27SCornelia Huck 	struct io_subchannel_private *private;
4283262d63SPeter Oberparleiter 	union orb *orb;
4314ff56bbSSebastian Ott 	int cc;
4414ff56bbSSebastian Ott 
4514ff56bbSSebastian Ott 	sch = to_subchannel(cdev->dev.parent);
46cd6b4f27SCornelia Huck 	private = to_io_private(sch);
4783262d63SPeter Oberparleiter 	orb = &private->orb;
4862e65da9SPeter Oberparleiter 	cc = stsch(sch->schid, &schib);
4914ff56bbSSebastian Ott 
5014ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
511aae0560SHeiko Carstens 	       "device information:\n", get_tod_clock());
5214ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: orb:\n");
5314ff56bbSSebastian Ott 	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
5483262d63SPeter Oberparleiter 		       orb, sizeof(*orb), 0);
552a0217d5SKay Sievers 	printk(KERN_WARNING "cio: ccw device bus id: %s\n",
562a0217d5SKay Sievers 	       dev_name(&cdev->dev));
572a0217d5SKay Sievers 	printk(KERN_WARNING "cio: subchannel bus id: %s\n",
582a0217d5SKay Sievers 	       dev_name(&sch->dev));
5914ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
6014ff56bbSSebastian Ott 	       "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
6114ff56bbSSebastian Ott 
6283262d63SPeter Oberparleiter 	if (orb->tm.b) {
6383262d63SPeter Oberparleiter 		printk(KERN_WARNING "cio: orb indicates transport mode\n");
6483262d63SPeter Oberparleiter 		printk(KERN_WARNING "cio: last tcw:\n");
6583262d63SPeter Oberparleiter 		print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
6683262d63SPeter Oberparleiter 			       (void *)(addr_t)orb->tm.tcw,
6783262d63SPeter Oberparleiter 			       sizeof(struct tcw), 0);
6883262d63SPeter Oberparleiter 	} else {
6983262d63SPeter Oberparleiter 		printk(KERN_WARNING "cio: orb indicates command mode\n");
7083262d63SPeter Oberparleiter 		if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
7183262d63SPeter Oberparleiter 		    (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
7283262d63SPeter Oberparleiter 			printk(KERN_WARNING "cio: last channel program "
7383262d63SPeter Oberparleiter 			       "(intern):\n");
7414ff56bbSSebastian Ott 		else
7514ff56bbSSebastian Ott 			printk(KERN_WARNING "cio: last channel program:\n");
7614ff56bbSSebastian Ott 
7714ff56bbSSebastian Ott 		print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
7883262d63SPeter Oberparleiter 			       (void *)(addr_t)orb->cmd.cpa,
79cd6b4f27SCornelia Huck 			       sizeof(struct ccw1), 0);
8083262d63SPeter Oberparleiter 	}
8114ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: ccw device state: %d\n",
8214ff56bbSSebastian Ott 	       cdev->private->state);
8314ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
8414ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: schib:\n");
8514ff56bbSSebastian Ott 	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
8614ff56bbSSebastian Ott 		       &schib, sizeof(schib), 0);
8714ff56bbSSebastian Ott 	printk(KERN_WARNING "cio: ccw device flags:\n");
8814ff56bbSSebastian Ott 	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
8914ff56bbSSebastian Ott 		       &cdev->private->flags, sizeof(cdev->private->flags), 0);
9014ff56bbSSebastian Ott }
9114ff56bbSSebastian Ott 
921da177e4SLinus Torvalds /*
931da177e4SLinus Torvalds  * Timeout function. It just triggers a DEV_EVENT_TIMEOUT.
941da177e4SLinus Torvalds  */
95846d0c6fSKees Cook void
96846d0c6fSKees Cook ccw_device_timeout(struct timer_list *t)
971da177e4SLinus Torvalds {
98846d0c6fSKees Cook 	struct ccw_device_private *priv = from_timer(priv, t, timer);
99846d0c6fSKees Cook 	struct ccw_device *cdev = priv->cdev;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	spin_lock_irq(cdev->ccwlock);
10214ff56bbSSebastian Ott 	if (timeout_log_enabled)
10314ff56bbSSebastian Ott 		ccw_timeout_log(cdev);
1041da177e4SLinus Torvalds 	dev_fsm_event(cdev, DEV_EVENT_TIMEOUT);
1051da177e4SLinus Torvalds 	spin_unlock_irq(cdev->ccwlock);
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds  * Set timeout
1101da177e4SLinus Torvalds  */
1111da177e4SLinus Torvalds void
1121da177e4SLinus Torvalds ccw_device_set_timeout(struct ccw_device *cdev, int expires)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds 	if (expires == 0) {
1151da177e4SLinus Torvalds 		del_timer(&cdev->private->timer);
1161da177e4SLinus Torvalds 		return;
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 	if (timer_pending(&cdev->private->timer)) {
1191da177e4SLinus Torvalds 		if (mod_timer(&cdev->private->timer, jiffies + expires))
1201da177e4SLinus Torvalds 			return;
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds 	cdev->private->timer.expires = jiffies + expires;
1231da177e4SLinus Torvalds 	add_timer(&cdev->private->timer);
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds int
1271da177e4SLinus Torvalds ccw_device_cancel_halt_clear(struct ccw_device *cdev)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds 	struct subchannel *sch;
1301da177e4SLinus Torvalds 	int ret;
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
1335434da4dSDong Jia Shi 	ret = cio_cancel_halt_clear(sch, &cdev->private->iretry);
1345434da4dSDong Jia Shi 
1355434da4dSDong Jia Shi 	if (ret == -EIO)
136376ae475SPeter Oberparleiter 		CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n",
1375434da4dSDong Jia Shi 			      cdev->private->dev_id.ssid,
1385434da4dSDong Jia Shi 			      cdev->private->dev_id.devno);
1395434da4dSDong Jia Shi 
1405434da4dSDong Jia Shi 	return ret;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143823d494aSSebastian Ott void ccw_device_update_sense_data(struct ccw_device *cdev)
1441da177e4SLinus Torvalds {
145823d494aSSebastian Ott 	memset(&cdev->id, 0, sizeof(cdev->id));
146823d494aSSebastian Ott 	cdev->id.cu_type   = cdev->private->senseid.cu_type;
147823d494aSSebastian Ott 	cdev->id.cu_model  = cdev->private->senseid.cu_model;
148823d494aSSebastian Ott 	cdev->id.dev_type  = cdev->private->senseid.dev_type;
149823d494aSSebastian Ott 	cdev->id.dev_model = cdev->private->senseid.dev_model;
1501da177e4SLinus Torvalds }
151823d494aSSebastian Ott 
152823d494aSSebastian Ott int ccw_device_test_sense_data(struct ccw_device *cdev)
153823d494aSSebastian Ott {
154823d494aSSebastian Ott 	return cdev->id.cu_type == cdev->private->senseid.cu_type &&
155823d494aSSebastian Ott 		cdev->id.cu_model == cdev->private->senseid.cu_model &&
156823d494aSSebastian Ott 		cdev->id.dev_type == cdev->private->senseid.dev_type &&
157823d494aSSebastian Ott 		cdev->id.dev_model == cdev->private->senseid.dev_model;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds /*
1611da177e4SLinus Torvalds  * The machine won't give us any notification by machine check if a chpid has
1621da177e4SLinus Torvalds  * been varied online on the SE so we have to find out by magic (i. e. driving
1631da177e4SLinus Torvalds  * the channel subsystem to device selection and updating our path masks).
1641da177e4SLinus Torvalds  */
1654d284cacSHeiko Carstens static void
1661da177e4SLinus Torvalds __recover_lost_chpids(struct subchannel *sch, int old_lpm)
1671da177e4SLinus Torvalds {
1681da177e4SLinus Torvalds 	int mask, i;
169f86635faSPeter Oberparleiter 	struct chp_id chpid;
1701da177e4SLinus Torvalds 
171f86635faSPeter Oberparleiter 	chp_id_init(&chpid);
1721da177e4SLinus Torvalds 	for (i = 0; i<8; i++) {
1731da177e4SLinus Torvalds 		mask = 0x80 >> i;
1741da177e4SLinus Torvalds 		if (!(sch->lpm & mask))
1751da177e4SLinus Torvalds 			continue;
1761da177e4SLinus Torvalds 		if (old_lpm & mask)
1771da177e4SLinus Torvalds 			continue;
178f86635faSPeter Oberparleiter 		chpid.id = sch->schib.pmcw.chpid[i];
17983b3370cSPeter Oberparleiter 		if (!chp_is_registered(chpid))
18083b3370cSPeter Oberparleiter 			css_schedule_eval_all();
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds /*
1851da177e4SLinus Torvalds  * Stop device recognition.
1861da177e4SLinus Torvalds  */
1871da177e4SLinus Torvalds static void
1881da177e4SLinus Torvalds ccw_device_recog_done(struct ccw_device *cdev, int state)
1891da177e4SLinus Torvalds {
1901da177e4SLinus Torvalds 	struct subchannel *sch;
191823d494aSSebastian Ott 	int old_lpm;
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
1941da177e4SLinus Torvalds 
195ec64333cSSebastian Ott 	if (cio_disable_subchannel(sch))
196ec64333cSSebastian Ott 		state = DEV_STATE_NOT_OPER;
1971da177e4SLinus Torvalds 	/*
1981da177e4SLinus Torvalds 	 * Now that we tried recognition, we have performed device selection
1991da177e4SLinus Torvalds 	 * through ssch() and the path information is up to date.
2001da177e4SLinus Torvalds 	 */
2011da177e4SLinus Torvalds 	old_lpm = sch->lpm;
202cdb912a4SSebastian Ott 
2034ffa9234SCornelia Huck 	/* Check since device may again have become not operational. */
204cdb912a4SSebastian Ott 	if (cio_update_schib(sch))
2054ffa9234SCornelia Huck 		state = DEV_STATE_NOT_OPER;
206cdb912a4SSebastian Ott 	else
207cdb912a4SSebastian Ott 		sch->lpm = sch->schib.pmcw.pam & sch->opm;
208cdb912a4SSebastian Ott 
2091da177e4SLinus Torvalds 	if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
2101da177e4SLinus Torvalds 		/* Force reprobe on all chpids. */
2111da177e4SLinus Torvalds 		old_lpm = 0;
2121da177e4SLinus Torvalds 	if (sch->lpm != old_lpm)
2131da177e4SLinus Torvalds 		__recover_lost_chpids(sch, old_lpm);
21447593bfaSSebastian Ott 	if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID &&
21547593bfaSSebastian Ott 	    (state == DEV_STATE_NOT_OPER || state == DEV_STATE_BOXED)) {
2161da177e4SLinus Torvalds 		cdev->private->flags.recog_done = 1;
2171da177e4SLinus Torvalds 		cdev->private->state = DEV_STATE_DISCONNECTED;
218156013ffSSebastian Ott 		wake_up(&cdev->private->wait_q);
2191da177e4SLinus Torvalds 		return;
2201da177e4SLinus Torvalds 	}
221823d494aSSebastian Ott 	if (cdev->private->flags.resuming) {
222823d494aSSebastian Ott 		cdev->private->state = state;
223823d494aSSebastian Ott 		cdev->private->flags.recog_done = 1;
224823d494aSSebastian Ott 		wake_up(&cdev->private->wait_q);
225823d494aSSebastian Ott 		return;
226823d494aSSebastian Ott 	}
2271da177e4SLinus Torvalds 	switch (state) {
2281da177e4SLinus Torvalds 	case DEV_STATE_NOT_OPER:
2291da177e4SLinus Torvalds 		break;
2301da177e4SLinus Torvalds 	case DEV_STATE_OFFLINE:
231823d494aSSebastian Ott 		if (!cdev->online) {
232823d494aSSebastian Ott 			ccw_device_update_sense_data(cdev);
2331da177e4SLinus Torvalds 			break;
234823d494aSSebastian Ott 		}
235823d494aSSebastian Ott 		cdev->private->state = DEV_STATE_OFFLINE;
236823d494aSSebastian Ott 		cdev->private->flags.recog_done = 1;
237823d494aSSebastian Ott 		if (ccw_device_test_sense_data(cdev)) {
238823d494aSSebastian Ott 			cdev->private->flags.donotify = 1;
239823d494aSSebastian Ott 			ccw_device_online(cdev);
240823d494aSSebastian Ott 			wake_up(&cdev->private->wait_q);
241823d494aSSebastian Ott 		} else {
242823d494aSSebastian Ott 			ccw_device_update_sense_data(cdev);
24337de53bbSPeter Oberparleiter 			ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
244823d494aSSebastian Ott 		}
245823d494aSSebastian Ott 		return;
2461da177e4SLinus Torvalds 	case DEV_STATE_BOXED:
24747593bfaSSebastian Ott 		if (cdev->id.cu_type != 0) { /* device was recognized before */
24847593bfaSSebastian Ott 			cdev->private->flags.recog_done = 1;
24947593bfaSSebastian Ott 			cdev->private->state = DEV_STATE_BOXED;
25047593bfaSSebastian Ott 			wake_up(&cdev->private->wait_q);
25147593bfaSSebastian Ott 			return;
25247593bfaSSebastian Ott 		}
2531da177e4SLinus Torvalds 		break;
2541da177e4SLinus Torvalds 	}
2551da177e4SLinus Torvalds 	cdev->private->state = state;
2561da177e4SLinus Torvalds 	io_subchannel_recog_done(cdev);
2571da177e4SLinus Torvalds 	wake_up(&cdev->private->wait_q);
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds /*
2611da177e4SLinus Torvalds  * Function called from device_id.c after sense id has completed.
2621da177e4SLinus Torvalds  */
2631da177e4SLinus Torvalds void
2641da177e4SLinus Torvalds ccw_device_sense_id_done(struct ccw_device *cdev, int err)
2651da177e4SLinus Torvalds {
2661da177e4SLinus Torvalds 	switch (err) {
2671da177e4SLinus Torvalds 	case 0:
2681da177e4SLinus Torvalds 		ccw_device_recog_done(cdev, DEV_STATE_OFFLINE);
2691da177e4SLinus Torvalds 		break;
2701da177e4SLinus Torvalds 	case -ETIME:		/* Sense id stopped by timeout. */
2711da177e4SLinus Torvalds 		ccw_device_recog_done(cdev, DEV_STATE_BOXED);
2721da177e4SLinus Torvalds 		break;
2731da177e4SLinus Torvalds 	default:
2741da177e4SLinus Torvalds 		ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
2751da177e4SLinus Torvalds 		break;
2761da177e4SLinus Torvalds 	}
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
27976e6fb4bSSebastian Ott /**
28076e6fb4bSSebastian Ott   * ccw_device_notify() - inform the device's driver about an event
28125985edcSLucas De Marchi   * @cdev: device for which an event occurred
28276e6fb4bSSebastian Ott   * @event: event that occurred
28376e6fb4bSSebastian Ott   *
28476e6fb4bSSebastian Ott   * Returns:
28576e6fb4bSSebastian Ott   *   -%EINVAL if the device is offline or has no driver.
28676e6fb4bSSebastian Ott   *   -%EOPNOTSUPP if the device's driver has no notifier registered.
28776e6fb4bSSebastian Ott   *   %NOTIFY_OK if the driver wants to keep the device.
28876e6fb4bSSebastian Ott   *   %NOTIFY_BAD if the driver doesn't want to keep the device.
28976e6fb4bSSebastian Ott   */
290c820de39SCornelia Huck int ccw_device_notify(struct ccw_device *cdev, int event)
291c820de39SCornelia Huck {
29276e6fb4bSSebastian Ott 	int ret = -EINVAL;
29376e6fb4bSSebastian Ott 
294c820de39SCornelia Huck 	if (!cdev->drv)
29576e6fb4bSSebastian Ott 		goto out;
296c820de39SCornelia Huck 	if (!cdev->online)
29776e6fb4bSSebastian Ott 		goto out;
29891c36919SPeter Oberparleiter 	CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
29991c36919SPeter Oberparleiter 		      cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
30091c36919SPeter Oberparleiter 		      event);
30176e6fb4bSSebastian Ott 	if (!cdev->drv->notify) {
30276e6fb4bSSebastian Ott 		ret = -EOPNOTSUPP;
30376e6fb4bSSebastian Ott 		goto out;
30476e6fb4bSSebastian Ott 	}
30576e6fb4bSSebastian Ott 	if (cdev->drv->notify(cdev, event))
30676e6fb4bSSebastian Ott 		ret = NOTIFY_OK;
30776e6fb4bSSebastian Ott 	else
30876e6fb4bSSebastian Ott 		ret = NOTIFY_BAD;
30976e6fb4bSSebastian Ott out:
31076e6fb4bSSebastian Ott 	return ret;
311c820de39SCornelia Huck }
312c820de39SCornelia Huck 
31391c36919SPeter Oberparleiter static void ccw_device_oper_notify(struct ccw_device *cdev)
31491c36919SPeter Oberparleiter {
315585b954eSSebastian Ott 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
316585b954eSSebastian Ott 
31776e6fb4bSSebastian Ott 	if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
31891c36919SPeter Oberparleiter 		/* Reenable channel measurements, if needed. */
31937de53bbSPeter Oberparleiter 		ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
320585b954eSSebastian Ott 		/* Save indication for new paths. */
321585b954eSSebastian Ott 		cdev->private->path_new_mask = sch->vpm;
32291c36919SPeter Oberparleiter 		return;
32391c36919SPeter Oberparleiter 	}
3241da177e4SLinus Torvalds 	/* Driver doesn't want device back. */
32591c36919SPeter Oberparleiter 	ccw_device_set_notoper(cdev);
32637de53bbSPeter Oberparleiter 	ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
32794bb0633SCornelia Huck }
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds /*
3301da177e4SLinus Torvalds  * Finished with online/offline processing.
3311da177e4SLinus Torvalds  */
3321da177e4SLinus Torvalds static void
3331da177e4SLinus Torvalds ccw_device_done(struct ccw_device *cdev, int state)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	struct subchannel *sch;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
3381da177e4SLinus Torvalds 
339f1ee3281SCornelia Huck 	ccw_device_set_timeout(cdev, 0);
340f1ee3281SCornelia Huck 
3411da177e4SLinus Torvalds 	if (state != DEV_STATE_ONLINE)
3421da177e4SLinus Torvalds 		cio_disable_subchannel(sch);
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 	/* Reset device status. */
3451da177e4SLinus Torvalds 	memset(&cdev->private->irb, 0, sizeof(struct irb));
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	cdev->private->state = state;
3481da177e4SLinus Torvalds 
3499a332116SPeter Oberparleiter 	switch (state) {
3509a332116SPeter Oberparleiter 	case DEV_STATE_BOXED:
351139b83ddSMichael Ernst 		CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
35278964268SCornelia Huck 			      cdev->private->dev_id.devno, sch->schid.sch_no);
35376e6fb4bSSebastian Ott 		if (cdev->online &&
35476e6fb4bSSebastian Ott 		    ccw_device_notify(cdev, CIO_BOXED) != NOTIFY_OK)
35537de53bbSPeter Oberparleiter 			ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
35647593bfaSSebastian Ott 		cdev->private->flags.donotify = 0;
3579a332116SPeter Oberparleiter 		break;
3589a332116SPeter Oberparleiter 	case DEV_STATE_NOT_OPER:
359626e476aSSebastian Ott 		CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n",
360626e476aSSebastian Ott 			      cdev->private->dev_id.devno, sch->schid.sch_no);
36176e6fb4bSSebastian Ott 		if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
36237de53bbSPeter Oberparleiter 			ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
3636afcc775SPeter Oberparleiter 		else
3646afcc775SPeter Oberparleiter 			ccw_device_set_disconnected(cdev);
365626e476aSSebastian Ott 		cdev->private->flags.donotify = 0;
3669a332116SPeter Oberparleiter 		break;
3679a332116SPeter Oberparleiter 	case DEV_STATE_DISCONNECTED:
3689a332116SPeter Oberparleiter 		CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel "
3699a332116SPeter Oberparleiter 			      "%04x\n", cdev->private->dev_id.devno,
3709a332116SPeter Oberparleiter 			      sch->schid.sch_no);
3719bf05098SSebastian Ott 		if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) {
3729bf05098SSebastian Ott 			cdev->private->state = DEV_STATE_NOT_OPER;
37337de53bbSPeter Oberparleiter 			ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
3749bf05098SSebastian Ott 		} else
3759a332116SPeter Oberparleiter 			ccw_device_set_disconnected(cdev);
3769a332116SPeter Oberparleiter 		cdev->private->flags.donotify = 0;
3779a332116SPeter Oberparleiter 		break;
3789a332116SPeter Oberparleiter 	default:
3799a332116SPeter Oberparleiter 		break;
380626e476aSSebastian Ott 	}
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 	if (cdev->private->flags.donotify) {
3831da177e4SLinus Torvalds 		cdev->private->flags.donotify = 0;
38491c36919SPeter Oberparleiter 		ccw_device_oper_notify(cdev);
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 	wake_up(&cdev->private->wait_q);
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds /*
3901da177e4SLinus Torvalds  * Start device recognition.
3911da177e4SLinus Torvalds  */
392736b5db8SPeter Oberparleiter void ccw_device_recognition(struct ccw_device *cdev)
3931da177e4SLinus Torvalds {
394736b5db8SPeter Oberparleiter 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	/*
3971da177e4SLinus Torvalds 	 * We used to start here with a sense pgid to find out whether a device
3981da177e4SLinus Torvalds 	 * is locked by someone else. Unfortunately, the sense pgid command
3991da177e4SLinus Torvalds 	 * code has other meanings on devices predating the path grouping
4001da177e4SLinus Torvalds 	 * algorithm, so we start with sense id and box the device after an
4011da177e4SLinus Torvalds 	 * timeout (or if sense pgid during path verification detects the device
4021da177e4SLinus Torvalds 	 * is locked, as may happen on newer devices).
4031da177e4SLinus Torvalds 	 */
4041da177e4SLinus Torvalds 	cdev->private->flags.recog_done = 0;
4051da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_SENSE_ID;
406736b5db8SPeter Oberparleiter 	if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) {
407736b5db8SPeter Oberparleiter 		ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
408736b5db8SPeter Oberparleiter 		return;
409736b5db8SPeter Oberparleiter 	}
4101da177e4SLinus Torvalds 	ccw_device_sense_id_start(cdev);
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds /*
41439f5360bSPeter Oberparleiter  * Handle events for states that use the ccw request infrastructure.
4151da177e4SLinus Torvalds  */
41639f5360bSPeter Oberparleiter static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
4171da177e4SLinus Torvalds {
41839f5360bSPeter Oberparleiter 	switch (e) {
41939f5360bSPeter Oberparleiter 	case DEV_EVENT_NOTOPER:
42039f5360bSPeter Oberparleiter 		ccw_request_notoper(cdev);
4211da177e4SLinus Torvalds 		break;
42239f5360bSPeter Oberparleiter 	case DEV_EVENT_INTERRUPT:
42339f5360bSPeter Oberparleiter 		ccw_request_handler(cdev);
42439f5360bSPeter Oberparleiter 		break;
42539f5360bSPeter Oberparleiter 	case DEV_EVENT_TIMEOUT:
42639f5360bSPeter Oberparleiter 		ccw_request_timeout(cdev);
4271da177e4SLinus Torvalds 		break;
4281da177e4SLinus Torvalds 	default:
42939f5360bSPeter Oberparleiter 		break;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
433585b954eSSebastian Ott static void ccw_device_report_path_events(struct ccw_device *cdev)
434585b954eSSebastian Ott {
435585b954eSSebastian Ott 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
436585b954eSSebastian Ott 	int path_event[8];
437585b954eSSebastian Ott 	int chp, mask;
438585b954eSSebastian Ott 
439585b954eSSebastian Ott 	for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
440585b954eSSebastian Ott 		path_event[chp] = PE_NONE;
441585b954eSSebastian Ott 		if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
442585b954eSSebastian Ott 			path_event[chp] |= PE_PATH_GONE;
443585b954eSSebastian Ott 		if (mask & cdev->private->path_new_mask & sch->vpm)
444585b954eSSebastian Ott 			path_event[chp] |= PE_PATH_AVAILABLE;
445585b954eSSebastian Ott 		if (mask & cdev->private->pgid_reset_mask & sch->vpm)
446585b954eSSebastian Ott 			path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
447585b954eSSebastian Ott 	}
448585b954eSSebastian Ott 	if (cdev->online && cdev->drv->path_event)
449585b954eSSebastian Ott 		cdev->drv->path_event(cdev, path_event);
450585b954eSSebastian Ott }
451585b954eSSebastian Ott 
452585b954eSSebastian Ott static void ccw_device_reset_path_events(struct ccw_device *cdev)
453585b954eSSebastian Ott {
454585b954eSSebastian Ott 	cdev->private->path_gone_mask = 0;
455585b954eSSebastian Ott 	cdev->private->path_new_mask = 0;
456585b954eSSebastian Ott 	cdev->private->pgid_reset_mask = 0;
457585b954eSSebastian Ott }
458585b954eSSebastian Ott 
45950c8e31fSSebastian Ott static void create_fake_irb(struct irb *irb, int type)
46050c8e31fSSebastian Ott {
46150c8e31fSSebastian Ott 	memset(irb, 0, sizeof(*irb));
46250c8e31fSSebastian Ott 	if (type == FAKE_CMD_IRB) {
46350c8e31fSSebastian Ott 		struct cmd_scsw *scsw = &irb->scsw.cmd;
46450c8e31fSSebastian Ott 		scsw->cc = 1;
46550c8e31fSSebastian Ott 		scsw->fctl = SCSW_FCTL_START_FUNC;
46650c8e31fSSebastian Ott 		scsw->actl = SCSW_ACTL_START_PEND;
46750c8e31fSSebastian Ott 		scsw->stctl = SCSW_STCTL_STATUS_PEND;
46850c8e31fSSebastian Ott 	} else if (type == FAKE_TM_IRB) {
46950c8e31fSSebastian Ott 		struct tm_scsw *scsw = &irb->scsw.tm;
47050c8e31fSSebastian Ott 		scsw->x = 1;
47150c8e31fSSebastian Ott 		scsw->cc = 1;
47250c8e31fSSebastian Ott 		scsw->fctl = SCSW_FCTL_START_FUNC;
47350c8e31fSSebastian Ott 		scsw->actl = SCSW_ACTL_START_PEND;
47450c8e31fSSebastian Ott 		scsw->stctl = SCSW_STCTL_STATUS_PEND;
47550c8e31fSSebastian Ott 	}
47650c8e31fSSebastian Ott }
47750c8e31fSSebastian Ott 
47855fb7347SSebastian Ott static void ccw_device_handle_broken_paths(struct ccw_device *cdev)
47955fb7347SSebastian Ott {
48055fb7347SSebastian Ott 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
48155fb7347SSebastian Ott 	u8 broken_paths = (sch->schib.pmcw.pam & sch->opm) ^ sch->vpm;
48255fb7347SSebastian Ott 
48355fb7347SSebastian Ott 	if (broken_paths && (cdev->private->path_broken_mask != broken_paths))
48455fb7347SSebastian Ott 		ccw_device_schedule_recovery();
48555fb7347SSebastian Ott 
48655fb7347SSebastian Ott 	cdev->private->path_broken_mask = broken_paths;
48755fb7347SSebastian Ott }
48855fb7347SSebastian Ott 
48950c8e31fSSebastian Ott void ccw_device_verify_done(struct ccw_device *cdev, int err)
4901da177e4SLinus Torvalds {
49128bdc6f6SPeter Oberparleiter 	struct subchannel *sch;
49228bdc6f6SPeter Oberparleiter 
49328bdc6f6SPeter Oberparleiter 	sch = to_subchannel(cdev->dev.parent);
49428bdc6f6SPeter Oberparleiter 	/* Update schib - pom may have changed. */
495cdb912a4SSebastian Ott 	if (cio_update_schib(sch)) {
4967c4d964fSPeter Oberparleiter 		err = -ENODEV;
4977c4d964fSPeter Oberparleiter 		goto callback;
498cdb912a4SSebastian Ott 	}
49928bdc6f6SPeter Oberparleiter 	/* Update lpm with verified path mask. */
50028bdc6f6SPeter Oberparleiter 	sch->lpm = sch->vpm;
50128bdc6f6SPeter Oberparleiter 	/* Repeat path verification? */
50228bdc6f6SPeter Oberparleiter 	if (cdev->private->flags.doverify) {
50328bdc6f6SPeter Oberparleiter 		ccw_device_verify_start(cdev);
50428bdc6f6SPeter Oberparleiter 		return;
50528bdc6f6SPeter Oberparleiter 	}
5067c4d964fSPeter Oberparleiter callback:
5071da177e4SLinus Torvalds 	switch (err) {
5081da177e4SLinus Torvalds 	case 0:
5091da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_ONLINE);
5101da177e4SLinus Torvalds 		/* Deliver fake irb to device driver, if needed. */
5111da177e4SLinus Torvalds 		if (cdev->private->flags.fake_irb) {
51250c8e31fSSebastian Ott 			create_fake_irb(&cdev->private->irb,
51350c8e31fSSebastian Ott 					cdev->private->flags.fake_irb);
5141da177e4SLinus Torvalds 			cdev->private->flags.fake_irb = 0;
5151da177e4SLinus Torvalds 			if (cdev->handler)
5161da177e4SLinus Torvalds 				cdev->handler(cdev, cdev->private->intparm,
5171da177e4SLinus Torvalds 					      &cdev->private->irb);
5181da177e4SLinus Torvalds 			memset(&cdev->private->irb, 0, sizeof(struct irb));
5191da177e4SLinus Torvalds 		}
520585b954eSSebastian Ott 		ccw_device_report_path_events(cdev);
52155fb7347SSebastian Ott 		ccw_device_handle_broken_paths(cdev);
5221da177e4SLinus Torvalds 		break;
5231da177e4SLinus Torvalds 	case -ETIME:
52452ef0608SPeter Oberparleiter 	case -EUSERS:
5258b42f5c2SPeter Oberparleiter 		/* Reset oper notify indication after verify error. */
5268b42f5c2SPeter Oberparleiter 		cdev->private->flags.donotify = 0;
5271da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_BOXED);
5281da177e4SLinus Torvalds 		break;
5297c4d964fSPeter Oberparleiter 	case -EACCES:
5307c4d964fSPeter Oberparleiter 		/* Reset oper notify indication after verify error. */
5317c4d964fSPeter Oberparleiter 		cdev->private->flags.donotify = 0;
5327c4d964fSPeter Oberparleiter 		ccw_device_done(cdev, DEV_STATE_DISCONNECTED);
5337c4d964fSPeter Oberparleiter 		break;
5341da177e4SLinus Torvalds 	default:
5358b42f5c2SPeter Oberparleiter 		/* Reset oper notify indication after verify error. */
5368b42f5c2SPeter Oberparleiter 		cdev->private->flags.donotify = 0;
5371da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_NOT_OPER);
5381da177e4SLinus Torvalds 		break;
5391da177e4SLinus Torvalds 	}
540585b954eSSebastian Ott 	ccw_device_reset_path_events(cdev);
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds /*
5441da177e4SLinus Torvalds  * Get device online.
5451da177e4SLinus Torvalds  */
5461da177e4SLinus Torvalds int
5471da177e4SLinus Torvalds ccw_device_online(struct ccw_device *cdev)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds 	struct subchannel *sch;
5501da177e4SLinus Torvalds 	int ret;
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 	if ((cdev->private->state != DEV_STATE_OFFLINE) &&
5531da177e4SLinus Torvalds 	    (cdev->private->state != DEV_STATE_BOXED))
5541da177e4SLinus Torvalds 		return -EINVAL;
5551da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
556edf22096SCornelia Huck 	ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
5571da177e4SLinus Torvalds 	if (ret != 0) {
5581da177e4SLinus Torvalds 		/* Couldn't enable the subchannel for i/o. Sick device. */
5591da177e4SLinus Torvalds 		if (ret == -ENODEV)
5601da177e4SLinus Torvalds 			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
5611da177e4SLinus Torvalds 		return ret;
5621da177e4SLinus Torvalds 	}
5637e560814SCornelia Huck 	/* Start initial path verification. */
5647e560814SCornelia Huck 	cdev->private->state = DEV_STATE_VERIFY;
5657e560814SCornelia Huck 	ccw_device_verify_start(cdev);
5661da177e4SLinus Torvalds 	return 0;
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds void
5701da177e4SLinus Torvalds ccw_device_disband_done(struct ccw_device *cdev, int err)
5711da177e4SLinus Torvalds {
5721da177e4SLinus Torvalds 	switch (err) {
5731da177e4SLinus Torvalds 	case 0:
5741da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_OFFLINE);
5751da177e4SLinus Torvalds 		break;
5761da177e4SLinus Torvalds 	case -ETIME:
5771da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_BOXED);
5781da177e4SLinus Torvalds 		break;
5791da177e4SLinus Torvalds 	default:
5803ecb0a5aSPeter Oberparleiter 		cdev->private->flags.donotify = 0;
5811da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_NOT_OPER);
5821da177e4SLinus Torvalds 		break;
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds /*
5871da177e4SLinus Torvalds  * Shutdown device.
5881da177e4SLinus Torvalds  */
5891da177e4SLinus Torvalds int
5901da177e4SLinus Torvalds ccw_device_offline(struct ccw_device *cdev)
5911da177e4SLinus Torvalds {
5921da177e4SLinus Torvalds 	struct subchannel *sch;
5931da177e4SLinus Torvalds 
594b301ea8cSPeter Oberparleiter 	/* Allow ccw_device_offline while disconnected. */
595b301ea8cSPeter Oberparleiter 	if (cdev->private->state == DEV_STATE_DISCONNECTED ||
596b301ea8cSPeter Oberparleiter 	    cdev->private->state == DEV_STATE_NOT_OPER) {
597b301ea8cSPeter Oberparleiter 		cdev->private->flags.donotify = 0;
598b301ea8cSPeter Oberparleiter 		ccw_device_done(cdev, DEV_STATE_NOT_OPER);
599b301ea8cSPeter Oberparleiter 		return 0;
600b301ea8cSPeter Oberparleiter 	}
601102e835dSPeter Oberparleiter 	if (cdev->private->state == DEV_STATE_BOXED) {
602102e835dSPeter Oberparleiter 		ccw_device_done(cdev, DEV_STATE_BOXED);
603102e835dSPeter Oberparleiter 		return 0;
604102e835dSPeter Oberparleiter 	}
605d7b5a4c9SCornelia Huck 	if (ccw_device_is_orphan(cdev)) {
606d7b5a4c9SCornelia Huck 		ccw_device_done(cdev, DEV_STATE_OFFLINE);
607d7b5a4c9SCornelia Huck 		return 0;
608d7b5a4c9SCornelia Huck 	}
6091da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
610cdb912a4SSebastian Ott 	if (cio_update_schib(sch))
6111da177e4SLinus Torvalds 		return -ENODEV;
61223d805b6SPeter Oberparleiter 	if (scsw_actl(&sch->schib.scsw) != 0)
6131da177e4SLinus Torvalds 		return -EBUSY;
61423d805b6SPeter Oberparleiter 	if (cdev->private->state != DEV_STATE_ONLINE)
6151da177e4SLinus Torvalds 		return -EINVAL;
6161da177e4SLinus Torvalds 	/* Are we doing path grouping? */
617454e1fa1SPeter Oberparleiter 	if (!cdev->private->flags.pgroup) {
6181da177e4SLinus Torvalds 		/* No, set state offline immediately. */
6191da177e4SLinus Torvalds 		ccw_device_done(cdev, DEV_STATE_OFFLINE);
6201da177e4SLinus Torvalds 		return 0;
6211da177e4SLinus Torvalds 	}
6221da177e4SLinus Torvalds 	/* Start Set Path Group commands. */
6231da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_DISBAND_PGID;
6241da177e4SLinus Torvalds 	ccw_device_disband_start(cdev);
6251da177e4SLinus Torvalds 	return 0;
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds /*
6293f4cf6e7SCornelia Huck  * Handle not operational event in non-special state.
6301da177e4SLinus Torvalds  */
6313f4cf6e7SCornelia Huck static void ccw_device_generic_notoper(struct ccw_device *cdev,
6323f4cf6e7SCornelia Huck 				       enum dev_event dev_event)
6331da177e4SLinus Torvalds {
63476e6fb4bSSebastian Ott 	if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
63537de53bbSPeter Oberparleiter 		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
6366afcc775SPeter Oberparleiter 	else
6376afcc775SPeter Oberparleiter 		ccw_device_set_disconnected(cdev);
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds /*
6411f1148c8SPeter Oberparleiter  * Handle path verification event in offline state.
6421f1148c8SPeter Oberparleiter  */
6431f1148c8SPeter Oberparleiter static void ccw_device_offline_verify(struct ccw_device *cdev,
6441f1148c8SPeter Oberparleiter 				      enum dev_event dev_event)
6451f1148c8SPeter Oberparleiter {
6461f1148c8SPeter Oberparleiter 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
6471f1148c8SPeter Oberparleiter 
6481f1148c8SPeter Oberparleiter 	css_schedule_eval(sch->schid);
6491f1148c8SPeter Oberparleiter }
6501f1148c8SPeter Oberparleiter 
6511f1148c8SPeter Oberparleiter /*
6521da177e4SLinus Torvalds  * Handle path verification event.
6531da177e4SLinus Torvalds  */
6541da177e4SLinus Torvalds static void
6551da177e4SLinus Torvalds ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
6561da177e4SLinus Torvalds {
6571da177e4SLinus Torvalds 	struct subchannel *sch;
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	if (cdev->private->state == DEV_STATE_W4SENSE) {
6601da177e4SLinus Torvalds 		cdev->private->flags.doverify = 1;
6611da177e4SLinus Torvalds 		return;
6621da177e4SLinus Torvalds 	}
6631da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
6641da177e4SLinus Torvalds 	/*
6651da177e4SLinus Torvalds 	 * Since we might not just be coming from an interrupt from the
6661da177e4SLinus Torvalds 	 * subchannel we have to update the schib.
6671da177e4SLinus Torvalds 	 */
668cdb912a4SSebastian Ott 	if (cio_update_schib(sch)) {
669cdb912a4SSebastian Ott 		ccw_device_verify_done(cdev, -ENODEV);
670cdb912a4SSebastian Ott 		return;
671cdb912a4SSebastian Ott 	}
6721da177e4SLinus Torvalds 
67323d805b6SPeter Oberparleiter 	if (scsw_actl(&sch->schib.scsw) != 0 ||
67423d805b6SPeter Oberparleiter 	    (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
67523d805b6SPeter Oberparleiter 	    (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
6761da177e4SLinus Torvalds 		/*
6771da177e4SLinus Torvalds 		 * No final status yet or final status not yet delivered
67825985edcSLucas De Marchi 		 * to the device driver. Can't do path verification now,
6791da177e4SLinus Torvalds 		 * delay until final status was delivered.
6801da177e4SLinus Torvalds 		 */
6811da177e4SLinus Torvalds 		cdev->private->flags.doverify = 1;
6821da177e4SLinus Torvalds 		return;
6831da177e4SLinus Torvalds 	}
6841da177e4SLinus Torvalds 	/* Device is idle, we can do the path verification. */
6851da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_VERIFY;
6861da177e4SLinus Torvalds 	ccw_device_verify_start(cdev);
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds /*
690d7d12ef2SPeter Oberparleiter  * Handle path verification event in boxed state.
691d7d12ef2SPeter Oberparleiter  */
692d7d12ef2SPeter Oberparleiter static void ccw_device_boxed_verify(struct ccw_device *cdev,
693d7d12ef2SPeter Oberparleiter 				    enum dev_event dev_event)
694d7d12ef2SPeter Oberparleiter {
695d7d12ef2SPeter Oberparleiter 	struct subchannel *sch = to_subchannel(cdev->dev.parent);
696d7d12ef2SPeter Oberparleiter 
697d7d12ef2SPeter Oberparleiter 	if (cdev->online) {
698d7d12ef2SPeter Oberparleiter 		if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
699d7d12ef2SPeter Oberparleiter 			ccw_device_done(cdev, DEV_STATE_NOT_OPER);
700d7d12ef2SPeter Oberparleiter 		else
701d7d12ef2SPeter Oberparleiter 			ccw_device_online_verify(cdev, dev_event);
702d7d12ef2SPeter Oberparleiter 	} else
703d7d12ef2SPeter Oberparleiter 		css_schedule_eval(sch->schid);
704d7d12ef2SPeter Oberparleiter }
705d7d12ef2SPeter Oberparleiter 
706d7d12ef2SPeter Oberparleiter /*
7078421d212SSebastian Ott  * Pass interrupt to device driver.
7088421d212SSebastian Ott  */
7098421d212SSebastian Ott static int ccw_device_call_handler(struct ccw_device *cdev)
7108421d212SSebastian Ott {
7118421d212SSebastian Ott 	unsigned int stctl;
7128421d212SSebastian Ott 	int ending_status;
7138421d212SSebastian Ott 
7148421d212SSebastian Ott 	/*
7158421d212SSebastian Ott 	 * we allow for the device action handler if .
7168421d212SSebastian Ott 	 *  - we received ending status
7178421d212SSebastian Ott 	 *  - the action handler requested to see all interrupts
7188421d212SSebastian Ott 	 *  - we received an intermediate status
7198421d212SSebastian Ott 	 *  - fast notification was requested (primary status)
7208421d212SSebastian Ott 	 *  - unsolicited interrupts
7218421d212SSebastian Ott 	 */
7228421d212SSebastian Ott 	stctl = scsw_stctl(&cdev->private->irb.scsw);
7238421d212SSebastian Ott 	ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
7248421d212SSebastian Ott 		(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
7258421d212SSebastian Ott 		(stctl == SCSW_STCTL_STATUS_PEND);
7268421d212SSebastian Ott 	if (!ending_status &&
7278421d212SSebastian Ott 	    !cdev->private->options.repall &&
7288421d212SSebastian Ott 	    !(stctl & SCSW_STCTL_INTER_STATUS) &&
7298421d212SSebastian Ott 	    !(cdev->private->options.fast &&
7308421d212SSebastian Ott 	      (stctl & SCSW_STCTL_PRIM_STATUS)))
7318421d212SSebastian Ott 		return 0;
7328421d212SSebastian Ott 
7338421d212SSebastian Ott 	if (ending_status)
7348421d212SSebastian Ott 		ccw_device_set_timeout(cdev, 0);
7358421d212SSebastian Ott 
7368421d212SSebastian Ott 	if (cdev->handler)
7378421d212SSebastian Ott 		cdev->handler(cdev, cdev->private->intparm,
7388421d212SSebastian Ott 			      &cdev->private->irb);
7398421d212SSebastian Ott 
7408421d212SSebastian Ott 	memset(&cdev->private->irb, 0, sizeof(struct irb));
7418421d212SSebastian Ott 	return 1;
7428421d212SSebastian Ott }
7438421d212SSebastian Ott 
7448421d212SSebastian Ott /*
7451da177e4SLinus Torvalds  * Got an interrupt for a normal io (state online).
7461da177e4SLinus Torvalds  */
7471da177e4SLinus Torvalds static void
7481da177e4SLinus Torvalds ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
7491da177e4SLinus Torvalds {
7501da177e4SLinus Torvalds 	struct irb *irb;
75183262d63SPeter Oberparleiter 	int is_cmd;
7521da177e4SLinus Torvalds 
7530bf7fcf1SChristoph Lameter 	irb = this_cpu_ptr(&cio_irb);
75483262d63SPeter Oberparleiter 	is_cmd = !scsw_is_tm(&irb->scsw);
7551da177e4SLinus Torvalds 	/* Check for unsolicited interrupt. */
75623d805b6SPeter Oberparleiter 	if (!scsw_is_solicited(&irb->scsw)) {
75783262d63SPeter Oberparleiter 		if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
7581da177e4SLinus Torvalds 		    !irb->esw.esw0.erw.cons) {
7591da177e4SLinus Torvalds 			/* Unit check but no sense data. Need basic sense. */
7601da177e4SLinus Torvalds 			if (ccw_device_do_sense(cdev, irb) != 0)
7611da177e4SLinus Torvalds 				goto call_handler_unsol;
762e0ec5749SCornelia Huck 			memcpy(&cdev->private->irb, irb, sizeof(struct irb));
7631da177e4SLinus Torvalds 			cdev->private->state = DEV_STATE_W4SENSE;
7641da177e4SLinus Torvalds 			cdev->private->intparm = 0;
7651da177e4SLinus Torvalds 			return;
7661da177e4SLinus Torvalds 		}
7671da177e4SLinus Torvalds call_handler_unsol:
7681da177e4SLinus Torvalds 		if (cdev->handler)
7691da177e4SLinus Torvalds 			cdev->handler (cdev, 0, irb);
77018374d37SCornelia Huck 		if (cdev->private->flags.doverify)
77118374d37SCornelia Huck 			ccw_device_online_verify(cdev, 0);
7721da177e4SLinus Torvalds 		return;
7731da177e4SLinus Torvalds 	}
7741da177e4SLinus Torvalds 	/* Accumulate status and find out if a basic sense is needed. */
7751da177e4SLinus Torvalds 	ccw_device_accumulate_irb(cdev, irb);
77683262d63SPeter Oberparleiter 	if (is_cmd && cdev->private->flags.dosense) {
7771da177e4SLinus Torvalds 		if (ccw_device_do_sense(cdev, irb) == 0) {
7781da177e4SLinus Torvalds 			cdev->private->state = DEV_STATE_W4SENSE;
7791da177e4SLinus Torvalds 		}
7801da177e4SLinus Torvalds 		return;
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 	/* Call the handler. */
7831da177e4SLinus Torvalds 	if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
7841da177e4SLinus Torvalds 		/* Start delayed path verification. */
7851da177e4SLinus Torvalds 		ccw_device_online_verify(cdev, 0);
7861da177e4SLinus Torvalds }
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds /*
7891da177e4SLinus Torvalds  * Got an timeout in online state.
7901da177e4SLinus Torvalds  */
7911da177e4SLinus Torvalds static void
7921da177e4SLinus Torvalds ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
7931da177e4SLinus Torvalds {
7941da177e4SLinus Torvalds 	int ret;
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 	ccw_device_set_timeout(cdev, 0);
797376ae475SPeter Oberparleiter 	cdev->private->iretry = 255;
798770b55c9SSebastian Ott 	cdev->private->async_kill_io_rc = -ETIMEDOUT;
7991da177e4SLinus Torvalds 	ret = ccw_device_cancel_halt_clear(cdev);
8001da177e4SLinus Torvalds 	if (ret == -EBUSY) {
8011da177e4SLinus Torvalds 		ccw_device_set_timeout(cdev, 3*HZ);
8021da177e4SLinus Torvalds 		cdev->private->state = DEV_STATE_TIMEOUT_KILL;
8031da177e4SLinus Torvalds 		return;
8041da177e4SLinus Torvalds 	}
805376ae475SPeter Oberparleiter 	if (ret)
8061da177e4SLinus Torvalds 		dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
8073f4cf6e7SCornelia Huck 	else if (cdev->handler)
8081da177e4SLinus Torvalds 		cdev->handler(cdev, cdev->private->intparm,
8091da177e4SLinus Torvalds 			      ERR_PTR(-ETIMEDOUT));
8101da177e4SLinus Torvalds }
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds /*
8131da177e4SLinus Torvalds  * Got an interrupt for a basic sense.
8141da177e4SLinus Torvalds  */
8152b67fc46SHeiko Carstens static void
8161da177e4SLinus Torvalds ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
8171da177e4SLinus Torvalds {
8181da177e4SLinus Torvalds 	struct irb *irb;
8191da177e4SLinus Torvalds 
8200bf7fcf1SChristoph Lameter 	irb = this_cpu_ptr(&cio_irb);
8211da177e4SLinus Torvalds 	/* Check for unsolicited interrupt. */
82223d805b6SPeter Oberparleiter 	if (scsw_stctl(&irb->scsw) ==
8231da177e4SLinus Torvalds 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
82423d805b6SPeter Oberparleiter 		if (scsw_cc(&irb->scsw) == 1)
8251da177e4SLinus Torvalds 			/* Basic sense hasn't started. Try again. */
8261da177e4SLinus Torvalds 			ccw_device_do_sense(cdev, irb);
8271da177e4SLinus Torvalds 		else {
828139b83ddSMichael Ernst 			CIO_MSG_EVENT(0, "0.%x.%04x: unsolicited "
829e556bbbdSCornelia Huck 				      "interrupt during w4sense...\n",
830e556bbbdSCornelia Huck 				      cdev->private->dev_id.ssid,
831e556bbbdSCornelia Huck 				      cdev->private->dev_id.devno);
8321da177e4SLinus Torvalds 			if (cdev->handler)
8331da177e4SLinus Torvalds 				cdev->handler (cdev, 0, irb);
8341da177e4SLinus Torvalds 		}
8351da177e4SLinus Torvalds 		return;
8361da177e4SLinus Torvalds 	}
8373ba1998eSCornelia Huck 	/*
8383ba1998eSCornelia Huck 	 * Check if a halt or clear has been issued in the meanwhile. If yes,
8393ba1998eSCornelia Huck 	 * only deliver the halt/clear interrupt to the device driver as if it
8403ba1998eSCornelia Huck 	 * had killed the original request.
8413ba1998eSCornelia Huck 	 */
84223d805b6SPeter Oberparleiter 	if (scsw_fctl(&irb->scsw) &
84323d805b6SPeter Oberparleiter 	    (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
8443ba1998eSCornelia Huck 		cdev->private->flags.dosense = 0;
8453ba1998eSCornelia Huck 		memset(&cdev->private->irb, 0, sizeof(struct irb));
8463ba1998eSCornelia Huck 		ccw_device_accumulate_irb(cdev, irb);
8473ba1998eSCornelia Huck 		goto call_handler;
8483ba1998eSCornelia Huck 	}
8491da177e4SLinus Torvalds 	/* Add basic sense info to irb. */
8501da177e4SLinus Torvalds 	ccw_device_accumulate_basic_sense(cdev, irb);
8511da177e4SLinus Torvalds 	if (cdev->private->flags.dosense) {
8521da177e4SLinus Torvalds 		/* Another basic sense is needed. */
8531da177e4SLinus Torvalds 		ccw_device_do_sense(cdev, irb);
8541da177e4SLinus Torvalds 		return;
8551da177e4SLinus Torvalds 	}
8563ba1998eSCornelia Huck call_handler:
8571da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_ONLINE;
858217ee6c6SMichael Ernst 	/* In case sensing interfered with setting the device online */
859217ee6c6SMichael Ernst 	wake_up(&cdev->private->wait_q);
8601da177e4SLinus Torvalds 	/* Call the handler. */
8611da177e4SLinus Torvalds 	if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
8621da177e4SLinus Torvalds 		/* Start delayed path verification. */
8631da177e4SLinus Torvalds 		ccw_device_online_verify(cdev, 0);
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds static void
8671da177e4SLinus Torvalds ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
8681da177e4SLinus Torvalds {
8691da177e4SLinus Torvalds 	ccw_device_set_timeout(cdev, 0);
8707c8427c3SCornelia Huck 	/* Start delayed path verification. */
8717c8427c3SCornelia Huck 	ccw_device_online_verify(cdev, 0);
8721da177e4SLinus Torvalds 	/* OK, i/o is dead now. Call interrupt handler. */
8731da177e4SLinus Torvalds 	if (cdev->handler)
8741da177e4SLinus Torvalds 		cdev->handler(cdev, cdev->private->intparm,
875770b55c9SSebastian Ott 			      ERR_PTR(cdev->private->async_kill_io_rc));
8761da177e4SLinus Torvalds }
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds static void
8791da177e4SLinus Torvalds ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
8801da177e4SLinus Torvalds {
8811da177e4SLinus Torvalds 	int ret;
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	ret = ccw_device_cancel_halt_clear(cdev);
8841da177e4SLinus Torvalds 	if (ret == -EBUSY) {
8851da177e4SLinus Torvalds 		ccw_device_set_timeout(cdev, 3*HZ);
8861da177e4SLinus Torvalds 		return;
8871da177e4SLinus Torvalds 	}
8887c8427c3SCornelia Huck 	/* Start delayed path verification. */
8897c8427c3SCornelia Huck 	ccw_device_online_verify(cdev, 0);
8901da177e4SLinus Torvalds 	if (cdev->handler)
8911da177e4SLinus Torvalds 		cdev->handler(cdev, cdev->private->intparm,
892770b55c9SSebastian Ott 			      ERR_PTR(cdev->private->async_kill_io_rc));
8931da177e4SLinus Torvalds }
8941da177e4SLinus Torvalds 
895c820de39SCornelia Huck void ccw_device_kill_io(struct ccw_device *cdev)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds 	int ret;
8981da177e4SLinus Torvalds 
899*410d5e13SSebastian Ott 	ccw_device_set_timeout(cdev, 0);
900376ae475SPeter Oberparleiter 	cdev->private->iretry = 255;
901770b55c9SSebastian Ott 	cdev->private->async_kill_io_rc = -EIO;
9021da177e4SLinus Torvalds 	ret = ccw_device_cancel_halt_clear(cdev);
9031da177e4SLinus Torvalds 	if (ret == -EBUSY) {
9041da177e4SLinus Torvalds 		ccw_device_set_timeout(cdev, 3*HZ);
9051da177e4SLinus Torvalds 		cdev->private->state = DEV_STATE_TIMEOUT_KILL;
9061da177e4SLinus Torvalds 		return;
9071da177e4SLinus Torvalds 	}
9087c8427c3SCornelia Huck 	/* Start delayed path verification. */
9097c8427c3SCornelia Huck 	ccw_device_online_verify(cdev, 0);
9101da177e4SLinus Torvalds 	if (cdev->handler)
9111da177e4SLinus Torvalds 		cdev->handler(cdev, cdev->private->intparm,
912e7769b48SCornelia Huck 			      ERR_PTR(-EIO));
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds static void
91628bdc6f6SPeter Oberparleiter ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
9171da177e4SLinus Torvalds {
91828bdc6f6SPeter Oberparleiter 	/* Start verification after current task finished. */
9191da177e4SLinus Torvalds 	cdev->private->flags.doverify = 1;
9201da177e4SLinus Torvalds }
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds static void
9231da177e4SLinus Torvalds ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
9241da177e4SLinus Torvalds {
9251da177e4SLinus Torvalds 	struct subchannel *sch;
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
928edf22096SCornelia Huck 	if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
9291da177e4SLinus Torvalds 		/* Couldn't enable the subchannel for i/o. Sick device. */
9301da177e4SLinus Torvalds 		return;
9311da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID;
9321da177e4SLinus Torvalds 	ccw_device_sense_id_start(cdev);
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
935c820de39SCornelia Huck void ccw_device_trigger_reprobe(struct ccw_device *cdev)
9361da177e4SLinus Torvalds {
937c820de39SCornelia Huck 	struct subchannel *sch;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 	if (cdev->private->state != DEV_STATE_DISCONNECTED)
9401da177e4SLinus Torvalds 		return;
9411da177e4SLinus Torvalds 
942c820de39SCornelia Huck 	sch = to_subchannel(cdev->dev.parent);
9431da177e4SLinus Torvalds 	/* Update some values. */
944cdb912a4SSebastian Ott 	if (cio_update_schib(sch))
9457674da77SCornelia Huck 		return;
9461da177e4SLinus Torvalds 	/*
9471da177e4SLinus Torvalds 	 * The pim, pam, pom values may not be accurate, but they are the best
9481da177e4SLinus Torvalds 	 * we have before performing device selection :/
9491da177e4SLinus Torvalds 	 */
95028bdc6f6SPeter Oberparleiter 	sch->lpm = sch->schib.pmcw.pam & sch->opm;
95113952ec1SSebastian Ott 	/*
95213952ec1SSebastian Ott 	 * Use the initial configuration since we can't be shure that the old
95313952ec1SSebastian Ott 	 * paths are valid.
95413952ec1SSebastian Ott 	 */
95513952ec1SSebastian Ott 	io_subchannel_init_config(sch);
956f444cc0eSSebastian Ott 	if (cio_commit_config(sch))
957f444cc0eSSebastian Ott 		return;
95813952ec1SSebastian Ott 
9591da177e4SLinus Torvalds 	/* We should also udate ssd info, but this has to wait. */
960d7b5a4c9SCornelia Huck 	/* Check if this is another device which appeared on the same sch. */
9615d6e6b6fSPeter Oberparleiter 	if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
9625d6e6b6fSPeter Oberparleiter 		css_schedule_eval(sch->schid);
9635d6e6b6fSPeter Oberparleiter 	else
9641da177e4SLinus Torvalds 		ccw_device_start_id(cdev, 0);
9651da177e4SLinus Torvalds }
9661da177e4SLinus Torvalds 
96716b9a057SPeter Oberparleiter static void ccw_device_disabled_irq(struct ccw_device *cdev,
96816b9a057SPeter Oberparleiter 				    enum dev_event dev_event)
9691da177e4SLinus Torvalds {
9701da177e4SLinus Torvalds 	struct subchannel *sch;
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 	sch = to_subchannel(cdev->dev.parent);
9731da177e4SLinus Torvalds 	/*
97416b9a057SPeter Oberparleiter 	 * An interrupt in a disabled state means a previous disable was not
975ed04b892SCornelia Huck 	 * successful - should not happen, but we try to disable again.
9761da177e4SLinus Torvalds 	 */
9771da177e4SLinus Torvalds 	cio_disable_subchannel(sch);
9781da177e4SLinus Torvalds }
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds static void
9811da177e4SLinus Torvalds ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event)
9821da177e4SLinus Torvalds {
9831da177e4SLinus Torvalds 	retry_set_schib(cdev);
9841da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_ONLINE;
9851da177e4SLinus Torvalds 	dev_fsm_event(cdev, dev_event);
9861da177e4SLinus Torvalds }
9871da177e4SLinus Torvalds 
98894bb0633SCornelia Huck static void ccw_device_update_cmfblock(struct ccw_device *cdev,
98994bb0633SCornelia Huck 				       enum dev_event dev_event)
99094bb0633SCornelia Huck {
99194bb0633SCornelia Huck 	cmf_retry_copy_block(cdev);
99294bb0633SCornelia Huck 	cdev->private->state = DEV_STATE_ONLINE;
99394bb0633SCornelia Huck 	dev_fsm_event(cdev, dev_event);
99494bb0633SCornelia Huck }
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds static void
9971da177e4SLinus Torvalds ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
9981da177e4SLinus Torvalds {
9991da177e4SLinus Torvalds 	ccw_device_set_timeout(cdev, 0);
10001da177e4SLinus Torvalds 	cdev->private->state = DEV_STATE_NOT_OPER;
10011da177e4SLinus Torvalds 	wake_up(&cdev->private->wait_q);
10021da177e4SLinus Torvalds }
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds static void
10051da177e4SLinus Torvalds ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
10061da177e4SLinus Torvalds {
10071da177e4SLinus Torvalds 	int ret;
10081da177e4SLinus Torvalds 
10091da177e4SLinus Torvalds 	ret = ccw_device_cancel_halt_clear(cdev);
101056e6b796SSebastian Ott 	if (ret == -EBUSY) {
101156e6b796SSebastian Ott 		ccw_device_set_timeout(cdev, HZ/10);
101256e6b796SSebastian Ott 	} else {
10131da177e4SLinus Torvalds 		cdev->private->state = DEV_STATE_NOT_OPER;
10141da177e4SLinus Torvalds 		wake_up(&cdev->private->wait_q);
10151da177e4SLinus Torvalds 	}
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds /*
10191da177e4SLinus Torvalds  * No operation action. This is used e.g. to ignore a timeout event in
10201da177e4SLinus Torvalds  * state offline.
10211da177e4SLinus Torvalds  */
10221da177e4SLinus Torvalds static void
10231da177e4SLinus Torvalds ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
10241da177e4SLinus Torvalds {
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds /*
10281da177e4SLinus Torvalds  * device statemachine
10291da177e4SLinus Torvalds  */
10301da177e4SLinus Torvalds fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
10311da177e4SLinus Torvalds 	[DEV_STATE_NOT_OPER] = {
10321da177e4SLinus Torvalds 		[DEV_EVENT_NOTOPER]	= ccw_device_nop,
103316b9a057SPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_disabled_irq,
10341da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_nop,
10351da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
10361da177e4SLinus Torvalds 	},
10371da177e4SLinus Torvalds 	[DEV_STATE_SENSE_ID] = {
103839f5360bSPeter Oberparleiter 		[DEV_EVENT_NOTOPER]	= ccw_device_request_event,
103939f5360bSPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_request_event,
104039f5360bSPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_request_event,
10411da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
10421da177e4SLinus Torvalds 	},
10431da177e4SLinus Torvalds 	[DEV_STATE_OFFLINE] = {
10443f4cf6e7SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_generic_notoper,
104516b9a057SPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_disabled_irq,
10461da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_nop,
10471f1148c8SPeter Oberparleiter 		[DEV_EVENT_VERIFY]	= ccw_device_offline_verify,
10481da177e4SLinus Torvalds 	},
10491da177e4SLinus Torvalds 	[DEV_STATE_VERIFY] = {
10509679baafSPeter Oberparleiter 		[DEV_EVENT_NOTOPER]	= ccw_device_request_event,
10519679baafSPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_request_event,
10529679baafSPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_request_event,
105328bdc6f6SPeter Oberparleiter 		[DEV_EVENT_VERIFY]	= ccw_device_delay_verify,
10541da177e4SLinus Torvalds 	},
10551da177e4SLinus Torvalds 	[DEV_STATE_ONLINE] = {
10563f4cf6e7SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_generic_notoper,
10571da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_irq,
10581da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_online_timeout,
10591da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_online_verify,
10601da177e4SLinus Torvalds 	},
10611da177e4SLinus Torvalds 	[DEV_STATE_W4SENSE] = {
10623f4cf6e7SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_generic_notoper,
10631da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_w4sense,
10641da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_nop,
10651da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_online_verify,
10661da177e4SLinus Torvalds 	},
10671da177e4SLinus Torvalds 	[DEV_STATE_DISBAND_PGID] = {
10689679baafSPeter Oberparleiter 		[DEV_EVENT_NOTOPER]	= ccw_device_request_event,
10699679baafSPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_request_event,
10709679baafSPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_request_event,
10711da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
10721da177e4SLinus Torvalds 	},
10731da177e4SLinus Torvalds 	[DEV_STATE_BOXED] = {
10743f4cf6e7SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_generic_notoper,
1075d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_nop,
1076d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_nop,
1077d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_VERIFY]	= ccw_device_boxed_verify,
10781da177e4SLinus Torvalds 	},
10791da177e4SLinus Torvalds 	/* states to wait for i/o completion before doing something */
10801da177e4SLinus Torvalds 	[DEV_STATE_TIMEOUT_KILL] = {
10813f4cf6e7SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_generic_notoper,
10821da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_killing_irq,
10831da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_killing_timeout,
10841da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop, //FIXME
10851da177e4SLinus Torvalds 	},
10861da177e4SLinus Torvalds 	[DEV_STATE_QUIESCE] = {
10871da177e4SLinus Torvalds 		[DEV_EVENT_NOTOPER]	= ccw_device_quiesce_done,
10881da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_quiesce_done,
10891da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_quiesce_timeout,
10901da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
10911da177e4SLinus Torvalds 	},
10921da177e4SLinus Torvalds 	/* special states for devices gone not operational */
10931da177e4SLinus Torvalds 	[DEV_STATE_DISCONNECTED] = {
10941da177e4SLinus Torvalds 		[DEV_EVENT_NOTOPER]	= ccw_device_nop,
10951da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_start_id,
109616b9a057SPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_nop,
109728bdc6f6SPeter Oberparleiter 		[DEV_EVENT_VERIFY]	= ccw_device_start_id,
10981da177e4SLinus Torvalds 	},
10991da177e4SLinus Torvalds 	[DEV_STATE_DISCONNECTED_SENSE_ID] = {
110039f5360bSPeter Oberparleiter 		[DEV_EVENT_NOTOPER]	= ccw_device_request_event,
110139f5360bSPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_request_event,
110239f5360bSPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_request_event,
11031da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
11041da177e4SLinus Torvalds 	},
11051da177e4SLinus Torvalds 	[DEV_STATE_CMFCHANGE] = {
11061da177e4SLinus Torvalds 		[DEV_EVENT_NOTOPER]	= ccw_device_change_cmfstate,
11071da177e4SLinus Torvalds 		[DEV_EVENT_INTERRUPT]	= ccw_device_change_cmfstate,
11081da177e4SLinus Torvalds 		[DEV_EVENT_TIMEOUT]	= ccw_device_change_cmfstate,
11091da177e4SLinus Torvalds 		[DEV_EVENT_VERIFY]	= ccw_device_change_cmfstate,
11101da177e4SLinus Torvalds 	},
111194bb0633SCornelia Huck 	[DEV_STATE_CMFUPDATE] = {
111294bb0633SCornelia Huck 		[DEV_EVENT_NOTOPER]	= ccw_device_update_cmfblock,
111394bb0633SCornelia Huck 		[DEV_EVENT_INTERRUPT]	= ccw_device_update_cmfblock,
111494bb0633SCornelia Huck 		[DEV_EVENT_TIMEOUT]	= ccw_device_update_cmfblock,
111594bb0633SCornelia Huck 		[DEV_EVENT_VERIFY]	= ccw_device_update_cmfblock,
111694bb0633SCornelia Huck 	},
1117d7d12ef2SPeter Oberparleiter 	[DEV_STATE_STEAL_LOCK] = {
1118d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_NOTOPER]	= ccw_device_request_event,
1119d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_INTERRUPT]	= ccw_device_request_event,
1120d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_TIMEOUT]	= ccw_device_request_event,
1121d7d12ef2SPeter Oberparleiter 		[DEV_EVENT_VERIFY]	= ccw_device_nop,
1122d7d12ef2SPeter Oberparleiter 	},
11231da177e4SLinus Torvalds };
11241da177e4SLinus Torvalds 
11251da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
1126