Merge tag 's390-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Heiko Carstens:

 - Drop support for outdated 3590/3592 and 3480 tape devices, and limit
   support to virtualized 3490E types devices

 - Implement exception based WARN() and WARN_ONCE() similar to x86

 - Slightly optimize preempt primitives like __preempt_count_add() and
   __preempt_count_dec_and_test()

 - A couple of small fixes and improvements

* tag 's390-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (35 commits)
  s390/tape: Consolidate tape config options and modules
  s390/cio: Fix device lifecycle handling in css_alloc_subchannel()
  s390/tape: Rename tape_34xx.c to tape_3490.c
  s390/tape: Cleanup sense data analysis and error handling
  s390/tape: Remove 3480 tape device type
  s390/tape: Remove unused command definitions
  s390/tape: Remove special block id handling
  s390/tape: Remove tape load display support
  s390/tape: Remove support for 3590/3592 models
  s390/kexec: Emit an error message when cmdline is too long
  s390/configs: Enable BLK_DEV_NULL_BLK as module
  s390: Document s390 stackprotector support
  s390/perf: Disable register readout on sampling events
  s390/Kconfig: Define non-zero ILLEGAL_POINTER_VALUE
  s390/bug: Prevent tail-call optimization
  s390/bug: Skip __WARN_trap() in call traces
  s390/bug: Implement WARN_ONCE()
  s390/bug: Implement __WARN_printf()
  s390/traps: Copy monitor code to pt_regs
  s390/bug: Introduce and use monitor code macro
  ...
diff --git a/Documentation/arch/s390/mm.rst b/Documentation/arch/s390/mm.rst
index 084adad..1968115 100644
--- a/Documentation/arch/s390/mm.rst
+++ b/Documentation/arch/s390/mm.rst
@@ -109,3 +109,7 @@
 		     |	 KASAN shadow	| KASAN untracked
 		     |			|
 		     +------------------+ ASCE limit
+		     |			|
+		     | CONFIG_ILLEGAL_POINTER_VALUE causes memory access fault
+		     |			|
+		     +------------------+
diff --git a/Documentation/features/debug/stackprotector/arch-support.txt b/Documentation/features/debug/stackprotector/arch-support.txt
index de8f43f..43e49c7 100644
--- a/Documentation/features/debug/stackprotector/arch-support.txt
+++ b/Documentation/features/debug/stackprotector/arch-support.txt
@@ -21,7 +21,7 @@
     |      parisc: | TODO |
     |     powerpc: |  ok  |
     |       riscv: |  ok  |
-    |        s390: | TODO |
+    |        s390: |  ok  |
     |          sh: |  ok  |
     |       sparc: | TODO |
     |          um: | TODO |
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0e5fad5..cda697a 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -69,6 +69,12 @@
 	  Clang versions before 19.1.0 do not support A,
 	  O, and R inline assembly format flags.
 
+config CC_HAS_ASM_IMMEDIATE_STRINGS
+	def_bool !(CC_IS_GCC && GCC_VERSION < 90000)
+	help
+	  GCC versions before 9.0.0 cannot handle strings as immediate
+	  input operands in inline assemblies.
+
 config CC_HAS_STACKPROTECTOR_GLOBAL
 	def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record)
 
@@ -85,6 +91,7 @@
 	select ARCH_ENABLE_MEMORY_HOTREMOVE
 	select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
 	select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE
+	select ARCH_HAS_CC_CAN_LINK
 	select ARCH_HAS_CPU_FINALIZE_INIT
 	select ARCH_HAS_CURRENT_STACK_POINTER
 	select ARCH_HAS_DEBUG_VIRTUAL
@@ -294,6 +301,14 @@
 
 source "kernel/livepatch/Kconfig"
 
+config ARCH_CC_CAN_LINK
+	bool
+	default $(cc_can_link_user,-m64)
+
+config ARCH_USERFLAGS
+	string
+	default "-m64"
+
 config ARCH_SUPPORTS_KEXEC
 	def_bool y
 
@@ -704,6 +719,10 @@
 config ARCH_SPARSEMEM_DEFAULT
 	def_bool y
 
+config ILLEGAL_POINTER_VALUE
+	hex
+	default 0xdead000000000000
+
 config MAX_PHYSMEM_BITS
 	int "Maximum size of supported physical memory in bits (42-53)"
 	range 42 53
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
index 490167f..a1e719a 100644
--- a/arch/s390/boot/Makefile
+++ b/arch/s390/boot/Makefile
@@ -21,6 +21,7 @@
 KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_MARCH),$(KBUILD_CFLAGS_DECOMPRESSOR))
 KBUILD_AFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS
 KBUILD_CFLAGS += $(CC_FLAGS_MARCH_MINIMUM) -D__DISABLE_EXPORTS
+KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
 
 CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
 
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c
index f77067d..7f33434 100644
--- a/arch/s390/boot/startup.c
+++ b/arch/s390/boot/startup.c
@@ -336,6 +336,7 @@ static unsigned long setup_kernel_memory_layout(unsigned long kernel_size)
 	BUILD_BUG_ON(!IS_ALIGNED(TEXT_OFFSET, THREAD_SIZE));
 	BUILD_BUG_ON(!IS_ALIGNED(__NO_KASLR_START_KERNEL, THREAD_SIZE));
 	BUILD_BUG_ON(__NO_KASLR_END_KERNEL > _REGION1_SIZE);
+	BUILD_BUG_ON(CONFIG_ILLEGAL_POINTER_VALUE < _REGION1_SIZE);
 	vsize = get_vmem_size(ident_map_size, vmemmap_size, vmalloc_size, _REGION3_SIZE);
 	boot_debug("vmem size estimated: 0x%016lx\n", vsize);
 	if (IS_ENABLED(CONFIG_KASAN) || __NO_KASLR_END_KERNEL > _REGION2_SIZE ||
diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig
index 0713914..7a91d30 100644
--- a/arch/s390/configs/debug_defconfig
+++ b/arch/s390/configs/debug_defconfig
@@ -446,6 +446,7 @@
 CONFIG_VIRTIO_BLK=y
 CONFIG_BLK_DEV_RBD=m
 CONFIG_BLK_DEV_NVME=m
+CONFIG_BLK_DEV_NULL_BLK=m
 CONFIG_ENCLOSURE_SERVICES=m
 CONFIG_GENWQE=m
 CONFIG_RAID_ATTRS=m
diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig
index c064e0c..3bb2aa8 100644
--- a/arch/s390/configs/defconfig
+++ b/arch/s390/configs/defconfig
@@ -436,6 +436,7 @@
 CONFIG_VIRTIO_BLK=y
 CONFIG_BLK_DEV_RBD=m
 CONFIG_BLK_DEV_NVME=m
+CONFIG_BLK_DEV_NULL_BLK=m
 CONFIG_ENCLOSURE_SERVICES=m
 CONFIG_GENWQE=m
 CONFIG_RAID_ATTRS=m
diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h
index b24459f..3b95c65 100644
--- a/arch/s390/include/asm/ap.h
+++ b/arch/s390/include/asm/ap.h
@@ -78,7 +78,7 @@ union ap_queue_status_reg {
 };
 
 /**
- * ap_intructions_available() - Test if AP instructions are available.
+ * ap_instructions_available() - Test if AP instructions are available.
  *
  * Returns true if the AP instructions are installed, otherwise false.
  */
diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h
index f662eb4..7bd1801 100644
--- a/arch/s390/include/asm/asm-prototypes.h
+++ b/arch/s390/include/asm/asm-prototypes.h
@@ -3,6 +3,7 @@
 
 #include <linux/kvm_host.h>
 #include <linux/ftrace.h>
+#include <asm/bug.h>
 #include <asm/fpu.h>
 #include <asm/nospec-branch.h>
 #include <asm-generic/asm-prototypes.h>
diff --git a/arch/s390/include/asm/asm.h b/arch/s390/include/asm/asm.h
index e9062b01..510901c 100644
--- a/arch/s390/include/asm/asm.h
+++ b/arch/s390/include/asm/asm.h
@@ -30,7 +30,7 @@
  */
 #if defined(__GCC_ASM_FLAG_OUTPUTS__) && !(IS_ENABLED(CONFIG_CC_ASM_FLAG_OUTPUT_BROKEN))
 
-#define __HAVE_ASM_FLAG_OUTPUTS__
+#define __HAVE_ASM_FLAG_OUTPUTS__ 1
 
 #define CC_IPM(sym)
 #define CC_OUT(sym, var)	"=@cc" (var)
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index ee9221b..59017fd 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -2,60 +2,127 @@
 #ifndef _ASM_S390_BUG_H
 #define _ASM_S390_BUG_H
 
-#include <linux/stringify.h>
+#include <linux/compiler.h>
+#include <linux/const.h>
 
-#ifdef CONFIG_BUG
+#define	MONCODE_BUG	_AC(0, U)
+#define	MONCODE_BUG_ARG _AC(1, U)
 
-#ifndef CONFIG_DEBUG_BUGVERBOSE
-#define _BUGVERBOSE_LOCATION(file, line)
+#ifndef __ASSEMBLER__
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+#define __BUG_ENTRY_VERBOSE(format, file, line)				\
+	"	.long	" format " - .	# bug_entry::format\n"		\
+	"	.long	" file " - .	# bug_entry::file\n"		\
+	"	.short	" line "	# bug_entry::line\n"
 #else
-#define __BUGVERBOSE_LOCATION(file, line)			\
-		.pushsection .rodata.str, "aMS", @progbits, 1;	\
-		.align 2;					\
-	10002:	.ascii file "\0";				\
-		.popsection;					\
-								\
-		.long 10002b - .;				\
-		.short line;
-#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
+#define __BUG_ENTRY_VERBOSE(format, file, line)
 #endif
 
-#ifndef CONFIG_GENERIC_BUG
-#define __BUG_ENTRY(cond_str, flags)
+#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
+#define WARN_CONDITION_STR(cond_str) cond_str
 #else
-#define __BUG_ENTRY(cond_str, flags)				\
-		.pushsection __bug_table, "aw";			\
-		.align 4;					\
-	10000:	.long 10001f - .;				\
-		_BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
-		.short flags;					\
-		.popsection;					\
-	10001:
+#define WARN_CONDITION_STR(cond_str) ""
 #endif
 
-#define ASM_BUG_FLAGS(cond_str, flags)				\
-	__BUG_ENTRY(cond_str, flags)				\
-	mc		0,0
+#define __BUG_ENTRY(format, file, line, flags, size)			\
+		"	.section __bug_table,\"aw\"\n"			\
+		"1:	.long	0b - .	# bug_entry::bug_addr\n"	\
+		__BUG_ENTRY_VERBOSE(format, file, line)			\
+		"	.short	"flags"	# bug_entry::flags\n"		\
+		"	.org	1b+"size"\n"				\
+		"	.previous"
 
-#define ASM_BUG()	ASM_BUG_FLAGS("", 0)
-
-#define __BUG_FLAGS(cond_str, flags)				\
-	asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
-
-#define __WARN_FLAGS(cond_str, flags)				\
-do {								\
-	__BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags));		\
+#define __BUG_ASM(cond_str, flags)					\
+do {									\
+	asm_inline volatile("\n"					\
+		"0:	mc	%[monc](%%r0),0\n"			\
+		__BUG_ENTRY("%[frmt]", "%[file]", "%[line]",		\
+			    "%[flgs]", "%[size]")			\
+		:							\
+		: [monc] "i" (MONCODE_BUG),				\
+		  [frmt] "i" (WARN_CONDITION_STR(cond_str)),		\
+		  [file] "i" (__FILE__),				\
+		  [line] "i" (__LINE__),				\
+		  [flgs] "i" (flags),					\
+		  [size] "i" (sizeof(struct bug_entry)));		\
 } while (0)
 
-#define BUG()							\
-do {								\
-	__BUG_FLAGS("", 0);					\
-	unreachable();						\
+#define BUG()								\
+do {									\
+	__BUG_ASM("", 0);						\
+	unreachable();							\
 } while (0)
 
+#define __WARN_FLAGS(cond_str, flags)					\
+do {									\
+	__BUG_ASM(cond_str, BUGFLAG_WARNING | (flags));			\
+} while (0)
+
+#define __WARN_bug_entry(flags, format)					\
+({									\
+	struct bug_entry *bug;						\
+									\
+	asm_inline volatile("\n"					\
+		"0:	larl	%[bug],1f\n"				\
+		__BUG_ENTRY("%[frmt]", "%[file]", "%[line]",		\
+			    "%[flgs]", "%[size]")			\
+		: [bug] "=d" (bug)					\
+		: [frmt] "i" (format),					\
+		  [file] "i" (__FILE__),				\
+		  [line] "i" (__LINE__),				\
+		  [flgs] "i" (flags),					\
+		  [size] "i" (sizeof(struct bug_entry)));		\
+	bug;								\
+})
+
+/*
+ * Variable Argument List (va_list) as defined in ELF Application
+ * Binary Interface s390x Supplement documentation.
+ */
+struct arch_va_list {
+	long __gpr;
+	long __fpr;
+	void *__overflow_arg_area;
+	void *__reg_save_area;
+};
+
+struct bug_entry;
+struct pt_regs;
+
+void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
+void __WARN_trap(struct bug_entry *bug, ...);
+
+#define __WARN_print_arg(flags, format, arg...)				\
+do {									\
+	int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS;		\
+									\
+	__WARN_trap(__WARN_bug_entry(__flags, format), ## arg);		\
+	/* prevent tail-call optimization */				\
+	asm("");							\
+} while (0)
+
+#define __WARN_printf(taint, fmt, arg...) \
+	__WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg)
+
+#define WARN_ONCE(cond, format, arg...)					\
+({									\
+	int __ret_warn_on = !!(cond);					\
+									\
+	if (unlikely(__ret_warn_on)) {					\
+		__WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\
+				format, ## arg);			\
+	}								\
+	__ret_warn_on;							\
+})
+
 #define HAVE_ARCH_BUG
+#define HAVE_ARCH_BUG_FORMAT
+#define HAVE_ARCH_BUG_FORMAT_ARGS
 
-#endif /* CONFIG_BUG */
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+#endif /* __ASSEMBLER__ */
 
 #include <asm-generic/bug.h>
 
diff --git a/arch/s390/include/asm/pci_io.h b/arch/s390/include/asm/pci_io.h
index 43a5ea4..f3bef5b 100644
--- a/arch/s390/include/asm/pci_io.h
+++ b/arch/s390/include/asm/pci_io.h
@@ -18,6 +18,7 @@
 #define ZPCI_IOMAP_SHIFT		48
 #define ZPCI_IOMAP_ADDR_SHIFT		62
 #define ZPCI_IOMAP_ADDR_BASE		(1UL << ZPCI_IOMAP_ADDR_SHIFT)
+#define ZPCI_IOMAP_ADDR_MAX		((1UL << (ZPCI_IOMAP_ADDR_SHIFT + 1)) - 1)
 #define ZPCI_IOMAP_ADDR_OFF_MASK	((1UL << ZPCI_IOMAP_SHIFT) - 1)
 #define ZPCI_IOMAP_MAX_ENTRIES							\
 	(1UL << (ZPCI_IOMAP_ADDR_SHIFT - ZPCI_IOMAP_SHIFT))
diff --git a/arch/s390/include/asm/preempt.h b/arch/s390/include/asm/preempt.h
index 6ccd033..6e5821b 100644
--- a/arch/s390/include/asm/preempt.h
+++ b/arch/s390/include/asm/preempt.h
@@ -8,7 +8,10 @@
 #include <asm/cmpxchg.h>
 #include <asm/march.h>
 
-/* We use the MSB mostly because its available */
+/*
+ * Use MSB so it is possible to read preempt_count with LLGT which
+ * reads the least significant 31 bits with a single instruction.
+ */
 #define PREEMPT_NEED_RESCHED	0x80000000
 
 /*
@@ -23,7 +26,20 @@
  */
 static __always_inline int preempt_count(void)
 {
-	return READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED;
+	unsigned long lc_preempt, count;
+
+	BUILD_BUG_ON(sizeof_field(struct lowcore, preempt_count) != sizeof(int));
+	lc_preempt = offsetof(struct lowcore, preempt_count);
+	/* READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED */
+	asm_inline(
+		ALTERNATIVE("llgt	%[count],%[offzero](%%r0)\n",
+			    "llgt	%[count],%[offalt](%%r0)\n",
+			    ALT_FEATURE(MFEATURE_LOWCORE))
+		: [count] "=d" (count)
+		: [offzero] "i" (lc_preempt),
+		  [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS),
+		  "m" (((struct lowcore *)0)->preempt_count));
+	return count;
 }
 
 static __always_inline void preempt_count_set(int pc)
@@ -68,7 +84,17 @@ static __always_inline void __preempt_count_add(int val)
 	 */
 	if (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES)) {
 		if (__builtin_constant_p(val) && (val >= -128) && (val <= 127)) {
-			__atomic_add_const(val, &get_lowcore()->preempt_count);
+			unsigned long lc_preempt;
+
+			lc_preempt = offsetof(struct lowcore, preempt_count);
+			asm_inline(
+				ALTERNATIVE("asi	%[offzero](%%r0),%[val]\n",
+					    "asi	%[offalt](%%r0),%[val]\n",
+					    ALT_FEATURE(MFEATURE_LOWCORE))
+				: "+m" (((struct lowcore *)0)->preempt_count)
+				: [offzero] "i" (lc_preempt), [val] "i" (val),
+				  [offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS)
+				: "cc");
 			return;
 		}
 	}
@@ -87,7 +113,22 @@ static __always_inline void __preempt_count_sub(int val)
  */
 static __always_inline bool __preempt_count_dec_and_test(void)
 {
+#ifdef __HAVE_ASM_FLAG_OUTPUTS__
+	unsigned long lc_preempt;
+	int cc;
+
+	lc_preempt = offsetof(struct lowcore, preempt_count);
+	asm_inline(
+		ALTERNATIVE("alsi	%[offzero](%%r0),%[val]\n",
+			    "alsi	%[offalt](%%r0),%[val]\n",
+			    ALT_FEATURE(MFEATURE_LOWCORE))
+		: "=@cc" (cc), "+m" (((struct lowcore *)0)->preempt_count)
+		: [offzero] "i" (lc_preempt), [val] "i" (-1),
+		[offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS));
+	return (cc == 0) || (cc == 2);
+#else
 	return __atomic_add_const_and_test(-1, &get_lowcore()->preempt_count);
+#endif
 }
 
 /*
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index 962cf04..aaceb1d 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -120,7 +120,10 @@ struct pt_regs {
 			unsigned long gprs[NUM_GPRS];
 		};
 	};
-	unsigned long orig_gpr2;
+	union {
+		unsigned long orig_gpr2;
+		unsigned long monitor_code;
+	};
 	union {
 		struct {
 			unsigned int int_code;
@@ -214,16 +217,23 @@ void update_cr_regs(struct task_struct *task);
 #define arch_has_single_step()	(1)
 #define arch_has_block_step()	(1)
 
-#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
-#define instruction_pointer(regs) ((regs)->psw.addr)
-#define user_stack_pointer(regs)((regs)->gprs[15])
 #define profile_pc(regs) instruction_pointer(regs)
 
-static inline long regs_return_value(struct pt_regs *regs)
+static __always_inline bool user_mode(const struct pt_regs *regs)
+{
+	return psw_bits(regs->psw).pstate;
+}
+
+static inline long regs_return_value(const struct pt_regs *regs)
 {
 	return regs->gprs[2];
 }
 
+static __always_inline unsigned long instruction_pointer(const struct pt_regs *regs)
+{
+	return regs->psw.addr;
+}
+
 static inline void instruction_pointer_set(struct pt_regs *regs,
 					   unsigned long val)
 {
@@ -233,19 +243,26 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
 int regs_query_register_offset(const char *name);
 const char *regs_query_register_name(unsigned int offset);
 
-static __always_inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+static __always_inline unsigned long kernel_stack_pointer(const struct pt_regs *regs)
 {
 	return regs->gprs[15];
 }
 
-static __always_inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset)
+static __always_inline unsigned long user_stack_pointer(const struct pt_regs *regs)
+{
+	return regs->gprs[15];
+}
+
+static __always_inline unsigned long regs_get_register(const struct pt_regs *regs,
+						       unsigned int offset)
 {
 	if (offset >= NUM_GPRS)
 		return 0;
 	return regs->gprs[offset];
 }
 
-static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+static __always_inline int regs_within_kernel_stack(const struct pt_regs *regs,
+						    unsigned long addr)
 {
 	unsigned long ksp = kernel_stack_pointer(regs);
 
@@ -261,7 +278,8 @@ static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsign
  * is specifined by @regs. If the @n th entry is NOT in the kernel stack,
  * this returns 0.
  */
-static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+static __always_inline unsigned long regs_get_kernel_stack_nth(const struct pt_regs *regs,
+							       unsigned int n)
 {
 	unsigned long addr;
 
@@ -278,8 +296,8 @@ static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *r
  *
  * regs_get_kernel_argument() returns @n th argument of the function call.
  */
-static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
-						     unsigned int n)
+static __always_inline unsigned long regs_get_kernel_argument(const struct pt_regs *regs,
+							      unsigned int n)
 {
 	unsigned int argoffset = STACK_FRAME_OVERHEAD / sizeof(long);
 
@@ -290,7 +308,7 @@ static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
 	return regs_get_kernel_stack_nth(regs, argoffset + n);
 }
 
