summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2002-12-06 00:20:14 -0800
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-12-06 00:20:14 -0800
commitc768f081338e304123df41e24739e992e5ce37f5 (patch)
treeef3ec19cec5ee56d4600673ab9be26b81df417d4 /include
parentf7a9fa93f5773cc707ce82c8b77239e1c05dfa00 (diff)
[PATCH] s390: tape driver.
Rework of tape driver Almost complete rewrite of the s390 tape driver code by Martin Schwidefsky. Skipping the 2.4 era, this makes the tape code ready for the future. This driver probably doesn't have to change much before 2.6. Authors: Martin Schwidefsky <schwidefsky@de.ibm.com> Stefan Bader <shbader@de.ibm.com>
Diffstat (limited to 'include')
-rw-r--r--include/asm-s390/idals.h134
-rw-r--r--include/asm-s390x/idals.h134
2 files changed, 268 insertions, 0 deletions
diff --git a/include/asm-s390/idals.h b/include/asm-s390/idals.h
index 53a3b0ef1e72..3cb1d7da0056 100644
--- a/include/asm-s390/idals.h
+++ b/include/asm-s390/idals.h
@@ -122,4 +122,138 @@ clear_normalized_cda(struct ccw1 * ccw)
ccw->cda = 0;
}
+/*
+ * Idal buffer extension
+ */
+struct idal_buffer {
+ size_t size;
+ size_t page_order;
+ void *data[0];
+};
+
+/*
+ * Allocate an idal buffer
+ */
+static inline struct idal_buffer *
+idal_buffer_alloc(size_t size, int page_order)
+{
+ struct idal_buffer *ib;
+ int nr_chunks, nr_ptrs, i;
+
+ nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
+ nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG;
+ ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
+ GFP_DMA | GFP_KERNEL);
+ if (ib == NULL)
+ return ERR_PTR(-ENOMEM);
+ ib->size = size;
+ ib->page_order = page_order;
+ for (i = 0; i < nr_ptrs; i++) {
+ if ((i & (nr_chunks - 1)) != 0) {
+ ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
+ continue;
+ }
+ ib->data[i] = (void *)
+ __get_free_pages(GFP_KERNEL, page_order);
+ if (ib->data[i] != NULL)
+ continue;
+ // Not enough memory
+ while (i >= nr_chunks) {
+ i -= nr_chunks;
+ free_pages((unsigned long) ib->data[i],
+ ib->page_order);
+ }
+ kfree(ib);
+ return ERR_PTR(-ENOMEM);
+ }
+ return ib;
+}
+
+/*
+ * Free an idal buffer.
+ */
+static inline void
+idal_buffer_free(struct idal_buffer *ib)
+{
+ int nr_chunks, nr_ptrs, i;
+
+ nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
+ nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
+ for (i = 0; i < nr_ptrs; i += nr_chunks)
+ free_pages((unsigned long) ib->data[i], ib->page_order);
+ kfree(ib);
+}
+
+/*
+ * Test if a idal list is really needed.
+ */
+static inline int
+__idal_buffer_is_needed(struct idal_buffer *ib)
+{
+#ifdef CONFIG_ARCH_S390X
+ return ib->size > (4096 << ib->page_order) ||
+ idal_is_needed(ib->data[0], ib->size);
+#else
+ return ib->size > (4096 << ib->page_order);
+#endif
+}
+
+/*
+ * Set channel data address to idal buffer.
+ */
+static inline void
+idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
+{
+ if (__idal_buffer_is_needed(ib)) {
+ // setup idals;
+ ccw->cda = (u32)(addr_t) ib->data;
+ ccw->flags |= CCW_FLAG_IDA;
+ } else
+ // we do not need idals - use direct addressing
+ ccw->cda = (u32)(addr_t) ib->data[0];
+ ccw->count = ib->size;
+}
+
+/*
+ * Copy count bytes from an idal buffer to user memory
+ */
+static inline size_t
+idal_buffer_to_user(struct idal_buffer *ib, void *to, size_t count)
+{
+ size_t left;
+ int i;
+
+ if (count > ib->size)
+ BUG();
+ for (i = 0; count > IDA_BLOCK_SIZE; i++) {
+ left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
+ if (left)
+ return left + count - IDA_BLOCK_SIZE;
+ (addr_t) to += IDA_BLOCK_SIZE;
+ count -= IDA_BLOCK_SIZE;
+ }
+ return copy_to_user(to, ib->data[i], count);
+}
+
+/*
+ * Copy count bytes from user memory to an idal buffer
+ */
+static inline size_t
+idal_buffer_from_user(struct idal_buffer *ib, const void *from, size_t count)
+{
+ size_t left;
+ int i;
+
+ if (count > ib->size)
+ BUG();
+ for (i = 0; count > IDA_BLOCK_SIZE; i++) {
+ left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
+ if (left)
+ return left + count - IDA_BLOCK_SIZE;
+ (addr_t) from += IDA_BLOCK_SIZE;
+ count -= IDA_BLOCK_SIZE;
+ }
+ return copy_from_user(ib->data[i], from, count);
+}
+
#endif
diff --git a/include/asm-s390x/idals.h b/include/asm-s390x/idals.h
index 53a3b0ef1e72..3cb1d7da0056 100644
--- a/include/asm-s390x/idals.h
+++ b/include/asm-s390x/idals.h
@@ -122,4 +122,138 @@ clear_normalized_cda(struct ccw1 * ccw)
ccw->cda = 0;
}
+/*
+ * Idal buffer extension
+ */
+struct idal_buffer {
+ size_t size;
+ size_t page_order;
+ void *data[0];
+};
+
+/*
+ * Allocate an idal buffer
+ */
+static inline struct idal_buffer *
+idal_buffer_alloc(size_t size, int page_order)
+{
+ struct idal_buffer *ib;
+ int nr_chunks, nr_ptrs, i;
+
+ nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
+ nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG;
+ ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
+ GFP_DMA | GFP_KERNEL);
+ if (ib == NULL)
+ return ERR_PTR(-ENOMEM);
+ ib->size = size;
+ ib->page_order = page_order;
+ for (i = 0; i < nr_ptrs; i++) {
+ if ((i & (nr_chunks - 1)) != 0) {
+ ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
+ continue;
+ }
+ ib->data[i] = (void *)
+ __get_free_pages(GFP_KERNEL, page_order);
+ if (ib->data[i] != NULL)
+ continue;
+ // Not enough memory
+ while (i >= nr_chunks) {
+ i -= nr_chunks;
+ free_pages((unsigned long) ib->data[i],
+ ib->page_order);
+ }
+ kfree(ib);
+ return ERR_PTR(-ENOMEM);
+ }
+ return ib;
+}
+
+/*
+ * Free an idal buffer.
+ */
+static inline void
+idal_buffer_free(struct idal_buffer *ib)
+{
+ int nr_chunks, nr_ptrs, i;
+
+ nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
+ nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
+ for (i = 0; i < nr_ptrs; i += nr_chunks)
+ free_pages((unsigned long) ib->data[i], ib->page_order);
+ kfree(ib);
+}
+
+/*
+ * Test if a idal list is really needed.
+ */
+static inline int
+__idal_buffer_is_needed(struct idal_buffer *ib)
+{
+#ifdef CONFIG_ARCH_S390X
+ return ib->size > (4096 << ib->page_order) ||
+ idal_is_needed(ib->data[0], ib->size);
+#else
+ return ib->size > (4096 << ib->page_order);
+#endif
+}
+
+/*
+ * Set channel data address to idal buffer.
+ */
+static inline void
+idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
+{
+ if (__idal_buffer_is_needed(ib)) {
+ // setup idals;
+ ccw->cda = (u32)(addr_t) ib->data;
+ ccw->flags |= CCW_FLAG_IDA;
+ } else
+ // we do not need idals - use direct addressing
+ ccw->cda = (u32)(addr_t) ib->data[0];
+ ccw->count = ib->size;
+}
+
+/*
+ * Copy count bytes from an idal buffer to user memory
+ */
+static inline size_t
+idal_buffer_to_user(struct idal_buffer *ib, void *to, size_t count)
+{
+ size_t left;
+ int i;
+
+ if (count > ib->size)
+ BUG();
+ for (i = 0; count > IDA_BLOCK_SIZE; i++) {
+ left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
+ if (left)
+ return left + count - IDA_BLOCK_SIZE;
+ (addr_t) to += IDA_BLOCK_SIZE;
+ count -= IDA_BLOCK_SIZE;
+ }
+ return copy_to_user(to, ib->data[i], count);
+}
+
+/*
+ * Copy count bytes from user memory to an idal buffer
+ */
+static inline size_t
+idal_buffer_from_user(struct idal_buffer *ib, const void *from, size_t count)
+{
+ size_t left;
+ int i;
+
+ if (count > ib->size)
+ BUG();
+ for (i = 0; count > IDA_BLOCK_SIZE; i++) {
+ left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
+ if (left)
+ return left + count - IDA_BLOCK_SIZE;
+ (addr_t) from += IDA_BLOCK_SIZE;
+ count -= IDA_BLOCK_SIZE;
+ }
+ return copy_from_user(ib->data[i], from, count);
+}
+
#endif