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