-static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
+static __always_inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
 {
 	regs->gprs[2] = rc;
 }
diff --git a/arch/s390/include/uapi/asm/tape390.h b/arch/s390/include/uapi/asm/tape390.h
deleted file mode 100644
index 90266c6..0000000
--- a/arch/s390/include/uapi/asm/tape390.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*************************************************************************
- *
- *	   enables user programs to display messages and control encryption
- *	   on s390 tape devices
- *
- *	   Copyright IBM Corp. 2001, 2006
- *	   Author(s): Michael Holzheu <holzheu@de.ibm.com>
- *
- *************************************************************************/
-
-#ifndef _TAPE390_H
-#define _TAPE390_H
-
-#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct)
-
-/*
- * The TAPE390_DISPLAY ioctl calls the Load Display command
- * which transfers 17 bytes of data from the channel to the subsystem:
- *     - 1 format control byte, and
- *     - two 8-byte messages
- *
- * Format control byte:
- *   0-2: New Message Overlay
- *     3: Alternate Messages
- *     4: Blink Message
- *     5: Display Low/High Message
- *     6: Reserved
- *     7: Automatic Load Request
- *
- */
-
-typedef struct display_struct {
-        char cntrl;
-        char message1[8];
-        char message2[8];
-} display_struct;
-
-/*
- * Tape encryption support
- */
-
-struct tape390_crypt_info {
-	char capability;
-	char status;
-	char medium_status;
-} __attribute__ ((packed));
-
-
-/* Macros for "capable" field */
-#define TAPE390_CRYPT_SUPPORTED_MASK 0x01
-#define TAPE390_CRYPT_SUPPORTED(x) \
-	((x.capability & TAPE390_CRYPT_SUPPORTED_MASK))
-
-/* Macros for "status" field */
-#define TAPE390_CRYPT_ON_MASK 0x01
-#define TAPE390_CRYPT_ON(x) (((x.status) & TAPE390_CRYPT_ON_MASK))
-
-/* Macros for "medium status" field */
-#define TAPE390_MEDIUM_LOADED_MASK 0x01
-#define TAPE390_MEDIUM_ENCRYPTED_MASK 0x02
-#define TAPE390_MEDIUM_ENCRYPTED(x) \
-	(((x.medium_status) & TAPE390_MEDIUM_ENCRYPTED_MASK))
-#define TAPE390_MEDIUM_LOADED(x) \
-	(((x.medium_status) & TAPE390_MEDIUM_LOADED_MASK))
-
-/*
- * The TAPE390_CRYPT_SET ioctl is used to switch on/off encryption.
- * The "encryption_capable" and "tape_status" fields are ignored for this ioctl!
- */
-#define TAPE390_CRYPT_SET _IOW('d', 2, struct tape390_crypt_info)
-
-/*
- * The TAPE390_CRYPT_QUERY ioctl is used to query the encryption state.
- */
-#define TAPE390_CRYPT_QUERY _IOR('d', 3, struct tape390_crypt_info)
-
-/* Values for "kekl1/2_type" and "kekl1/2_type_on_tape" fields */
-#define TAPE390_KEKL_TYPE_NONE 0
-#define TAPE390_KEKL_TYPE_LABEL 1
-#define TAPE390_KEKL_TYPE_HASH 2
-
-struct tape390_kekl {
-	unsigned char type;
-	unsigned char type_on_tape;
-	char label[65];
-} __attribute__ ((packed));
-
-struct tape390_kekl_pair {
-	struct tape390_kekl kekl[2];
-} __attribute__ ((packed));
-
-/*
- * The TAPE390_KEKL_SET ioctl is used to set Key Encrypting Key labels.
- */
-#define TAPE390_KEKL_SET _IOW('d', 4, struct tape390_kekl_pair)
-
-/*
- * The TAPE390_KEKL_QUERY ioctl is used to query Key Encrypting Key labels.
- */
-#define TAPE390_KEKL_QUERY _IOR('d', 5, struct tape390_kekl_pair)
-
-#endif 
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index b7f1553..4873fe9 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -23,6 +23,7 @@
 #include <asm/unistd.h>
 #include <asm/page.h>
 #include <asm/sigp.h>
+#include <asm/bug.h>
 #include <asm/irq.h>
 #include <asm/fpu-insn.h>
 #include <asm/setup.h>
@@ -173,6 +174,16 @@
 	BR_EX	%r14
 SYM_FUNC_END(__switch_to_asm)
 
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+SYM_FUNC_START(__WARN_trap)
+	mc	MONCODE_BUG_ARG(%r0),0
+	BR_EX	%r14
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
+
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+
 #if IS_ENABLED(CONFIG_KVM)
 /*
  * __sie64a calling convention:
diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c
index a36d731..1bf59c3 100644
--- a/arch/s390/kernel/machine_kexec_file.c
+++ b/arch/s390/kernel/machine_kexec_file.c
@@ -270,8 +270,10 @@ void *kexec_file_add_components(struct kimage *image,
 	if (image->kernel_buf_len < minsize + max_command_line_size)
 		goto out;
 
-	if (image->cmdline_buf_len >= max_command_line_size)
+	if (image->cmdline_buf_len >= max_command_line_size) {
+		pr_err("Kernel command line exceeds supported limit of %lu", max_command_line_size);
 		goto out;
+	}
 
 	memcpy(data.parm->command_line, image->cmdline_buf,
 	       image->cmdline_buf_len);
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index 459af23..e8bd19a 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -841,7 +841,7 @@ static bool is_callchain_event(struct perf_event *event)
 	u64 sample_type = event->attr.sample_type;
 
 	return sample_type & (PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER |
-			      PERF_SAMPLE_STACK_USER);
+			      PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_STACK_USER);
 }
 
 static int cpumsf_pmu_event_init(struct perf_event *event)
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 19687da..1b5c6fc 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -23,6 +23,7 @@
 #include <linux/cpu.h>
 #include <linux/entry-common.h>
 #include <linux/kmsan.h>
+#include <linux/bug.h>
 #include <asm/asm-extable.h>
 #include <asm/irqflags.h>
 #include <asm/ptrace.h>
@@ -220,11 +221,48 @@ static void space_switch_exception(struct pt_regs *regs)
 	do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
 }
 
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
+{
+	struct stack_frame *stack_frame;
+
+	/*
+	 * Generate va_list from pt_regs. See ELF Application Binary Interface
+	 * s390x Supplement documentation for details.
+	 *
+	 * - __overflow_arg_area needs to point to the parameter area, which
+	 *   is right above the standard stack frame (160 bytes)
+	 *
+	 * - __reg_save_area needs to point to a register save area where
+	 *   general registers (%r2 - %r6) can be found at offset 16. Which
+	 *   means that the gprs save area of pt_regs can be used
+	 *
+	 * - __gpr must be set to one, since the first parameter has been
+	 *   processed (pointer to bug_entry)
+	 */
+	stack_frame = (struct stack_frame *)regs->gprs[15];
+	args->__overflow_arg_area = stack_frame + 1;
+	args->__reg_save_area = regs->gprs;
+	args->__gpr = 1;
+	return args;
+}
+
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+
 static void monitor_event_exception(struct pt_regs *regs)
 {
+	enum bug_trap_type btt;
+
 	if (user_mode(regs))
 		return;
-	switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) {
+	if (regs->monitor_code == MONCODE_BUG_ARG) {
+		regs->psw.addr = regs->gprs[14];
+		btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs);
+	} else {
+		btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs);
+	}
+	switch (btt) {
 	case BUG_TRAP_TYPE_NONE:
 		fixup_exception(regs);
 		break;
@@ -258,11 +296,12 @@ static void __init test_monitor_call(void)
 	if (!IS_ENABLED(CONFIG_BUG))
 		return;
 	asm_inline volatile(
-		"	mc	0,0\n"
+		"	mc	%[monc](%%r0),0\n"
 		"0:	lhi	%[val],0\n"
 		"1:\n"
 		EX_TABLE(0b, 1b)
-		: [val] "+d" (val));
+		: [val] "+d" (val)
+		: [monc] "i" (MONCODE_BUG));
 	if (!val)
 		panic("Monitor call doesn't work!\n");
 }
@@ -297,6 +336,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
 	teid.val = lc->trans_exc_code;
 	regs->int_code = lc->pgm_int_code;
 	regs->int_parm_long = teid.val;
+	regs->monitor_code = lc->monitor_code;
 	/*
 	 * In case of a guest fault, short-circuit the fault handler and return.
 	 * This way the sie64a() function will return 0; fault address and
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 57f3980..97bab20 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -231,24 +231,33 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev)
 static int zpci_cfg_load(struct zpci_dev *zdev, int offset, u32 *val, u8 len)
 {
 	u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len);
+	int rc = -ENODEV;
 	u64 data;
-	int rc;
+
+	if (!zdev_enabled(zdev))
+		goto out_err;
 
 	rc = __zpci_load(&data, req, offset);
-	if (!rc) {
-		data = le64_to_cpu((__force __le64) data);
-		data >>= (8 - len) * 8;
-		*val = (u32) data;
-	} else
-		*val = 0xffffffff;
+	if (rc)
+		goto out_err;
+	data = le64_to_cpu((__force __le64)data);
+	data >>= (8 - len) * 8;
+	*val = (u32)data;
+	return 0;
+
+out_err:
+	PCI_SET_ERROR_RESPONSE(val);
 	return rc;
 }
 
 static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len)
 {
 	u64 req = ZPCI_CREATE_REQ(zdev->fh, ZPCI_PCIAS_CFGSPC, len);
+	int rc = -ENODEV;
 	u64 data = val;
-	int rc;
+
+	if (!zdev_enabled(zdev))
+		return rc;
 
 	data <<= (8 - len) * 8;
 	data = (__force u64) cpu_to_le64(data);
@@ -397,7 +406,9 @@ static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
 {
 	struct zpci_dev *zdev = zdev_from_bus(bus, devfn);
 
-	return (zdev) ? zpci_cfg_load(zdev, where, val, size) : -ENODEV;
+	if (!zdev || zpci_cfg_load(zdev, where, val, size))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
 }
 
 static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
@@ -405,7 +416,9 @@ static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
 {
 	struct zpci_dev *zdev = zdev_from_bus(bus, devfn);
 
-	return (zdev) ? zpci_cfg_store(zdev, where, val, size) : -ENODEV;
+	if (!zdev || zpci_cfg_store(zdev, where, val, size))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
 }
 
 static struct pci_ops pci_root_ops = {
@@ -1052,6 +1065,8 @@ static int zpci_mem_init(void)
 {
 	BUILD_BUG_ON(!is_power_of_2(__alignof__(struct zpci_fmb)) ||
 		     __alignof__(struct zpci_fmb) < sizeof(struct zpci_fmb));
+	BUILD_BUG_ON((CONFIG_ILLEGAL_POINTER_VALUE + 0x10000 > ZPCI_IOMAP_ADDR_BASE) &&
+		     (CONFIG_ILLEGAL_POINTER_VALUE <= ZPCI_IOMAP_ADDR_MAX));
 
 	zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb),
 					   __alignof__(struct zpci_fmb), 0, NULL);
diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile
index 0c196a5..61d240a 100644
--- a/arch/s390/purgatory/Makefile
+++ b/arch/s390/purgatory/Makefile
@@ -23,6 +23,7 @@
 KBUILD_CFLAGS += $(CLANG_FLAGS)
 KBUILD_CFLAGS += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag)
 KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
+KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
 KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS))
 KBUILD_AFLAGS += -D__DISABLE_EXPORTS
 
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index 80c4e51..4d8f099 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -106,37 +106,12 @@
 
 config S390_TAPE
 	def_tristate m
-	prompt "S/390 tape device support"
+	prompt "Support for 3490E tape on VTS"
 	depends on CCW
 	help
-	  Select this option if you want to access channel-attached tape
-	  devices on IBM S/390 or zSeries.
-	  If you select this option you will also want to select at
-	  least one of the tape interface options and one of the tape
-	  hardware options in order to access a tape device.
-	  This option is also available as a module. The module will be
-	  called tape390 and include all selected interfaces and
-	  hardware drivers.
-
-comment "S/390 tape hardware support"
-	depends on S390_TAPE
-
-config S390_TAPE_34XX
-	def_tristate m
-	prompt "Support for 3480/3490 tape hardware"
-	depends on S390_TAPE
-	help
-	  Select this option if you want to access IBM 3480/3490 magnetic
-	  tape subsystems and 100% compatibles.
-	  It is safe to say "Y" here.
-
-config S390_TAPE_3590
-	def_tristate m
-	prompt "Support for 3590 tape hardware"
-	depends on S390_TAPE
-	help
-	  Select this option if you want to access IBM 3590 magnetic
-	  tape subsystems and 100% compatibles.
+	  Select this option if you want to access channel-attached IBM 3490E
+	  tape devices on VTS, such as IBM TS7700.
+	  This option is also available as a module.
 	  It is safe to say "Y" here.
 
 config VMLOGRDR
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index dcbd511..126a87c 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -39,10 +39,8 @@
 obj-$(CONFIG_VMCP) += vmcp.o
 
 tape-$(CONFIG_PROC_FS) += tape_proc.o
-tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
-obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
-obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
-obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
+tape_s390-objs := tape_3490.o tape_char.o tape_class.o tape_core.o tape_std.o $(tape-y)
+obj-$(CONFIG_S390_TAPE) += tape_s390.o
 obj-$(CONFIG_MONREADER) += monreader.o
 obj-$(CONFIG_MONWRITER) += monwriter.o
 obj-$(CONFIG_S390_VMUR) += vmur.o
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index 3953b31..59541bd 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- *    tape device driver for 3480/3490E/3590 tapes.
+ *    tape device driver for 3490E tapes.
  *
  *  S390 and zSeries version
  *    Copyright IBM Corp. 2001, 2009
@@ -98,10 +98,6 @@ enum tape_op {
 	TO_DIS,		/* Tape display */
 	TO_ASSIGN,	/* Assign tape to channel path */
 	TO_UNASSIGN,	/* Unassign tape from channel path */
-	TO_CRYPT_ON,	/* Enable encrpytion */
-	TO_CRYPT_OFF,	/* Disable encrpytion */
-	TO_KEKL_SET,	/* Set KEK label */
-	TO_KEKL_QUERY,	/* Query KEK label */
 	TO_RDC,		/* Read device characteristics */
 	TO_SIZE,	/* #entries in tape_op_t */
 };
@@ -155,8 +151,6 @@ struct tape_discipline {
 	struct tape_request *(*read_block)(struct tape_device *);
 	struct tape_request *(*write_block)(struct tape_device *);
 	void (*process_eov)(struct tape_device*);
-	/* ioctl function for additional ioctls. */
-	int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long);
 	/* Array of tape commands with TAPE_NR_MTOPS entries */
 	tape_mtop_fn *mtop_array;
 };
