Linux Audio

Check our new training course

Loading...
v3.5.6
  1/* 
  2 * File...........: linux/include/asm-s390x/idals.h
  3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
  4 *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
  5 * Bugreports.to..: <Linux390@de.ibm.com>
  6 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000a
  7 
  8 * History of changes
  9 * 07/24/00 new file
 10 * 05/04/02 code restructuring.
 11 */
 12
 13#ifndef _S390_IDALS_H
 14#define _S390_IDALS_H
 15
 16#include <linux/errno.h>
 17#include <linux/err.h>
 18#include <linux/types.h>
 19#include <linux/slab.h>
 20#include <asm/cio.h>
 21#include <asm/uaccess.h>
 22
 23#ifdef CONFIG_64BIT
 24#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
 25#else
 26#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
 27#endif
 28#define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG)
 29
 30/*
 31 * Test if an address/length pair needs an idal list.
 32 */
 33static inline int
 34idal_is_needed(void *vaddr, unsigned int length)
 35{
 36#ifdef CONFIG_64BIT
 37	return ((__pa(vaddr) + length - 1) >> 31) != 0;
 38#else
 39	return 0;
 40#endif
 41}
 42
 43
 44/*
 45 * Return the number of idal words needed for an address/length pair.
 46 */
 47static inline unsigned int idal_nr_words(void *vaddr, unsigned int length)
 48{
 49	return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
 50		(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
 51}
 52
 53/*
 54 * Create the list of idal words for an address/length pair.
 55 */
 56static inline unsigned long *idal_create_words(unsigned long *idaws,
 57					       void *vaddr, unsigned int length)
 58{
 59	unsigned long paddr;
 60	unsigned int cidaw;
 61
 62	paddr = __pa(vaddr);
 63	cidaw = ((paddr & (IDA_BLOCK_SIZE-1)) + length + 
 64		 (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
 65	*idaws++ = paddr;
 66	paddr &= -IDA_BLOCK_SIZE;
 67	while (--cidaw > 0) {
 68		paddr += IDA_BLOCK_SIZE;
 69		*idaws++ = paddr;
 70	}
 71	return idaws;
 72}
 73
 74/*
 75 * Sets the address of the data in CCW.
 76 * If necessary it allocates an IDAL and sets the appropriate flags.
 77 */
 78static inline int
 79set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 80{
 81#ifdef CONFIG_64BIT
 82	unsigned int nridaws;
 83	unsigned long *idal;
 84
 85	if (ccw->flags & CCW_FLAG_IDA)
 86		return -EINVAL;
 87	nridaws = idal_nr_words(vaddr, ccw->count);
 88	if (nridaws > 0) {
 89		idal = kmalloc(nridaws * sizeof(unsigned long),
 90			       GFP_ATOMIC | GFP_DMA );
 91		if (idal == NULL)
 92			return -ENOMEM;
 93		idal_create_words(idal, vaddr, ccw->count);
 94		ccw->flags |= CCW_FLAG_IDA;
 95		vaddr = idal;
 96	}
 97#endif
 98	ccw->cda = (__u32)(unsigned long) vaddr;
 99	return 0;
100}
101
102/*
103 * Releases any allocated IDAL related to the CCW.
104 */
105static inline void
106clear_normalized_cda(struct ccw1 * ccw)
107{
108#ifdef CONFIG_64BIT
109	if (ccw->flags & CCW_FLAG_IDA) {
110		kfree((void *)(unsigned long) ccw->cda);
111		ccw->flags &= ~CCW_FLAG_IDA;
112	}
113#endif
114	ccw->cda = 0;
115}
116
117/*
118 * Idal buffer extension
119 */
120struct idal_buffer {
121	size_t size;
122	size_t page_order;
123	void *data[0];
124};
125
126/*
127 * Allocate an idal buffer
128 */
129static inline struct idal_buffer *
130idal_buffer_alloc(size_t size, int page_order)
131{
132	struct idal_buffer *ib;
133	int nr_chunks, nr_ptrs, i;
134
135	nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
136	nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG;
137	ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
138		     GFP_DMA | GFP_KERNEL);
139	if (ib == NULL)
140		return ERR_PTR(-ENOMEM);
141	ib->size = size;
142	ib->page_order = page_order;
143	for (i = 0; i < nr_ptrs; i++) {
144		if ((i & (nr_chunks - 1)) != 0) {
145			ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
146			continue;
147		}
148		ib->data[i] = (void *)
149			__get_free_pages(GFP_KERNEL, page_order);
150		if (ib->data[i] != NULL)
151			continue;
152		// Not enough memory
153		while (i >= nr_chunks) {
154			i -= nr_chunks;
155			free_pages((unsigned long) ib->data[i],
156				   ib->page_order);
157		}
158		kfree(ib);
159		return ERR_PTR(-ENOMEM);
160	}
161	return ib;
162}
163
164/*
165 * Free an idal buffer.
166 */
167static inline void
168idal_buffer_free(struct idal_buffer *ib)
169{
170	int nr_chunks, nr_ptrs, i;
171
172	nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
173	nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
174	for (i = 0; i < nr_ptrs; i += nr_chunks)
175		free_pages((unsigned long) ib->data[i], ib->page_order);
176	kfree(ib);
177}
178
179/*
180 * Test if a idal list is really needed.
181 */
182static inline int
183__idal_buffer_is_needed(struct idal_buffer *ib)
184{
185#ifdef CONFIG_64BIT
186	return ib->size > (4096ul << ib->page_order) ||
187		idal_is_needed(ib->data[0], ib->size);
188#else
189	return ib->size > (4096ul << ib->page_order);
190#endif
191}
192
193/*
194 * Set channel data address to idal buffer.
195 */
196static inline void
197idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
198{
199	if (__idal_buffer_is_needed(ib)) {
200		// setup idals;
201		ccw->cda = (u32)(addr_t) ib->data;
202		ccw->flags |= CCW_FLAG_IDA;
203	} else
204		// we do not need idals - use direct addressing
205		ccw->cda = (u32)(addr_t) ib->data[0];
206	ccw->count = ib->size;
207}
208
209/*
210 * Copy count bytes from an idal buffer to user memory
211 */
212static inline size_t
213idal_buffer_to_user(struct idal_buffer *ib, void __user *to, size_t count)
214{
215	size_t left;
216	int i;
217
218	BUG_ON(count > ib->size);
219	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
220		left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
221		if (left)
222			return left + count - IDA_BLOCK_SIZE;
223		to = (void __user *) to + IDA_BLOCK_SIZE;
224		count -= IDA_BLOCK_SIZE;
225	}
226	return copy_to_user(to, ib->data[i], count);
227}
228
229/*
230 * Copy count bytes from user memory to an idal buffer
231 */
232static inline size_t
233idal_buffer_from_user(struct idal_buffer *ib, const void __user *from, size_t count)
234{
235	size_t left;
236	int i;
237
238	BUG_ON(count > ib->size);
239	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
240		left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
241		if (left)
242			return left + count - IDA_BLOCK_SIZE;
243		from = (void __user *) from + IDA_BLOCK_SIZE;
244		count -= IDA_BLOCK_SIZE;
245	}
246	return copy_from_user(ib->data[i], from, count);
247}
248
249#endif
v3.15
  1/* 
 
  2 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
  3 *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
  4 * Bugreports.to..: <Linux390@de.ibm.com>
  5 * Copyright IBM Corp. 2000
  6 *
  7 * History of changes
  8 * 07/24/00 new file
  9 * 05/04/02 code restructuring.
 10 */
 11
 12#ifndef _S390_IDALS_H
 13#define _S390_IDALS_H
 14
 15#include <linux/errno.h>
 16#include <linux/err.h>
 17#include <linux/types.h>
 18#include <linux/slab.h>
 19#include <asm/cio.h>
 20#include <asm/uaccess.h>
 21
 22#ifdef CONFIG_64BIT
 23#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
 24#else
 25#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
 26#endif
 27#define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG)
 28
 29/*
 30 * Test if an address/length pair needs an idal list.
 31 */
 32static inline int
 33idal_is_needed(void *vaddr, unsigned int length)
 34{
 35#ifdef CONFIG_64BIT
 36	return ((__pa(vaddr) + length - 1) >> 31) != 0;
 37#else
 38	return 0;
 39#endif
 40}
 41
 42
 43/*
 44 * Return the number of idal words needed for an address/length pair.
 45 */
 46static inline unsigned int idal_nr_words(void *vaddr, unsigned int length)
 47{
 48	return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
 49		(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
 50}
 51
 52/*
 53 * Create the list of idal words for an address/length pair.
 54 */
 55static inline unsigned long *idal_create_words(unsigned long *idaws,
 56					       void *vaddr, unsigned int length)
 57{
 58	unsigned long paddr;
 59	unsigned int cidaw;
 60
 61	paddr = __pa(vaddr);
 62	cidaw = ((paddr & (IDA_BLOCK_SIZE-1)) + length + 
 63		 (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
 64	*idaws++ = paddr;
 65	paddr &= -IDA_BLOCK_SIZE;
 66	while (--cidaw > 0) {
 67		paddr += IDA_BLOCK_SIZE;
 68		*idaws++ = paddr;
 69	}
 70	return idaws;
 71}
 72
 73/*
 74 * Sets the address of the data in CCW.
 75 * If necessary it allocates an IDAL and sets the appropriate flags.
 76 */
 77static inline int
 78set_normalized_cda(struct ccw1 * ccw, void *vaddr)
 79{
 80#ifdef CONFIG_64BIT
 81	unsigned int nridaws;
 82	unsigned long *idal;
 83
 84	if (ccw->flags & CCW_FLAG_IDA)
 85		return -EINVAL;
 86	nridaws = idal_nr_words(vaddr, ccw->count);
 87	if (nridaws > 0) {
 88		idal = kmalloc(nridaws * sizeof(unsigned long),
 89			       GFP_ATOMIC | GFP_DMA );
 90		if (idal == NULL)
 91			return -ENOMEM;
 92		idal_create_words(idal, vaddr, ccw->count);
 93		ccw->flags |= CCW_FLAG_IDA;
 94		vaddr = idal;
 95	}
 96#endif
 97	ccw->cda = (__u32)(unsigned long) vaddr;
 98	return 0;
 99}
100
101/*
102 * Releases any allocated IDAL related to the CCW.
103 */
104static inline void
105clear_normalized_cda(struct ccw1 * ccw)
106{
107#ifdef CONFIG_64BIT
108	if (ccw->flags & CCW_FLAG_IDA) {
109		kfree((void *)(unsigned long) ccw->cda);
110		ccw->flags &= ~CCW_FLAG_IDA;
111	}
112#endif
113	ccw->cda = 0;
114}
115
116/*
117 * Idal buffer extension
118 */
119struct idal_buffer {
120	size_t size;
121	size_t page_order;
122	void *data[0];
123};
124
125/*
126 * Allocate an idal buffer
127 */
128static inline struct idal_buffer *
129idal_buffer_alloc(size_t size, int page_order)
130{
131	struct idal_buffer *ib;
132	int nr_chunks, nr_ptrs, i;
133
134	nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
135	nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG;
136	ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
137		     GFP_DMA | GFP_KERNEL);
138	if (ib == NULL)
139		return ERR_PTR(-ENOMEM);
140	ib->size = size;
141	ib->page_order = page_order;
142	for (i = 0; i < nr_ptrs; i++) {
143		if ((i & (nr_chunks - 1)) != 0) {
144			ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
145			continue;
146		}
147		ib->data[i] = (void *)
148			__get_free_pages(GFP_KERNEL, page_order);
149		if (ib->data[i] != NULL)
150			continue;
151		// Not enough memory
152		while (i >= nr_chunks) {
153			i -= nr_chunks;
154			free_pages((unsigned long) ib->data[i],
155				   ib->page_order);
156		}
157		kfree(ib);
158		return ERR_PTR(-ENOMEM);
159	}
160	return ib;
161}
162
163/*
164 * Free an idal buffer.
165 */
166static inline void
167idal_buffer_free(struct idal_buffer *ib)
168{
169	int nr_chunks, nr_ptrs, i;
170
171	nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
172	nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
173	for (i = 0; i < nr_ptrs; i += nr_chunks)
174		free_pages((unsigned long) ib->data[i], ib->page_order);
175	kfree(ib);
176}
177
178/*
179 * Test if a idal list is really needed.
180 */
181static inline int
182__idal_buffer_is_needed(struct idal_buffer *ib)
183{
184#ifdef CONFIG_64BIT
185	return ib->size > (4096ul << ib->page_order) ||
186		idal_is_needed(ib->data[0], ib->size);
187#else
188	return ib->size > (4096ul << ib->page_order);
189#endif
190}
191
192/*
193 * Set channel data address to idal buffer.
194 */
195static inline void
196idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
197{
198	if (__idal_buffer_is_needed(ib)) {
199		// setup idals;
200		ccw->cda = (u32)(addr_t) ib->data;
201		ccw->flags |= CCW_FLAG_IDA;
202	} else
203		// we do not need idals - use direct addressing
204		ccw->cda = (u32)(addr_t) ib->data[0];
205	ccw->count = ib->size;
206}
207
208/*
209 * Copy count bytes from an idal buffer to user memory
210 */
211static inline size_t
212idal_buffer_to_user(struct idal_buffer *ib, void __user *to, size_t count)
213{
214	size_t left;
215	int i;
216
217	BUG_ON(count > ib->size);
218	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
219		left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
220		if (left)
221			return left + count - IDA_BLOCK_SIZE;
222		to = (void __user *) to + IDA_BLOCK_SIZE;
223		count -= IDA_BLOCK_SIZE;
224	}
225	return copy_to_user(to, ib->data[i], count);
226}
227
228/*
229 * Copy count bytes from user memory to an idal buffer
230 */
231static inline size_t
232idal_buffer_from_user(struct idal_buffer *ib, const void __user *from, size_t count)
233{
234	size_t left;
235	int i;
236
237	BUG_ON(count > ib->size);
238	for (i = 0; count > IDA_BLOCK_SIZE; i++) {
239		left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
240		if (left)
241			return left + count - IDA_BLOCK_SIZE;
242		from = (void __user *) from + IDA_BLOCK_SIZE;
243		count -= IDA_BLOCK_SIZE;
244	}
245	return copy_from_user(ib->data[i], from, count);
246}
247
248#endif