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