@@ -192,7 +186,6 @@ struct tape_device {
 
 	/* Device discipline information. */
 	struct tape_discipline *	discipline;
-	void *				discdata;
 
 	/* Generic status flags */
 	long				tape_generic_status;
@@ -281,6 +274,10 @@ extern void tapechar_exit(void);
 extern int  tapechar_setup_device(struct tape_device *);
 extern void tapechar_cleanup_device(struct tape_device *);
 
+/* Externals from tape_3490.c */
+extern int tape_3490_init(void);
+extern void tape_3490_exit(void);
+
 /* tape initialisation functions */
 #ifdef CONFIG_PROC_FS
 extern void tape_proc_init (void);
diff --git a/drivers/s390/char/tape_3490.c b/drivers/s390/char/tape_3490.c
new file mode 100644
index 0000000..c4ea32a
--- /dev/null
+++ b/drivers/s390/char/tape_3490.c
@@ -0,0 +1,825 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *    tape device discipline for 3490 tapes.
+ *
+ *    Copyright IBM Corp. 2001, 2009
+ *    Author(s): Carsten Otte <cotte@de.ibm.com>
+ *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
+ *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#define pr_fmt(fmt) "tape_3490: " fmt
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bio.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define TAPE_DBF_AREA	tape_3490_dbf
+
+#include "tape.h"
+#include "tape_std.h"
+
+/*
+ * Pointer to debug area.
+ */
+debug_info_t *TAPE_DBF_AREA = NULL;
+EXPORT_SYMBOL(TAPE_DBF_AREA);
+
+struct tape_3490_block_id {
+	unsigned int	unused		: 10;
+	unsigned int	block		: 22;
+};
+
+/*
+ * Medium sense for 3490 tapes. There is no 'real' medium sense call.
+ * So we just do a normal sense.
+ */
+static void __tape_3490_medium_sense(struct tape_request *request)
+{
+	struct tape_device *device = request->device;
+	unsigned char *sense;
+
+	if (request->rc == 0) {
+		sense = request->cpdata;
+
+		/*
+		 * This isn't quite correct. But since INTERVENTION_REQUIRED
+		 * means that the drive is 'neither ready nor on-line' it is
+		 * only slightly inaccurate to say there is no tape loaded if
+		 * the drive isn't online...
+		 */
+		if (sense[0] & SENSE_INTERVENTION_REQUIRED)
+			tape_med_state_set(device, MS_UNLOADED);
+		else
+			tape_med_state_set(device, MS_LOADED);
+
+		if (sense[1] & SENSE_WRITE_PROTECT)
+			device->tape_generic_status |= GMT_WR_PROT(~0);
+		else
+			device->tape_generic_status &= ~GMT_WR_PROT(~0);
+	} else
+		DBF_EVENT(4, "tape_3490: medium sense failed with rc=%d\n",
+			request->rc);
+	tape_free_request(request);
+}
+
+static int tape_3490_medium_sense(struct tape_device *device)
+{
+	struct tape_request *request;
+	int rc;
+
+	request = tape_alloc_request(1, 32);
+	if (IS_ERR(request)) {
+		DBF_EXCEPTION(6, "MSEN fail\n");
+		return PTR_ERR(request);
+	}
+
+	request->op = TO_MSEN;
+	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
+	rc = tape_do_io_interruptible(device, request);
+	__tape_3490_medium_sense(request);
+	return rc;
+}
+
+static void tape_3490_medium_sense_async(struct tape_device *device)
+{
+	struct tape_request *request;
+
+	request = tape_alloc_request(1, 32);
+	if (IS_ERR(request)) {
+		DBF_EXCEPTION(6, "MSEN fail\n");
+		return;
+	}
+
+	request->op = TO_MSEN;
+	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
+	request->callback = (void *) __tape_3490_medium_sense;
+	request->callback_data = NULL;
+	tape_do_io_async(device, request);
+}
+
+struct tape_3490_work {
+	struct tape_device	*device;
+	enum tape_op		 op;
+	struct work_struct	 work;
+};
+
+/*
+ * These functions are currently used only to schedule a medium_sense for
+ * later execution. This is because we get an interrupt whenever a medium
+ * is inserted but cannot call tape_do_io* from an interrupt context.
+ * Maybe that's useful for other actions we want to start from the
+ * interrupt handler.
+ * Note: the work handler is called by the system work queue. The tape
+ * commands started by the handler need to be asynchrounous, otherwise
+ * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
+ */
+static void
+tape_3490_work_handler(struct work_struct *work)
+{
+	struct tape_3490_work *p =
+		container_of(work, struct tape_3490_work, work);
+	struct tape_device *device = p->device;
+
+	switch(p->op) {
+		case TO_MSEN:
+			tape_3490_medium_sense_async(device);
+			break;
+		default:
+			DBF_EVENT(3, "T3490: internal error: unknown work\n");
+	}
+	tape_put_device(device);
+	kfree(p);
+}
+
+static int
+tape_3490_schedule_work(struct tape_device *device, enum tape_op op)
+{
+	struct tape_3490_work *p;
+
+	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
+		return -ENOMEM;
+
+	INIT_WORK(&p->work, tape_3490_work_handler);
+
+	p->device = tape_get_device(device);
+	p->op     = op;
+
+	schedule_work(&p->work);
+	return 0;
+}
+
+/*
+ * Done Handler is called when dev stat = DEVICE-END (successful operation)
+ */
+static inline int
+tape_3490_done(struct tape_request *request)
+{
+	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
+	return TAPE_IO_SUCCESS;
+}
+
+static inline int
+tape_3490_erp_failed(struct tape_request *request, int rc)
+{
+	DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n",
+		  tape_op_verbose[request->op], rc);
+	return rc;
+}
+
+static inline int
+tape_3490_erp_succeeded(struct tape_request *request)
+{
+	DBF_EVENT(3, "Error Recovery successful for %s\n",
+		  tape_op_verbose[request->op]);
+	return tape_3490_done(request);
+}
+
+static inline int
+tape_3490_erp_retry(struct tape_request *request)
+{
+	DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]);
+	return TAPE_IO_RETRY;
+}
+
+/*
+ * This function is called, when no request is outstanding and we get an
+ * interrupt
+ */
+static int
+tape_3490_unsolicited_irq(struct tape_device *device, struct irb *irb)
+{
+	if (irb->scsw.cmd.dstat == 0x85) { /* READY */
+		/* A medium was inserted in the drive. */
+		DBF_EVENT(6, "xuud med\n");
+		tape_3490_schedule_work(device, TO_MSEN);
+	} else {
+		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
+		tape_dump_sense_dbf(device, NULL, irb);
+	}
+	return TAPE_IO_SUCCESS;
+}
+
+static int
+tape_3490_erp_bug(struct tape_device *device, struct tape_request *request,
+		  struct irb *irb, int no)
+{
+	if (request->op != TO_ASSIGN) {
+		dev_err(&device->cdev->dev, "An unexpected condition %d "
+			"occurred in tape error recovery\n", no);
+		tape_dump_sense_dbf(device, request, irb);
+	}
+	return tape_3490_erp_failed(request, -EIO);
+}
+
+/*
+ * Handle data overrun between cu and drive. The channel speed might
+ * be too slow.
+ */
+static int
+tape_3490_erp_overrun(struct tape_device *device, struct tape_request *request,
+		      struct irb *irb)
+{
+	if (irb->ecw[3] == 0x40) {
+		dev_warn (&device->cdev->dev, "A data overrun occurred between"
+			" the control unit and tape unit\n");
+		return tape_3490_erp_failed(request, -EIO);
+	}
+	return tape_3490_erp_bug(device, request, irb, -1);
+}
+
+/*
+ * Handle record sequence error.
+ */
+static int
+tape_3490_erp_sequence(struct tape_device *device,
+		       struct tape_request *request, struct irb *irb)
+{
+	if (irb->ecw[3] == 0x41) {
+		/*
+		 * cu detected incorrect block-id sequence on tape.
+		 */
+		dev_warn (&device->cdev->dev, "The block ID sequence on the "
+			"tape is incorrect\n");
+		return tape_3490_erp_failed(request, -EIO);
+	}
+	/*
+	 * Record sequence error bit is set, but erpa does not
+	 * show record sequence error.
+	 */
+	return tape_3490_erp_bug(device, request, irb, -2);
+}
+
+/*
+ * This function analyses the tape's sense-data in case of a unit-check.
+ * If possible, it tries to recover from the error. Else the user is
+ * informed about the problem.
+ */
+static int
+tape_3490_unit_check(struct tape_device *device, struct tape_request *request,
+		     struct irb *irb)
+{
+	int inhibit_cu_recovery;
+	__u8* sense;
+
+	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
+	sense = irb->ecw;
+
+	if (
+		sense[0] & SENSE_COMMAND_REJECT &&
+		sense[1] & SENSE_WRITE_PROTECT
+	) {
+		if (
+			request->op == TO_DSE ||
+			request->op == TO_WRI ||
+			request->op == TO_WTM
+		) {
+			/* medium is write protected */
+			return tape_3490_erp_failed(request, -EACCES);
+		} else {
+			return tape_3490_erp_bug(device, request, irb, -3);
+		}
+	}
+
+	/*
+	 * Special cases for various tape-states when reaching
+	 * end of recorded area
+	 *
+	 * FIXME: Maybe a special case of the special case:
+	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
+	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
+	 *        sense[3] == 0x47 (Volume Fenced)
+	 *
+	 *        This was caused by continued FSF or FSR after an
+	 *        'End Of Data'.
+	 */
+	if ((
+		sense[0] == SENSE_DATA_CHECK      ||
+		sense[0] == SENSE_EQUIPMENT_CHECK ||
+		sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK)
+	) && (
+		sense[1] == SENSE_DRIVE_ONLINE ||
+		sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE)
+	)) {
+		switch (request->op) {
+		/*
+		 * sense[0] == SENSE_DATA_CHECK   &&
+		 * sense[1] == SENSE_DRIVE_ONLINE
+		 * sense[3] == 0x36 (End Of Data)
+		 *
+		 * Further seeks might return a 'Volume Fenced'.
+		 */
+		case TO_FSF:
+		case TO_FSB:
+			/* Trying to seek beyond end of recorded area */
+			return tape_3490_erp_failed(request, -ENOSPC);
+		case TO_BSB:
+			return tape_3490_erp_retry(request);
+
+		/*
+		 * sense[0] == SENSE_DATA_CHECK   &&
+		 * sense[1] == SENSE_DRIVE_ONLINE &&
+		 * sense[3] == 0x36 (End Of Data)
+		 */
+		case TO_LBL:
+			/* Block could not be located. */
+			return tape_3490_erp_failed(request, -EIO);
+
+		case TO_RFO:
+			/* Read beyond end of recorded area -> 0 bytes read */
+			return tape_3490_erp_failed(request, 0);
+
+		/*
+		 * sense[0] == SENSE_EQUIPMENT_CHECK &&
+		 * sense[1] == SENSE_DRIVE_ONLINE    &&
+		 * sense[3] == 0x38 (Physical End Of Volume)
+		 */
+		case TO_WRI:
+			/* Writing at physical end of volume */
+			return tape_3490_erp_failed(request, -ENOSPC);
+		default:
+			return tape_3490_erp_failed(request, 0);
+		}
+	}
+
+	/* Sensing special bits */
+	if (sense[0] & SENSE_BUS_OUT_CHECK)
+		return tape_3490_erp_retry(request);
+
+	if (sense[0] & SENSE_DATA_CHECK) {
+		/*
+		 * hardware failure, damaged tape or improper
+		 * operating conditions
+		 */
+		switch (sense[3]) {
+		case 0x23:
+			/* a read data check occurred */
+			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
+			    inhibit_cu_recovery)
+				// data check is not permanent, may be
+				// recovered. We always use async-mode with
+				// cu-recovery, so this should *never* happen.
+				return tape_3490_erp_bug(device, request,
+							 irb, -4);
+
+			/* data check is permanent, CU recovery has failed */
+			dev_warn (&device->cdev->dev, "A read error occurred "
+				"that cannot be recovered\n");
+			return tape_3490_erp_failed(request, -EIO);
+		case 0x25:
+			// a write data check occurred
+			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
+			    inhibit_cu_recovery)
+				// data check is not permanent, may be
+				// recovered. We always use async-mode with
+				// cu-recovery, so this should *never* happen.
+				return tape_3490_erp_bug(device, request,
+							 irb, -5);
+
+			// data check is permanent, cu-recovery has failed
+			dev_warn (&device->cdev->dev, "A write error on the "
+				"tape cannot be recovered\n");
+			return tape_3490_erp_failed(request, -EIO);
+		case 0x28:
+			/* ID-Mark at tape start couldn't be written */
+			dev_warn (&device->cdev->dev, "Writing the ID-mark "
+				"failed\n");
+			return tape_3490_erp_failed(request, -EIO);
+		case 0x31:
+			/* Tape void. Tried to read beyond end of device. */
+			dev_warn (&device->cdev->dev, "Reading the tape beyond"
+				" the end of the recorded area failed\n");
+			return tape_3490_erp_failed(request, -ENOSPC);
+		case 0x41:
+			/* Record sequence error. */
+			dev_warn (&device->cdev->dev, "The tape contains an "
+				"incorrect block ID sequence\n");
+			return tape_3490_erp_failed(request, -EIO);
+		}
+	}
+
+	if (sense[0] & SENSE_OVERRUN)
+		return tape_3490_erp_overrun(device, request, irb);
+
+	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
+		return tape_3490_erp_sequence(device, request, irb);
+
+	/* Sensing erpa codes */
+	switch (sense[3]) {
+	case 0x00:
+		/* Unit check with erpa code 0. Report and ignore. */
+		return TAPE_IO_SUCCESS;
+	case 0x27:
+		/*
+		 * Command reject. May indicate illegal channel program or
+		 * buffer over/underrun. Since all channel programs are
+		 * issued by this driver and ought be correct, we assume a
+		 * over/underrun situation and retry the channel program.
+		 */
+		return tape_3490_erp_retry(request);
+	case 0x29:
+		/*
+		 * Function incompatible. Either the tape is idrc compressed
+		 * but the hardware isn't capable to do idrc, or a perform
+		 * subsystem func is issued and the CU is not on-line.
+		 */
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x2b:
+		/*
+		 * Environmental data present. Indicates either unload
+		 * completed ok or read buffered log command completed ok.
+		 */
+		if (request->op == TO_RUN) {
+			/* Rewind unload completed ok. */
+			tape_med_state_set(device, MS_UNLOADED);
+			return tape_3490_erp_succeeded(request);
+		}
+		/* tape_3490 doesn't use read buffered log commands. */
+		return tape_3490_erp_bug(device, request, irb, sense[3]);
+	case 0x2c:
+		/*
+		 * Permanent equipment check. CU has tried recovery, but
+		 * did not succeed.
+		 */
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x2d:
+		/* Data security erase failure. */
+		if (request->op == TO_DSE)
+			return tape_3490_erp_failed(request, -EIO);
+		/* Data security erase failure, but no such command issued. */
+		return tape_3490_erp_bug(device, request, irb, sense[3]);
+	case 0x2e:
+		/*
+		 * Not capable. This indicates either that the drive fails
+		 * reading the format id mark or that format specified
+		 * is not supported by the drive.
+		 */
+		dev_warn (&device->cdev->dev, "The tape unit cannot process "
+			"the tape format\n");
+		return tape_3490_erp_failed(request, -EMEDIUMTYPE);
+	case 0x30:
+		/* The medium is write protected. */
+		dev_warn (&device->cdev->dev, "The tape medium is write-"
+			"protected\n");
+		return tape_3490_erp_failed(request, -EACCES);
+	case 0x35:
+		/*
+		 * Drive equipment check. One of the following:
+		 * - cu cannot recover from a drive detected error
+		 * - a check code message is shown on drive display
+		 * - the cartridge loader does not respond correctly
+		 * - a failure occurs during an index, load, or unload cycle
+		 */
+		dev_warn (&device->cdev->dev, "An equipment check has occurred"
+			" on the tape unit\n");
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x36:
+		/* End of data. */
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x38:
+		/*
+		 * Physical end of tape. A read/write operation reached
+		 * the physical end of tape.
+		 */
+		if (request->op==TO_WRI ||
+		    request->op==TO_DSE ||
+		    request->op==TO_WTM)
+			return tape_3490_erp_failed(request, -ENOSPC);
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x39:
+		/* Backward at Beginning of tape. */
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x42:
+		/*
+		 * Degraded mode. A condition that can cause degraded
+		 * performance is detected.
+		 */
+		dev_warn (&device->cdev->dev, "The tape subsystem is running "
+			"in degraded mode\n");
+		return tape_3490_erp_retry(request);
+	case 0x43:
+		/* Drive not ready. */
+		tape_med_state_set(device, MS_UNLOADED);
+		/* Some commands commands are successful even in this case */
+		if (sense[1] & SENSE_DRIVE_ONLINE) {
+			switch(request->op) {
+				case TO_ASSIGN:
+				case TO_UNASSIGN:
+				case TO_DIS:
+				case TO_NOP:
+					return tape_3490_done(request);
+					break;
+				default:
+					break;
+			}
+		}
+		return tape_3490_erp_failed(request, -ENOMEDIUM);
+	case 0x44:
+		/* Locate Block unsuccessful. */
+		if (request->op != TO_BLOCK && request->op != TO_LBL)
+			/* No locate block was issued. */
+			return tape_3490_erp_bug(device, request,
+						 irb, sense[3]);
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x45:
+		/* The drive is assigned to a different channel path. */
+		dev_warn (&device->cdev->dev, "The tape unit is already "
+			"assigned\n");
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x47:
+		/* Volume fenced. CU reports volume integrity is lost. */
+		dev_warn (&device->cdev->dev, "The control unit has fenced "
+			"access to the tape volume\n");
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x48:
+		/* Log sense data and retry request. */
+		return tape_3490_erp_retry(request);
+	case 0x4d:
+		/*
+		 * Resetting event received. Since the driver does
+		 * not support resetting event recovery (which has to
+		 * be handled by the I/O Layer), retry our command.
+		 */
+		return tape_3490_erp_retry(request);
+	case 0x4e:
+		/*
+		 * Maximum block size exceeded. This indicates, that
+		 * the block to be written is larger than allowed for
+		 * buffered mode.
+		 */
+		dev_warn (&device->cdev->dev,
+			  "The maximum block size for buffered mode is exceeded\n");
+		return tape_3490_erp_failed(request, -ENOBUFS);
+	case 0x50:
+		/*
+		 * Read buffered log (Overflow). CU is running in extended
+		 * buffered log mode, and a counter overflows. This should
+		 * never happen, since we're never running in extended
+		 * buffered log mode.
+		 */
+		return tape_3490_erp_retry(request);
+	case 0x51:
+		/*
+		 * Read buffered log (EOV). EOF processing occurs while the
+		 * CU is in extended buffered log mode. This should never
+		 * happen, since we're never running in extended buffered
+		 * log mode.
+		 */
+		return tape_3490_erp_retry(request);
+	case 0x52:
+		/* End of Volume complete. Rewind unload completed ok. */
+		if (request->op == TO_RUN) {
+			tape_med_state_set(device, MS_UNLOADED);
+			return tape_3490_erp_succeeded(request);
+		}
+		return tape_3490_erp_bug(device, request, irb, sense[3]);
+	case 0x53:
+		/* Global command intercept. */
+		return tape_3490_erp_retry(request);
+	case 0x54:
+		/* Channel interface recovery (temporary). */
+		return tape_3490_erp_retry(request);
+	case 0x55:
+		/* Channel interface recovery (permanent). */
+		dev_warn (&device->cdev->dev, "A channel interface error cannot be"
+			" recovered\n");
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x56:
+		/* Channel protocol error. */
+		dev_warn (&device->cdev->dev, "A channel protocol error "
+			"occurred\n");
+		return tape_3490_erp_failed(request, -EIO);
+	case 0x57:
+		/* Global status intercept. */
+		return tape_3490_erp_retry(request);
+		/* The following erpas should have been covered earlier. */
+	case 0x23: /* Read data check. */
+	case 0x25: /* Write data check. */
+	case 0x28: /* Write id mark check. */
+	case 0x31: /* Tape void. */
+	case 0x40: /* Overrun error. */
+	case 0x41: /* Record sequence error. */
+		/* All other erpas are reserved for future use. */
+	default:
+		return tape_3490_erp_bug(device, request, irb, sense[3]);
+	}
+}
+
+/*
+ * 3490 interrupt handler
+ */
+static int
+tape_3490_irq(struct tape_device *device, struct tape_request *request,
+	      struct irb *irb)
+{
+	if (request == NULL)
+		return tape_3490_unsolicited_irq(device, irb);
+
+	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
+	    (request->op == TO_WRI)) {
+		/* Write at end of volume */
+		return tape_3490_erp_failed(request, -ENOSPC);
+	}
+
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
+		return tape_3490_unit_check(device, request, irb);
+
+	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+		/*
+		 * A unit exception occurs on skipping over a tapemark block.
+		 */
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
+			if (request->op == TO_BSB || request->op == TO_FSB)
+				request->rescnt++;
+			else
+				DBF_EVENT(5, "Unit Exception!\n");
+		}
+		return tape_3490_done(request);
+	}
+
+	DBF_EVENT(6, "xunknownirq\n");
+	tape_dump_sense_dbf(device, request, irb);
+	return TAPE_IO_STOP;
+}
+
+static int
+tape_3490_setup_device(struct tape_device * device)
+{
+	int rc;
+
+	DBF_EVENT(6, "3490 device setup\n");
+	if ((rc = tape_std_assign(device)) == 0) {
+		if ((rc = tape_3490_medium_sense(device)) != 0) {
+			DBF_LH(3, "3490 medium sense returned %d\n", rc);
+		}
+	}
+	return rc;
+}
+
+static void
+tape_3490_cleanup_device(struct tape_device *device)
+{
+	tape_std_unassign(device);
+}
+
+
+/*
+ * MTTELL: Tell block. Return the number of block relative to current file.
+ */
+static int
+tape_3490_mttell(struct tape_device *device, int mt_count)
+{
+	struct {
+		struct tape_3490_block_id	cbid;
+		struct tape_3490_block_id	dbid;
+	} __attribute__ ((packed)) block_id;
+	int rc;
+
+	rc = tape_std_read_block_id(device, (__u64 *) &block_id);
+	if (rc)
+		return rc;
+
+	return block_id.cbid.block;
+}
+
+/*
+ * MTSEEK: seek to the specified block.
+ */
+static int
+tape_3490_mtseek(struct tape_device *device, int mt_count)
+{
+	struct tape_request *request;
+	struct tape_3490_block_id *	bid;
+
+	if (mt_count > 0x3fffff) {
+		DBF_EXCEPTION(6, "xsee parm\n");
+		return -EINVAL;
+	}
+	request = tape_alloc_request(3, 4);
+	if (IS_ERR(request))
+		return PTR_ERR(request);
+
+	/* setup ccws */
+	request->op = TO_LBL;
+	bid         = (struct tape_3490_block_id *) request->cpdata;
+	bid->block  = mt_count;
+
+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
+	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
+	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
+
+	/* execute it */
+	return tape_do_io_free(device, request);
+}
+
+/*
+ * List of 3490 tape commands.
+ */
+static tape_mtop_fn tape_3490_mtop[TAPE_NR_MTOPS] = {
+	[MTRESET]	 = tape_std_mtreset,
+	[MTFSF]		 = tape_std_mtfsf,
+	[MTBSF]		 = tape_std_mtbsf,
+	[MTFSR]		 = tape_std_mtfsr,
+	[MTBSR]		 = tape_std_mtbsr,
+	[MTWEOF]	 = tape_std_mtweof,
+	[MTREW]		 = tape_std_mtrew,
+	[MTOFFL]	 = tape_std_mtoffl,
+	[MTNOP]		 = tape_std_mtnop,
+	[MTRETEN]	 = tape_std_mtreten,
+	[MTBSFM]	 = tape_std_mtbsfm,
+	[MTFSFM]	 = tape_std_mtfsfm,
+	[MTEOM]		 = tape_std_mteom,
+	[MTERASE]	 = tape_std_mterase,
+	[MTRAS1]	 = NULL,
+	[MTRAS2]	 = NULL,
+	[MTRAS3]	 = NULL,
+	[MTSETBLK]	 = tape_std_mtsetblk,
+	[MTSETDENSITY]	 = NULL,
+	[MTSEEK]	 = tape_3490_mtseek,
+	[MTTELL]	 = tape_3490_mttell,
+	[MTSETDRVBUFFER] = NULL,
+	[MTFSS]		 = NULL,
+	[MTBSS]		 = NULL,
+	[MTWSM]		 = NULL,
+	[MTLOCK]	 = NULL,
+	[MTUNLOCK]	 = NULL,
+	[MTLOAD]	 = tape_std_mtload,
+	[MTUNLOAD]	 = tape_std_mtunload,
+	[MTCOMPRESSION]	 = tape_std_mtcompression,
+	[MTSETPART]	 = NULL,
+	[MTMKPART]	 = NULL
+};
+
+/*
+ * Tape discipline structure for 3490.
+ */
+static struct tape_discipline tape_discipline_3490 = {
+	.owner = THIS_MODULE,
+	.setup_device = tape_3490_setup_device,
+	.cleanup_device = tape_3490_cleanup_device,
+	.process_eov = tape_std_process_eov,
+	.irq = tape_3490_irq,
+	.read_block = tape_std_read_block,
+	.write_block = tape_std_write_block,
+	.mtop_array = tape_3490_mtop
+};
+
+static struct ccw_device_id tape_3490_ids[] = {
+	{ CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490},
+	{ /* end of list */ },
+};
+
+static int
+tape_3490_online(struct ccw_device *cdev)
+{
+	return tape_generic_online(
+		dev_get_drvdata(&cdev->dev),
+		&tape_discipline_3490
+	);
+}
+
+static struct ccw_driver tape_3490_driver = {
+	.driver = {
+		.name = "tape_3490",
+		.owner = THIS_MODULE,
+	},
+	.ids = tape_3490_ids,
+	.probe = tape_generic_probe,
+	.remove = tape_generic_remove,
+	.set_online = tape_3490_online,
+	.set_offline = tape_generic_offline,
+	.int_class = IRQIO_TAP,
+};
+
+int tape_3490_init(void)
+{
+	int rc;
+
+	TAPE_DBF_AREA = debug_register ( "tape_3490", 2, 2, 4*sizeof(long));
+	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
+#ifdef DBF_LIKE_HELL
+	debug_set_level(TAPE_DBF_AREA, 6);
+#endif
+
+	DBF_EVENT(3, "3490 init\n");
+	/* Register driver for 3490 tapes. */
+	rc = ccw_driver_register(&tape_3490_driver);
+	if (rc)
+		DBF_EVENT(3, "3490 init failed\n");
+	else
+		DBF_EVENT(3, "3490 registered\n");
+	return rc;
+}
+
+void tape_3490_exit(void)
+{
+	ccw_driver_unregister(&tape_3490_driver);
+
+	debug_unregister(TAPE_DBF_AREA);
+}
+
+MODULE_DEVICE_TABLE(ccw, tape_3490_ids);
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
deleted file mode 100644
index a13e0ac..0000000
--- a/drivers/s390/char/tape_34xx.c
+++ /dev/null
@@ -1,1204 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- *    tape device discipline for 3480/3490 tapes.
- *
- *    Copyright IBM Corp. 2001, 2009
- *    Author(s): Carsten Otte <cotte@de.ibm.com>
- *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
- *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
- */
-
-#define pr_fmt(fmt) "tape_34xx: " fmt
-
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/bio.h>
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-
-#define TAPE_DBF_AREA	tape_34xx_dbf
-
-#include "tape.h"
-#include "tape_std.h"
-
-/*
- * Pointer to debug area.
- */
-debug_info_t *TAPE_DBF_AREA = NULL;
-EXPORT_SYMBOL(TAPE_DBF_AREA);
-
-#define TAPE34XX_FMT_3480	0
-#define TAPE34XX_FMT_3480_2_XF	1
-#define TAPE34XX_FMT_3480_XF	2
-
-struct tape_34xx_block_id {
-	unsigned int	wrap		: 1;
-	unsigned int	segment		: 7;
-	unsigned int	format		: 2;
-	unsigned int	block		: 22;
-};
-
-/*
- * A list of block ID's is used to faster seek blocks.
- */
-struct tape_34xx_sbid {
-	struct list_head		list;
-	struct tape_34xx_block_id	bid;
-};
-
-static void tape_34xx_delete_sbid_from(struct tape_device *, int);
-
-/*
- * Medium sense for 34xx tapes. There is no 'real' medium sense call.
- * So we just do a normal sense.
- */
-static void __tape_34xx_medium_sense(struct tape_request *request)
-{
-	struct tape_device *device = request->device;
-	unsigned char *sense;
-
-	if (request->rc == 0) {
-		sense = request->cpdata;
-
-		/*
-		 * This isn't quite correct. But since INTERVENTION_REQUIRED
-		 * means that the drive is 'neither ready nor on-line' it is
-		 * only slightly inaccurate to say there is no tape loaded if
-		 * the drive isn't online...
-		 */
-		if (sense[0] & SENSE_INTERVENTION_REQUIRED)
-			tape_med_state_set(device, MS_UNLOADED);
-		else
-			tape_med_state_set(device, MS_LOADED);
-
-		if (sense[1] & SENSE_WRITE_PROTECT)
-			device->tape_generic_status |= GMT_WR_PROT(~0);
-		else
-			device->tape_generic_status &= ~GMT_WR_PROT(~0);
-	} else
-		DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n",
-			request->rc);
-	tape_free_request(request);
-}
-
-static int tape_34xx_medium_sense(struct tape_device *device)
-{
-	struct tape_request *request;
-	int rc;
-
-	request = tape_alloc_request(1, 32);
-	if (IS_ERR(request)) {
-		DBF_EXCEPTION(6, "MSEN fail\n");
-		return PTR_ERR(request);
-	}
-
-	request->op = TO_MSEN;
-	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
-	rc = tape_do_io_interruptible(device, request);
-	__tape_34xx_medium_sense(request);
-	return rc;
-}
-
-static void tape_34xx_medium_sense_async(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = tape_alloc_request(1, 32);
-	if (IS_ERR(request)) {
-		DBF_EXCEPTION(6, "MSEN fail\n");
-		return;
-	}
-
-	request->op = TO_MSEN;
-	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
-	request->callback = (void *) __tape_34xx_medium_sense;
-	request->callback_data = NULL;
-	tape_do_io_async(device, request);
-}
-
-struct tape_34xx_work {
-	struct tape_device	*device;
-	enum tape_op		 op;
-	struct work_struct	 work;
-};
-
-/*
- * These functions are currently used only to schedule a medium_sense for
- * later execution. This is because we get an interrupt whenever a medium
- * is inserted but cannot call tape_do_io* from an interrupt context.
- * Maybe that's useful for other actions we want to start from the
- * interrupt handler.
- * Note: the work handler is called by the system work queue. The tape
- * commands started by the handler need to be asynchrounous, otherwise
- * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
- */
-static void
-tape_34xx_work_handler(struct work_struct *work)
-{
-	struct tape_34xx_work *p =
-		container_of(work, struct tape_34xx_work, work);
-	struct tape_device *device = p->device;
-
-	switch(p->op) {
-		case TO_MSEN:
-			tape_34xx_medium_sense_async(device);
-			break;
-		default:
-			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
-	}
-	tape_put_device(device);
-	kfree(p);
-}
-
-static int
-tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
-{
-	struct tape_34xx_work *p;
-
-	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
-		return -ENOMEM;
-
-	INIT_WORK(&p->work, tape_34xx_work_handler);
-
-	p->device = tape_get_device(device);
-	p->op     = op;
-
-	schedule_work(&p->work);
-	return 0;
-}
-
-/*
- * Done Handler is called when dev stat = DEVICE-END (successful operation)
- */
-static inline int
-tape_34xx_done(struct tape_request *request)
-{
-	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
-
-	switch (request->op) {
-		case TO_DSE:
-		case TO_RUN:
-		case TO_WRI:
-		case TO_WTM:
-		case TO_ASSIGN:
-		case TO_UNASSIGN:
-			tape_34xx_delete_sbid_from(request->device, 0);
-			break;
-		default:
-			;
-	}
-	return TAPE_IO_SUCCESS;
-}
-
-static inline int
-tape_34xx_erp_failed(struct tape_request *request, int rc)
-{
-	DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n",
-		  tape_op_verbose[request->op], rc);
-	return rc;
-}
-
-static inline int
-tape_34xx_erp_succeeded(struct tape_request *request)
-{
-	DBF_EVENT(3, "Error Recovery successful for %s\n",
-		  tape_op_verbose[request->op]);
-	return tape_34xx_done(request);
-}
-
-static inline int
-tape_34xx_erp_retry(struct tape_request *request)
-{
-	DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]);
-	return TAPE_IO_RETRY;
-}
-
-/*
- * This function is called, when no request is outstanding and we get an
- * interrupt
- */
-static int
-tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
-{
-	if (irb->scsw.cmd.dstat == 0x85) { /* READY */
-		/* A medium was inserted in the drive. */
-		DBF_EVENT(6, "xuud med\n");
-		tape_34xx_delete_sbid_from(device, 0);
-		tape_34xx_schedule_work(device, TO_MSEN);
-	} else {
-		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
-		tape_dump_sense_dbf(device, NULL, irb);
-	}
-	return TAPE_IO_SUCCESS;
-}
-
-static int
-tape_34xx_erp_bug(struct tape_device *device, struct tape_request *request,
-		  struct irb *irb, int no)
-{
-	if (request->op != TO_ASSIGN) {
-		dev_err(&device->cdev->dev, "An unexpected condition %d "
-			"occurred in tape error recovery\n", no);
-		tape_dump_sense_dbf(device, request, irb);
-	}
-	return tape_34xx_erp_failed(request, -EIO);
-}
-
-/*
- * Handle data overrun between cu and drive. The channel speed might
- * be too slow.
- */
-static int
-tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request,
-		      struct irb *irb)
-{
-	if (irb->ecw[3] == 0x40) {
-		dev_warn (&device->cdev->dev, "A data overrun occurred between"
-			" the control unit and tape unit\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	}
-	return tape_34xx_erp_bug(device, request, irb, -1);
-}
-
-/*
- * Handle record sequence error.
- */
-static int
-tape_34xx_erp_sequence(struct tape_device *device,
-		       struct tape_request *request, struct irb *irb)
-{
-	if (irb->ecw[3] == 0x41) {
-		/*
-		 * cu detected incorrect block-id sequence on tape.
-		 */
-		dev_warn (&device->cdev->dev, "The block ID sequence on the "
-			"tape is incorrect\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	}
-	/*
-	 * Record sequence error bit is set, but erpa does not
-	 * show record sequence error.
-	 */
-	return tape_34xx_erp_bug(device, request, irb, -2);
-}
-
-/*
- * This function analyses the tape's sense-data in case of a unit-check.
- * If possible, it tries to recover from the error. Else the user is
- * informed about the problem.
- */
-static int
-tape_34xx_unit_check(struct tape_device *device, struct tape_request *request,
-		     struct irb *irb)
-{
-	int inhibit_cu_recovery;
-	__u8* sense;
-
-	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
-	sense = irb->ecw;
-
-	if (
-		sense[0] & SENSE_COMMAND_REJECT &&
-		sense[1] & SENSE_WRITE_PROTECT
-	) {
-		if (
-			request->op == TO_DSE ||
-			request->op == TO_WRI ||
-			request->op == TO_WTM
-		) {
-			/* medium is write protected */
-			return tape_34xx_erp_failed(request, -EACCES);
-		} else {
-			return tape_34xx_erp_bug(device, request, irb, -3);
-		}
-	}
-
-	/*
-	 * Special cases for various tape-states when reaching
-	 * end of recorded area
-	 *
-	 * FIXME: Maybe a special case of the special case:
-	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
-	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
-	 *        sense[3] == 0x47 (Volume Fenced)
-	 *
-	 *        This was caused by continued FSF or FSR after an
-	 *        'End Of Data'.
-	 */
-	if ((
-		sense[0] == SENSE_DATA_CHECK      ||
-		sense[0] == SENSE_EQUIPMENT_CHECK ||
-		sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK)
-	) && (
-		sense[1] == SENSE_DRIVE_ONLINE ||
-		sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE)
-	)) {
-		switch (request->op) {
-		/*
-		 * sense[0] == SENSE_DATA_CHECK   &&
-		 * sense[1] == SENSE_DRIVE_ONLINE
-		 * sense[3] == 0x36 (End Of Data)
-		 *
-		 * Further seeks might return a 'Volume Fenced'.
-		 */
-		case TO_FSF:
-		case TO_FSB:
-			/* Trying to seek beyond end of recorded area */
-			return tape_34xx_erp_failed(request, -ENOSPC);
-		case TO_BSB:
-			return tape_34xx_erp_retry(request);
-
-		/*
-		 * sense[0] == SENSE_DATA_CHECK   &&
-		 * sense[1] == SENSE_DRIVE_ONLINE &&
-		 * sense[3] == 0x36 (End Of Data)
-		 */
-		case TO_LBL:
-			/* Block could not be located. */
-			tape_34xx_delete_sbid_from(device, 0);
-			return tape_34xx_erp_failed(request, -EIO);
-
-		case TO_RFO:
-			/* Read beyond end of recorded area -> 0 bytes read */
-			return tape_34xx_erp_failed(request, 0);
-
-		/*
-		 * sense[0] == SENSE_EQUIPMENT_CHECK &&
-		 * sense[1] == SENSE_DRIVE_ONLINE    &&
-		 * sense[3] == 0x38 (Physical End Of Volume)
-		 */
-		case TO_WRI:
-			/* Writing at physical end of volume */
-			return tape_34xx_erp_failed(request, -ENOSPC);
-		default:
-			return tape_34xx_erp_failed(request, 0);
-		}
-	}
-
-	/* Sensing special bits */
-	if (sense[0] & SENSE_BUS_OUT_CHECK)
-		return tape_34xx_erp_retry(request);
-
-	if (sense[0] & SENSE_DATA_CHECK) {
-		/*
-		 * hardware failure, damaged tape or improper
-		 * operating conditions
-		 */
-		switch (sense[3]) {
-		case 0x23:
-			/* a read data check occurred */
-			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
-			    inhibit_cu_recovery)
-				// data check is not permanent, may be
-				// recovered. We always use async-mode with
-				// cu-recovery, so this should *never* happen.
-				return tape_34xx_erp_bug(device, request,
-							 irb, -4);
-
-			/* data check is permanent, CU recovery has failed */
-			dev_warn (&device->cdev->dev, "A read error occurred "
-				"that cannot be recovered\n");
-			return tape_34xx_erp_failed(request, -EIO);
-		case 0x25:
-			// a write data check occurred
-			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
-			    inhibit_cu_recovery)
-				// data check is not permanent, may be
-				// recovered. We always use async-mode with
-				// cu-recovery, so this should *never* happen.
-				return tape_34xx_erp_bug(device, request,
-							 irb, -5);
-
-			// data check is permanent, cu-recovery has failed
-			dev_warn (&device->cdev->dev, "A write error on the "
-				"tape cannot be recovered\n");
-			return tape_34xx_erp_failed(request, -EIO);
-		case 0x28:
-			/* ID-Mark at tape start couldn't be written */
-			dev_warn (&device->cdev->dev, "Writing the ID-mark "
-				"failed\n");
-			return tape_34xx_erp_failed(request, -EIO);
-		case 0x31:
-			/* Tape void. Tried to read beyond end of device. */
-			dev_warn (&device->cdev->dev, "Reading the tape beyond"
-				" the end of the recorded area failed\n");
-			return tape_34xx_erp_failed(request, -ENOSPC);
-		case 0x41:
-			/* Record sequence error. */
-			dev_warn (&device->cdev->dev, "The tape contains an "
-				"incorrect block ID sequence\n");
-			return tape_34xx_erp_failed(request, -EIO);
-		default:
-			/* all data checks for 3480 should result in one of
-			 * the above erpa-codes. For 3490, other data-check
-			 * conditions do exist. */
-			if (device->cdev->id.driver_info == tape_3480)
-				return tape_34xx_erp_bug(device, request,
-							 irb, -6);
-		}
-	}
-
-	if (sense[0] & SENSE_OVERRUN)
-		return tape_34xx_erp_overrun(device, request, irb);
-
-	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
-		return tape_34xx_erp_sequence(device, request, irb);
-
-	/* Sensing erpa codes */
-	switch (sense[3]) {
-	case 0x00:
-		/* Unit check with erpa code 0. Report and ignore. */
-		return TAPE_IO_SUCCESS;
-	case 0x21:
-		/*
-		 * Data streaming not operational. CU will switch to
-		 * interlock mode. Reissue the command.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x22:
-		/*
-		 * Path equipment check. Might be drive adapter error, buffer
-		 * error on the lower interface, internal path not usable,
-		 * or error during cartridge load.
-		 */
-		dev_warn (&device->cdev->dev, "A path equipment check occurred"
-			" for the tape device\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x24:
-		/*
-		 * Load display check. Load display was command was issued,
-		 * but the drive is displaying a drive check message. Can
-		 * be threated as "device end".
-		 */
-		return tape_34xx_erp_succeeded(request);
-	case 0x27:
-		/*
-		 * Command reject. May indicate illegal channel program or
-		 * buffer over/underrun. Since all channel programs are
-		 * issued by this driver and ought be correct, we assume a
-		 * over/underrun situation and retry the channel program.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x29:
-		/*
-		 * Function incompatible. Either the tape is idrc compressed
-		 * but the hardware isn't capable to do idrc, or a perform
-		 * subsystem func is issued and the CU is not on-line.
-		 */
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x2a:
-		/*
-		 * Unsolicited environmental data. An internal counter
-		 * overflows, we can ignore this and reissue the cmd.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x2b:
-		/*
-		 * Environmental data present. Indicates either unload
-		 * completed ok or read buffered log command completed ok.
-		 */
-		if (request->op == TO_RUN) {
-			/* Rewind unload completed ok. */
-			tape_med_state_set(device, MS_UNLOADED);
-			return tape_34xx_erp_succeeded(request);
-		}
-		/* tape_34xx doesn't use read buffered log commands. */
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x2c:
-		/*
-		 * Permanent equipment check. CU has tried recovery, but
-		 * did not succeed.
-		 */
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x2d:
-		/* Data security erase failure. */
-		if (request->op == TO_DSE)
-			return tape_34xx_erp_failed(request, -EIO);
-		/* Data security erase failure, but no such command issued. */
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x2e:
-		/*
-		 * Not capable. This indicates either that the drive fails
-		 * reading the format id mark or that format specified
-		 * is not supported by the drive.
-		 */
-		dev_warn (&device->cdev->dev, "The tape unit cannot process "
-			"the tape format\n");
-		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
-	case 0x30:
-		/* The medium is write protected. */
-		dev_warn (&device->cdev->dev, "The tape medium is write-"
-			"protected\n");
-		return tape_34xx_erp_failed(request, -EACCES);
-	case 0x32:
-		// Tension loss. We cannot recover this, it's an I/O error.
-		dev_warn (&device->cdev->dev, "The tape does not have the "
-			"required tape tension\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x33:
-		/*
-		 * Load Failure. The cartridge was not inserted correctly or
-		 * the tape is not threaded correctly.
-		 */
-		dev_warn (&device->cdev->dev, "The tape unit failed to load"
-			" the cartridge\n");
-		tape_34xx_delete_sbid_from(device, 0);
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x34:
-		/*
-		 * Unload failure. The drive cannot maintain tape tension
-		 * and control tape movement during an unload operation.
-		 */
-		dev_warn (&device->cdev->dev, "Automatic unloading of the tape"
-			" cartridge failed\n");
-		if (request->op == TO_RUN)
-			return tape_34xx_erp_failed(request, -EIO);
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x35:
-		/*
-		 * Drive equipment check. One of the following:
-		 * - cu cannot recover from a drive detected error
-		 * - a check code message is shown on drive display
-		 * - the cartridge loader does not respond correctly
-		 * - a failure occurs during an index, load, or unload cycle
-		 */
-		dev_warn (&device->cdev->dev, "An equipment check has occurred"
-			" on the tape unit\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x36:
-		if (device->cdev->id.driver_info == tape_3490)
-			/* End of data. */
-			return tape_34xx_erp_failed(request, -EIO);
-		/* This erpa is reserved for 3480 */
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x37:
-		/*
-		 * Tape length error. The tape is shorter than reported in
-		 * the beginning-of-tape data.
-		 */
-		dev_warn (&device->cdev->dev, "The tape information states an"
-			" incorrect length\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x38:
-		/*
-		 * Physical end of tape. A read/write operation reached
-		 * the physical end of tape.
-		 */
-		if (request->op==TO_WRI ||
-		    request->op==TO_DSE ||
-		    request->op==TO_WTM)
-			return tape_34xx_erp_failed(request, -ENOSPC);
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x39:
-		/* Backward at Beginning of tape. */
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x3a:
-		/* Drive switched to not ready. */
-		dev_warn (&device->cdev->dev, "The tape unit is not ready\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x3b:
-		/* Manual rewind or unload. This causes an I/O error. */
-		dev_warn (&device->cdev->dev, "The tape medium has been "
-			"rewound or unloaded manually\n");
-		tape_34xx_delete_sbid_from(device, 0);
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x42:
-		/*
-		 * Degraded mode. A condition that can cause degraded
-		 * performance is detected.
-		 */
-		dev_warn (&device->cdev->dev, "The tape subsystem is running "
-			"in degraded mode\n");
-		return tape_34xx_erp_retry(request);
-	case 0x43:
-		/* Drive not ready. */
-		tape_34xx_delete_sbid_from(device, 0);
-		tape_med_state_set(device, MS_UNLOADED);
-		/* Some commands commands are successful even in this case */
-		if (sense[1] & SENSE_DRIVE_ONLINE) {
-			switch(request->op) {
-				case TO_ASSIGN:
-				case TO_UNASSIGN:
-				case TO_DIS:
-				case TO_NOP:
-					return tape_34xx_done(request);
-					break;
-				default:
-					break;
-			}
-		}
-		return tape_34xx_erp_failed(request, -ENOMEDIUM);
-	case 0x44:
-		/* Locate Block unsuccessful. */
-		if (request->op != TO_BLOCK && request->op != TO_LBL)
-			/* No locate block was issued. */
-			return tape_34xx_erp_bug(device, request,
-						 irb, sense[3]);
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x45:
-		/* The drive is assigned to a different channel path. */
-		dev_warn (&device->cdev->dev, "The tape unit is already "
-			"assigned\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x46:
-		/*
-		 * Drive not on-line. Drive may be switched offline,
-		 * the power supply may be switched off or
-		 * the drive address may not be set correctly.
-		 */
-		dev_warn (&device->cdev->dev, "The tape unit is not online\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x47:
-		/* Volume fenced. CU reports volume integrity is lost. */
-		dev_warn (&device->cdev->dev, "The control unit has fenced "
-			"access to the tape volume\n");
-		tape_34xx_delete_sbid_from(device, 0);
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x48:
-		/* Log sense data and retry request. */
-		return tape_34xx_erp_retry(request);
-	case 0x49:
-		/* Bus out check. A parity check error on the bus was found. */
-		dev_warn (&device->cdev->dev, "A parity error occurred on the "
-			"tape bus\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x4a:
-		/* Control unit erp failed. */
-		dev_warn (&device->cdev->dev, "I/O error recovery failed on "
-			"the tape control unit\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x4b:
-		/*
-		 * CU and drive incompatible. The drive requests micro-program
-		 * patches, which are not available on the CU.
-		 */
-		dev_warn (&device->cdev->dev, "The tape unit requires a "
-			"firmware update\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x4c:
-		/*
-		 * Recovered Check-One failure. Cu develops a hardware error,
-		 * but is able to recover.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x4d:
-		if (device->cdev->id.driver_info == tape_3490)
-			/*
-			 * Resetting event received. Since the driver does
-			 * not support resetting event recovery (which has to
-			 * be handled by the I/O Layer), retry our command.
-			 */
-			return tape_34xx_erp_retry(request);
-		/* This erpa is reserved for 3480. */
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x4e:
-		if (device->cdev->id.driver_info == tape_3490) {
-			/*
-			 * Maximum block size exceeded. This indicates, that
-			 * the block to be written is larger than allowed for
-			 * buffered mode.
-			 */
-			dev_warn (&device->cdev->dev, "The maximum block size"
-				" for buffered mode is exceeded\n");
-			return tape_34xx_erp_failed(request, -ENOBUFS);
-		}
-		/* This erpa is reserved for 3480. */
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x50:
-		/*
-		 * Read buffered log (Overflow). CU is running in extended
-		 * buffered log mode, and a counter overflows. This should
-		 * never happen, since we're never running in extended
-		 * buffered log mode.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x51:
-		/*
-		 * Read buffered log (EOV). EOF processing occurs while the
-		 * CU is in extended buffered log mode. This should never
-		 * happen, since we're never running in extended buffered
-		 * log mode.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x52:
-		/* End of Volume complete. Rewind unload completed ok. */
-		if (request->op == TO_RUN) {
-			tape_med_state_set(device, MS_UNLOADED);
-			tape_34xx_delete_sbid_from(device, 0);
-			return tape_34xx_erp_succeeded(request);
-		}
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	case 0x53:
-		/* Global command intercept. */
-		return tape_34xx_erp_retry(request);
-	case 0x54:
-		/* Channel interface recovery (temporary). */
-		return tape_34xx_erp_retry(request);
-	case 0x55:
-		/* Channel interface recovery (permanent). */
-		dev_warn (&device->cdev->dev, "A channel interface error cannot be"
-			" recovered\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x56:
-		/* Channel protocol error. */
-		dev_warn (&device->cdev->dev, "A channel protocol error "
-			"occurred\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x57:
-		/*
-		 * 3480: Attention intercept.
-		 * 3490: Global status intercept.
-		 */
-		return tape_34xx_erp_retry(request);
-	case 0x5a:
-		/*
-		 * Tape length incompatible. The tape inserted is too long,
-		 * which could cause damage to the tape or the drive.
-		 */
-		dev_warn (&device->cdev->dev, "The tape unit does not support "
-			"the tape length\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x5b:
-		/* Format 3480 XF incompatible */
-		if (sense[1] & SENSE_BEGINNING_OF_TAPE)
-			/* The tape will get overwritten. */
-			return tape_34xx_erp_retry(request);
-		dev_warn (&device->cdev->dev, "The tape unit does not support"
-			" format 3480 XF\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x5c:
-		/* Format 3480-2 XF incompatible */
-		dev_warn (&device->cdev->dev, "The tape unit does not support tape "
-			"format 3480-2 XF\n");
-		return tape_34xx_erp_failed(request, -EIO);
-	case 0x5d:
-		/* Tape length violation. */
-		dev_warn (&device->cdev->dev, "The tape unit does not support"
-			" the current tape length\n");
-		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
-	case 0x5e:
-		/* Compaction algorithm incompatible. */
-		dev_warn (&device->cdev->dev, "The tape unit does not support"
-			" the compaction algorithm\n");
-		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
-
-		/* The following erpas should have been covered earlier. */
-	case 0x23: /* Read data check. */
-	case 0x25: /* Write data check. */
-	case 0x26: /* Data check (read opposite). */
-	case 0x28: /* Write id mark check. */
-	case 0x31: /* Tape void. */
-	case 0x40: /* Overrun error. */
-	case 0x41: /* Record sequence error. */
-		/* All other erpas are reserved for future use. */
-	default:
-		return tape_34xx_erp_bug(device, request, irb, sense[3]);
-	}
-}
-
-/*
- * 3480/3490 interrupt handler
- */
-static int
-tape_34xx_irq(struct tape_device *device, struct tape_request *request,
-	      struct irb *irb)
-{
-	if (request == NULL)
-		return tape_34xx_unsolicited_irq(device, irb);
-
-	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
-	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
-	    (request->op == TO_WRI)) {
-		/* Write at end of volume */
-		return tape_34xx_erp_failed(request, -ENOSPC);
-	}
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
-		return tape_34xx_unit_check(device, request, irb);
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
-		/*
-		 * A unit exception occurs on skipping over a tapemark block.
-		 */
-		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
-			if (request->op == TO_BSB || request->op == TO_FSB)
-				request->rescnt++;
-			else
-				DBF_EVENT(5, "Unit Exception!\n");
-		}
-		return tape_34xx_done(request);
-	}
-
-	DBF_EVENT(6, "xunknownirq\n");
-	tape_dump_sense_dbf(device, request, irb);
-	return TAPE_IO_STOP;
-}
-
-/*
- * ioctl_overload
- */
-static int
-tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
-{
-	if (cmd == TAPE390_DISPLAY) {
-		struct display_struct disp;
-
-		if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)) != 0)
-			return -EFAULT;
-
-		return tape_std_display(device, &disp);
-	} else
-		return -EINVAL;
-}
-
-static inline void
-tape_34xx_append_new_sbid(struct tape_34xx_block_id bid, struct list_head *l)
-{
-	struct tape_34xx_sbid *	new_sbid;
-
-	new_sbid = kmalloc(sizeof(*new_sbid), GFP_ATOMIC);
-	if (!new_sbid)
-		return;
-
-	new_sbid->bid = bid;
-	list_add(&new_sbid->list, l);
-}
-
-/*
- * Build up the search block ID list. The block ID consists of a logical
- * block number and a hardware specific part. The hardware specific part
- * helps the tape drive to speed up searching for a specific block.
- */
-static void
-tape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid)
-{
-	struct list_head *	sbid_list;
-	struct tape_34xx_sbid *	sbid;
-	struct list_head *	l;
-
-	/*
-	 * immediately return if there is no list at all or the block to add
-	 * is located in segment 1 of wrap 0 because this position is used
-	 * if no hardware position data is supplied.
-	 */
-	sbid_list = (struct list_head *) device->discdata;
-	if (!sbid_list || (bid.segment < 2 && bid.wrap == 0))
-		return;
-
-	/*
-	 * Search the position where to insert the new entry. Hardware
-	 * acceleration uses only the segment and wrap number. So we
-	 * need only one entry for a specific wrap/segment combination.
-	 * If there is a block with a lower number but the same hard-
-	 * ware position data we just update the block number in the
-	 * existing entry.
-	 */
-	list_for_each(l, sbid_list) {
-		sbid = list_entry(l, struct tape_34xx_sbid, list);
-
-		if (
-			(sbid->bid.segment == bid.segment) &&
-			(sbid->bid.wrap    == bid.wrap)
-		) {
-			if (bid.block < sbid->bid.block)
-				sbid->bid = bid;
-			else return;
-			break;
-		}
-
-		/* Sort in according to logical block number. */
-		if (bid.block < sbid->bid.block) {
-			tape_34xx_append_new_sbid(bid, l->prev);
-			break;
-		}
-	}
-	/* List empty or new block bigger than last entry. */
-	if (l == sbid_list)
-		tape_34xx_append_new_sbid(bid, l->prev);
-
-	DBF_LH(4, "Current list is:\n");
-	list_for_each(l, sbid_list) {
-		sbid = list_entry(l, struct tape_34xx_sbid, list);
-		DBF_LH(4, "%d:%03d@%05d\n",
-			sbid->bid.wrap,
-			sbid->bid.segment,
-			sbid->bid.block
-		);
-	}
-}
-
-/*
- * Delete all entries from the search block ID list that belong to tape blocks
- * equal or higher than the given number.
- */
-static void
-tape_34xx_delete_sbid_from(struct tape_device *device, int from)
-{
-	struct list_head *	sbid_list;
-	struct tape_34xx_sbid *	sbid;
-	struct list_head *	l;
-	struct list_head *	n;
-
-	sbid_list = (struct list_head *) device->discdata;
-	if (!sbid_list)
-		return;
-
-	list_for_each_safe(l, n, sbid_list) {
-		sbid = list_entry(l, struct tape_34xx_sbid, list);
-		if (sbid->bid.block >= from) {
-			DBF_LH(4, "Delete sbid %d:%03d@%05d\n",
-				sbid->bid.wrap,
-				sbid->bid.segment,
-				sbid->bid.block
-			);
-			list_del(l);
-			kfree(sbid);
-		}
-	}
-}
-
-/*
- * Merge hardware position data into a block id.
- */
-static void
-tape_34xx_merge_sbid(
-	struct tape_device *		device,
-	struct tape_34xx_block_id *	bid
-) {
-	struct tape_34xx_sbid *	sbid;
-	struct tape_34xx_sbid *	sbid_to_use;
-	struct list_head *	sbid_list;
-	struct list_head *	l;
-
-	sbid_list = (struct list_head *) device->discdata;
-	bid->wrap    = 0;
-	bid->segment = 1;
-
-	if (!sbid_list || list_empty(sbid_list))
-		return;
-
-	sbid_to_use = NULL;
-	list_for_each(l, sbid_list) {
-		sbid = list_entry(l, struct tape_34xx_sbid, list);
-
-		if (sbid->bid.block >= bid->block)
-			break;
-		sbid_to_use = sbid;
-	}
-	if (sbid_to_use) {
-		bid->wrap    = sbid_to_use->bid.wrap;
-		bid->segment = sbid_to_use->bid.segment;
-		DBF_LH(4, "Use %d:%03d@%05d for %05d\n",
-			sbid_to_use->bid.wrap,
-			sbid_to_use->bid.segment,
-			sbid_to_use->bid.block,
-			bid->block
-		);
-	}
-}
-
-static int
-tape_34xx_setup_device(struct tape_device * device)
-{
-	int			rc;
-	struct list_head *	discdata;
-
-	DBF_EVENT(6, "34xx device setup\n");
-	if ((rc = tape_std_assign(device)) == 0) {
-		if ((rc = tape_34xx_medium_sense(device)) != 0) {
-			DBF_LH(3, "34xx medium sense returned %d\n", rc);
-		}
-	}
-	discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL);
-	if (discdata) {
-			INIT_LIST_HEAD(discdata);
-			device->discdata = discdata;
-	}
-
-	return rc;
-}
-
-static void
-tape_34xx_cleanup_device(struct tape_device *device)
-{
-	tape_std_unassign(device);
-	
-	if (device->discdata) {
-		tape_34xx_delete_sbid_from(device, 0);
-		kfree(device->discdata);
-		device->discdata = NULL;
-	}
-}
-
-
-/*
- * MTTELL: Tell block. Return the number of block relative to current file.
- */
-static int
-tape_34xx_mttell(struct tape_device *device, int mt_count)
-{
-	struct {
-		struct tape_34xx_block_id	cbid;
-		struct tape_34xx_block_id	dbid;
-	} __attribute__ ((packed)) block_id;
-	int rc;
-
-	rc = tape_std_read_block_id(device, (__u64 *) &block_id);
-	if (rc)
-		return rc;
-
-	tape_34xx_add_sbid(device, block_id.cbid);
-	return block_id.cbid.block;
-}
-
-/*
- * MTSEEK: seek to the specified block.
- */
-static int
-tape_34xx_mtseek(struct tape_device *device, int mt_count)
-{
-	struct tape_request *request;
-	struct tape_34xx_block_id *	bid;
-
-	if (mt_count > 0x3fffff) {
-		DBF_EXCEPTION(6, "xsee parm\n");
-		return -EINVAL;
-	}
-	request = tape_alloc_request(3, 4);
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-
-	/* setup ccws */
-	request->op = TO_LBL;
-	bid         = (struct tape_34xx_block_id *) request->cpdata;
-	bid->format = (*device->modeset_byte & 0x08) ?
-			TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480;
-	bid->block  = mt_count;
-	tape_34xx_merge_sbid(device, bid);
-
-	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
-	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-
-	/* execute it */
-	return tape_do_io_free(device, request);
-}
-
-/*
- * List of 3480/3490 magnetic tape commands.
- */
-static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = {
-	[MTRESET]	 = tape_std_mtreset,
-	[MTFSF]		 = tape_std_mtfsf,
-	[MTBSF]		 = tape_std_mtbsf,
-	[MTFSR]		 = tape_std_mtfsr,
-	[MTBSR]		 = tape_std_mtbsr,
-	[MTWEOF]	 = tape_std_mtweof,
-	[MTREW]		 = tape_std_mtrew,
-	[MTOFFL]	 = tape_std_mtoffl,
-	[MTNOP]		 = tape_std_mtnop,
-	[MTRETEN]	 = tape_std_mtreten,
-	[MTBSFM]	 = tape_std_mtbsfm,
-	[MTFSFM]	 = tape_std_mtfsfm,
-	[MTEOM]		 = tape_std_mteom,
-	[MTERASE]	 = tape_std_mterase,
-	[MTRAS1]	 = NULL,
-	[MTRAS2]	 = NULL,
-	[MTRAS3]	 = NULL,
-	[MTSETBLK]	 = tape_std_mtsetblk,
-	[MTSETDENSITY]	 = NULL,
-	[MTSEEK]	 = tape_34xx_mtseek,
-	[MTTELL]	 = tape_34xx_mttell,
-	[MTSETDRVBUFFER] = NULL,
-	[MTFSS]		 = NULL,
-	[MTBSS]		 = NULL,
-	[MTWSM]		 = NULL,
-	[MTLOCK]	 = NULL,
-	[MTUNLOCK]	 = NULL,
-	[MTLOAD]	 = tape_std_mtload,
-	[MTUNLOAD]	 = tape_std_mtunload,
-	[MTCOMPRESSION]	 = tape_std_mtcompression,
-	[MTSETPART]	 = NULL,
-	[MTMKPART]	 = NULL
-};
-
-/*
- * Tape discipline structure for 3480 and 3490.
- */
-static struct tape_discipline tape_discipline_34xx = {
-	.owner = THIS_MODULE,
-	.setup_device = tape_34xx_setup_device,
-	.cleanup_device = tape_34xx_cleanup_device,
-	.process_eov = tape_std_process_eov,
-	.irq = tape_34xx_irq,
-	.read_block = tape_std_read_block,
-	.write_block = tape_std_write_block,
-	.ioctl_fn = tape_34xx_ioctl,
-	.mtop_array = tape_34xx_mtop
-};
-
-static struct ccw_device_id tape_34xx_ids[] = {
-	{ CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), .driver_info = tape_3480},
-	{ CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490},
-	{ /* end of list */ },
-};
-
-static int
-tape_34xx_online(struct ccw_device *cdev)
-{
-	return tape_generic_online(
-		dev_get_drvdata(&cdev->dev),
-		&tape_discipline_34xx
-	);
-}
-
-static struct ccw_driver tape_34xx_driver = {
-	.driver = {
-		.name = "tape_34xx",
-		.owner = THIS_MODULE,
-	},
-	.ids = tape_34xx_ids,
-	.probe = tape_generic_probe,
-	.remove = tape_generic_remove,
-	.set_online = tape_34xx_online,
-	.set_offline = tape_generic_offline,
-	.int_class = IRQIO_TAP,
-};
-
-static int
-tape_34xx_init (void)
-{
-	int rc;
-
-	TAPE_DBF_AREA = debug_register ( "tape_34xx", 2, 2, 4*sizeof(long));
-	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
-#ifdef DBF_LIKE_HELL
-	debug_set_level(TAPE_DBF_AREA, 6);
-#endif
-
-	DBF_EVENT(3, "34xx init\n");
-	/* Register driver for 3480/3490 tapes. */
-	rc = ccw_driver_register(&tape_34xx_driver);
-	if (rc)
-		DBF_EVENT(3, "34xx init failed\n");
-	else
-		DBF_EVENT(3, "34xx registered\n");
-	return rc;
-}
-
-static void
-tape_34xx_exit(void)
-{
-	ccw_driver_unregister(&tape_34xx_driver);
-
-	debug_unregister(TAPE_DBF_AREA);
-}
-
-MODULE_DEVICE_TABLE(ccw, tape_34xx_ids);
-MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
-MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape device driver");
-MODULE_LICENSE("GPL");
-
-module_init(tape_34xx_init);
-module_exit(tape_34xx_exit);
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
deleted file mode 100644
index 0d80f43..0000000
--- a/drivers/s390/char/tape_3590.c
+++ /dev/null
@@ -1,1612 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- *    tape device discipline for 3590 tapes.
- *
- *    Copyright IBM Corp. 2001, 2009
- *    Author(s): Stefan Bader <shbader@de.ibm.com>
- *		 Michael Holzheu <holzheu@de.ibm.com>
- *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
- */
-
-#define pr_fmt(fmt) "tape_3590: " fmt
-
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/bio.h>
-#include <asm/ebcdic.h>
-
-#define TAPE_DBF_AREA	tape_3590_dbf
-#define BUFSIZE 512	/* size of buffers for dynamic generated messages */
-
-#include "tape.h"
-#include "tape_std.h"
-#include "tape_3590.h"
-
-static struct workqueue_struct *tape_3590_wq;
-
-/*
- * Pointer to debug area.
- */
-debug_info_t *TAPE_DBF_AREA = NULL;
-EXPORT_SYMBOL(TAPE_DBF_AREA);
-
-/*******************************************************************
- * Error Recovery functions:
- * - Read Opposite:		 implemented
- * - Read Device (buffered) log: BRA
- * - Read Library log:		 BRA
- * - Swap Devices:		 BRA
- * - Long Busy:			 implemented
- * - Special Intercept:		 BRA
- * - Read Alternate:		 implemented
- *******************************************************************/
-
-static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = {
-	[0x00] = "",
-	[0x10] = "Lost Sense",
-	[0x11] = "Assigned Elsewhere",
-	[0x12] = "Allegiance Reset",
-	[0x13] = "Shared Access Violation",
-	[0x20] = "Command Reject",
-	[0x21] = "Configuration Error",
-	[0x22] = "Protection Exception",
-	[0x23] = "Write Protect",
-	[0x24] = "Write Length",
-	[0x25] = "Read-Only Format",
-	[0x31] = "Beginning of Partition",
-	[0x33] = "End of Partition",
-	[0x34] = "End of Data",
-	[0x35] = "Block not found",
-	[0x40] = "Device Intervention",
-	[0x41] = "Loader Intervention",
-	[0x42] = "Library Intervention",
-	[0x50] = "Write Error",
-	[0x51] = "Erase Error",
-	[0x52] = "Formatting Error",
-	[0x53] = "Read Error",
-	[0x54] = "Unsupported Format",
-	[0x55] = "No Formatting",
-	[0x56] = "Positioning lost",
-	[0x57] = "Read Length",
-	[0x60] = "Unsupported Medium",
-	[0x61] = "Medium Length Error",
-	[0x62] = "Medium removed",
-	[0x64] = "Load Check",
-	[0x65] = "Unload Check",
-	[0x70] = "Equipment Check",
-	[0x71] = "Bus out Check",
-	[0x72] = "Protocol Error",
-	[0x73] = "Interface Error",
-	[0x74] = "Overrun",
-	[0x75] = "Halt Signal",
-	[0x90] = "Device fenced",
-	[0x91] = "Device Path fenced",
-	[0xa0] = "Volume misplaced",
-	[0xa1] = "Volume inaccessible",
-	[0xa2] = "Volume in input",
-	[0xa3] = "Volume ejected",
-	[0xa4] = "All categories reserved",
-	[0xa5] = "Duplicate Volume",
-	[0xa6] = "Library Manager Offline",
-	[0xa7] = "Library Output Station full",
-	[0xa8] = "Vision System non-operational",
-	[0xa9] = "Library Manager Equipment Check",
-	[0xaa] = "Library Equipment Check",
-	[0xab] = "All Library Cells full",
-	[0xac] = "No Cleaner Volumes in Library",
-	[0xad] = "I/O Station door open",
-	[0xae] = "Subsystem environmental alert",
-};
-
-static int crypt_supported(struct tape_device *device)
-{
-	return TAPE390_CRYPT_SUPPORTED(TAPE_3590_CRYPT_INFO(device));
-}
-
-static int crypt_enabled(struct tape_device *device)
-{
-	return TAPE390_CRYPT_ON(TAPE_3590_CRYPT_INFO(device));
-}
-
-static void ext_to_int_kekl(struct tape390_kekl *in,
-			    struct tape3592_kekl *out)
-{
-	int len;
-
-	memset(out, 0, sizeof(*out));
-	if (in->type == TAPE390_KEKL_TYPE_HASH)
-		out->flags |= 0x40;
-	if (in->type_on_tape == TAPE390_KEKL_TYPE_HASH)
-		out->flags |= 0x80;
-	len = min(sizeof(out->label), strlen(in->label));
-	memcpy(out->label, in->label, len);
-	memset(out->label + len, ' ', sizeof(out->label) - len);
-	ASCEBC(out->label, sizeof(out->label));
-}
-
-static void int_to_ext_kekl(struct tape3592_kekl *in,
-			    struct tape390_kekl *out)
-{
-	memset(out, 0, sizeof(*out));
-	if(in->flags & 0x40)
-		out->type = TAPE390_KEKL_TYPE_HASH;
-	else
-		out->type = TAPE390_KEKL_TYPE_LABEL;
-	if(in->flags & 0x80)
-		out->type_on_tape = TAPE390_KEKL_TYPE_HASH;
-	else
-		out->type_on_tape = TAPE390_KEKL_TYPE_LABEL;
-	memcpy(out->label, in->label, sizeof(in->label));
-	EBCASC(out->label, sizeof(in->label));
-	strim(out->label);
-}
-
-static void int_to_ext_kekl_pair(struct tape3592_kekl_pair *in,
-				 struct tape390_kekl_pair *out)
-{
-	if (in->count == 0) {
-		out->kekl[0].type = TAPE390_KEKL_TYPE_NONE;
-		out->kekl[0].type_on_tape = TAPE390_KEKL_TYPE_NONE;
-		out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
-		out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
-	} else if (in->count == 1) {
-		int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
-		out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
-		out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
-	} else if (in->count == 2) {
-		int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
-		int_to_ext_kekl(&in->kekl[1], &out->kekl[1]);
-	} else {
-		printk("Invalid KEKL number: %d\n", in->count);
-		BUG();
-	}
-}
-
-static int check_ext_kekl(struct tape390_kekl *kekl)
-{
-	if (kekl->type == TAPE390_KEKL_TYPE_NONE)
-		goto invalid;
-	if (kekl->type > TAPE390_KEKL_TYPE_HASH)
-		goto invalid;
-	if (kekl->type_on_tape == TAPE390_KEKL_TYPE_NONE)
-		goto invalid;
-	if (kekl->type_on_tape > TAPE390_KEKL_TYPE_HASH)
-		goto invalid;
-	if ((kekl->type == TAPE390_KEKL_TYPE_HASH) &&
-	    (kekl->type_on_tape == TAPE390_KEKL_TYPE_LABEL))
-		goto invalid;
-
-	return 0;
-invalid:
-	return -EINVAL;
-}
-
-static int check_ext_kekl_pair(struct tape390_kekl_pair *kekls)
-{
-	if (check_ext_kekl(&kekls->kekl[0]))
-		goto invalid;
-	if (check_ext_kekl(&kekls->kekl[1]))
-		goto invalid;
-
-	return 0;
-invalid:
-	return -EINVAL;
-}
-
-/*
- * Query KEKLs
- */
-static int tape_3592_kekl_query(struct tape_device *device,
-				struct tape390_kekl_pair *ext_kekls)
-{
-	struct tape_request *request;
-	struct tape3592_kekl_query_order *order;
-	struct tape3592_kekl_query_data *int_kekls;
-	int rc;
-
-	DBF_EVENT(6, "tape3592_kekl_query\n");
-	int_kekls = kmalloc(sizeof(*int_kekls), GFP_KERNEL|GFP_DMA);
-	if (!int_kekls)
-		return -ENOMEM;
-	request = tape_alloc_request(2, sizeof(*order));
-	if (IS_ERR(request)) {
-		rc = PTR_ERR(request);
-		goto fail_malloc;
-	}
-	order = request->cpdata;
-	memset(order,0,sizeof(*order));
-	order->code = 0xe2;
-	order->max_count = 2;
-	request->op = TO_KEKL_QUERY;
-	tape_ccw_cc(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
-	tape_ccw_end(request->cpaddr + 1, READ_SS_DATA, sizeof(*int_kekls),
-		     int_kekls);
-	rc = tape_do_io(device, request);
-	if (rc)
-		goto fail_request;
-	int_to_ext_kekl_pair(&int_kekls->kekls, ext_kekls);
-
-	rc = 0;
-fail_request:
-	tape_free_request(request);
-fail_malloc:
-	kfree(int_kekls);
-	return rc;
-}
-
-/*
- * IOCTL: Query KEKLs
- */
-static int tape_3592_ioctl_kekl_query(struct tape_device *device,
-				      unsigned long arg)
-{
-	int rc;
-	struct tape390_kekl_pair *ext_kekls;
-
-	DBF_EVENT(6, "tape_3592_ioctl_kekl_query\n");
-	if (!crypt_supported(device))
-		return -ENOSYS;
-	if (!crypt_enabled(device))
-		return -EUNATCH;
-	ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL);
-	if (!ext_kekls)
-		return -ENOMEM;
-	rc = tape_3592_kekl_query(device, ext_kekls);
-	if (rc != 0)
-		goto fail;
-	if (copy_to_user((char __user *) arg, ext_kekls, sizeof(*ext_kekls))) {
-		rc = -EFAULT;
-		goto fail;
-	}
-	rc = 0;
-fail:
-	kfree(ext_kekls);
-	return rc;
-}
-
-static int tape_3590_mttell(struct tape_device *device, int mt_count);
-
-/*
- * Set KEKLs
- */
-static int tape_3592_kekl_set(struct tape_device *device,
-			      struct tape390_kekl_pair *ext_kekls)
-{
-	struct tape_request *request;
-	struct tape3592_kekl_set_order *order;
-
-	DBF_EVENT(6, "tape3592_kekl_set\n");
-	if (check_ext_kekl_pair(ext_kekls)) {
-		DBF_EVENT(6, "invalid kekls\n");
-		return -EINVAL;
-	}
-	if (tape_3590_mttell(device, 0) != 0)
-		return -EBADSLT;
-	request = tape_alloc_request(1, sizeof(*order));
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	order = request->cpdata;
-	memset(order, 0, sizeof(*order));
-	order->code = 0xe3;
-	order->kekls.count = 2;
-	ext_to_int_kekl(&ext_kekls->kekl[0], &order->kekls.kekl[0]);
-	ext_to_int_kekl(&ext_kekls->kekl[1], &order->kekls.kekl[1]);
-	request->op = TO_KEKL_SET;
-	tape_ccw_end(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
-
-	return tape_do_io_free(device, request);
-}
-
-/*
- * IOCTL: Set KEKLs
- */
-static int tape_3592_ioctl_kekl_set(struct tape_device *device,
-				    unsigned long arg)
-{
-	int rc;
-	struct tape390_kekl_pair *ext_kekls;
-
-	DBF_EVENT(6, "tape_3592_ioctl_kekl_set\n");
-	if (!crypt_supported(device))
-		return -ENOSYS;
-	if (!crypt_enabled(device))
-		return -EUNATCH;
-	ext_kekls = memdup_user((char __user *)arg, sizeof(*ext_kekls));
-	if (IS_ERR(ext_kekls))
-		return PTR_ERR(ext_kekls);
-	rc = tape_3592_kekl_set(device, ext_kekls);
-	kfree(ext_kekls);
-	return rc;
-}
-
-/*
- * Enable encryption
- */
-static struct tape_request *__tape_3592_enable_crypt(struct tape_device *device)
-{
-	struct tape_request *request;
-	char *data;
-
-	DBF_EVENT(6, "tape_3592_enable_crypt\n");
-	if (!crypt_supported(device))
-		return ERR_PTR(-ENOSYS);
-	request = tape_alloc_request(2, 72);
-	if (IS_ERR(request))
-		return request;
-	data = request->cpdata;
-	memset(data,0,72);
-
-	data[0]       = 0x05;
-	data[36 + 0]  = 0x03;
-	data[36 + 1]  = 0x03;
-	data[36 + 4]  = 0x40;
-	data[36 + 6]  = 0x01;
-	data[36 + 14] = 0x2f;
-	data[36 + 18] = 0xc3;
-	data[36 + 35] = 0x72;
-	request->op = TO_CRYPT_ON;
-	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
-	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
-	return request;
-}
-
-static int tape_3592_enable_crypt(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = __tape_3592_enable_crypt(device);
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	return tape_do_io_free(device, request);
-}
-
-static void tape_3592_enable_crypt_async(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = __tape_3592_enable_crypt(device);
-	if (!IS_ERR(request))
-		tape_do_io_async_free(device, request);
-}
-
-/*
- * Disable encryption
- */
-static struct tape_request *__tape_3592_disable_crypt(struct tape_device *device)
-{
-	struct tape_request *request;
-	char *data;
-
-	DBF_EVENT(6, "tape_3592_disable_crypt\n");
-	if (!crypt_supported(device))
-		return ERR_PTR(-ENOSYS);
-	request = tape_alloc_request(2, 72);
-	if (IS_ERR(request))
-		return request;
-	data = request->cpdata;
-	memset(data,0,72);
-
-	data[0]       = 0x05;
-	data[36 + 0]  = 0x03;
-	data[36 + 1]  = 0x03;
-	data[36 + 35] = 0x32;
-
-	request->op = TO_CRYPT_OFF;
-	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
-	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
-
-	return request;
-}
-
-static int tape_3592_disable_crypt(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = __tape_3592_disable_crypt(device);
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	return tape_do_io_free(device, request);
-}
-
-static void tape_3592_disable_crypt_async(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = __tape_3592_disable_crypt(device);
-	if (!IS_ERR(request))
-		tape_do_io_async_free(device, request);
-}
-
-/*
- * IOCTL: Set encryption status
- */
-static int tape_3592_ioctl_crypt_set(struct tape_device *device,
-				     unsigned long arg)
-{
-	struct tape390_crypt_info info;
-
-	DBF_EVENT(6, "tape_3592_ioctl_crypt_set\n");
-	if (!crypt_supported(device))
-		return -ENOSYS;
-	if (copy_from_user(&info, (char __user *)arg, sizeof(info)))
-		return -EFAULT;
-	if (info.status & ~TAPE390_CRYPT_ON_MASK)
-		return -EINVAL;
-	if (info.status & TAPE390_CRYPT_ON_MASK)
-		return tape_3592_enable_crypt(device);
-	else
-		return tape_3592_disable_crypt(device);
-}
-
-static int tape_3590_sense_medium(struct tape_device *device);
-
-/*
- * IOCTL: Query enryption status
- */
-static int tape_3592_ioctl_crypt_query(struct tape_device *device,
-				       unsigned long arg)
-{
-	DBF_EVENT(6, "tape_3592_ioctl_crypt_query\n");
-	if (!crypt_supported(device))
-		return -ENOSYS;
-	tape_3590_sense_medium(device);
-	if (copy_to_user((char __user *) arg, &TAPE_3590_CRYPT_INFO(device),
-		sizeof(TAPE_3590_CRYPT_INFO(device))))
-		return -EFAULT;
-	else
-		return 0;
-}
-
-/*
- * 3590 IOCTL Overload
- */
-static int
-tape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
-{
-	switch (cmd) {
-	case TAPE390_DISPLAY: {
-		struct display_struct disp;
-
-		if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)))
-			return -EFAULT;
-
-		return tape_std_display(device, &disp);
-	}
-	case TAPE390_KEKL_SET:
-		return tape_3592_ioctl_kekl_set(device, arg);
-	case TAPE390_KEKL_QUERY:
-		return tape_3592_ioctl_kekl_query(device, arg);
-	case TAPE390_CRYPT_SET:
-		return tape_3592_ioctl_crypt_set(device, arg);
-	case TAPE390_CRYPT_QUERY:
-		return tape_3592_ioctl_crypt_query(device, arg);
-	default:
-		return -EINVAL;	/* no additional ioctls */
-	}
-}
-
-/*
- * SENSE Medium: Get Sense data about medium state
- */
-static int tape_3590_sense_medium(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = tape_alloc_request(1, 128);
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	request->op = TO_MSEN;
-	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata);
-	return tape_do_io_free(device, request);
-}
-
-static void tape_3590_sense_medium_async(struct tape_device *device)
-{
-	struct tape_request *request;
-
-	request = tape_alloc_request(1, 128);
-	if (IS_ERR(request))
-		return;
-	request->op = TO_MSEN;
-	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata);
-	tape_do_io_async_free(device, request);
-}
-
-/*
- * MTTELL: Tell block. Return the number of block relative to current file.
- */
-static int
-tape_3590_mttell(struct tape_device *device, int mt_count)
-{
-	__u64 block_id;
-	int rc;
-
-	rc = tape_std_read_block_id(device, &block_id);
-	if (rc)
-		return rc;
-	return block_id >> 32;
-}
-
-/*
- * MTSEEK: seek to the specified block.
- */
-static int
-tape_3590_mtseek(struct tape_device *device, int count)
-{
-	struct tape_request *request;
-
-	DBF_EVENT(6, "xsee id: %x\n", count);
-	request = tape_alloc_request(3, 4);
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	request->op = TO_LBL;
-	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-	*(__u32 *) request->cpdata = count;
-	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
-	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-	return tape_do_io_free(device, request);
-}
-
-/*
- * Read Attention Msg
- * This should be done after an interrupt with attention bit (0x80)
- * in device state.
- *
- * After a "read attention message" request there are two possible
- * results:
- *
- * 1. A unit check is presented, when attention sense is present (e.g. when
- * a medium has been unloaded). The attention sense comes then
- * together with the unit check. The recovery action is either "retry"
- * (in case there is an attention message pending) or "permanent error".
- *
- * 2. The attention msg is written to the "read subsystem data" buffer.
- * In this case we probably should print it to the console.
- */
-static void tape_3590_read_attmsg_async(struct tape_device *device)
-{
-	struct tape_request *request;
-	char *buf;
-
-	request = tape_alloc_request(3, 4096);
-	if (IS_ERR(request))
-		return;
-	request->op = TO_READ_ATTMSG;
-	buf = request->cpdata;
-	buf[0] = PREP_RD_SS_DATA;
-	buf[6] = RD_ATTMSG;	/* read att msg */
-	tape_ccw_cc(request->cpaddr, PERFORM_SS_FUNC, 12, buf);
-	tape_ccw_cc(request->cpaddr + 1, READ_SS_DATA, 4096 - 12, buf + 12);
-	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-	tape_do_io_async_free(device, request);
-}
-
-/*
- * These functions are used to schedule follow-up actions from within an
- * interrupt context (like unsolicited interrupts).
- * Note: the work handler is called by the system work queue. The tape
- * commands started by the handler need to be asynchrounous, otherwise
- * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
- */
-struct work_handler_data {
-	struct tape_device *device;
-	enum tape_op        op;
-	struct work_struct  work;
-};
-
-static void
-tape_3590_work_handler(struct work_struct *work)
-{
-	struct work_handler_data *p =
-		container_of(work, struct work_handler_data, work);
-
-	switch (p->op) {
-	case TO_MSEN:
-		tape_3590_sense_medium_async(p->device);
-		break;
-	case TO_READ_ATTMSG:
-		tape_3590_read_attmsg_async(p->device);
-		break;
-	case TO_CRYPT_ON:
-		tape_3592_enable_crypt_async(p->device);
-		break;
-	case TO_CRYPT_OFF:
-		tape_3592_disable_crypt_async(p->device);
-		break;
-	default:
-		DBF_EVENT(3, "T3590: work handler undefined for "
-			  "operation 0x%02x\n", p->op);
-	}
-	tape_put_device(p->device);
-	kfree(p);
-}
-
-static int
-tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
-{
-	struct work_handler_data *p;
-
-	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
-		return -ENOMEM;
-
-	INIT_WORK(&p->work, tape_3590_work_handler);
-
-	p->device = tape_get_device(device);
-	p->op = op;
-
-	queue_work(tape_3590_wq, &p->work);
-	return 0;
-}
-
-static void tape_3590_med_state_set(struct tape_device *device,
-				    struct tape_3590_med_sense *sense)
-{
-	struct tape390_crypt_info *c_info;
-
-	c_info = &TAPE_3590_CRYPT_INFO(device);
-
-	DBF_EVENT(6, "medium state: %x:%x\n", sense->macst, sense->masst);
-	switch (sense->macst) {
-	case 0x04:
-	case 0x05:
-	case 0x06:
-		tape_med_state_set(device, MS_UNLOADED);
-		TAPE_3590_CRYPT_INFO(device).medium_status = 0;
-		return;
-	case 0x08:
-	case 0x09:
-		tape_med_state_set(device, MS_LOADED);
-		break;
-	default:
-		tape_med_state_set(device, MS_UNKNOWN);
-		return;
-	}
-	c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK;
-	if (sense->flags & MSENSE_CRYPT_MASK) {
-		DBF_EVENT(6, "Medium is encrypted (%04x)\n", sense->flags);
-		c_info->medium_status |= TAPE390_MEDIUM_ENCRYPTED_MASK;
-	} else	{
-		DBF_EVENT(6, "Medium is not encrypted %04x\n", sense->flags);
-		c_info->medium_status &= ~TAPE390_MEDIUM_ENCRYPTED_MASK;
-	}
-}
-
-/*
- * The done handler is called at device/channel end and wakes up the sleeping
- * process
- */
-static int
-tape_3590_done(struct tape_device *device, struct tape_request *request)
-{
-
-	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
-
-	switch (request->op) {
-	case TO_BSB:
-	case TO_BSF:
-	case TO_DSE:
-	case TO_FSB:
-	case TO_FSF:
-	case TO_LBL:
-	case TO_RFO:
-	case TO_RBA:
-	case TO_REW:
-	case TO_WRI:
-	case TO_WTM:
-	case TO_BLOCK:
-	case TO_LOAD:
-		tape_med_state_set(device, MS_LOADED);
-		break;
-	case TO_RUN:
-		tape_med_state_set(device, MS_UNLOADED);
-		tape_3590_schedule_work(device, TO_CRYPT_OFF);
-		break;
-	case TO_MSEN:
-		tape_3590_med_state_set(device, request->cpdata);
-		break;
-	case TO_CRYPT_ON:
-		TAPE_3590_CRYPT_INFO(device).status
-			|= TAPE390_CRYPT_ON_MASK;
-		*(device->modeset_byte) |= 0x03;
-		break;
-	case TO_CRYPT_OFF:
-		TAPE_3590_CRYPT_INFO(device).status
-			&= ~TAPE390_CRYPT_ON_MASK;
-		*(device->modeset_byte) &= ~0x03;
-		break;
-	case TO_RBI:	/* RBI seems to succeed even without medium loaded. */
-	case TO_NOP:	/* Same to NOP. */
-	case TO_READ_CONFIG:
-	case TO_READ_ATTMSG:
-	case TO_DIS:
-	case TO_ASSIGN:
-	case TO_UNASSIGN:
-	case TO_SIZE:
-	case TO_KEKL_SET:
-	case TO_KEKL_QUERY:
-	case TO_RDC:
-		break;
-	}
-	return TAPE_IO_SUCCESS;
-}
-
-/*
- * This function is called, when error recovery was successful
- */
-static inline int
-tape_3590_erp_succeeded(struct tape_device *device, struct tape_request *request)
-{
-	DBF_EVENT(3, "Error Recovery successful for %s\n",
-		  tape_op_verbose[request->op]);
-	return tape_3590_done(device, request);
-}
-
-/*
- * This function is called, when error recovery was not successful
- */
-static inline int
-tape_3590_erp_failed(struct tape_device *device, struct tape_request *request,
-		     struct irb *irb, int rc)
-{
-	DBF_EVENT(3, "Error Recovery failed for %s\n",
-		  tape_op_verbose[request->op]);
-	tape_dump_sense_dbf(device, request, irb);
-	return rc;
-}
-
-/*
- * Error Recovery do retry
- */
-static inline int
-tape_3590_erp_retry(struct tape_device *device, struct tape_request *request,
-		    struct irb *irb)
-{
-	DBF_EVENT(2, "Retry: %s\n", tape_op_verbose[request->op]);
-	tape_dump_sense_dbf(device, request, irb);
-	return TAPE_IO_RETRY;
-}
-
-/*
- * Handle unsolicited interrupts
- */
-static int
-tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
-{
-	if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END)
-		/* Probably result of halt ssch */
-		return TAPE_IO_PENDING;
-	else if (irb->scsw.cmd.dstat == 0x85)
-		/* Device Ready */
-		DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id);
-	else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
-		tape_3590_schedule_work(device, TO_READ_ATTMSG);
-	} else {
-		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
-		tape_dump_sense_dbf(device, NULL, irb);
-	}
-	/* check medium state */
-	tape_3590_schedule_work(device, TO_MSEN);
-	return TAPE_IO_SUCCESS;
-}
-
-/*
- * Basic Recovery routine
- */
-static int
-tape_3590_erp_basic(struct tape_device *device, struct tape_request *request,
-		    struct irb *irb, int rc)
-{
-	struct tape_3590_sense *sense;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-
-	switch (sense->bra) {
-	case SENSE_BRA_PER:
-		return tape_3590_erp_failed(device, request, irb, rc);
-	case SENSE_BRA_CONT:
-		return tape_3590_erp_succeeded(device, request);
-	case SENSE_BRA_RE:
-		return tape_3590_erp_retry(device, request, irb);
-	case SENSE_BRA_DRE:
-		return tape_3590_erp_failed(device, request, irb, rc);
-	default:
-		BUG();
-		return TAPE_IO_STOP;
-	}
-}
-
-/*
- *  RDL: Read Device (buffered) log
- */
-static int
-tape_3590_erp_read_buf_log(struct tape_device *device,
-			   struct tape_request *request, struct irb *irb)
-{
-	/*
-	 * We just do the basic error recovery at the moment (retry).
-	 * Perhaps in the future, we read the log and dump it somewhere...
-	 */
-	return tape_3590_erp_basic(device, request, irb, -EIO);
-}
-
-/*
- *  SWAP: Swap Devices
- */
-static int
-tape_3590_erp_swap(struct tape_device *device, struct tape_request *request,
-		   struct irb *irb)
-{
-	/*
-	 * This error recovery should swap the tapes
-	 * if the original has a problem. The operation
-	 * should proceed with the new tape... this
-	 * should probably be done in user space!
-	 */
-	dev_warn (&device->cdev->dev, "The tape medium must be loaded into a "
-		"different tape unit\n");
-	return tape_3590_erp_basic(device, request, irb, -EIO);
-}
-
-/*
- *  LBY: Long Busy
- */
-static int
-tape_3590_erp_long_busy(struct tape_device *device,
-			struct tape_request *request, struct irb *irb)
-{
-	DBF_EVENT(6, "Device is busy\n");
-	return TAPE_IO_LONG_BUSY;
-}
-
-/*
- *  SPI: Special Intercept
- */
-static int
-tape_3590_erp_special_interrupt(struct tape_device *device,
-				struct tape_request *request, struct irb *irb)
-{
-	return tape_3590_erp_basic(device, request, irb, -EIO);
-}
-
-/*
- * Print an MIM (Media Information  Message) (message code f0)
- */
-static void
-tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb)
-{
-	struct tape_3590_sense *sense;
-	char *exception, *service;
-
-	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
-	service = kmalloc(BUFSIZE, GFP_ATOMIC);
-
-	if (!exception || !service)
-		goto out_nomem;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-	/* Exception Message */
-	switch (sense->fmt.f70.emc) {
-	case 0x02:
-		snprintf(exception, BUFSIZE, "Data degraded");
-		break;
-	case 0x03:
-		snprintf(exception, BUFSIZE, "Data degraded in partition %i",
-			sense->fmt.f70.mp);
-		break;
-	case 0x04:
-		snprintf(exception, BUFSIZE, "Medium degraded");
-		break;
-	case 0x05:
-		snprintf(exception, BUFSIZE, "Medium degraded in partition %i",
-			sense->fmt.f70.mp);
-		break;
-	case 0x06:
-		snprintf(exception, BUFSIZE, "Block 0 Error");
-		break;
-	case 0x07:
-		snprintf(exception, BUFSIZE, "Medium Exception 0x%02x",
-			sense->fmt.f70.md);
-		break;
-	default:
-		snprintf(exception, BUFSIZE, "0x%02x",
-			sense->fmt.f70.emc);
-		break;
-	}
-	/* Service Message */
-	switch (sense->fmt.f70.smc) {
-	case 0x02:
-		snprintf(service, BUFSIZE, "Reference Media maintenance "
-			"procedure %i", sense->fmt.f70.md);
-		break;
-	default:
-		snprintf(service, BUFSIZE, "0x%02x",
-			sense->fmt.f70.smc);
-		break;
-	}
-
-	dev_warn (&device->cdev->dev, "Tape media information: exception %s, "
-		"service %s\n", exception, service);
-
-out_nomem:
-	kfree(exception);
-	kfree(service);
-}
-
-/*
- * Print an I/O Subsystem Service Information Message (message code f1)
- */
-static void
-tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb)
-{
-	struct tape_3590_sense *sense;
-	char *exception, *service;
-
-	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
-	service = kmalloc(BUFSIZE, GFP_ATOMIC);
-
-	if (!exception || !service)
-		goto out_nomem;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-	/* Exception Message */
-	switch (sense->fmt.f71.emc) {
-	case 0x01:
-		snprintf(exception, BUFSIZE, "Effect of failure is unknown");
-		break;
-	case 0x02:
-		snprintf(exception, BUFSIZE, "CU Exception - no performance "
-			"impact");
-		break;
-	case 0x03:
-		snprintf(exception, BUFSIZE, "CU Exception on channel "
-			"interface 0x%02x", sense->fmt.f71.md[0]);
-		break;
-	case 0x04:
-		snprintf(exception, BUFSIZE, "CU Exception on device path "
-			"0x%02x", sense->fmt.f71.md[0]);
-		break;
-	case 0x05:
-		snprintf(exception, BUFSIZE, "CU Exception on library path "
-			"0x%02x", sense->fmt.f71.md[0]);
-		break;
-	case 0x06:
-		snprintf(exception, BUFSIZE, "CU Exception on node 0x%02x",
-			sense->fmt.f71.md[0]);
-		break;
-	case 0x07:
-		snprintf(exception, BUFSIZE, "CU Exception on partition "
-			"0x%02x", sense->fmt.f71.md[0]);
-		break;
-	default:
-		snprintf(exception, BUFSIZE, "0x%02x",
-			sense->fmt.f71.emc);
-	}
-	/* Service Message */
-	switch (sense->fmt.f71.smc) {
-	case 0x01:
-		snprintf(service, BUFSIZE, "Repair impact is unknown");
-		break;
-	case 0x02:
-		snprintf(service, BUFSIZE, "Repair will not impact cu "
-			"performance");
-		break;
-	case 0x03:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable node "
-				"0x%x on CU", sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"nodes (0x%x-0x%x) on CU", sense->fmt.f71.md[1],
-				sense->fmt.f71.md[2]);
-		break;
-	case 0x04:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"channel path 0x%x on CU",
-				sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable channel"
-				" paths (0x%x-0x%x) on CU",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x05:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable device"
-				" path 0x%x on CU", sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable device"
-				" paths (0x%x-0x%x) on CU",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x06:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"library path 0x%x on CU",
-				sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"library paths (0x%x-0x%x) on CU",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x07:
-		snprintf(service, BUFSIZE, "Repair will disable access to CU");
-		break;
-	default:
-		snprintf(service, BUFSIZE, "0x%02x",
-			sense->fmt.f71.smc);
-	}
-
-	dev_warn (&device->cdev->dev, "I/O subsystem information: exception"
-		" %s, service %s\n", exception, service);
-out_nomem:
-	kfree(exception);
-	kfree(service);
-}
-
-/*
- * Print an Device Subsystem Service Information Message (message code f2)
- */
-static void
-tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb)
-{
-	struct tape_3590_sense *sense;
-	char *exception, *service;
-
-	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
-	service = kmalloc(BUFSIZE, GFP_ATOMIC);
-
-	if (!exception || !service)
-		goto out_nomem;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-	/* Exception Message */
-	switch (sense->fmt.f71.emc) {
-	case 0x01:
-		snprintf(exception, BUFSIZE, "Effect of failure is unknown");
-		break;
-	case 0x02:
-		snprintf(exception, BUFSIZE, "DV Exception - no performance"
-			" impact");
-		break;
-	case 0x03:
-		snprintf(exception, BUFSIZE, "DV Exception on channel "
-			"interface 0x%02x", sense->fmt.f71.md[0]);
-		break;
-	case 0x04:
-		snprintf(exception, BUFSIZE, "DV Exception on loader 0x%02x",
-			sense->fmt.f71.md[0]);
-		break;
-	case 0x05:
-		snprintf(exception, BUFSIZE, "DV Exception on message display"
-			" 0x%02x", sense->fmt.f71.md[0]);
-		break;
-	case 0x06:
-		snprintf(exception, BUFSIZE, "DV Exception in tape path");
-		break;
-	case 0x07:
-		snprintf(exception, BUFSIZE, "DV Exception in drive");
-		break;
-	default:
-		snprintf(exception, BUFSIZE, "0x%02x",
-			sense->fmt.f71.emc);
-	}
-	/* Service Message */
-	switch (sense->fmt.f71.smc) {
-	case 0x01:
-		snprintf(service, BUFSIZE, "Repair impact is unknown");
-		break;
-	case 0x02:
-		snprintf(service, BUFSIZE, "Repair will not impact device "
-			"performance");
-		break;
-	case 0x03:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"channel path 0x%x on DV",
-				sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"channel path (0x%x-0x%x) on DV",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x04:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"interface 0x%x on DV", sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"interfaces (0x%x-0x%x) on DV",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x05:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable loader"
-				" 0x%x on DV", sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable loader"
-				" (0x%x-0x%x) on DV",
-				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x07:
-		snprintf(service, BUFSIZE, "Repair will disable access to DV");
-		break;
-	case 0x08:
-		if (sense->fmt.f71.mdf == 0)
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"message display 0x%x on DV",
-				sense->fmt.f71.md[1]);
-		else
-			snprintf(service, BUFSIZE, "Repair will disable "
-				"message displays (0x%x-0x%x) on DV",
-				 sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
-		break;
-	case 0x09:
-		snprintf(service, BUFSIZE, "Clean DV");
-		break;
-	default:
-		snprintf(service, BUFSIZE, "0x%02x",
-			sense->fmt.f71.smc);
-	}
-
-	dev_warn (&device->cdev->dev, "Device subsystem information: exception"
-		" %s, service %s\n", exception, service);
-out_nomem:
-	kfree(exception);
-	kfree(service);
-}
-
-/*
- * Print standard ERA Message
- */
-static void
-tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
-{
-	struct tape_3590_sense *sense;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-	if (sense->mc == 0)
-		return;
-	if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) {
-		if (tape_3590_msg[sense->mc] != NULL)
-			dev_warn (&device->cdev->dev, "The tape unit has "
-				"issued sense message %s\n",
-				tape_3590_msg[sense->mc]);
-		else
-			dev_warn (&device->cdev->dev, "The tape unit has "
-				"issued an unknown sense message code 0x%x\n",
-				sense->mc);
-		return;
-	}
-	if (sense->mc == 0xf0) {
-		/* Standard Media Information Message */
-		dev_warn (&device->cdev->dev, "MIM SEV=%i, MC=%02x, ES=%x/%x, "
-			"RC=%02x-%04x-%02x\n", sense->fmt.f70.sev, sense->mc,
-			sense->fmt.f70.emc, sense->fmt.f70.smc,
-			sense->fmt.f70.refcode, sense->fmt.f70.mid,
-			sense->fmt.f70.fid);
-		tape_3590_print_mim_msg_f0(device, irb);
-		return;
-	}
-	if (sense->mc == 0xf1) {
-		/* Standard I/O Subsystem Service Information Message */
-		dev_warn (&device->cdev->dev, "IOSIM SEV=%i, DEVTYPE=3590/%02x,"
-			" MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
-			sense->fmt.f71.sev, device->cdev->id.dev_model,
-			sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc,
-			sense->fmt.f71.refcode1, sense->fmt.f71.refcode2,
-			sense->fmt.f71.refcode3);
-		tape_3590_print_io_sim_msg_f1(device, irb);
-		return;
-	}
-	if (sense->mc == 0xf2) {
-		/* Standard Device Service Information Message */
-		dev_warn (&device->cdev->dev, "DEVSIM SEV=%i, DEVTYPE=3590/%02x"
-			", MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
-			sense->fmt.f71.sev, device->cdev->id.dev_model,
-			sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc,
-			sense->fmt.f71.refcode1, sense->fmt.f71.refcode2,
-			sense->fmt.f71.refcode3);
-		tape_3590_print_dev_sim_msg_f2(device, irb);
-		return;
-	}
-	if (sense->mc == 0xf3) {
-		/* Standard Library Service Information Message */
-		return;
-	}
-	dev_warn (&device->cdev->dev, "The tape unit has issued an unknown "
-		"sense message code %x\n", sense->mc);
-}
-
-static int tape_3590_crypt_error(struct tape_device *device,
-				 struct tape_request *request, struct irb *irb)
-{
-	u8 cu_rc;
-	u16 ekm_rc2;
-	char *sense;
-
-	sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
-	cu_rc = sense[0];
-	ekm_rc2 = *((u16*) &sense[10]);
-	if ((cu_rc == 0) && (ekm_rc2 == 0xee31))
-		/* key not defined on EKM */
-		return tape_3590_erp_basic(device, request, irb, -EKEYREJECTED);
-	if ((cu_rc == 1) || (cu_rc == 2))
-		/* No connection to EKM */
-		return tape_3590_erp_basic(device, request, irb, -ENOTCONN);
-
-	dev_err (&device->cdev->dev, "The tape unit failed to obtain the "
-		"encryption key from EKM\n");
-
-	return tape_3590_erp_basic(device, request, irb, -ENOKEY);
-}
-
-/*
- *  3590 error Recovery routine:
- *  If possible, it tries to recover from the error. If this is not possible,
- *  inform the user about the problem.
- */
-static int
-tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
-		     struct irb *irb)
-{
-	struct tape_3590_sense *sense;
-
-	sense = (struct tape_3590_sense *) irb->ecw;
-
-	DBF_EVENT(6, "Unit Check: RQC = %x\n", sense->rc_rqc);
-
-	/*
-	 * First check all RC-QRCs where we want to do something special
-	 *   - "break":     basic error recovery is done
-	 *   - "goto out:": just print error message if available
-	 */
-	switch (sense->rc_rqc) {
-
-	case 0x1110:
-		tape_3590_print_era_msg(device, irb);
-		return tape_3590_erp_read_buf_log(device, request, irb);
-
-	case 0x2230:
-	case 0x2231:
-		tape_3590_print_era_msg(device, irb);
-		return tape_3590_erp_special_interrupt(device, request, irb);
-	case 0x2240:
-		return tape_3590_crypt_error(device, request, irb);
-
-	case 0x3010:
-		DBF_EVENT(2, "(%08x): Backward at Beginning of Partition\n",
-			  device->cdev_id);
-		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
-	case 0x3012:
-		DBF_EVENT(2, "(%08x): Forward at End of Partition\n",
-			  device->cdev_id);
-		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
-	case 0x3020:
-		DBF_EVENT(2, "(%08x): End of Data Mark\n", device->cdev_id);
-		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
-
-	case 0x3122:
-		DBF_EVENT(2, "(%08x): Rewind Unload initiated\n",
-			  device->cdev_id);
-		return tape_3590_erp_basic(device, request, irb, -EIO);
-	case 0x3123:
-		DBF_EVENT(2, "(%08x): Rewind Unload complete\n",
-			  device->cdev_id);
-		tape_med_state_set(device, MS_UNLOADED);
-		tape_3590_schedule_work(device, TO_CRYPT_OFF);
-		return tape_3590_erp_basic(device, request, irb, 0);
-
-	case 0x4010:
-		/*
-		 * print additional msg since default msg
-		 * "device intervention" is not very meaningfull
-		 */
-		tape_med_state_set(device, MS_UNLOADED);
-		tape_3590_schedule_work(device, TO_CRYPT_OFF);
-		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
-	case 0x4012:		/* Device Long Busy */
-		/* XXX: Also use long busy handling here? */
-		DBF_EVENT(6, "(%08x): LONG BUSY\n", device->cdev_id);
-		tape_3590_print_era_msg(device, irb);
-		return tape_3590_erp_basic(device, request, irb, -EBUSY);
-	case 0x4014:
-		DBF_EVENT(6, "(%08x): Crypto LONG BUSY\n", device->cdev_id);
-		return tape_3590_erp_long_busy(device, request, irb);
-
-	case 0x5010:
-		if (sense->rac == 0xd0) {
-			/* Swap */
-			tape_3590_print_era_msg(device, irb);
-			return tape_3590_erp_swap(device, request, irb);
-		}
-		return tape_3590_erp_basic(device, request, irb, -EIO);
-	case 0x5020:
-	case 0x5021:
-	case 0x5022:
-	case 0x5040:
-	case 0x5041:
-	case 0x5042:
-		tape_3590_print_era_msg(device, irb);
-		return tape_3590_erp_swap(device, request, irb);
-
-	case 0x5110:
-	case 0x5111:
-		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
-
-	case 0x5120:
-	case 0x1120:
-		tape_med_state_set(device, MS_UNLOADED);
-		tape_3590_schedule_work(device, TO_CRYPT_OFF);
-		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
-
-	case 0x6020:
-		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
-
-	case 0x8011:
-		return tape_3590_erp_basic(device, request, irb, -EPERM);
-	case 0x8013:
-		dev_warn (&device->cdev->dev, "A different host has privileged"
-			" access to the tape unit\n");
-		return tape_3590_erp_basic(device, request, irb, -EPERM);
-	default:
-		return tape_3590_erp_basic(device, request, irb, -EIO);
-	}
-}
-
-/*
- * 3590 interrupt handler:
- */
-static int
-tape_3590_irq(struct tape_device *device, struct tape_request *request,
-	      struct irb *irb)
-{
-	if (request == NULL)
-		return tape_3590_unsolicited_irq(device, irb);
-
-	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
-	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
-	    (request->op == TO_WRI)) {
-		/* Write at end of volume */
-		DBF_EVENT(2, "End of volume\n");
-		return tape_3590_erp_failed(device, request, irb, -ENOSPC);
-	}
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
-		return tape_3590_unit_check(device, request, irb);
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
-		if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) {
-			if (request->op == TO_FSB || request->op == TO_BSB)
-				request->rescnt++;
-			else
-				DBF_EVENT(5, "Unit Exception!\n");
-		}
-
-		return tape_3590_done(device, request);
-	}
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) {
-		DBF_EVENT(2, "channel end\n");
-		return TAPE_IO_PENDING;
-	}
-
-	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
-		DBF_EVENT(2, "Unit Attention when busy..\n");
-		return TAPE_IO_PENDING;
-	}
-
-	DBF_EVENT(6, "xunknownirq\n");
-	tape_dump_sense_dbf(device, request, irb);
-	return TAPE_IO_STOP;
-}
-
-
-static int tape_3590_read_dev_chars(struct tape_device *device,
-				    struct tape_3590_rdc_data *rdc_data)
-{
-	int rc;
-	struct tape_request *request;
-
-	request = tape_alloc_request(1, sizeof(*rdc_data));
-	if (IS_ERR(request))
-		return PTR_ERR(request);
-	request->op = TO_RDC;
-	tape_ccw_end(request->cpaddr, CCW_CMD_RDC, sizeof(*rdc_data),
-		     request->cpdata);
-	rc = tape_do_io(device, request);
-	if (rc == 0)
-		memcpy(rdc_data, request->cpdata, sizeof(*rdc_data));
-	tape_free_request(request);
-	return rc;
-}
-
-/*
- * Setup device function
- */
-static int
-tape_3590_setup_device(struct tape_device *device)
-{
-	int rc;
-	struct tape_3590_disc_data *data;
-	struct tape_3590_rdc_data *rdc_data;
-
-	DBF_EVENT(6, "3590 device setup\n");
-	data = kzalloc(sizeof(struct tape_3590_disc_data), GFP_KERNEL | GFP_DMA);
-	if (data == NULL)
-		return -ENOMEM;
-	data->read_back_op = READ_PREVIOUS;
-	device->discdata = data;
-
-	rdc_data = kmalloc(sizeof(*rdc_data), GFP_KERNEL | GFP_DMA);
-	if (!rdc_data) {
-		rc = -ENOMEM;
-		goto fail_kmalloc;
-	}
-	rc = tape_3590_read_dev_chars(device, rdc_data);
-	if (rc) {
-		DBF_LH(3, "Read device characteristics failed!\n");
-		goto fail_rdc_data;
-	}
-	rc = tape_std_assign(device);
-	if (rc)
-		goto fail_rdc_data;
-	if (rdc_data->data[31] == 0x13) {
-		data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK;
-		tape_3592_disable_crypt(device);
-	} else {
-		DBF_EVENT(6, "Device has NO crypto support\n");
-	}
-	/* Try to find out if medium is loaded */
-	rc = tape_3590_sense_medium(device);
-	if (rc) {
-		DBF_LH(3, "3590 medium sense returned %d\n", rc);
-		goto fail_rdc_data;
-	}
-	return 0;
-
-fail_rdc_data:
-	kfree(rdc_data);
-fail_kmalloc:
-	kfree(data);
-	return rc;
-}
-
-/*
- * Cleanup device function
- */
-static void
-tape_3590_cleanup_device(struct tape_device *device)
-{
-	flush_workqueue(tape_3590_wq);
-	tape_std_unassign(device);
-
-	kfree(device->discdata);
-	device->discdata = NULL;
-}
-
-/*
- * List of 3590 magnetic tape commands.
- */
-static tape_mtop_fn tape_3590_mtop[TAPE_NR_MTOPS] = {
-	[MTRESET]	 = tape_std_mtreset,
-	[MTFSF]		 = tape_std_mtfsf,
-	[MTBSF]		 = tape_std_mtbsf,
-	[MTFSR]		 = tape_std_mtfsr,
-	[MTBSR]		 = tape_std_mtbsr,
-	[MTWEOF]	 = tape_std_mtweof,
-	[MTREW]		 = tape_std_mtrew,
-	[MTOFFL]	 = tape_std_mtoffl,
-	[MTNOP]		 = tape_std_mtnop,
-	[MTRETEN]	 = tape_std_mtreten,
-	[MTBSFM]	 = tape_std_mtbsfm,
-	[MTFSFM]	 = tape_std_mtfsfm,
-	[MTEOM]		 = tape_std_mteom,
-	[MTERASE]	 = tape_std_mterase,
-	[MTRAS1]	 = NULL,
-	[MTRAS2]	 = NULL,
-	[MTRAS3]	 = NULL,
-	[MTSETBLK]	 = tape_std_mtsetblk,
-	[MTSETDENSITY]	 = NULL,
-	[MTSEEK]	 = tape_3590_mtseek,
-	[MTTELL]	 = tape_3590_mttell,
-	[MTSETDRVBUFFER] = NULL,
-	[MTFSS]		 = NULL,
-	[MTBSS]		 = NULL,
-	[MTWSM]		 = NULL,
-	[MTLOCK]	 = NULL,
-	[MTUNLOCK]	 = NULL,
-	[MTLOAD]	 = tape_std_mtload,
-	[MTUNLOAD]	 = tape_std_mtunload,
-	[MTCOMPRESSION]	 = tape_std_mtcompression,
-	[MTSETPART]	 = NULL,
-	[MTMKPART]	 = NULL
-};
-
-/*
- * Tape discipline structure for 3590.
- */
-static struct tape_discipline tape_discipline_3590 = {
-	.owner = THIS_MODULE,
-	.setup_device = tape_3590_setup_device,
-	.cleanup_device = tape_3590_cleanup_device,
-	.process_eov = tape_std_process_eov,
-	.irq = tape_3590_irq,
-	.read_block = tape_std_read_block,
-	.write_block = tape_std_write_block,
-	.ioctl_fn = tape_3590_ioctl,
-	.mtop_array = tape_3590_mtop
-};
-
-static struct ccw_device_id tape_3590_ids[] = {
-	{CCW_DEVICE_DEVTYPE(0x3590, 0, 0x3590, 0), .driver_info = tape_3590},
-	{CCW_DEVICE_DEVTYPE(0x3592, 0, 0x3592, 0), .driver_info = tape_3592},
-	{ /* end of list */ }
-};
-
-static int
-tape_3590_online(struct ccw_device *cdev)
-{
-	return tape_generic_online(dev_get_drvdata(&cdev->dev),
-				   &tape_discipline_3590);
-}
-
-static struct ccw_driver tape_3590_driver = {
-	.driver = {
-		.name = "tape_3590",
-		.owner = THIS_MODULE,
-	},
-	.ids = tape_3590_ids,
-	.probe = tape_generic_probe,
-	.remove = tape_generic_remove,
-	.set_offline = tape_generic_offline,
-	.set_online = tape_3590_online,
-	.int_class = IRQIO_TAP,
-};
-
-/*
- * Setup discipline structure.
- */
-static int
-tape_3590_init(void)
-{
-	int rc;
-
-	TAPE_DBF_AREA = debug_register("tape_3590", 2, 2, 4 * sizeof(long));
-	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
-#ifdef DBF_LIKE_HELL
-	debug_set_level(TAPE_DBF_AREA, 6);
-#endif
-
-	DBF_EVENT(3, "3590 init\n");
-
-	tape_3590_wq = alloc_workqueue("tape_3590", WQ_PERCPU, 0);
-	if (!tape_3590_wq)
-		return -ENOMEM;
-
-	/* Register driver for 3590 tapes. */
-	rc = ccw_driver_register(&tape_3590_driver);
-	if (rc) {
-		destroy_workqueue(tape_3590_wq);
-		DBF_EVENT(3, "3590 init failed\n");
-	} else
-		DBF_EVENT(3, "3590 registered\n");
-	return rc;
-}
-
-static void
-tape_3590_exit(void)
-{
-	ccw_driver_unregister(&tape_3590_driver);
-	destroy_workqueue(tape_3590_wq);
-	debug_unregister(TAPE_DBF_AREA);
-}
-
-MODULE_DEVICE_TABLE(ccw, tape_3590_ids);
-MODULE_AUTHOR("(C) 2001,2006 IBM Corporation");
-MODULE_DESCRIPTION("Linux on zSeries channel attached 3590 tape device driver");
-MODULE_LICENSE("GPL");
-
-module_init(tape_3590_init);
-module_exit(tape_3590_exit);
diff --git a/drivers/s390/char/tape_3590.h b/drivers/s390/char/tape_3590.h
deleted file mode 100644
index b398d8a..0000000
--- a/drivers/s390/char/tape_3590.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- *    tape device discipline for 3590 tapes.
- *
- *    Copyright IBM Corp. 2001, 2006
- *    Author(s): Stefan Bader <shbader@de.ibm.com>
- *		 Michael Holzheu <holzheu@de.ibm.com>
- *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
- */
-
-#ifndef _TAPE_3590_H
-#define _TAPE_3590_H
-
-#define MEDIUM_SENSE	0xc2
-#define READ_PREVIOUS	0x0a
-#define MODE_SENSE	0xcf
-#define PERFORM_SS_FUNC 0x77
-#define READ_SS_DATA	0x3e
-
-#define PREP_RD_SS_DATA 0x18
-#define RD_ATTMSG	0x3
-
-#define SENSE_BRA_PER  0
-#define SENSE_BRA_CONT 1
-#define SENSE_BRA_RE   2
-#define SENSE_BRA_DRE  3
-
-#define SENSE_FMT_LIBRARY	0x23
-#define SENSE_FMT_UNSOLICITED	0x40
-#define SENSE_FMT_COMMAND_REJ	0x41
-#define SENSE_FMT_COMMAND_EXEC0 0x50
-#define SENSE_FMT_COMMAND_EXEC1 0x51
-#define SENSE_FMT_EVENT0	0x60
-#define SENSE_FMT_EVENT1	0x61
-#define SENSE_FMT_MIM		0x70
-#define SENSE_FMT_SIM		0x71
-
-#define MSENSE_UNASSOCIATED	 0x00
-#define MSENSE_ASSOCIATED_MOUNT	 0x01
-#define MSENSE_ASSOCIATED_UMOUNT 0x02
-#define MSENSE_CRYPT_MASK	 0x00000010
-
-#define TAPE_3590_MAX_MSG	 0xb0
-
-/* Datatypes */
-
-struct tape_3590_disc_data {
-	struct tape390_crypt_info crypt_info;
-	int read_back_op;
-};
-
-#define TAPE_3590_CRYPT_INFO(device) \
-	((struct tape_3590_disc_data*)(device->discdata))->crypt_info
-#define TAPE_3590_READ_BACK_OP(device) \
-	((struct tape_3590_disc_data*)(device->discdata))->read_back_op
-
-struct tape_3590_sense {
-
-	unsigned int command_rej:1;
-	unsigned int interv_req:1;
-	unsigned int bus_out_check:1;
-	unsigned int eq_check:1;
-	unsigned int data_check:1;
-	unsigned int overrun:1;
-	unsigned int def_unit_check:1;
-	unsigned int assgnd_elsew:1;
-
-	unsigned int locate_fail:1;
-	unsigned int inst_online:1;
-	unsigned int reserved:1;
-	unsigned int blk_seq_err:1;
-	unsigned int begin_part:1;
-	unsigned int wr_mode:1;
-	unsigned int wr_prot:1;
-	unsigned int not_cap:1;
-
-	unsigned int bra:2;
-	unsigned int lc:3;
-	unsigned int vlf_active:1;
-	unsigned int stm:1;
-	unsigned int med_pos:1;
-
-	unsigned int rac:8;
-
-	unsigned int rc_rqc:16;
-
-	unsigned int mc:8;
-
-	unsigned int sense_fmt:8;
-
-	union {
-		struct {
-			unsigned int emc:4;
-			unsigned int smc:4;
-			unsigned int sev:2;
-			unsigned int reserved:6;
-			unsigned int md:8;
-			unsigned int refcode:8;
-			unsigned int mid:16;
-			unsigned int mp:16;
-			unsigned char volid[6];
-			unsigned int fid:8;
-		} f70;
-		struct {
-			unsigned int emc:4;
-			unsigned int smc:4;
-			unsigned int sev:2;
-			unsigned int reserved1:5;
-			unsigned int mdf:1;
-			unsigned char md[3];
-			unsigned int simid:8;
-			unsigned int uid:16;
-			unsigned int refcode1:16;
-			unsigned int refcode2:16;
-			unsigned int refcode3:16;
-			unsigned int reserved2:8;
-		} f71;
-		unsigned char data[14];
-	} fmt;
-	unsigned char pad[10];
-
-} __attribute__ ((packed));
-
-struct tape_3590_med_sense {
-	unsigned int macst:4;
-	unsigned int masst:4;
-	char pad1[7];
-	unsigned int flags;
-	char pad2[116];
-} __attribute__ ((packed));
-
-struct tape_3590_rdc_data {
-	char data[64];
-} __attribute__ ((packed));
-
-/* Datastructures for 3592 encryption support */
-
-struct tape3592_kekl {
-	__u8 flags;
-	char label[64];
-} __attribute__ ((packed));
-
-struct tape3592_kekl_pair {
-	__u8 count;
-	struct tape3592_kekl kekl[2];
-} __attribute__ ((packed));
-
-struct tape3592_kekl_query_data {
-	__u16 len;
-	__u8  fmt;
-	__u8  mc;
-	__u32 id;
-	__u8  flags;
-	struct tape3592_kekl_pair kekls;
-	char reserved[116];
-} __attribute__ ((packed));
-
-struct tape3592_kekl_query_order {
-	__u8 code;
-	__u8 flags;
-	char reserved1[2];
-	__u8 max_count;
-	char reserved2[35];
-} __attribute__ ((packed));
-
-struct tape3592_kekl_set_order {
-	__u8 code;
-	__u8 flags;
-	char reserved1[2];
-	__u8 op;
-	struct tape3592_kekl_pair kekls;
-	char reserved2[120];
-} __attribute__ ((packed));
-
-#endif /* _TAPE_3590_H */
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c
index c5d3c30..879331e 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -412,10 +412,7 @@ __tapechar_ioctl(struct tape_device *device,
 
 		return put_user_mtget(data, &get);
 	}
-	/* Try the discipline ioctl function. */
-	if (device->discipline->ioctl_fn == NULL)
-		return -EINVAL;
-	return device->discipline->ioctl_fn(device, no, (unsigned long)data);
+	return -EINVAL;
 }
 
 static long
diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c
index 6fa7b78..1ae9ad2 100644
--- a/drivers/s390/char/tape_class.c
+++ b/drivers/s390/char/tape_class.c
@@ -15,13 +15,6 @@
 
 #include "tape_class.h"
 
-MODULE_AUTHOR("Stefan Bader <shbader@de.ibm.com>");
-MODULE_DESCRIPTION(
-	"Copyright IBM Corp. 2004   All Rights Reserved.\n"
-	"tape_class.c"
-);
-MODULE_LICENSE("GPL");
-
 static const struct class tape_class = {
 	.name = "tape390",
 };
@@ -116,16 +109,12 @@ void unregister_tape_dev(struct device *device, struct tape_class_device *tcd)
 }
 EXPORT_SYMBOL(unregister_tape_dev);
 
-
-static int __init tape_init(void)
+int tape_class_init(void)
 {
 	return class_register(&tape_class);
 }
 
-static void __exit tape_exit(void)
+void tape_class_exit(void)
 {
 	class_unregister(&tape_class);
 }
