162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   S/390 common I/O routines -- low level i/o calls
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright IBM Corp. 1999, 2008
662306a36Sopenharmony_ci *    Author(s): Ingo Adlung (adlung@de.ibm.com)
762306a36Sopenharmony_ci *		 Cornelia Huck (cornelia.huck@de.ibm.com)
862306a36Sopenharmony_ci *		 Arnd Bergmann (arndb@de.ibm.com)
962306a36Sopenharmony_ci *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define KMSG_COMPONENT "cio"
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/ftrace.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/device.h>
2062306a36Sopenharmony_ci#include <linux/kernel_stat.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/irq.h>
2362306a36Sopenharmony_ci#include <asm/cio.h>
2462306a36Sopenharmony_ci#include <asm/delay.h>
2562306a36Sopenharmony_ci#include <asm/irq.h>
2662306a36Sopenharmony_ci#include <asm/irq_regs.h>
2762306a36Sopenharmony_ci#include <asm/setup.h>
2862306a36Sopenharmony_ci#include <asm/ipl.h>
2962306a36Sopenharmony_ci#include <asm/chpid.h>
3062306a36Sopenharmony_ci#include <asm/airq.h>
3162306a36Sopenharmony_ci#include <asm/isc.h>
3262306a36Sopenharmony_ci#include <linux/sched/cputime.h>
3362306a36Sopenharmony_ci#include <asm/fcx.h>
3462306a36Sopenharmony_ci#include <asm/nmi.h>
3562306a36Sopenharmony_ci#include <asm/crw.h>
3662306a36Sopenharmony_ci#include "cio.h"
3762306a36Sopenharmony_ci#include "css.h"
3862306a36Sopenharmony_ci#include "chsc.h"
3962306a36Sopenharmony_ci#include "ioasm.h"
4062306a36Sopenharmony_ci#include "io_sch.h"
4162306a36Sopenharmony_ci#include "blacklist.h"
4262306a36Sopenharmony_ci#include "cio_debug.h"
4362306a36Sopenharmony_ci#include "chp.h"
4462306a36Sopenharmony_ci#include "trace.h"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cidebug_info_t *cio_debug_msg_id;
4762306a36Sopenharmony_cidebug_info_t *cio_debug_trace_id;
4862306a36Sopenharmony_cidebug_info_t *cio_debug_crw_id;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciDEFINE_PER_CPU_ALIGNED(struct irb, cio_irb);
5162306a36Sopenharmony_ciEXPORT_PER_CPU_SYMBOL(cio_irb);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * Function: cio_debug_init
5562306a36Sopenharmony_ci * Initializes three debug logs for common I/O:
5662306a36Sopenharmony_ci * - cio_msg logs generic cio messages
5762306a36Sopenharmony_ci * - cio_trace logs the calling of different functions
5862306a36Sopenharmony_ci * - cio_crw logs machine check related cio messages
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic int __init cio_debug_init(void)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	cio_debug_msg_id = debug_register("cio_msg", 16, 1, 11 * sizeof(long));
6362306a36Sopenharmony_ci	if (!cio_debug_msg_id)
6462306a36Sopenharmony_ci		goto out_unregister;
6562306a36Sopenharmony_ci	debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
6662306a36Sopenharmony_ci	debug_set_level(cio_debug_msg_id, 2);
6762306a36Sopenharmony_ci	cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16);
6862306a36Sopenharmony_ci	if (!cio_debug_trace_id)
6962306a36Sopenharmony_ci		goto out_unregister;
7062306a36Sopenharmony_ci	debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view);
7162306a36Sopenharmony_ci	debug_set_level(cio_debug_trace_id, 2);
7262306a36Sopenharmony_ci	cio_debug_crw_id = debug_register("cio_crw", 8, 1, 8 * sizeof(long));
7362306a36Sopenharmony_ci	if (!cio_debug_crw_id)
7462306a36Sopenharmony_ci		goto out_unregister;
7562306a36Sopenharmony_ci	debug_register_view(cio_debug_crw_id, &debug_sprintf_view);
7662306a36Sopenharmony_ci	debug_set_level(cio_debug_crw_id, 4);
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciout_unregister:
8062306a36Sopenharmony_ci	debug_unregister(cio_debug_msg_id);
8162306a36Sopenharmony_ci	debug_unregister(cio_debug_trace_id);
8262306a36Sopenharmony_ci	debug_unregister(cio_debug_crw_id);
8362306a36Sopenharmony_ci	return -1;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciarch_initcall (cio_debug_init);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciint cio_set_options(struct subchannel *sch, int flags)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct io_subchannel_private *priv = to_io_private(sch);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	priv->options.suspend = (flags & DOIO_ALLOW_SUSPEND) != 0;
9362306a36Sopenharmony_ci	priv->options.prefetch = (flags & DOIO_DENY_PREFETCH) != 0;
9462306a36Sopenharmony_ci	priv->options.inter = (flags & DOIO_SUPPRESS_INTER) != 0;
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int
9962306a36Sopenharmony_cicio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	char dbf_text[15];
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (lpm != 0)
10462306a36Sopenharmony_ci		sch->lpm &= ~lpm;
10562306a36Sopenharmony_ci	else
10662306a36Sopenharmony_ci		sch->lpm = 0;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
10962306a36Sopenharmony_ci		      "subchannel 0.%x.%04x!\n", sch->schid.ssid,
11062306a36Sopenharmony_ci		      sch->schid.sch_no);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (cio_update_schib(sch))
11362306a36Sopenharmony_ci		return -ENODEV;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	sprintf(dbf_text, "no%s", dev_name(&sch->dev));
11662306a36Sopenharmony_ci	CIO_TRACE_EVENT(0, dbf_text);
11762306a36Sopenharmony_ci	CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return (sch->lpm ? -EACCES : -ENODEV);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint
12362306a36Sopenharmony_cicio_start_key (struct subchannel *sch,	/* subchannel structure */
12462306a36Sopenharmony_ci	       struct ccw1 * cpa,	/* logical channel prog addr */
12562306a36Sopenharmony_ci	       __u8 lpm,		/* logical path mask */
12662306a36Sopenharmony_ci	       __u8 key)                /* storage key */
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct io_subchannel_private *priv = to_io_private(sch);
12962306a36Sopenharmony_ci	union orb *orb = &priv->orb;
13062306a36Sopenharmony_ci	int ccode;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	CIO_TRACE_EVENT(5, "stIO");
13362306a36Sopenharmony_ci	CIO_TRACE_EVENT(5, dev_name(&sch->dev));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	memset(orb, 0, sizeof(union orb));
13662306a36Sopenharmony_ci	/* sch is always under 2G. */
13762306a36Sopenharmony_ci	orb->cmd.intparm = (u32)virt_to_phys(sch);
13862306a36Sopenharmony_ci	orb->cmd.fmt = 1;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	orb->cmd.pfch = priv->options.prefetch == 0;
14162306a36Sopenharmony_ci	orb->cmd.spnd = priv->options.suspend;
14262306a36Sopenharmony_ci	orb->cmd.ssic = priv->options.suspend && priv->options.inter;
14362306a36Sopenharmony_ci	orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * for 64 bit we always support 64 bit IDAWs with 4k page size only
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	orb->cmd.c64 = 1;
14862306a36Sopenharmony_ci	orb->cmd.i2k = 0;
14962306a36Sopenharmony_ci	orb->cmd.key = key >> 4;
15062306a36Sopenharmony_ci	/* issue "Start Subchannel" */
15162306a36Sopenharmony_ci	orb->cmd.cpa = (u32)virt_to_phys(cpa);
15262306a36Sopenharmony_ci	ccode = ssch(sch->schid, orb);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* process condition code */
15562306a36Sopenharmony_ci	CIO_HEX_EVENT(5, &ccode, sizeof(ccode));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	switch (ccode) {
15862306a36Sopenharmony_ci	case 0:
15962306a36Sopenharmony_ci		/*
16062306a36Sopenharmony_ci		 * initialize device status information
16162306a36Sopenharmony_ci		 */
16262306a36Sopenharmony_ci		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
16362306a36Sopenharmony_ci		return 0;
16462306a36Sopenharmony_ci	case 1:		/* status pending */
16562306a36Sopenharmony_ci	case 2:		/* busy */
16662306a36Sopenharmony_ci		return -EBUSY;
16762306a36Sopenharmony_ci	case 3:		/* device/path not operational */
16862306a36Sopenharmony_ci		return cio_start_handle_notoper(sch, lpm);
16962306a36Sopenharmony_ci	default:
17062306a36Sopenharmony_ci		return ccode;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_start_key);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciint
17662306a36Sopenharmony_cicio_start (struct subchannel *sch, struct ccw1 *cpa, __u8 lpm)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	return cio_start_key(sch, cpa, lpm, PAGE_DEFAULT_KEY);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_start);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * resume suspended I/O operation
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciint
18662306a36Sopenharmony_cicio_resume (struct subchannel *sch)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int ccode;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	CIO_TRACE_EVENT(4, "resIO");
19162306a36Sopenharmony_ci	CIO_TRACE_EVENT(4, dev_name(&sch->dev));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ccode = rsch (sch->schid);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	CIO_HEX_EVENT(4, &ccode, sizeof(ccode));
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	switch (ccode) {
19862306a36Sopenharmony_ci	case 0:
19962306a36Sopenharmony_ci		sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND;
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci	case 1:
20262306a36Sopenharmony_ci		return -EBUSY;
20362306a36Sopenharmony_ci	case 2:
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci	default:
20662306a36Sopenharmony_ci		/*
20762306a36Sopenharmony_ci		 * useless to wait for request completion
20862306a36Sopenharmony_ci		 *  as device is no longer operational !
20962306a36Sopenharmony_ci		 */
21062306a36Sopenharmony_ci		return -ENODEV;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_resume);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * halt I/O operation
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_ciint
21962306a36Sopenharmony_cicio_halt(struct subchannel *sch)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	int ccode;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!sch)
22462306a36Sopenharmony_ci		return -ENODEV;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "haltIO");
22762306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Issue "Halt subchannel" and process condition code
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	ccode = hsch (sch->schid);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	CIO_HEX_EVENT(2, &ccode, sizeof(ccode));
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	switch (ccode) {
23762306a36Sopenharmony_ci	case 0:
23862306a36Sopenharmony_ci		sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
23962306a36Sopenharmony_ci		return 0;
24062306a36Sopenharmony_ci	case 1:		/* status pending */
24162306a36Sopenharmony_ci	case 2:		/* busy */
24262306a36Sopenharmony_ci		return -EBUSY;
24362306a36Sopenharmony_ci	default:		/* device not operational */
24462306a36Sopenharmony_ci		return -ENODEV;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_halt);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/*
25062306a36Sopenharmony_ci * Clear I/O operation
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_ciint
25362306a36Sopenharmony_cicio_clear(struct subchannel *sch)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int ccode;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!sch)
25862306a36Sopenharmony_ci		return -ENODEV;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "clearIO");
26162306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/*
26462306a36Sopenharmony_ci	 * Issue "Clear subchannel" and process condition code
26562306a36Sopenharmony_ci	 */
26662306a36Sopenharmony_ci	ccode = csch (sch->schid);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	CIO_HEX_EVENT(2, &ccode, sizeof(ccode));
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	switch (ccode) {
27162306a36Sopenharmony_ci	case 0:
27262306a36Sopenharmony_ci		sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND;
27362306a36Sopenharmony_ci		return 0;
27462306a36Sopenharmony_ci	default:		/* device not operational */
27562306a36Sopenharmony_ci		return -ENODEV;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_clear);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci * Function: cio_cancel
28262306a36Sopenharmony_ci * Issues a "Cancel Subchannel" on the specified subchannel
28362306a36Sopenharmony_ci * Note: We don't need any fancy intparms and flags here
28462306a36Sopenharmony_ci *	 since xsch is executed synchronously.
28562306a36Sopenharmony_ci * Only for common I/O internal use as for now.
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_ciint
28862306a36Sopenharmony_cicio_cancel (struct subchannel *sch)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int ccode;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (!sch)
29362306a36Sopenharmony_ci		return -ENODEV;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "cancelIO");
29662306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ccode = xsch (sch->schid);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	CIO_HEX_EVENT(2, &ccode, sizeof(ccode));
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	switch (ccode) {
30362306a36Sopenharmony_ci	case 0:		/* success */
30462306a36Sopenharmony_ci		/* Update information in scsw. */
30562306a36Sopenharmony_ci		if (cio_update_schib(sch))
30662306a36Sopenharmony_ci			return -ENODEV;
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci	case 1:		/* status pending */
30962306a36Sopenharmony_ci		return -EBUSY;
31062306a36Sopenharmony_ci	case 2:		/* not applicable */
31162306a36Sopenharmony_ci		return -EINVAL;
31262306a36Sopenharmony_ci	default:	/* not oper */
31362306a36Sopenharmony_ci		return -ENODEV;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_cancel);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/**
31962306a36Sopenharmony_ci * cio_cancel_halt_clear - Cancel running I/O by performing cancel, halt
32062306a36Sopenharmony_ci * and clear ordinally if subchannel is valid.
32162306a36Sopenharmony_ci * @sch: subchannel on which to perform the cancel_halt_clear operation
32262306a36Sopenharmony_ci * @iretry: the number of the times remained to retry the next operation
32362306a36Sopenharmony_ci *
32462306a36Sopenharmony_ci * This should be called repeatedly since halt/clear are asynchronous
32562306a36Sopenharmony_ci * operations. We do one try with cio_cancel, three tries with cio_halt,
32662306a36Sopenharmony_ci * 255 tries with cio_clear. The caller should initialize @iretry with
32762306a36Sopenharmony_ci * the value 255 for its first call to this, and keep using the same
32862306a36Sopenharmony_ci * @iretry in the subsequent calls until it gets a non -EBUSY return.
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * Returns 0 if device now idle, -ENODEV for device not operational,
33162306a36Sopenharmony_ci * -EBUSY if an interrupt is expected (either from halt/clear or from a
33262306a36Sopenharmony_ci * status pending), and -EIO if out of retries.
33362306a36Sopenharmony_ci */
33462306a36Sopenharmony_ciint cio_cancel_halt_clear(struct subchannel *sch, int *iretry)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (cio_update_schib(sch))
33962306a36Sopenharmony_ci		return -ENODEV;
34062306a36Sopenharmony_ci	if (!sch->schib.pmcw.ena)
34162306a36Sopenharmony_ci		/* Not operational -> done. */
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci	/* Stage 1: cancel io. */
34462306a36Sopenharmony_ci	if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
34562306a36Sopenharmony_ci	    !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
34662306a36Sopenharmony_ci		if (!scsw_is_tm(&sch->schib.scsw)) {
34762306a36Sopenharmony_ci			ret = cio_cancel(sch);
34862306a36Sopenharmony_ci			if (ret != -EINVAL)
34962306a36Sopenharmony_ci				return ret;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		/*
35262306a36Sopenharmony_ci		 * Cancel io unsuccessful or not applicable (transport mode).
35362306a36Sopenharmony_ci		 * Continue with asynchronous instructions.
35462306a36Sopenharmony_ci		 */
35562306a36Sopenharmony_ci		*iretry = 3;	/* 3 halt retries. */
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	/* Stage 2: halt io. */
35862306a36Sopenharmony_ci	if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
35962306a36Sopenharmony_ci		if (*iretry) {
36062306a36Sopenharmony_ci			*iretry -= 1;
36162306a36Sopenharmony_ci			ret = cio_halt(sch);
36262306a36Sopenharmony_ci			if (ret != -EBUSY)
36362306a36Sopenharmony_ci				return (ret == 0) ? -EBUSY : ret;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci		/* Halt io unsuccessful. */
36662306a36Sopenharmony_ci		*iretry = 255;	/* 255 clear retries. */
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	/* Stage 3: clear io. */
36962306a36Sopenharmony_ci	if (*iretry) {
37062306a36Sopenharmony_ci		*iretry -= 1;
37162306a36Sopenharmony_ci		ret = cio_clear(sch);
37262306a36Sopenharmony_ci		return (ret == 0) ? -EBUSY : ret;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	/* Function was unsuccessful */
37562306a36Sopenharmony_ci	return -EIO;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_cancel_halt_clear);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void cio_apply_config(struct subchannel *sch, struct schib *schib)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	schib->pmcw.intparm = sch->config.intparm;
38262306a36Sopenharmony_ci	schib->pmcw.mbi = sch->config.mbi;
38362306a36Sopenharmony_ci	schib->pmcw.isc = sch->config.isc;
38462306a36Sopenharmony_ci	schib->pmcw.ena = sch->config.ena;
38562306a36Sopenharmony_ci	schib->pmcw.mme = sch->config.mme;
38662306a36Sopenharmony_ci	schib->pmcw.mp = sch->config.mp;
38762306a36Sopenharmony_ci	schib->pmcw.csense = sch->config.csense;
38862306a36Sopenharmony_ci	schib->pmcw.mbfc = sch->config.mbfc;
38962306a36Sopenharmony_ci	if (sch->config.mbfc)
39062306a36Sopenharmony_ci		schib->mba = sch->config.mba;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int cio_check_config(struct subchannel *sch, struct schib *schib)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	return (schib->pmcw.intparm == sch->config.intparm) &&
39662306a36Sopenharmony_ci		(schib->pmcw.mbi == sch->config.mbi) &&
39762306a36Sopenharmony_ci		(schib->pmcw.isc == sch->config.isc) &&
39862306a36Sopenharmony_ci		(schib->pmcw.ena == sch->config.ena) &&
39962306a36Sopenharmony_ci		(schib->pmcw.mme == sch->config.mme) &&
40062306a36Sopenharmony_ci		(schib->pmcw.mp == sch->config.mp) &&
40162306a36Sopenharmony_ci		(schib->pmcw.csense == sch->config.csense) &&
40262306a36Sopenharmony_ci		(schib->pmcw.mbfc == sch->config.mbfc) &&
40362306a36Sopenharmony_ci		(!sch->config.mbfc || (schib->mba == sch->config.mba));
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/*
40762306a36Sopenharmony_ci * cio_commit_config - apply configuration to the subchannel
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_ciint cio_commit_config(struct subchannel *sch)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	int ccode, retry, ret = 0;
41262306a36Sopenharmony_ci	struct schib schib;
41362306a36Sopenharmony_ci	struct irb irb;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
41662306a36Sopenharmony_ci		return -ENODEV;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	for (retry = 0; retry < 5; retry++) {
41962306a36Sopenharmony_ci		/* copy desired changes to local schib */
42062306a36Sopenharmony_ci		cio_apply_config(sch, &schib);
42162306a36Sopenharmony_ci		ccode = msch(sch->schid, &schib);
42262306a36Sopenharmony_ci		if (ccode < 0) /* -EIO if msch gets a program check. */
42362306a36Sopenharmony_ci			return ccode;
42462306a36Sopenharmony_ci		switch (ccode) {
42562306a36Sopenharmony_ci		case 0: /* successful */
42662306a36Sopenharmony_ci			if (stsch(sch->schid, &schib) ||
42762306a36Sopenharmony_ci			    !css_sch_is_valid(&schib))
42862306a36Sopenharmony_ci				return -ENODEV;
42962306a36Sopenharmony_ci			if (cio_check_config(sch, &schib)) {
43062306a36Sopenharmony_ci				/* commit changes from local schib */
43162306a36Sopenharmony_ci				memcpy(&sch->schib, &schib, sizeof(schib));
43262306a36Sopenharmony_ci				return 0;
43362306a36Sopenharmony_ci			}
43462306a36Sopenharmony_ci			ret = -EAGAIN;
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci		case 1: /* status pending */
43762306a36Sopenharmony_ci			ret = -EBUSY;
43862306a36Sopenharmony_ci			if (tsch(sch->schid, &irb))
43962306a36Sopenharmony_ci				return ret;
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci		case 2: /* busy */
44262306a36Sopenharmony_ci			udelay(100); /* allow for recovery */
44362306a36Sopenharmony_ci			ret = -EBUSY;
44462306a36Sopenharmony_ci			break;
44562306a36Sopenharmony_ci		case 3: /* not operational */
44662306a36Sopenharmony_ci			return -ENODEV;
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	return ret;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_commit_config);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * cio_update_schib - Perform stsch and update schib if subchannel is valid.
45562306a36Sopenharmony_ci * @sch: subchannel on which to perform stsch
45662306a36Sopenharmony_ci * Return zero on success, -ENODEV otherwise.
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_ciint cio_update_schib(struct subchannel *sch)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct schib schib;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
46362306a36Sopenharmony_ci		return -ENODEV;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	memcpy(&sch->schib, &schib, sizeof(schib));
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_update_schib);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci/**
47162306a36Sopenharmony_ci * cio_enable_subchannel - enable a subchannel.
47262306a36Sopenharmony_ci * @sch: subchannel to be enabled
47362306a36Sopenharmony_ci * @intparm: interruption parameter to set
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_ciint cio_enable_subchannel(struct subchannel *sch, u32 intparm)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	int ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "ensch");
48062306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (sch_is_pseudo_sch(sch))
48362306a36Sopenharmony_ci		return -EINVAL;
48462306a36Sopenharmony_ci	if (cio_update_schib(sch))
48562306a36Sopenharmony_ci		return -ENODEV;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	sch->config.ena = 1;
48862306a36Sopenharmony_ci	sch->config.isc = sch->isc;
48962306a36Sopenharmony_ci	sch->config.intparm = intparm;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ret = cio_commit_config(sch);
49262306a36Sopenharmony_ci	if (ret == -EIO) {
49362306a36Sopenharmony_ci		/*
49462306a36Sopenharmony_ci		 * Got a program check in msch. Try without
49562306a36Sopenharmony_ci		 * the concurrent sense bit the next time.
49662306a36Sopenharmony_ci		 */
49762306a36Sopenharmony_ci		sch->config.csense = 0;
49862306a36Sopenharmony_ci		ret = cio_commit_config(sch);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	CIO_HEX_EVENT(2, &ret, sizeof(ret));
50162306a36Sopenharmony_ci	return ret;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_enable_subchannel);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/**
50662306a36Sopenharmony_ci * cio_disable_subchannel - disable a subchannel.
50762306a36Sopenharmony_ci * @sch: subchannel to disable
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_ciint cio_disable_subchannel(struct subchannel *sch)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "dissch");
51462306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (sch_is_pseudo_sch(sch))
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci	if (cio_update_schib(sch))
51962306a36Sopenharmony_ci		return -ENODEV;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	sch->config.ena = 0;
52262306a36Sopenharmony_ci	ret = cio_commit_config(sch);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	CIO_HEX_EVENT(2, &ret, sizeof(ret));
52562306a36Sopenharmony_ci	return ret;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_disable_subchannel);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/*
53062306a36Sopenharmony_ci * do_cio_interrupt() handles all normal I/O device IRQ's
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_cistatic irqreturn_t do_cio_interrupt(int irq, void *dummy)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct tpi_info *tpi_info;
53562306a36Sopenharmony_ci	struct subchannel *sch;
53662306a36Sopenharmony_ci	struct irb *irb;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	set_cpu_flag(CIF_NOHZ_DELAY);
53962306a36Sopenharmony_ci	tpi_info = &get_irq_regs()->tpi_info;
54062306a36Sopenharmony_ci	trace_s390_cio_interrupt(tpi_info);
54162306a36Sopenharmony_ci	irb = this_cpu_ptr(&cio_irb);
54262306a36Sopenharmony_ci	if (!tpi_info->intparm) {
54362306a36Sopenharmony_ci		/* Clear pending interrupt condition. */
54462306a36Sopenharmony_ci		inc_irq_stat(IRQIO_CIO);
54562306a36Sopenharmony_ci		tsch(tpi_info->schid, irb);
54662306a36Sopenharmony_ci		return IRQ_HANDLED;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	sch = phys_to_virt(tpi_info->intparm);
54962306a36Sopenharmony_ci	spin_lock(sch->lock);
55062306a36Sopenharmony_ci	/* Store interrupt response block to lowcore. */
55162306a36Sopenharmony_ci	if (tsch(tpi_info->schid, irb) == 0) {
55262306a36Sopenharmony_ci		/* Keep subchannel information word up to date. */
55362306a36Sopenharmony_ci		memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw));
55462306a36Sopenharmony_ci		/* Call interrupt handler if there is one. */
55562306a36Sopenharmony_ci		if (sch->driver && sch->driver->irq)
55662306a36Sopenharmony_ci			sch->driver->irq(sch);
55762306a36Sopenharmony_ci		else
55862306a36Sopenharmony_ci			inc_irq_stat(IRQIO_CIO);
55962306a36Sopenharmony_ci	} else
56062306a36Sopenharmony_ci		inc_irq_stat(IRQIO_CIO);
56162306a36Sopenharmony_ci	spin_unlock(sch->lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return IRQ_HANDLED;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_civoid __init init_cio_interrupts(void)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	irq_set_chip_and_handler(IO_INTERRUPT,
56962306a36Sopenharmony_ci				 &dummy_irq_chip, handle_percpu_irq);
57062306a36Sopenharmony_ci	if (request_irq(IO_INTERRUPT, do_cio_interrupt, 0, "I/O", NULL))
57162306a36Sopenharmony_ci		panic("Failed to register I/O interrupt\n");
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci#ifdef CONFIG_CCW_CONSOLE
57562306a36Sopenharmony_cistatic struct subchannel *console_sch;
57662306a36Sopenharmony_cistatic struct lock_class_key console_sch_key;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/*
57962306a36Sopenharmony_ci * Use cio_tsch to update the subchannel status and call the interrupt handler
58062306a36Sopenharmony_ci * if status had been pending. Called with the subchannel's lock held.
58162306a36Sopenharmony_ci */
58262306a36Sopenharmony_civoid cio_tsch(struct subchannel *sch)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct irb *irb;
58562306a36Sopenharmony_ci	int irq_context;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	irb = this_cpu_ptr(&cio_irb);
58862306a36Sopenharmony_ci	/* Store interrupt response block to lowcore. */
58962306a36Sopenharmony_ci	if (tsch(sch->schid, irb) != 0)
59062306a36Sopenharmony_ci		/* Not status pending or not operational. */
59162306a36Sopenharmony_ci		return;
59262306a36Sopenharmony_ci	memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
59362306a36Sopenharmony_ci	/* Call interrupt handler with updated status. */
59462306a36Sopenharmony_ci	irq_context = in_interrupt();
59562306a36Sopenharmony_ci	if (!irq_context) {
59662306a36Sopenharmony_ci		local_bh_disable();
59762306a36Sopenharmony_ci		irq_enter();
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci	kstat_incr_irq_this_cpu(IO_INTERRUPT);
60062306a36Sopenharmony_ci	if (sch->driver && sch->driver->irq)
60162306a36Sopenharmony_ci		sch->driver->irq(sch);
60262306a36Sopenharmony_ci	else
60362306a36Sopenharmony_ci		inc_irq_stat(IRQIO_CIO);
60462306a36Sopenharmony_ci	if (!irq_context) {
60562306a36Sopenharmony_ci		irq_exit();
60662306a36Sopenharmony_ci		_local_bh_enable();
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int cio_test_for_console(struct subchannel_id schid, void *data)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct schib schib;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (stsch(schid, &schib) != 0)
61562306a36Sopenharmony_ci		return -ENXIO;
61662306a36Sopenharmony_ci	if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv &&
61762306a36Sopenharmony_ci	    (schib.pmcw.dev == console_devno)) {
61862306a36Sopenharmony_ci		console_irq = schid.sch_no;
61962306a36Sopenharmony_ci		return 1; /* found */
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int cio_get_console_sch_no(void)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct subchannel_id schid;
62762306a36Sopenharmony_ci	struct schib schib;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	init_subchannel_id(&schid);
63062306a36Sopenharmony_ci	if (console_irq != -1) {
63162306a36Sopenharmony_ci		/* VM provided us with the irq number of the console. */
63262306a36Sopenharmony_ci		schid.sch_no = console_irq;
63362306a36Sopenharmony_ci		if (stsch(schid, &schib) != 0 ||
63462306a36Sopenharmony_ci		    (schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !schib.pmcw.dnv)
63562306a36Sopenharmony_ci			return -1;
63662306a36Sopenharmony_ci		console_devno = schib.pmcw.dev;
63762306a36Sopenharmony_ci	} else if (console_devno != -1) {
63862306a36Sopenharmony_ci		/* At least the console device number is known. */
63962306a36Sopenharmony_ci		for_each_subchannel(cio_test_for_console, NULL);
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci	return console_irq;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistruct subchannel *cio_probe_console(void)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct subchannel_id schid;
64762306a36Sopenharmony_ci	struct subchannel *sch;
64862306a36Sopenharmony_ci	struct schib schib;
64962306a36Sopenharmony_ci	int sch_no, ret;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	sch_no = cio_get_console_sch_no();
65262306a36Sopenharmony_ci	if (sch_no == -1) {
65362306a36Sopenharmony_ci		pr_warn("No CCW console was found\n");
65462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	init_subchannel_id(&schid);
65762306a36Sopenharmony_ci	schid.sch_no = sch_no;
65862306a36Sopenharmony_ci	ret = stsch(schid, &schib);
65962306a36Sopenharmony_ci	if (ret)
66062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	sch = css_alloc_subchannel(schid, &schib);
66362306a36Sopenharmony_ci	if (IS_ERR(sch))
66462306a36Sopenharmony_ci		return sch;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	lockdep_set_class(sch->lock, &console_sch_key);
66762306a36Sopenharmony_ci	isc_register(CONSOLE_ISC);
66862306a36Sopenharmony_ci	sch->config.isc = CONSOLE_ISC;
66962306a36Sopenharmony_ci	sch->config.intparm = (u32)virt_to_phys(sch);
67062306a36Sopenharmony_ci	ret = cio_commit_config(sch);
67162306a36Sopenharmony_ci	if (ret) {
67262306a36Sopenharmony_ci		isc_unregister(CONSOLE_ISC);
67362306a36Sopenharmony_ci		put_device(&sch->dev);
67462306a36Sopenharmony_ci		return ERR_PTR(ret);
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci	console_sch = sch;
67762306a36Sopenharmony_ci	return sch;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ciint cio_is_console(struct subchannel_id schid)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	if (!console_sch)
68362306a36Sopenharmony_ci		return 0;
68462306a36Sopenharmony_ci	return schid_equal(&schid, &console_sch->schid);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_civoid cio_register_early_subchannels(void)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	int ret;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (!console_sch)
69262306a36Sopenharmony_ci		return;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	ret = css_register_subchannel(console_sch);
69562306a36Sopenharmony_ci	if (ret)
69662306a36Sopenharmony_ci		put_device(&console_sch->dev);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci#endif /* CONFIG_CCW_CONSOLE */
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/**
70162306a36Sopenharmony_ci * cio_tm_start_key - perform start function
70262306a36Sopenharmony_ci * @sch: subchannel on which to perform the start function
70362306a36Sopenharmony_ci * @tcw: transport-command word to be started
70462306a36Sopenharmony_ci * @lpm: mask of paths to use
70562306a36Sopenharmony_ci * @key: storage key to use for storage access
70662306a36Sopenharmony_ci *
70762306a36Sopenharmony_ci * Start the tcw on the given subchannel. Return zero on success, non-zero
70862306a36Sopenharmony_ci * otherwise.
70962306a36Sopenharmony_ci */
71062306a36Sopenharmony_ciint cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	int cc;
71362306a36Sopenharmony_ci	union orb *orb = &to_io_private(sch)->orb;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	memset(orb, 0, sizeof(union orb));
71662306a36Sopenharmony_ci	orb->tm.intparm = (u32)virt_to_phys(sch);
71762306a36Sopenharmony_ci	orb->tm.key = key >> 4;
71862306a36Sopenharmony_ci	orb->tm.b = 1;
71962306a36Sopenharmony_ci	orb->tm.lpm = lpm ? lpm : sch->lpm;
72062306a36Sopenharmony_ci	orb->tm.tcw = (u32)virt_to_phys(tcw);
72162306a36Sopenharmony_ci	cc = ssch(sch->schid, orb);
72262306a36Sopenharmony_ci	switch (cc) {
72362306a36Sopenharmony_ci	case 0:
72462306a36Sopenharmony_ci		return 0;
72562306a36Sopenharmony_ci	case 1:
72662306a36Sopenharmony_ci	case 2:
72762306a36Sopenharmony_ci		return -EBUSY;
72862306a36Sopenharmony_ci	default:
72962306a36Sopenharmony_ci		return cio_start_handle_notoper(sch, lpm);
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_tm_start_key);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci/**
73562306a36Sopenharmony_ci * cio_tm_intrg - perform interrogate function
73662306a36Sopenharmony_ci * @sch: subchannel on which to perform the interrogate function
73762306a36Sopenharmony_ci *
73862306a36Sopenharmony_ci * If the specified subchannel is running in transport-mode, perform the
73962306a36Sopenharmony_ci * interrogate function. Return zero on success, non-zero otherwie.
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_ciint cio_tm_intrg(struct subchannel *sch)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int cc;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (!to_io_private(sch)->orb.tm.b)
74662306a36Sopenharmony_ci		return -EINVAL;
74762306a36Sopenharmony_ci	cc = xsch(sch->schid);
74862306a36Sopenharmony_ci	switch (cc) {
74962306a36Sopenharmony_ci	case 0:
75062306a36Sopenharmony_ci	case 2:
75162306a36Sopenharmony_ci		return 0;
75262306a36Sopenharmony_ci	case 1:
75362306a36Sopenharmony_ci		return -EBUSY;
75462306a36Sopenharmony_ci	default:
75562306a36Sopenharmony_ci		return -ENODEV;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_tm_intrg);
759