Linux Audio

Check our new training course

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