-
-postcore_initcall(tape_init);
-module_exit(tape_exit);
diff --git a/drivers/s390/char/tape_class.h b/drivers/s390/char/tape_class.h
index d25ac07..0bb6f0c 100644
--- a/drivers/s390/char/tape_class.h
+++ b/drivers/s390/char/tape_class.h
@@ -55,5 +55,7 @@ struct tape_class_device *register_tape_dev(
 	char *			node_name
 );
 void unregister_tape_dev(struct device *device, struct tape_class_device *tcd);
+int tape_class_init(void);
+void tape_class_exit(void);
 
 #endif /* __TAPE_CLASS_H__ */
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 0250076..a62b650 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -28,6 +28,7 @@
 
 #include "tape.h"
 #include "tape_std.h"
+#include "tape_class.h"
 
 #define LONG_BUSY_TIMEOUT 180 /* seconds */
 
@@ -74,9 +75,7 @@ const char *tape_op_verbose[TO_SIZE] =
 	[TO_LOAD] = "LOA",	[TO_READ_CONFIG] = "RCF",
 	[TO_READ_ATTMSG] = "RAT",
 	[TO_DIS] = "DIS",	[TO_ASSIGN] = "ASS",
-	[TO_UNASSIGN] = "UAS",  [TO_CRYPT_ON] = "CON",
-	[TO_CRYPT_OFF] = "COF",	[TO_KEKL_SET] = "KLS",
-	[TO_KEKL_QUERY] = "KLQ",[TO_RDC] = "RDC",
+	[TO_UNASSIGN] = "UAS",  [TO_RDC] = "RDC",
 };
 
 static int devid_to_int(struct ccw_dev_id *dev_id)
