📄 cio.c
字号:
/* * drivers/s390/cio/cio.c * S/390 common I/O routines -- low level i/o calls * $Revision: 1.123 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cohuck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */#include <linux/module.h>#include <linux/config.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/kernel_stat.h>#include <asm/hardirq.h>#include <asm/cio.h>#include <asm/delay.h>#include <asm/irq.h>#include "airq.h"#include "cio.h"#include "css.h"#include "chsc.h"#include "ioasm.h"#include "blacklist.h"#include "cio_debug.h"debug_info_t *cio_debug_msg_id;debug_info_t *cio_debug_trace_id;debug_info_t *cio_debug_crw_id;int cio_show_msg;static int __initcio_setup (char *parm){ if (!strcmp (parm, "yes")) cio_show_msg = 1; else if (!strcmp (parm, "no")) cio_show_msg = 0; else printk (KERN_ERR "cio_setup : invalid cio_msg parameter '%s'", parm); return 1;}__setup ("cio_msg=", cio_setup);/* * Function: cio_debug_init * Initializes three debug logs (under /proc/s390dbf) for common I/O: * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on * - cio_trace logs the calling of different functions * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW */static int __initcio_debug_init (void){ cio_debug_msg_id = debug_register ("cio_msg", 4, 4, 16*sizeof (long)); if (!cio_debug_msg_id) goto out_unregister; debug_register_view (cio_debug_msg_id, &debug_sprintf_view); debug_set_level (cio_debug_msg_id, 2); cio_debug_trace_id = debug_register ("cio_trace", 4, 4, 8); if (!cio_debug_trace_id) goto out_unregister; debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view); debug_set_level (cio_debug_trace_id, 2); cio_debug_crw_id = debug_register ("cio_crw", 2, 4, 16*sizeof (long)); if (!cio_debug_crw_id) goto out_unregister; debug_register_view (cio_debug_crw_id, &debug_sprintf_view); debug_set_level (cio_debug_crw_id, 2); pr_debug("debugging initialized\n"); return 0;out_unregister: if (cio_debug_msg_id) debug_unregister (cio_debug_msg_id); if (cio_debug_trace_id) debug_unregister (cio_debug_trace_id); if (cio_debug_crw_id) debug_unregister (cio_debug_crw_id); pr_debug("could not initialize debugging\n"); return -1;}arch_initcall (cio_debug_init);intcio_set_options (struct subchannel *sch, int flags){ sch->options.suspend = (flags & DOIO_ALLOW_SUSPEND) != 0; sch->options.prefetch = (flags & DOIO_DENY_PREFETCH) != 0; sch->options.inter = (flags & DOIO_SUPPRESS_INTER) != 0; return 0;}/* FIXME: who wants to use this? */intcio_get_options (struct subchannel *sch){ int flags; flags = 0; if (sch->options.suspend) flags |= DOIO_ALLOW_SUSPEND; if (sch->options.prefetch) flags |= DOIO_DENY_PREFETCH; if (sch->options.inter) flags |= DOIO_SUPPRESS_INTER; return flags;}/* * Use tpi to get a pending interrupt, call the interrupt handler and * return a pointer to the subchannel structure. */static inline intcio_tpi(void){ struct tpi_info *tpi_info; struct subchannel *sch; struct irb *irb; tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID; if (tpi (NULL) != 1) return 0; irb = (struct irb *) __LC_IRB; /* Store interrupt response block to lowcore. */ if (tsch (tpi_info->irq, irb) != 0) /* Not status pending or not operational. */ return 1; sch = (struct subchannel *)(unsigned long)tpi_info->intparm; if (!sch) return 1; irq_enter (); spin_lock(&sch->lock); memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); if (sch->driver && sch->driver->irq) sch->driver->irq(&sch->dev); spin_unlock(&sch->lock); irq_exit (); return 1;}static inline intcio_start_handle_notoper(struct subchannel *sch, __u8 lpm){ char dbf_text[15]; if (lpm != 0) sch->lpm &= ~lpm; else sch->lpm = 0; stsch (sch->irq, &sch->schib); CIO_MSG_EVENT(0, "cio_start: 'not oper' status for " "subchannel %04x!\n", sch->irq); sprintf(dbf_text, "no%s", sch->dev.bus_id); CIO_TRACE_EVENT(0, dbf_text); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); return (sch->lpm ? -EACCES : -ENODEV);}intcio_start (struct subchannel *sch, /* subchannel structure */ struct ccw1 * cpa, /* logical channel prog addr */ __u8 lpm) /* logical path mask */{ char dbf_txt[15]; int ccode; CIO_TRACE_EVENT (4, "stIO"); CIO_TRACE_EVENT (4, sch->dev.bus_id); /* sch is always under 2G. */ sch->orb.intparm = (__u32)(unsigned long)sch; sch->orb.fmt = 1; sch->orb.pfch = sch->options.prefetch == 0; sch->orb.spnd = sch->options.suspend; sch->orb.ssic = sch->options.suspend && sch->options.inter; sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm;#ifdef CONFIG_ARCH_S390X /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ sch->orb.c64 = 1; sch->orb.i2k = 0;#endif sch->orb.cpa = (__u32) __pa (cpa); /* * Issue "Start subchannel" and process condition code */ ccode = ssch (sch->irq, &sch->orb); sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (4, dbf_txt); switch (ccode) { case 0: /* * initialize device status information */ sch->schib.scsw.actl |= SCSW_ACTL_START_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ return -EBUSY; default: /* device/path not operational */ return cio_start_handle_notoper(sch, lpm); }}/* * resume suspended I/O operation */intcio_resume (struct subchannel *sch){ char dbf_txt[15]; int ccode; CIO_TRACE_EVENT (4, "resIO"); CIO_TRACE_EVENT (4, sch->dev.bus_id); ccode = rsch (sch->irq); sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (4, dbf_txt); switch (ccode) { case 0: sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND; return 0; case 1: return -EBUSY; case 2: return -EINVAL; default: /* * useless to wait for request completion * as device is no longer operational ! */ return -ENODEV; }}/* * halt I/O operation */intcio_halt(struct subchannel *sch){ char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT (2, "haltIO"); CIO_TRACE_EVENT (2, sch->dev.bus_id); /* * Issue "Halt subchannel" and process condition code */ ccode = hsch (sch->irq); sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (2, dbf_txt); switch (ccode) { case 0: sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ return -EBUSY; default: /* device not operational */ return -ENODEV; }}/* * Clear I/O operation */intcio_clear(struct subchannel *sch){ char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT (2, "clearIO"); CIO_TRACE_EVENT (2, sch->dev.bus_id); /* * Issue "Clear subchannel" and process condition code */ ccode = csch (sch->irq); sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (2, dbf_txt); switch (ccode) { case 0: sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND; return 0; default: /* device not operational */ return -ENODEV; }}/* * Function: cio_cancel * Issues a "Cancel Subchannel" on the specified subchannel * Note: We don't need any fancy intparms and flags here * since xsch is executed synchronously. * Only for common I/O internal use as for now. */intcio_cancel (struct subchannel *sch){ char dbf_txt[15]; int ccode; if (!sch) return -ENODEV; CIO_TRACE_EVENT (2, "cancelIO"); CIO_TRACE_EVENT (2, sch->dev.bus_id); ccode = xsch (sch->irq); sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (2, dbf_txt); switch (ccode) { case 0: /* success */ /* Update information in scsw. */ stsch (sch->irq, &sch->schib); return 0; case 1: /* status pending */ return -EBUSY; case 2: /* not applicable */ return -EINVAL; default: /* not oper */ return -ENODEV; }}/* * Function: cio_modify * Issues a "Modify Subchannel" on the specified subchannel */intcio_modify (struct subchannel *sch){ int ccode, retry, ret; ret = 0; for (retry = 0; retry < 5; retry++) { ccode = msch_err (sch->irq, &sch->schib); if (ccode < 0) /* -EIO if msch gets a program check. */ return ccode; switch (ccode) { case 0: /* successfull */ return 0; case 1: /* status pending */ return -EBUSY; case 2: /* busy */ udelay (100); /* allow for recovery */ ret = -EBUSY; break; case 3: /* not operational */ return -ENODEV; } } return ret;}/* * Enable subchannel. */intcio_enable_subchannel (struct subchannel *sch, unsigned int isc){ char dbf_txt[15]; int ccode; int retry; int ret; CIO_TRACE_EVENT (2, "ensch"); CIO_TRACE_EVENT (2, sch->dev.bus_id); ccode = stsch (sch->irq, &sch->schib); if (ccode) return -ENODEV; sch->schib.pmcw.ena = 1; sch->schib.pmcw.isc = isc; sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; for (retry = 5, ret = 0; retry > 0; retry--) { ret = cio_modify(sch); if (ret == -ENODEV) break; if (ret == -EIO) /* * Got a program check in cio_modify. Try without
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -