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