@@ -1312,7 +1311,9 @@ tape_init (void)
 #endif
 	DBF_EVENT(3, "tape init\n");
 	tape_proc_init();
+	tape_class_init();
 	tapechar_init ();
+	tape_3490_init();
 	return 0;
 }
 
@@ -1325,14 +1326,15 @@ tape_exit(void)
 	DBF_EVENT(6, "tape exit\n");
 
 	/* Get rid of the frontends */
+	tape_3490_exit();
 	tapechar_exit();
+	tape_class_exit();
 	tape_proc_cleanup();
 	debug_unregister (TAPE_DBF_AREA);
 }
 
-MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
-	      "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)");
-MODULE_DESCRIPTION("Linux on zSeries channel attached tape device driver");
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("s390 channel-attached tape device driver");
 MODULE_LICENSE("GPL");
 
 module_init(tape_init);
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 43a5586..96b7440 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -22,7 +22,6 @@
 #include <asm/types.h>
 #include <asm/idals.h>
 #include <asm/ebcdic.h>
-#include <asm/tape390.h>
 
 #define TAPE_DBF_AREA	tape_core_dbf
 
@@ -119,36 +118,6 @@ tape_std_unassign (struct tape_device *device)
 }
 
 /*
- * TAPE390_DISPLAY: Show a string on the tape display.
- */
-int
-tape_std_display(struct tape_device *device, struct display_struct *disp)
-{
-	struct tape_request *request;
-	int rc;
-
-	request = tape_alloc_request(2, 17);
-	if (IS_ERR(request)) {
-		DBF_EVENT(3, "TAPE: load display failed\n");
-		return PTR_ERR(request);
-	}
-	request->op = TO_DIS;
-
-	*(unsigned char *) request->cpdata = disp->cntrl;
-	DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl);
-	memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8);
-	memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8);
-	ASCEBC(((unsigned char*) request->cpdata) + 1, 16);
-
-	tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata);
-	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-
-	rc = tape_do_io_interruptible(device, request);
-	tape_free_request(request);
-	return rc;
-}
-
-/*
  * Read block id.
  */
 int
