aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>2008-07-14 03:58:51 -0400
committerHeiko Carstens <heiko.carstens@de.ibm.com>2008-07-14 04:02:08 -0400
commit83262d6349e60b9d10798d489719d80029c00798 (patch)
treef12bb266672c0e1df62b4194ea3618fda30f6b9a /drivers/s390
parent23d805b647db6c2063a13089497615efa9deacdd (diff)
[S390] cio: provide functions for fcx enabled I/O
Provide functions for assembling and starting fcx enabled I/O request blocks. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/cio.c81
-rw-r--r--drivers/s390/cio/cio.h5
-rw-r--r--drivers/s390/cio/device_fsm.c49
-rw-r--r--drivers/s390/cio/device_id.c2
-rw-r--r--drivers/s390/cio/device_ops.c117
-rw-r--r--drivers/s390/cio/device_pgid.c2
-rw-r--r--drivers/s390/cio/fcx.c350
-rw-r--r--drivers/s390/cio/io_sch.h46
9 files changed, 616 insertions, 38 deletions
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index 9c22e27391dc..ee3c416243b4 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,7 +2,7 @@
2# Makefile for the S/390 common i/o drivers 2# Makefile for the S/390 common i/o drivers
3# 3#
4 4
5obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o scsw.o 5obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o scsw.o fcx.o
6ccw_device-objs += device.o device_fsm.o device_ops.o 6ccw_device-objs += device.o device_fsm.o device_ops.o
7ccw_device-objs += device_id.o device_pgid.o device_status.o 7ccw_device-objs += device_id.o device_pgid.o device_status.o
8obj-y += ccw_device.o cmf.o 8obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 40b2884126da..c24dfcd858da 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -25,6 +25,7 @@
25#include <asm/chpid.h> 25#include <asm/chpid.h>
26#include <asm/airq.h> 26#include <asm/airq.h>
27#include <asm/cpu.h> 27#include <asm/cpu.h>
28#include <asm/fcx.h>
28#include "cio.h" 29#include "cio.h"
29#include "css.h" 30#include "css.h"
30#include "chsc.h" 31#include "chsc.h"
@@ -167,30 +168,30 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
167{ 168{
168 char dbf_txt[15]; 169 char dbf_txt[15];
169 int ccode; 170 int ccode;
170 struct orb *orb; 171 union orb *orb;
171 172
172 CIO_TRACE_EVENT(4, "stIO"); 173 CIO_TRACE_EVENT(4, "stIO");
173 CIO_TRACE_EVENT(4, sch->dev.bus_id); 174 CIO_TRACE_EVENT(4, sch->dev.bus_id);
174 175
175 orb = &to_io_private(sch)->orb; 176 orb = &to_io_private(sch)->orb;
176 /* sch is always under 2G. */ 177 /* sch is always under 2G. */
177 orb->intparm = (u32)(addr_t)sch; 178 orb->cmd.intparm = (u32)(addr_t)sch;
178 orb->fmt = 1; 179 orb->cmd.fmt = 1;
179 180
180 orb->pfch = sch->options.prefetch == 0; 181 orb->cmd.pfch = sch->options.prefetch == 0;
181 orb->spnd = sch->options.suspend; 182 orb->cmd.spnd = sch->options.suspend;
182 orb->ssic = sch->options.suspend && sch->options.inter; 183 orb->cmd.ssic = sch->options.suspend && sch->options.inter;
183 orb->lpm = (lpm != 0) ? lpm : sch->lpm; 184 orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
184#ifdef CONFIG_64BIT 185#ifdef CONFIG_64BIT
185 /* 186 /*
186 * for 64 bit we always support 64 bit IDAWs with 4k page size only 187 * for 64 bit we always support 64 bit IDAWs with 4k page size only
187 */ 188 */
188 orb->c64 = 1; 189 orb->cmd.c64 = 1;
189 orb->i2k = 0; 190 orb->cmd.i2k = 0;
190#endif 191#endif
191 orb->key = key >> 4; 192 orb->cmd.key = key >> 4;
192 /* issue "Start Subchannel" */ 193 /* issue "Start Subchannel" */
193 orb->cpa = (__u32) __pa(cpa); 194 orb->cmd.cpa = (__u32) __pa(cpa);
194 ccode = ssch(sch->schid, orb); 195 ccode = ssch(sch->schid, orb);
195 196
196 /* process condition code */ 197 /* process condition code */
@@ -1067,3 +1068,61 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
1067 iplinfo->is_qdio = schib.pmcw.qf; 1068 iplinfo->is_qdio = schib.pmcw.qf;
1068 return 0; 1069 return 0;
1069} 1070}
1071
1072/**
1073 * cio_tm_start_key - perform start function
1074 * @sch: subchannel on which to perform the start function
1075 * @tcw: transport-command word to be started
1076 * @lpm: mask of paths to use
1077 * @key: storage key to use for storage access
1078 *
1079 * Start the tcw on the given subchannel. Return zero on success, non-zero
1080 * otherwise.
1081 */
1082int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
1083{
1084 int cc;
1085 union orb *orb = &to_io_private(sch)->orb;
1086
1087 memset(orb, 0, sizeof(union orb));
1088 orb->tm.intparm = (u32) (addr_t) sch;
1089 orb->tm.key = key >> 4;
1090 orb->tm.b = 1;
1091 orb->tm.lpm = lpm ? lpm : sch->lpm;
1092 orb->tm.tcw = (u32) (addr_t) tcw;
1093 cc = ssch(sch->schid, orb);
1094 switch (cc) {
1095 case 0:
1096 return 0;
1097 case 1:
1098 case 2:
1099 return -EBUSY;
1100 default:
1101 return cio_start_handle_notoper(sch, lpm);
1102 }
1103}
1104
1105/**
1106 * cio_tm_intrg - perform interrogate function
1107 * @sch - subchannel on which to perform the interrogate function
1108 *
1109 * If the specified subchannel is running in transport-mode, perform the
1110 * interrogate function. Return zero on success, non-zero otherwie.
1111 */
1112int cio_tm_intrg(struct subchannel *sch)
1113{
1114 int cc;
1115
1116 if (!to_io_private(sch)->orb.tm.b)
1117 return -EINVAL;
1118 cc = xsch(sch->schid);
1119 switch (cc) {
1120 case 0:
1121 case 2:
1122 return 0;
1123 case 1:
1124 return -EBUSY;
1125 default:
1126 return -ENODEV;
1127 }
1128}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index f7a0cb9fac9b..49ee6395116d 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -5,6 +5,8 @@
5#include <linux/device.h> 5#include <linux/device.h>
6#include <linux/mod_devicetable.h> 6#include <linux/mod_devicetable.h>
7#include <asm/chpid.h> 7#include <asm/chpid.h>
8#include <asm/cio.h>
9#include <asm/fcx.h>
8#include "chsc.h" 10#include "chsc.h"
9#include "schid.h" 11#include "schid.h"
10 12
@@ -100,6 +102,9 @@ extern int cio_set_options (struct subchannel *, int);
100extern int cio_get_options (struct subchannel *); 102extern int cio_get_options (struct subchannel *);
101extern int cio_modify (struct subchannel *); 103extern int cio_modify (struct subchannel *);
102 104
105int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
106int cio_tm_intrg(struct subchannel *sch);
107
103int cio_create_sch_lock(struct subchannel *); 108int cio_create_sch_lock(struct subchannel *);
104void do_adapter_IO(void); 109void do_adapter_IO(void);
105void do_IRQ(struct pt_regs *); 110void do_IRQ(struct pt_regs *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index dc9373031af0..cd31bb5177e6 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -39,31 +39,43 @@ static void ccw_timeout_log(struct ccw_device *cdev)
39 struct schib schib; 39 struct schib schib;
40 struct subchannel *sch; 40 struct subchannel *sch;
41 struct io_subchannel_private *private; 41 struct io_subchannel_private *private;
42 union orb *orb;
42 int cc; 43 int cc;
43 44
44 sch = to_subchannel(cdev->dev.parent); 45 sch = to_subchannel(cdev->dev.parent);
45 private = to_io_private(sch); 46 private = to_io_private(sch);
47 orb = &private->orb;
46 cc = stsch(sch->schid, &schib); 48 cc = stsch(sch->schid, &schib);
47 49
48 printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " 50 printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
49 "device information:\n", get_clock()); 51 "device information:\n", get_clock());
50 printk(KERN_WARNING "cio: orb:\n"); 52 printk(KERN_WARNING "cio: orb:\n");
51 print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, 53 print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
52 &private->orb, sizeof(private->orb), 0); 54 orb, sizeof(*orb), 0);
53 printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id); 55 printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
54 printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id); 56 printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
55 printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " 57 printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
56 "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); 58 "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
57 59
58 if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw || 60 if (orb->tm.b) {
59 (void *)(addr_t)private->orb.cpa == cdev->private->iccws) 61 printk(KERN_WARNING "cio: orb indicates transport mode\n");
60 printk(KERN_WARNING "cio: last channel program (intern):\n"); 62 printk(KERN_WARNING "cio: last tcw:\n");
61 else 63 print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
62 printk(KERN_WARNING "cio: last channel program:\n"); 64 (void *)(addr_t)orb->tm.tcw,
63 65 sizeof(struct tcw), 0);
64 print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, 66 } else {
65 (void *)(addr_t)private->orb.cpa, 67 printk(KERN_WARNING "cio: orb indicates command mode\n");
66 sizeof(struct ccw1), 0); 68 if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
69 (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
70 printk(KERN_WARNING "cio: last channel program "
71 "(intern):\n");
72 else
73 printk(KERN_WARNING "cio: last channel program:\n");
74
75 print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
76 (void *)(addr_t)orb->cmd.cpa,
77 sizeof(struct ccw1), 0);
78 }
67 printk(KERN_WARNING "cio: ccw device state: %d\n", 79 printk(KERN_WARNING "cio: ccw device state: %d\n",
68 cdev->private->state); 80 cdev->private->state);
69 printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc); 81 printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
@@ -135,10 +147,13 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
135 /* Stage 1: cancel io. */ 147 /* Stage 1: cancel io. */
136 if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) && 148 if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
137 !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { 149 !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
138 ret = cio_cancel(sch); 150 if (!scsw_is_tm(&sch->schib.scsw)) {
139 if (ret != -EINVAL) 151 ret = cio_cancel(sch);
140 return ret; 152 if (ret != -EINVAL)
141 /* cancel io unsuccessful. From now on it is asynchronous. */ 153 return ret;
154 }
155 /* cancel io unsuccessful or not applicable (transport mode).
156 * Continue with asynchronous instructions. */
142 cdev->private->iretry = 3; /* 3 halt retries. */ 157 cdev->private->iretry = 3; /* 3 halt retries. */
143 } 158 }
144 if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { 159 if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
@@ -751,11 +766,13 @@ static void
751ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event) 766ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
752{ 767{
753 struct irb *irb; 768 struct irb *irb;
769 int is_cmd;
754 770
755 irb = (struct irb *) __LC_IRB; 771 irb = (struct irb *) __LC_IRB;
772 is_cmd = !scsw_is_tm(&irb->scsw);
756 /* Check for unsolicited interrupt. */ 773 /* Check for unsolicited interrupt. */
757 if (!scsw_is_solicited(&irb->scsw)) { 774 if (!scsw_is_solicited(&irb->scsw)) {
758 if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && 775 if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
759 !irb->esw.esw0.erw.cons) { 776 !irb->esw.esw0.erw.cons) {
760 /* Unit check but no sense data. Need basic sense. */ 777 /* Unit check but no sense data. Need basic sense. */
761 if (ccw_device_do_sense(cdev, irb) != 0) 778 if (ccw_device_do_sense(cdev, irb) != 0)
@@ -774,7 +791,7 @@ call_handler_unsol:
774 } 791 }
775 /* Accumulate status and find out if a basic sense is needed. */ 792 /* Accumulate status and find out if a basic sense is needed. */
776 ccw_device_accumulate_irb(cdev, irb); 793 ccw_device_accumulate_irb(cdev, irb);
777 if (cdev->private->flags.dosense) { 794 if (is_cmd && cdev->private->flags.dosense) {
778 if (ccw_device_do_sense(cdev, irb) == 0) { 795 if (ccw_device_do_sense(cdev, irb) == 0) {
779 cdev->private->state = DEV_STATE_W4SENSE; 796 cdev->private->state = DEV_STATE_W4SENSE;
780 } 797 }
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 5214b2b5425c..1bdaa614e34f 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -237,7 +237,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
237 if (irb->scsw.cmd.cc == 3) { 237 if (irb->scsw.cmd.cc == 3) {
238 u8 lpm; 238 u8 lpm;
239 239
240 lpm = to_io_private(sch)->orb.lpm; 240 lpm = to_io_private(sch)->orb.cmd.lpm;
241 if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) 241 if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
242 CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x " 242 CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
243 "on subchannel 0.%x.%04x is " 243 "on subchannel 0.%x.%04x is "
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 10f72c5c005a..ee1a28310fbb 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -17,6 +17,7 @@
17#include <asm/ccwdev.h> 17#include <asm/ccwdev.h>
18#include <asm/idals.h> 18#include <asm/idals.h>
19#include <asm/chpid.h> 19#include <asm/chpid.h>
20#include <asm/fcx.h>
20 21
21#include "cio.h" 22#include "cio.h"
22#include "cio_debug.h" 23#include "cio_debug.h"
@@ -569,6 +570,122 @@ void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id)
569} 570}
570EXPORT_SYMBOL(ccw_device_get_id); 571EXPORT_SYMBOL(ccw_device_get_id);
571 572
573/**
574 * ccw_device_tm_start_key - perform start function
575 * @cdev: ccw device on which to perform the start function
576 * @tcw: transport-command word to be started
577 * @intparm: user defined parameter to be passed to the interrupt handler
578 * @lpm: mask of paths to use
579 * @key: storage key to use for storage access
580 *
581 * Start the tcw on the given ccw device. Return zero on success, non-zero
582 * otherwise.
583 */
584int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
585 unsigned long intparm, u8 lpm, u8 key)
586{
587 struct subchannel *sch;
588 int rc;
589
590 sch = to_subchannel(cdev->dev.parent);
591 if (cdev->private->state != DEV_STATE_ONLINE)
592 return -EIO;
593 /* Adjust requested path mask to excluded varied off paths. */
594 if (lpm) {
595 lpm &= sch->opm;
596 if (lpm == 0)
597 return -EACCES;
598 }
599 rc = cio_tm_start_key(sch, tcw, lpm, key);
600 if (rc == 0)
601 cdev->private->intparm = intparm;
602 return rc;
603}
604EXPORT_SYMBOL(ccw_device_tm_start_key);
605
606/**
607 * ccw_device_tm_start_timeout_key - perform start function
608 * @cdev: ccw device on which to perform the start function
609 * @tcw: transport-command word to be started
610 * @intparm: user defined parameter to be passed to the interrupt handler
611 * @lpm: mask of paths to use
612 * @key: storage key to use for storage access
613 * @expires: time span in jiffies after which to abort request
614 *
615 * Start the tcw on the given ccw device. Return zero on success, non-zero
616 * otherwise.
617 */
618int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
619 unsigned long intparm, u8 lpm, u8 key,
620 int expires)
621{
622 int ret;
623
624 ccw_device_set_timeout(cdev, expires);
625 ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key);
626 if (ret != 0)
627 ccw_device_set_timeout(cdev, 0);
628 return ret;
629}
630EXPORT_SYMBOL(ccw_device_tm_start_timeout_key);
631
632/**
633 * ccw_device_tm_start - perform start function
634 * @cdev: ccw device on which to perform the start function
635 * @tcw: transport-command word to be started
636 * @intparm: user defined parameter to be passed to the interrupt handler
637 * @lpm: mask of paths to use
638 *
639 * Start the tcw on the given ccw device. Return zero on success, non-zero
640 * otherwise.
641 */
642int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw,
643 unsigned long intparm, u8 lpm)
644{
645 return ccw_device_tm_start_key(cdev, tcw, intparm, lpm,
646 PAGE_DEFAULT_KEY);
647}
648EXPORT_SYMBOL(ccw_device_tm_start);
649
650/**
651 * ccw_device_tm_start_timeout - perform start function
652 * @cdev: ccw device on which to perform the start function
653 * @tcw: transport-command word to be started
654 * @intparm: user defined parameter to be passed to the interrupt handler
655 * @lpm: mask of paths to use
656 * @expires: time span in jiffies after which to abort request
657 *
658 * Start the tcw on the given ccw device. Return zero on success, non-zero
659 * otherwise.
660 */
661int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw,
662 unsigned long intparm, u8 lpm, int expires)
663{
664 return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm,
665 PAGE_DEFAULT_KEY, expires);
666}
667EXPORT_SYMBOL(ccw_device_tm_start_timeout);
668
669/**
670 * ccw_device_tm_intrg - perform interrogate function
671 * @cdev: ccw device on which to perform the interrogate function
672 *
673 * Perform an interrogate function on the given ccw device. Return zero on
674 * success, non-zero otherwise.
675 */
676int ccw_device_tm_intrg(struct ccw_device *cdev)
677{
678 struct subchannel *sch = to_subchannel(cdev->dev.parent);
679
680 if (cdev->private->state != DEV_STATE_ONLINE)
681 return -EIO;
682 if (!scsw_is_tm(&sch->schib.scsw) ||
683 !(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND))
684 return -EINVAL;
685 return cio_tm_intrg(sch);
686}
687EXPORT_SYMBOL(ccw_device_tm_intrg);
688
572// FIXME: these have to go: 689// FIXME: these have to go:
573 690
574int 691int
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 22a711bb5444..86bc94eb607f 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -158,7 +158,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
158 if (irb->scsw.cmd.cc == 3) { 158 if (irb->scsw.cmd.cc == 3) {
159 u8 lpm; 159 u8 lpm;
160 160
161 lpm = to_io_private(sch)->orb.lpm; 161 lpm = to_io_private(sch)->orb.cmd.lpm;
162 CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," 162 CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
163 " lpm %02X, became 'not operational'\n", 163 " lpm %02X, became 'not operational'\n",
164 cdev->private->dev_id.devno, sch->schid.ssid, 164 cdev->private->dev_id.devno, sch->schid.ssid,
diff --git a/drivers/s390/cio/fcx.c b/drivers/s390/cio/fcx.c
new file mode 100644
index 000000000000..61677dfbdc9b
--- /dev/null
+++ b/drivers/s390/cio/fcx.c
@@ -0,0 +1,350 @@
1/*
2 * Functions for assembling fcx enabled I/O control blocks.
3 *
4 * Copyright IBM Corp. 2008
5 * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
6 */
7
8#include <linux/kernel.h>
9#include <linux/types.h>
10#include <linux/string.h>
11#include <linux/errno.h>
12#include <linux/err.h>
13#include <linux/module.h>
14#include <asm/fcx.h>
15#include "cio.h"
16
17/**
18 * tcw_get_intrg - return pointer to associated interrogate tcw
19 * @tcw: pointer to the original tcw
20 *
21 * Return a pointer to the interrogate tcw associated with the specified tcw
22 * or %NULL if there is no associated interrogate tcw.
23 */
24struct tcw *tcw_get_intrg(struct tcw *tcw)
25{
26 return (struct tcw *) ((addr_t) tcw->intrg);
27}
28EXPORT_SYMBOL(tcw_get_intrg);
29
30/**
31 * tcw_get_data - return pointer to input/output data associated with tcw
32 * @tcw: pointer to the tcw
33 *
34 * Return the input or output data address specified in the tcw depending
35 * on whether the r-bit or the w-bit is set. If neither bit is set, return
36 * %NULL.
37 */
38void *tcw_get_data(struct tcw *tcw)
39{
40 if (tcw->r)
41 return (void *) ((addr_t) tcw->input);
42 if (tcw->w)
43 return (void *) ((addr_t) tcw->output);
44 return NULL;
45}
46EXPORT_SYMBOL(tcw_get_data);
47
48/**
49 * tcw_get_tccb - return pointer to tccb associated with tcw
50 * @tcw: pointer to the tcw
51 *
52 * Return pointer to the tccb associated with this tcw.
53 */
54struct tccb *tcw_get_tccb(struct tcw *tcw)
55{
56 return (struct tccb *) ((addr_t) tcw->tccb);
57}
58EXPORT_SYMBOL(tcw_get_tccb);
59
60/**
61 * tcw_get_tsb - return pointer to tsb associated with tcw
62 * @tcw: pointer to the tcw
63 *
64 * Return pointer to the tsb associated with this tcw.
65 */
66struct tsb *tcw_get_tsb(struct tcw *tcw)
67{
68 return (struct tsb *) ((addr_t) tcw->tsb);
69}
70EXPORT_SYMBOL(tcw_get_tsb);
71
72/**
73 * tcw_init - initialize tcw data structure
74 * @tcw: pointer to the tcw to be initialized
75 * @r: initial value of the r-bit
76 * @w: initial value of the w-bit
77 *
78 * Initialize all fields of the specified tcw data structure with zero and
79 * fill in the format, flags, r and w fields.
80 */
81void tcw_init(struct tcw *tcw, int r, int w)
82{
83 memset(tcw, 0, sizeof(struct tcw));
84 tcw->format = TCW_FORMAT_DEFAULT;
85 tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT);
86 if (r)
87 tcw->r = 1;
88 if (w)
89 tcw->w = 1;
90}
91EXPORT_SYMBOL(tcw_init);
92
93static inline size_t tca_size(struct tccb *tccb)
94{
95 return tccb->tcah.tcal - 12;
96}
97
98static u32 calc_dcw_count(struct tccb *tccb)
99{
100 int offset;
101 struct dcw *dcw;
102 u32 count = 0;
103 size_t size;
104
105 size = tca_size(tccb);
106 for (offset = 0; offset < size;) {
107 dcw = (struct dcw *) &tccb->tca[offset];
108 count += dcw->count;
109 if (!(dcw->flags & DCW_FLAGS_CC))
110 break;
111 offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4);
112 }
113 return count;
114}
115
116static u32 calc_cbc_size(struct tidaw *tidaw, int num)
117{
118 int i;
119 u32 cbc_data;
120 u32 cbc_count = 0;
121 u64 data_count = 0;
122
123 for (i = 0; i < num; i++) {
124 if (tidaw[i].flags & TIDAW_FLAGS_LAST)
125 break;
126 /* TODO: find out if padding applies to total of data
127 * transferred or data transferred by this tidaw. Assumption:
128 * applies to total. */
129 data_count += tidaw[i].count;
130 if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) {
131 cbc_data = 4 + ALIGN(data_count, 4) - data_count;
132 cbc_count += cbc_data;
133 data_count += cbc_data;
134 }
135 }
136 return cbc_count;
137}
138
139/**
140 * tcw_finalize - finalize tcw length fields and tidaw list
141 * @tcw: pointer to the tcw
142 * @num_tidaws: the number of tidaws used to address input/output data or zero
143 * if no tida is used
144 *
145 * Calculate the input-/output-count and tccbl field in the tcw, add a
146 * tcat the tccb and terminate the data tidaw list if used.
147 *
148 * Note: in case input- or output-tida is used, the tidaw-list must be stored
149 * in contiguous storage (no ttic). The tcal field in the tccb must be
150 * up-to-date.
151 */
152void tcw_finalize(struct tcw *tcw, int num_tidaws)
153{
154 struct tidaw *tidaw;
155 struct tccb *tccb;
156 struct tccb_tcat *tcat;
157 u32 count;
158
159 /* Terminate tidaw list. */
160 tidaw = tcw_get_data(tcw);
161 if (num_tidaws > 0)
162 tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST;
163 /* Add tcat to tccb. */
164 tccb = tcw_get_tccb(tcw);
165 tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)];
166 memset(tcat, 0, sizeof(tcat));
167 /* Calculate tcw input/output count and tcat transport count. */
168 count = calc_dcw_count(tccb);
169 if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA))
170 count += calc_cbc_size(tidaw, num_tidaws);
171 if (tcw->r)
172 tcw->input_count = count;
173 else if (tcw->w)
174 tcw->output_count = count;
175 tcat->count = ALIGN(count, 4) + 4;
176 /* Calculate tccbl. */
177 tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) +
178 sizeof(struct tccb_tcat) - 20) >> 2;
179}
180EXPORT_SYMBOL(tcw_finalize);
181
182/**
183 * tcw_set_intrg - set the interrogate tcw address of a tcw
184 * @tcw: the tcw address
185 * @intrg_tcw: the address of the interrogate tcw
186 *
187 * Set the address of the interrogate tcw in the specified tcw.
188 */
189void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
190{
191 tcw->intrg = (u32) ((addr_t) intrg_tcw);
192}
193EXPORT_SYMBOL(tcw_set_intrg);
194
195/**
196 * tcw_set_data - set data address and tida flag of a tcw
197 * @tcw: the tcw address
198 * @data: the data address
199 * @use_tidal: zero of the data address specifies a contiguous block of data,
200 * non-zero if it specifies a list if tidaws.
201 *
202 * Set the input/output data address of a tcw (depending on the value of the
203 * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
204 * is set as well.
205 */
206void tcw_set_data(struct tcw *tcw, void *data, int use_tidal)
207{
208 if (tcw->r) {
209 tcw->input = (u64) ((addr_t) data);
210 if (use_tidal)
211 tcw->flags |= TCW_FLAGS_INPUT_TIDA;
212 } else if (tcw->w) {
213 tcw->output = (u64) ((addr_t) data);
214 if (use_tidal)
215 tcw->flags |= TCW_FLAGS_OUTPUT_TIDA;
216 }
217}
218EXPORT_SYMBOL(tcw_set_data);
219
220/**
221 * tcw_set_tccb - set tccb address of a tcw
222 * @tcw: the tcw address
223 * @tccb: the tccb address
224 *
225 * Set the address of the tccb in the specified tcw.
226 */
227void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
228{
229 tcw->tccb = (u64) ((addr_t) tccb);
230}
231EXPORT_SYMBOL(tcw_set_tccb);
232
233/**
234 * tcw_set_tsb - set tsb address of a tcw
235 * @tcw: the tcw address
236 * @tsb: the tsb address
237 *
238 * Set the address of the tsb in the specified tcw.
239 */
240void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
241{
242 tcw->tsb = (u64) ((addr_t) tsb);
243}
244EXPORT_SYMBOL(tcw_set_tsb);
245
246/**
247 * tccb_init - initialize tccb
248 * @tccb: the tccb address
249 * @size: the maximum size of the tccb
250 * @sac: the service-action-code to be user
251 *
252 * Initialize the header of the specified tccb by resetting all values to zero
253 * and filling in defaults for format, sac and initial tcal fields.
254 */
255void tccb_init(struct tccb *tccb, size_t size, u32 sac)
256{
257 memset(tccb, 0, size);
258 tccb->tcah.format = TCCB_FORMAT_DEFAULT;
259 tccb->tcah.sac = sac;
260 tccb->tcah.tcal = 12;
261}
262EXPORT_SYMBOL(tccb_init);
263
264/**
265 * tsb_init - initialize tsb
266 * @tsb: the tsb address
267 *
268 * Initialize the specified tsb by resetting all values to zero.
269 */
270void tsb_init(struct tsb *tsb)
271{
272 memset(tsb, 0, sizeof(tsb));
273}
274EXPORT_SYMBOL(tsb_init);
275
276/**
277 * tccb_add_dcw - add a dcw to the tccb
278 * @tccb: the tccb address
279 * @tccb_size: the maximum tccb size
280 * @cmd: the dcw command
281 * @flags: flags for the dcw
282 * @cd: pointer to control data for this dcw or NULL if none is required
283 * @cd_count: number of control data bytes for this dcw
284 * @count: number of data bytes for this dcw
285 *
286 * Add a new dcw to the specified tccb by writing the dcw information specified
287 * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
288 * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
289 * would exceed the available space as defined by @tccb_size.
290 *
291 * Note: the tcal field of the tccb header will be updates to reflect added
292 * content.
293 */
294struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
295 void *cd, u8 cd_count, u32 count)
296{
297 struct dcw *dcw;
298 int size;
299 int tca_offset;
300
301 /* Check for space. */
302 tca_offset = tca_size(tccb);
303 size = ALIGN(sizeof(struct dcw) + cd_count, 4);
304 if (sizeof(struct tccb_tcah) + tca_offset + size +
305 sizeof(struct tccb_tcat) > tccb_size)
306 return ERR_PTR(-ENOSPC);
307 /* Add dcw to tca. */
308 dcw = (struct dcw *) &tccb->tca[tca_offset];
309 memset(dcw, 0, size);
310 dcw->cmd = cmd;
311 dcw->flags = flags;
312 dcw->count = count;
313 dcw->cd_count = cd_count;
314 if (cd)
315 memcpy(&dcw->cd[0], cd, cd_count);
316 tccb->tcah.tcal += size;
317 return dcw;
318}
319EXPORT_SYMBOL(tccb_add_dcw);
320
321/**
322 * tcw_add_tidaw - add a tidaw to a tcw
323 * @tcw: the tcw address
324 * @num_tidaws: the current number of tidaws
325 * @flags: flags for the new tidaw
326 * @addr: address value for the new tidaw
327 * @count: count value for the new tidaw
328 *
329 * Add a new tidaw to the input/output data tidaw-list of the specified tcw
330 * (depending on the value of the r-flag and w-flag) and return a pointer to
331 * the new tidaw.
332 *
333 * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller
334 * must ensure that there is enough space for the new tidaw. The last-tidaw
335 * flag for the last tidaw in the list will be set by tcw_finalize.
336 */
337struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
338 void *addr, u32 count)
339{
340 struct tidaw *tidaw;
341
342 /* Add tidaw to tidaw-list. */
343 tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws;
344 memset(tidaw, 0, sizeof(struct tidaw));
345 tidaw->flags = flags;
346 tidaw->count = count;
347 tidaw->addr = (u64) ((addr_t) addr);
348 return tidaw;
349}
350EXPORT_SYMBOL(tcw_add_tidaw);
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 8c613160bfce..b774960e76af 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -4,9 +4,9 @@
4#include "schid.h" 4#include "schid.h"
5 5
6/* 6/*
7 * operation request block 7 * command-mode operation request block
8 */ 8 */
9struct orb { 9struct cmd_orb {
10 u32 intparm; /* interruption parameter */ 10 u32 intparm; /* interruption parameter */
11 u32 key : 4; /* flags, like key, suspend control, etc. */ 11 u32 key : 4; /* flags, like key, suspend control, etc. */
12 u32 spnd : 1; /* suspend control */ 12 u32 spnd : 1; /* suspend control */
@@ -28,8 +28,36 @@ struct orb {
28 u32 cpa; /* channel program address */ 28 u32 cpa; /* channel program address */
29} __attribute__ ((packed, aligned(4))); 29} __attribute__ ((packed, aligned(4)));
30 30
31/*
32 * transport-mode operation request block
33 */
34struct tm_orb {
35 u32 intparm;
36 u32 key:4;
37 u32 :9;
38 u32 b:1;
39 u32 :2;
40 u32 lpm:8;
41 u32 :7;
42 u32 x:1;
43 u32 tcw;
44 u32 prio:8;
45 u32 :8;
46 u32 rsvpgm:8;
47 u32 :8;
48 u32 :32;
49 u32 :32;
50 u32 :32;
51 u32 :32;
52} __attribute__ ((packed, aligned(4)));
53
54union orb {
55 struct cmd_orb cmd;
56 struct tm_orb tm;
57} __attribute__ ((packed, aligned(4)));
58
31struct io_subchannel_private { 59struct io_subchannel_private {
32 struct orb orb; /* operation request block */ 60 union orb orb; /* operation request block */
33 struct ccw1 sense_ccw; /* static ccw for sense command */ 61 struct ccw1 sense_ccw; /* static ccw for sense command */
34} __attribute__ ((aligned(8))); 62} __attribute__ ((aligned(8)));
35 63
@@ -95,16 +123,18 @@ struct ccw_device_private {
95 void *cmb_wait; /* deferred cmb enable/disable */ 123 void *cmb_wait; /* deferred cmb enable/disable */
96}; 124};
97 125
98static inline int ssch(struct subchannel_id schid, volatile struct orb *addr) 126static inline int ssch(struct subchannel_id schid, volatile union orb *addr)
99{ 127{
100 register struct subchannel_id reg1 asm("1") = schid; 128 register struct subchannel_id reg1 asm("1") = schid;
101 int ccode; 129 int ccode = -EIO;
102 130
103 asm volatile( 131 asm volatile(
104 " ssch 0(%2)\n" 132 " ssch 0(%2)\n"
105 " ipm %0\n" 133 "0: ipm %0\n"
106 " srl %0,28" 134 " srl %0,28\n"
107 : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); 135 "1:\n"
136 EX_TABLE(0b, 1b)
137 : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
108 return ccode; 138 return ccode;
109} 139}
110 140