18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * S/390 common I/O routines -- low level i/o calls 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2008 68c2ecf20Sopenharmony_ci * Author(s): Ingo Adlung (adlung@de.ibm.com) 78c2ecf20Sopenharmony_ci * Cornelia Huck (cornelia.huck@de.ibm.com) 88c2ecf20Sopenharmony_ci * Arnd Bergmann (arndb@de.ibm.com) 98c2ecf20Sopenharmony_ci * Martin Schwidefsky (schwidefsky@de.ibm.com) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "cio" 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/ftrace.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/device.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/irq.h> 238c2ecf20Sopenharmony_ci#include <asm/cio.h> 248c2ecf20Sopenharmony_ci#include <asm/delay.h> 258c2ecf20Sopenharmony_ci#include <asm/irq.h> 268c2ecf20Sopenharmony_ci#include <asm/irq_regs.h> 278c2ecf20Sopenharmony_ci#include <asm/setup.h> 288c2ecf20Sopenharmony_ci#include <asm/ipl.h> 298c2ecf20Sopenharmony_ci#include <asm/chpid.h> 308c2ecf20Sopenharmony_ci#include <asm/airq.h> 318c2ecf20Sopenharmony_ci#include <asm/isc.h> 328c2ecf20Sopenharmony_ci#include <linux/sched/cputime.h> 338c2ecf20Sopenharmony_ci#include <asm/fcx.h> 348c2ecf20Sopenharmony_ci#include <asm/nmi.h> 358c2ecf20Sopenharmony_ci#include <asm/crw.h> 368c2ecf20Sopenharmony_ci#include "cio.h" 378c2ecf20Sopenharmony_ci#include "css.h" 388c2ecf20Sopenharmony_ci#include "chsc.h" 398c2ecf20Sopenharmony_ci#include "ioasm.h" 408c2ecf20Sopenharmony_ci#include "io_sch.h" 418c2ecf20Sopenharmony_ci#include "blacklist.h" 428c2ecf20Sopenharmony_ci#include "cio_debug.h" 438c2ecf20Sopenharmony_ci#include "chp.h" 448c2ecf20Sopenharmony_ci#include "trace.h" 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cidebug_info_t *cio_debug_msg_id; 478c2ecf20Sopenharmony_cidebug_info_t *cio_debug_trace_id; 488c2ecf20Sopenharmony_cidebug_info_t *cio_debug_crw_id; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciDEFINE_PER_CPU_ALIGNED(struct irb, cio_irb); 518c2ecf20Sopenharmony_ciEXPORT_PER_CPU_SYMBOL(cio_irb); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Function: cio_debug_init 558c2ecf20Sopenharmony_ci * Initializes three debug logs for common I/O: 568c2ecf20Sopenharmony_ci * - cio_msg logs generic cio messages 578c2ecf20Sopenharmony_ci * - cio_trace logs the calling of different functions 588c2ecf20Sopenharmony_ci * - cio_crw logs machine check related cio messages 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int __init cio_debug_init(void) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci cio_debug_msg_id = debug_register("cio_msg", 16, 1, 11 * sizeof(long)); 638c2ecf20Sopenharmony_ci if (!cio_debug_msg_id) 648c2ecf20Sopenharmony_ci goto out_unregister; 658c2ecf20Sopenharmony_ci debug_register_view(cio_debug_msg_id, &debug_sprintf_view); 668c2ecf20Sopenharmony_ci debug_set_level(cio_debug_msg_id, 2); 678c2ecf20Sopenharmony_ci cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16); 688c2ecf20Sopenharmony_ci if (!cio_debug_trace_id) 698c2ecf20Sopenharmony_ci goto out_unregister; 708c2ecf20Sopenharmony_ci debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view); 718c2ecf20Sopenharmony_ci debug_set_level(cio_debug_trace_id, 2); 728c2ecf20Sopenharmony_ci cio_debug_crw_id = debug_register("cio_crw", 8, 1, 8 * sizeof(long)); 738c2ecf20Sopenharmony_ci if (!cio_debug_crw_id) 748c2ecf20Sopenharmony_ci goto out_unregister; 758c2ecf20Sopenharmony_ci debug_register_view(cio_debug_crw_id, &debug_sprintf_view); 768c2ecf20Sopenharmony_ci debug_set_level(cio_debug_crw_id, 4); 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciout_unregister: 808c2ecf20Sopenharmony_ci debug_unregister(cio_debug_msg_id); 818c2ecf20Sopenharmony_ci debug_unregister(cio_debug_trace_id); 828c2ecf20Sopenharmony_ci debug_unregister(cio_debug_crw_id); 838c2ecf20Sopenharmony_ci return -1; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciarch_initcall (cio_debug_init); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint cio_set_options(struct subchannel *sch, int flags) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct io_subchannel_private *priv = to_io_private(sch); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci priv->options.suspend = (flags & DOIO_ALLOW_SUSPEND) != 0; 938c2ecf20Sopenharmony_ci priv->options.prefetch = (flags & DOIO_DENY_PREFETCH) != 0; 948c2ecf20Sopenharmony_ci priv->options.inter = (flags & DOIO_SUPPRESS_INTER) != 0; 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int 998c2ecf20Sopenharmony_cicio_start_handle_notoper(struct subchannel *sch, __u8 lpm) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci char dbf_text[15]; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (lpm != 0) 1048c2ecf20Sopenharmony_ci sch->lpm &= ~lpm; 1058c2ecf20Sopenharmony_ci else 1068c2ecf20Sopenharmony_ci sch->lpm = 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci CIO_MSG_EVENT(2, "cio_start: 'not oper' status for " 1098c2ecf20Sopenharmony_ci "subchannel 0.%x.%04x!\n", sch->schid.ssid, 1108c2ecf20Sopenharmony_ci sch->schid.sch_no); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 1138c2ecf20Sopenharmony_ci return -ENODEV; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci sprintf(dbf_text, "no%s", dev_name(&sch->dev)); 1168c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(0, dbf_text); 1178c2ecf20Sopenharmony_ci CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return (sch->lpm ? -EACCES : -ENODEV); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciint 1238c2ecf20Sopenharmony_cicio_start_key (struct subchannel *sch, /* subchannel structure */ 1248c2ecf20Sopenharmony_ci struct ccw1 * cpa, /* logical channel prog addr */ 1258c2ecf20Sopenharmony_ci __u8 lpm, /* logical path mask */ 1268c2ecf20Sopenharmony_ci __u8 key) /* storage key */ 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct io_subchannel_private *priv = to_io_private(sch); 1298c2ecf20Sopenharmony_ci union orb *orb = &priv->orb; 1308c2ecf20Sopenharmony_ci int ccode; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(5, "stIO"); 1338c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(5, dev_name(&sch->dev)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci memset(orb, 0, sizeof(union orb)); 1368c2ecf20Sopenharmony_ci /* sch is always under 2G. */ 1378c2ecf20Sopenharmony_ci orb->cmd.intparm = (u32)(addr_t)sch; 1388c2ecf20Sopenharmony_ci orb->cmd.fmt = 1; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci orb->cmd.pfch = priv->options.prefetch == 0; 1418c2ecf20Sopenharmony_ci orb->cmd.spnd = priv->options.suspend; 1428c2ecf20Sopenharmony_ci orb->cmd.ssic = priv->options.suspend && priv->options.inter; 1438c2ecf20Sopenharmony_ci orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm; 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * for 64 bit we always support 64 bit IDAWs with 4k page size only 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci orb->cmd.c64 = 1; 1488c2ecf20Sopenharmony_ci orb->cmd.i2k = 0; 1498c2ecf20Sopenharmony_ci orb->cmd.key = key >> 4; 1508c2ecf20Sopenharmony_ci /* issue "Start Subchannel" */ 1518c2ecf20Sopenharmony_ci orb->cmd.cpa = (__u32) __pa(cpa); 1528c2ecf20Sopenharmony_ci ccode = ssch(sch->schid, orb); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* process condition code */ 1558c2ecf20Sopenharmony_ci CIO_HEX_EVENT(5, &ccode, sizeof(ccode)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci switch (ccode) { 1588c2ecf20Sopenharmony_ci case 0: 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * initialize device status information 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci case 1: /* status pending */ 1658c2ecf20Sopenharmony_ci case 2: /* busy */ 1668c2ecf20Sopenharmony_ci return -EBUSY; 1678c2ecf20Sopenharmony_ci case 3: /* device/path not operational */ 1688c2ecf20Sopenharmony_ci return cio_start_handle_notoper(sch, lpm); 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci return ccode; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_start_key); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint 1768c2ecf20Sopenharmony_cicio_start (struct subchannel *sch, struct ccw1 *cpa, __u8 lpm) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return cio_start_key(sch, cpa, lpm, PAGE_DEFAULT_KEY); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_start); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* 1838c2ecf20Sopenharmony_ci * resume suspended I/O operation 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ciint 1868c2ecf20Sopenharmony_cicio_resume (struct subchannel *sch) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int ccode; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(4, "resIO"); 1918c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(4, dev_name(&sch->dev)); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ccode = rsch (sch->schid); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci CIO_HEX_EVENT(4, &ccode, sizeof(ccode)); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch (ccode) { 1988c2ecf20Sopenharmony_ci case 0: 1998c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND; 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci case 1: 2028c2ecf20Sopenharmony_ci return -EBUSY; 2038c2ecf20Sopenharmony_ci case 2: 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * useless to wait for request completion 2088c2ecf20Sopenharmony_ci * as device is no longer operational ! 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci return -ENODEV; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_resume); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * halt I/O operation 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ciint 2198c2ecf20Sopenharmony_cicio_halt(struct subchannel *sch) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int ccode; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (!sch) 2248c2ecf20Sopenharmony_ci return -ENODEV; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, "haltIO"); 2278c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, dev_name(&sch->dev)); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * Issue "Halt subchannel" and process condition code 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci ccode = hsch (sch->schid); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci switch (ccode) { 2378c2ecf20Sopenharmony_ci case 0: 2388c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci case 1: /* status pending */ 2418c2ecf20Sopenharmony_ci case 2: /* busy */ 2428c2ecf20Sopenharmony_ci return -EBUSY; 2438c2ecf20Sopenharmony_ci default: /* device not operational */ 2448c2ecf20Sopenharmony_ci return -ENODEV; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_halt); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * Clear I/O operation 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ciint 2538c2ecf20Sopenharmony_cicio_clear(struct subchannel *sch) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ccode; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!sch) 2588c2ecf20Sopenharmony_ci return -ENODEV; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, "clearIO"); 2618c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, dev_name(&sch->dev)); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * Issue "Clear subchannel" and process condition code 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci ccode = csch (sch->schid); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci switch (ccode) { 2718c2ecf20Sopenharmony_ci case 0: 2728c2ecf20Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND; 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci default: /* device not operational */ 2758c2ecf20Sopenharmony_ci return -ENODEV; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_clear); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * Function: cio_cancel 2828c2ecf20Sopenharmony_ci * Issues a "Cancel Subchannel" on the specified subchannel 2838c2ecf20Sopenharmony_ci * Note: We don't need any fancy intparms and flags here 2848c2ecf20Sopenharmony_ci * since xsch is executed synchronously. 2858c2ecf20Sopenharmony_ci * Only for common I/O internal use as for now. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ciint 2888c2ecf20Sopenharmony_cicio_cancel (struct subchannel *sch) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int ccode; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!sch) 2938c2ecf20Sopenharmony_ci return -ENODEV; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, "cancelIO"); 2968c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, dev_name(&sch->dev)); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ccode = xsch (sch->schid); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci CIO_HEX_EVENT(2, &ccode, sizeof(ccode)); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci switch (ccode) { 3038c2ecf20Sopenharmony_ci case 0: /* success */ 3048c2ecf20Sopenharmony_ci /* Update information in scsw. */ 3058c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 3068c2ecf20Sopenharmony_ci return -ENODEV; 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci case 1: /* status pending */ 3098c2ecf20Sopenharmony_ci return -EBUSY; 3108c2ecf20Sopenharmony_ci case 2: /* not applicable */ 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci default: /* not oper */ 3138c2ecf20Sopenharmony_ci return -ENODEV; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_cancel); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/** 3198c2ecf20Sopenharmony_ci * cio_cancel_halt_clear - Cancel running I/O by performing cancel, halt 3208c2ecf20Sopenharmony_ci * and clear ordinally if subchannel is valid. 3218c2ecf20Sopenharmony_ci * @sch: subchannel on which to perform the cancel_halt_clear operation 3228c2ecf20Sopenharmony_ci * @iretry: the number of the times remained to retry the next operation 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * This should be called repeatedly since halt/clear are asynchronous 3258c2ecf20Sopenharmony_ci * operations. We do one try with cio_cancel, three tries with cio_halt, 3268c2ecf20Sopenharmony_ci * 255 tries with cio_clear. The caller should initialize @iretry with 3278c2ecf20Sopenharmony_ci * the value 255 for its first call to this, and keep using the same 3288c2ecf20Sopenharmony_ci * @iretry in the subsequent calls until it gets a non -EBUSY return. 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * Returns 0 if device now idle, -ENODEV for device not operational, 3318c2ecf20Sopenharmony_ci * -EBUSY if an interrupt is expected (either from halt/clear or from a 3328c2ecf20Sopenharmony_ci * status pending), and -EIO if out of retries. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ciint cio_cancel_halt_clear(struct subchannel *sch, int *iretry) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 3398c2ecf20Sopenharmony_ci return -ENODEV; 3408c2ecf20Sopenharmony_ci if (!sch->schib.pmcw.ena) 3418c2ecf20Sopenharmony_ci /* Not operational -> done. */ 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci /* Stage 1: cancel io. */ 3448c2ecf20Sopenharmony_ci if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) && 3458c2ecf20Sopenharmony_ci !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { 3468c2ecf20Sopenharmony_ci if (!scsw_is_tm(&sch->schib.scsw)) { 3478c2ecf20Sopenharmony_ci ret = cio_cancel(sch); 3488c2ecf20Sopenharmony_ci if (ret != -EINVAL) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * Cancel io unsuccessful or not applicable (transport mode). 3538c2ecf20Sopenharmony_ci * Continue with asynchronous instructions. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci *iretry = 3; /* 3 halt retries. */ 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci /* Stage 2: halt io. */ 3588c2ecf20Sopenharmony_ci if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { 3598c2ecf20Sopenharmony_ci if (*iretry) { 3608c2ecf20Sopenharmony_ci *iretry -= 1; 3618c2ecf20Sopenharmony_ci ret = cio_halt(sch); 3628c2ecf20Sopenharmony_ci if (ret != -EBUSY) 3638c2ecf20Sopenharmony_ci return (ret == 0) ? -EBUSY : ret; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci /* Halt io unsuccessful. */ 3668c2ecf20Sopenharmony_ci *iretry = 255; /* 255 clear retries. */ 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci /* Stage 3: clear io. */ 3698c2ecf20Sopenharmony_ci if (*iretry) { 3708c2ecf20Sopenharmony_ci *iretry -= 1; 3718c2ecf20Sopenharmony_ci ret = cio_clear(sch); 3728c2ecf20Sopenharmony_ci return (ret == 0) ? -EBUSY : ret; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci /* Function was unsuccessful */ 3758c2ecf20Sopenharmony_ci return -EIO; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_cancel_halt_clear); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void cio_apply_config(struct subchannel *sch, struct schib *schib) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci schib->pmcw.intparm = sch->config.intparm; 3828c2ecf20Sopenharmony_ci schib->pmcw.mbi = sch->config.mbi; 3838c2ecf20Sopenharmony_ci schib->pmcw.isc = sch->config.isc; 3848c2ecf20Sopenharmony_ci schib->pmcw.ena = sch->config.ena; 3858c2ecf20Sopenharmony_ci schib->pmcw.mme = sch->config.mme; 3868c2ecf20Sopenharmony_ci schib->pmcw.mp = sch->config.mp; 3878c2ecf20Sopenharmony_ci schib->pmcw.csense = sch->config.csense; 3888c2ecf20Sopenharmony_ci schib->pmcw.mbfc = sch->config.mbfc; 3898c2ecf20Sopenharmony_ci if (sch->config.mbfc) 3908c2ecf20Sopenharmony_ci schib->mba = sch->config.mba; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int cio_check_config(struct subchannel *sch, struct schib *schib) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci return (schib->pmcw.intparm == sch->config.intparm) && 3968c2ecf20Sopenharmony_ci (schib->pmcw.mbi == sch->config.mbi) && 3978c2ecf20Sopenharmony_ci (schib->pmcw.isc == sch->config.isc) && 3988c2ecf20Sopenharmony_ci (schib->pmcw.ena == sch->config.ena) && 3998c2ecf20Sopenharmony_ci (schib->pmcw.mme == sch->config.mme) && 4008c2ecf20Sopenharmony_ci (schib->pmcw.mp == sch->config.mp) && 4018c2ecf20Sopenharmony_ci (schib->pmcw.csense == sch->config.csense) && 4028c2ecf20Sopenharmony_ci (schib->pmcw.mbfc == sch->config.mbfc) && 4038c2ecf20Sopenharmony_ci (!sch->config.mbfc || (schib->mba == sch->config.mba)); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * cio_commit_config - apply configuration to the subchannel 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ciint cio_commit_config(struct subchannel *sch) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci int ccode, retry, ret = 0; 4128c2ecf20Sopenharmony_ci struct schib schib; 4138c2ecf20Sopenharmony_ci struct irb irb; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib)) 4168c2ecf20Sopenharmony_ci return -ENODEV; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci for (retry = 0; retry < 5; retry++) { 4198c2ecf20Sopenharmony_ci /* copy desired changes to local schib */ 4208c2ecf20Sopenharmony_ci cio_apply_config(sch, &schib); 4218c2ecf20Sopenharmony_ci ccode = msch(sch->schid, &schib); 4228c2ecf20Sopenharmony_ci if (ccode < 0) /* -EIO if msch gets a program check. */ 4238c2ecf20Sopenharmony_ci return ccode; 4248c2ecf20Sopenharmony_ci switch (ccode) { 4258c2ecf20Sopenharmony_ci case 0: /* successful */ 4268c2ecf20Sopenharmony_ci if (stsch(sch->schid, &schib) || 4278c2ecf20Sopenharmony_ci !css_sch_is_valid(&schib)) 4288c2ecf20Sopenharmony_ci return -ENODEV; 4298c2ecf20Sopenharmony_ci if (cio_check_config(sch, &schib)) { 4308c2ecf20Sopenharmony_ci /* commit changes from local schib */ 4318c2ecf20Sopenharmony_ci memcpy(&sch->schib, &schib, sizeof(schib)); 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci ret = -EAGAIN; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci case 1: /* status pending */ 4378c2ecf20Sopenharmony_ci ret = -EBUSY; 4388c2ecf20Sopenharmony_ci if (tsch(sch->schid, &irb)) 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 2: /* busy */ 4428c2ecf20Sopenharmony_ci udelay(100); /* allow for recovery */ 4438c2ecf20Sopenharmony_ci ret = -EBUSY; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case 3: /* not operational */ 4468c2ecf20Sopenharmony_ci return -ENODEV; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci return ret; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_commit_config); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/** 4548c2ecf20Sopenharmony_ci * cio_update_schib - Perform stsch and update schib if subchannel is valid. 4558c2ecf20Sopenharmony_ci * @sch: subchannel on which to perform stsch 4568c2ecf20Sopenharmony_ci * Return zero on success, -ENODEV otherwise. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ciint cio_update_schib(struct subchannel *sch) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct schib schib; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib)) 4638c2ecf20Sopenharmony_ci return -ENODEV; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci memcpy(&sch->schib, &schib, sizeof(schib)); 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_update_schib); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/** 4718c2ecf20Sopenharmony_ci * cio_enable_subchannel - enable a subchannel. 4728c2ecf20Sopenharmony_ci * @sch: subchannel to be enabled 4738c2ecf20Sopenharmony_ci * @intparm: interruption parameter to set 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ciint cio_enable_subchannel(struct subchannel *sch, u32 intparm) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, "ensch"); 4808c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, dev_name(&sch->dev)); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (sch_is_pseudo_sch(sch)) 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 4858c2ecf20Sopenharmony_ci return -ENODEV; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci sch->config.ena = 1; 4888c2ecf20Sopenharmony_ci sch->config.isc = sch->isc; 4898c2ecf20Sopenharmony_ci sch->config.intparm = intparm; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ret = cio_commit_config(sch); 4928c2ecf20Sopenharmony_ci if (ret == -EIO) { 4938c2ecf20Sopenharmony_ci /* 4948c2ecf20Sopenharmony_ci * Got a program check in msch. Try without 4958c2ecf20Sopenharmony_ci * the concurrent sense bit the next time. 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ci sch->config.csense = 0; 4988c2ecf20Sopenharmony_ci ret = cio_commit_config(sch); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci CIO_HEX_EVENT(2, &ret, sizeof(ret)); 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_enable_subchannel); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * cio_disable_subchannel - disable a subchannel. 5078c2ecf20Sopenharmony_ci * @sch: subchannel to disable 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ciint cio_disable_subchannel(struct subchannel *sch) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci int ret; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, "dissch"); 5148c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(2, dev_name(&sch->dev)); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (sch_is_pseudo_sch(sch)) 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) 5198c2ecf20Sopenharmony_ci return -ENODEV; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci sch->config.ena = 0; 5228c2ecf20Sopenharmony_ci ret = cio_commit_config(sch); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci CIO_HEX_EVENT(2, &ret, sizeof(ret)); 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_disable_subchannel); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/* 5308c2ecf20Sopenharmony_ci * do_cio_interrupt() handles all normal I/O device IRQ's 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic irqreturn_t do_cio_interrupt(int irq, void *dummy) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct tpi_info *tpi_info; 5358c2ecf20Sopenharmony_ci struct subchannel *sch; 5368c2ecf20Sopenharmony_ci struct irb *irb; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci set_cpu_flag(CIF_NOHZ_DELAY); 5398c2ecf20Sopenharmony_ci tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; 5408c2ecf20Sopenharmony_ci trace_s390_cio_interrupt(tpi_info); 5418c2ecf20Sopenharmony_ci irb = this_cpu_ptr(&cio_irb); 5428c2ecf20Sopenharmony_ci sch = (struct subchannel *)(unsigned long) tpi_info->intparm; 5438c2ecf20Sopenharmony_ci if (!sch) { 5448c2ecf20Sopenharmony_ci /* Clear pending interrupt condition. */ 5458c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_CIO); 5468c2ecf20Sopenharmony_ci tsch(tpi_info->schid, irb); 5478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci spin_lock(sch->lock); 5508c2ecf20Sopenharmony_ci /* Store interrupt response block to lowcore. */ 5518c2ecf20Sopenharmony_ci if (tsch(tpi_info->schid, irb) == 0) { 5528c2ecf20Sopenharmony_ci /* Keep subchannel information word up to date. */ 5538c2ecf20Sopenharmony_ci memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw)); 5548c2ecf20Sopenharmony_ci /* Call interrupt handler if there is one. */ 5558c2ecf20Sopenharmony_ci if (sch->driver && sch->driver->irq) 5568c2ecf20Sopenharmony_ci sch->driver->irq(sch); 5578c2ecf20Sopenharmony_ci else 5588c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_CIO); 5598c2ecf20Sopenharmony_ci } else 5608c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_CIO); 5618c2ecf20Sopenharmony_ci spin_unlock(sch->lock); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_civoid __init init_cio_interrupts(void) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci irq_set_chip_and_handler(IO_INTERRUPT, 5698c2ecf20Sopenharmony_ci &dummy_irq_chip, handle_percpu_irq); 5708c2ecf20Sopenharmony_ci if (request_irq(IO_INTERRUPT, do_cio_interrupt, 0, "I/O", NULL)) 5718c2ecf20Sopenharmony_ci panic("Failed to register I/O interrupt\n"); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci#ifdef CONFIG_CCW_CONSOLE 5758c2ecf20Sopenharmony_cistatic struct subchannel *console_sch; 5768c2ecf20Sopenharmony_cistatic struct lock_class_key console_sch_key; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* 5798c2ecf20Sopenharmony_ci * Use cio_tsch to update the subchannel status and call the interrupt handler 5808c2ecf20Sopenharmony_ci * if status had been pending. Called with the subchannel's lock held. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_civoid cio_tsch(struct subchannel *sch) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct irb *irb; 5858c2ecf20Sopenharmony_ci int irq_context; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci irb = this_cpu_ptr(&cio_irb); 5888c2ecf20Sopenharmony_ci /* Store interrupt response block to lowcore. */ 5898c2ecf20Sopenharmony_ci if (tsch(sch->schid, irb) != 0) 5908c2ecf20Sopenharmony_ci /* Not status pending or not operational. */ 5918c2ecf20Sopenharmony_ci return; 5928c2ecf20Sopenharmony_ci memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); 5938c2ecf20Sopenharmony_ci /* Call interrupt handler with updated status. */ 5948c2ecf20Sopenharmony_ci irq_context = in_interrupt(); 5958c2ecf20Sopenharmony_ci if (!irq_context) { 5968c2ecf20Sopenharmony_ci local_bh_disable(); 5978c2ecf20Sopenharmony_ci irq_enter(); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci kstat_incr_irq_this_cpu(IO_INTERRUPT); 6008c2ecf20Sopenharmony_ci if (sch->driver && sch->driver->irq) 6018c2ecf20Sopenharmony_ci sch->driver->irq(sch); 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_CIO); 6048c2ecf20Sopenharmony_ci if (!irq_context) { 6058c2ecf20Sopenharmony_ci irq_exit(); 6068c2ecf20Sopenharmony_ci _local_bh_enable(); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int cio_test_for_console(struct subchannel_id schid, void *data) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct schib schib; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (stsch(schid, &schib) != 0) 6158c2ecf20Sopenharmony_ci return -ENXIO; 6168c2ecf20Sopenharmony_ci if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && 6178c2ecf20Sopenharmony_ci (schib.pmcw.dev == console_devno)) { 6188c2ecf20Sopenharmony_ci console_irq = schid.sch_no; 6198c2ecf20Sopenharmony_ci return 1; /* found */ 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic int cio_get_console_sch_no(void) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct subchannel_id schid; 6278c2ecf20Sopenharmony_ci struct schib schib; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci init_subchannel_id(&schid); 6308c2ecf20Sopenharmony_ci if (console_irq != -1) { 6318c2ecf20Sopenharmony_ci /* VM provided us with the irq number of the console. */ 6328c2ecf20Sopenharmony_ci schid.sch_no = console_irq; 6338c2ecf20Sopenharmony_ci if (stsch(schid, &schib) != 0 || 6348c2ecf20Sopenharmony_ci (schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !schib.pmcw.dnv) 6358c2ecf20Sopenharmony_ci return -1; 6368c2ecf20Sopenharmony_ci console_devno = schib.pmcw.dev; 6378c2ecf20Sopenharmony_ci } else if (console_devno != -1) { 6388c2ecf20Sopenharmony_ci /* At least the console device number is known. */ 6398c2ecf20Sopenharmony_ci for_each_subchannel(cio_test_for_console, NULL); 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci return console_irq; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistruct subchannel *cio_probe_console(void) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct subchannel_id schid; 6478c2ecf20Sopenharmony_ci struct subchannel *sch; 6488c2ecf20Sopenharmony_ci struct schib schib; 6498c2ecf20Sopenharmony_ci int sch_no, ret; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci sch_no = cio_get_console_sch_no(); 6528c2ecf20Sopenharmony_ci if (sch_no == -1) { 6538c2ecf20Sopenharmony_ci pr_warn("No CCW console was found\n"); 6548c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci init_subchannel_id(&schid); 6578c2ecf20Sopenharmony_ci schid.sch_no = sch_no; 6588c2ecf20Sopenharmony_ci ret = stsch(schid, &schib); 6598c2ecf20Sopenharmony_ci if (ret) 6608c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci sch = css_alloc_subchannel(schid, &schib); 6638c2ecf20Sopenharmony_ci if (IS_ERR(sch)) 6648c2ecf20Sopenharmony_ci return sch; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci lockdep_set_class(sch->lock, &console_sch_key); 6678c2ecf20Sopenharmony_ci isc_register(CONSOLE_ISC); 6688c2ecf20Sopenharmony_ci sch->config.isc = CONSOLE_ISC; 6698c2ecf20Sopenharmony_ci sch->config.intparm = (u32)(addr_t)sch; 6708c2ecf20Sopenharmony_ci ret = cio_commit_config(sch); 6718c2ecf20Sopenharmony_ci if (ret) { 6728c2ecf20Sopenharmony_ci isc_unregister(CONSOLE_ISC); 6738c2ecf20Sopenharmony_ci put_device(&sch->dev); 6748c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci console_sch = sch; 6778c2ecf20Sopenharmony_ci return sch; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ciint cio_is_console(struct subchannel_id schid) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci if (!console_sch) 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci return schid_equal(&schid, &console_sch->schid); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_civoid cio_register_early_subchannels(void) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci int ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (!console_sch) 6928c2ecf20Sopenharmony_ci return; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci ret = css_register_subchannel(console_sch); 6958c2ecf20Sopenharmony_ci if (ret) 6968c2ecf20Sopenharmony_ci put_device(&console_sch->dev); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci#endif /* CONFIG_CCW_CONSOLE */ 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/** 7018c2ecf20Sopenharmony_ci * cio_tm_start_key - perform start function 7028c2ecf20Sopenharmony_ci * @sch: subchannel on which to perform the start function 7038c2ecf20Sopenharmony_ci * @tcw: transport-command word to be started 7048c2ecf20Sopenharmony_ci * @lpm: mask of paths to use 7058c2ecf20Sopenharmony_ci * @key: storage key to use for storage access 7068c2ecf20Sopenharmony_ci * 7078c2ecf20Sopenharmony_ci * Start the tcw on the given subchannel. Return zero on success, non-zero 7088c2ecf20Sopenharmony_ci * otherwise. 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ciint cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci int cc; 7138c2ecf20Sopenharmony_ci union orb *orb = &to_io_private(sch)->orb; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci memset(orb, 0, sizeof(union orb)); 7168c2ecf20Sopenharmony_ci orb->tm.intparm = (u32) (addr_t) sch; 7178c2ecf20Sopenharmony_ci orb->tm.key = key >> 4; 7188c2ecf20Sopenharmony_ci orb->tm.b = 1; 7198c2ecf20Sopenharmony_ci orb->tm.lpm = lpm ? lpm : sch->lpm; 7208c2ecf20Sopenharmony_ci orb->tm.tcw = (u32) (addr_t) tcw; 7218c2ecf20Sopenharmony_ci cc = ssch(sch->schid, orb); 7228c2ecf20Sopenharmony_ci switch (cc) { 7238c2ecf20Sopenharmony_ci case 0: 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci case 1: 7268c2ecf20Sopenharmony_ci case 2: 7278c2ecf20Sopenharmony_ci return -EBUSY; 7288c2ecf20Sopenharmony_ci default: 7298c2ecf20Sopenharmony_ci return cio_start_handle_notoper(sch, lpm); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_tm_start_key); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/** 7358c2ecf20Sopenharmony_ci * cio_tm_intrg - perform interrogate function 7368c2ecf20Sopenharmony_ci * @sch: subchannel on which to perform the interrogate function 7378c2ecf20Sopenharmony_ci * 7388c2ecf20Sopenharmony_ci * If the specified subchannel is running in transport-mode, perform the 7398c2ecf20Sopenharmony_ci * interrogate function. Return zero on success, non-zero otherwie. 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_ciint cio_tm_intrg(struct subchannel *sch) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci int cc; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (!to_io_private(sch)->orb.tm.b) 7468c2ecf20Sopenharmony_ci return -EINVAL; 7478c2ecf20Sopenharmony_ci cc = xsch(sch->schid); 7488c2ecf20Sopenharmony_ci switch (cc) { 7498c2ecf20Sopenharmony_ci case 0: 7508c2ecf20Sopenharmony_ci case 2: 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci case 1: 7538c2ecf20Sopenharmony_ci return -EBUSY; 7548c2ecf20Sopenharmony_ci default: 7558c2ecf20Sopenharmony_ci return -ENODEV; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cio_tm_intrg); 759