@@ -696,7 +665,6 @@ tape_std_process_eov(struct tape_device *device)
 
 EXPORT_SYMBOL(tape_std_assign);
 EXPORT_SYMBOL(tape_std_unassign);
-EXPORT_SYMBOL(tape_std_display);
 EXPORT_SYMBOL(tape_std_read_block_id);
 EXPORT_SYMBOL(tape_std_mtload);
 EXPORT_SYMBOL(tape_std_mtsetblk);
diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h
index 2cf9f72..2b67b0d 100644
--- a/drivers/s390/char/tape_std.h
+++ b/drivers/s390/char/tape_std.h
@@ -11,8 +11,6 @@
 #ifndef _TAPE_STD_H
 #define _TAPE_STD_H
 
-#include <asm/tape390.h>
-
 /*
  * Biggest block size of 256K to handle.
  */
@@ -21,54 +19,25 @@
 /*
  * The CCW commands for the Tape type of command.
  */
-#define INVALID_00		0x00	/* Invalid cmd */
 #define BACKSPACEBLOCK		0x27	/* Back Space block */
 #define BACKSPACEFILE		0x2f	/* Back Space file */
 #define DATA_SEC_ERASE		0x97	/* Data security erase */
 #define ERASE_GAP		0x17	/* Erase Gap */
 #define FORSPACEBLOCK		0x37	/* Forward space block */
 #define FORSPACEFILE		0x3F	/* Forward Space file */
-#define FORCE_STREAM_CNT	0xEB	/* Forced streaming count # */
 #define NOP			0x03	/* No operation	*/
 #define READ_FORWARD		0x02	/* Read forward */
 #define REWIND			0x07	/* Rewind */
 #define REWIND_UNLOAD		0x0F	/* Rewind and Unload */
 #define SENSE			0x04	/* Sense */
-#define NEW_MODE_SET		0xEB	/* Guess it is Mode set */
 #define WRITE_CMD		0x01	/* Write */
 #define WRITETAPEMARK		0x1F	/* Write Tape Mark */
 
-#define ASSIGN			0xB7	/* 3420 REJECT,3480 OK	*/
-#define CONTROL_ACCESS		0xE3	/* Set high speed */
-#define DIAG_MODE_SET		0x0B	/* 3420 NOP, 3480 REJECT */
-#define LOAD_DISPLAY		0x9F	/* 3420 REJECT,3480 OK */
-#define LOCATE			0x4F	/* 3420 REJ, 3480 NOP */
-#define LOOP_WRITE_TO_READ	0x8B	/* 3480 REJECT */
-#define MODE_SET_DB		0xDB	/* 3420 REJECT,3480 OK */
-#define MODE_SET_C3		0xC3	/* for 3420 */
-#define MODE_SET_CB		0xCB	/* for 3420 */
-#define MODE_SET_D3		0xD3	/* for 3420 */
-#define READ_BACKWARD		0x0C	/* */
-#define READ_BLOCK_ID		0x22	/* 3420 REJECT,3480 OK */
-#define READ_BUFFER		0x12	/* 3420 REJECT,3480 OK */
-#define READ_BUFF_LOG		0x24	/* 3420 REJECT,3480 OK */
-#define RELEASE			0xD4	/* 3420 NOP, 3480 REJECT */
-#define REQ_TRK_IN_ERROR	0x1B	/* 3420 NOP, 3480 REJECT */
-#define RESERVE			0xF4	/* 3420 NOP, 3480 REJECT */
-#define SENSE_GROUP_ID		0x34	/* 3420 REJECT,3480 OK */
-#define SENSE_ID		0xE4	/* 3420 REJECT,3480 OK */
-#define READ_DEV_CHAR		0x64	/* Read device characteristics */
-#define SET_DIAGNOSE		0x4B	/* 3420 NOP, 3480 REJECT */
-#define SET_GROUP_ID		0xAF	/* 3420 REJECT,3480 OK */
-#define SET_TAPE_WRITE_IMMED	0xC3	/* for 3480 */
-#define SUSPEND			0x5B	/* 3420 REJ, 3480 NOP */
-#define SYNC			0x43	/* Synchronize (flush buffer) */
-#define UNASSIGN		0xC7	/* 3420 REJECT,3480 OK */
-#define PERF_SUBSYS_FUNC	0x77	/* 3490 CMD */
-#define READ_CONFIG_DATA	0xFA	/* 3490 CMD */
-#define READ_MESSAGE_ID		0x4E	/* 3490 CMD */
-#define READ_SUBSYS_DATA	0x3E	/* 3490 CMD */
-#define SET_INTERFACE_ID	0x73	/* 3490 CMD */
+#define ASSIGN			0xB7	/* Assign */
+#define LOCATE			0x4F	/* Locate Block */
+#define MODE_SET_DB		0xDB	/* Mode Set */
+#define READ_BLOCK_ID		0x22	/* Read Block ID */
+#define UNASSIGN		0xC7	/* Unassign */
 
 #define SENSE_COMMAND_REJECT		0x80
 #define SENSE_INTERVENTION_REQUIRED	0x40
@@ -105,7 +74,6 @@ struct tape_request *tape_std_write_block(struct tape_device *);
 int tape_std_assign(struct tape_device *);
 int tape_std_unassign(struct tape_device *);
 int tape_std_read_block_id(struct tape_device *device, __u64 *id);
-int tape_std_display(struct tape_device *, struct display_struct *disp);
 int tape_std_terminate_write(struct tape_device *);
 
 /* Standard magnetic tape commands. */
@@ -133,10 +101,7 @@ void tape_std_process_eov(struct tape_device *);
 
 /* S390 tape types */
 enum s390_tape_type {
-        tape_3480,
         tape_3490,
-        tape_3590,
-        tape_3592,
 };
 
 #endif // _TAPE_STD_H
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 4c85df7..ac24e01 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -235,7 +235,7 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid,
 	return sch;
 
 err:
-	kfree(sch);
+	put_device(&sch->dev);
 	return ERR_PTR(ret);
 }
 
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index a445494..19cd27e 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -52,24 +52,24 @@ MODULE_LICENSE("GPL");
 
 int ap_domain_index = -1;	/* Adjunct Processor Domain Index */
 static DEFINE_SPINLOCK(ap_domain_lock);
-module_param_named(domain, ap_domain_index, int, 0440);
+module_param_named(domain, ap_domain_index, int, 0444);
 MODULE_PARM_DESC(domain, "domain index for ap devices");
 EXPORT_SYMBOL(ap_domain_index);
 
 static int ap_thread_flag;
-module_param_named(poll_thread, ap_thread_flag, int, 0440);
+module_param_named(poll_thread, ap_thread_flag, int, 0444);
 MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
 
 static char *apm_str;
-module_param_named(apmask, apm_str, charp, 0440);
+module_param_named(apmask, apm_str, charp, 0444);
 MODULE_PARM_DESC(apmask, "AP bus adapter mask.");
 
 static char *aqm_str;
-module_param_named(aqmask, aqm_str, charp, 0440);
+module_param_named(aqmask, aqm_str, charp, 0444);
 MODULE_PARM_DESC(aqmask, "AP bus domain mask.");
 
 static int ap_useirq = 1;
-module_param_named(useirq, ap_useirq, int, 0440);
+module_param_named(useirq, ap_useirq, int, 0444);
 MODULE_PARM_DESC(useirq, "Use interrupt if available, default is 1 (on).");
 
 atomic_t ap_max_msg_size = ATOMIC_INIT(AP_DEFAULT_MAX_MSG_SIZE);
@@ -130,7 +130,7 @@ debug_info_t *ap_dbf_info;
  */
 static mempool_t *ap_msg_pool;
 static unsigned int ap_msg_pool_min_items = 8;
-module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0440);
+module_param_named(msgpool_min_items, ap_msg_pool_min_items, uint, 0400);
 MODULE_PARM_DESC(msgpool_min_items, "AP message pool minimal items");
 
 /*
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 7a3b99f..c796773 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -50,7 +50,7 @@ MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
 MODULE_LICENSE("GPL");
 
 unsigned int zcrypt_mempool_threshold = 5;
-module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0440);
+module_param_named(mempool_threshold, zcrypt_mempool_threshold, uint, 0400);
 MODULE_PARM_DESC(mempool_threshold, "CCA and EP11 request/reply mempool minimal items (min: 1)");
 
 /*