1 /* 2 * CAAM/SEC 4.x functions for using scatterlists in caam driver 3 * 4 * Copyright 2008-2011 Freescale Semiconductor, Inc. 5 * 6 */ 7 8 struct sec4_sg_entry; 9 10 /* 11 * convert single dma address to h/w link table format 12 */ 13 static inline void dma_to_sec4_sg_one(struct sec4_sg_entry *sec4_sg_ptr, 14 dma_addr_t dma, u32 len, u32 offset) 15 { 16 sec4_sg_ptr->ptr = dma; 17 sec4_sg_ptr->len = len; 18 sec4_sg_ptr->buf_pool_id = 0; 19 sec4_sg_ptr->offset = offset; 20 #ifdef DEBUG 21 print_hex_dump(KERN_ERR, "sec4_sg_ptr@: ", 22 DUMP_PREFIX_ADDRESS, 16, 4, sec4_sg_ptr, 23 sizeof(struct sec4_sg_entry), 1); 24 #endif 25 } 26 27 /* 28 * convert scatterlist to h/w link table format 29 * but does not have final bit; instead, returns last entry 30 */ 31 static inline struct sec4_sg_entry * 32 sg_to_sec4_sg(struct scatterlist *sg, int sg_count, 33 struct sec4_sg_entry *sec4_sg_ptr, u32 offset) 34 { 35 while (sg_count) { 36 dma_to_sec4_sg_one(sec4_sg_ptr, sg_dma_address(sg), 37 sg_dma_len(sg), offset); 38 sec4_sg_ptr++; 39 sg = sg_next(sg); 40 sg_count--; 41 } 42 return sec4_sg_ptr - 1; 43 } 44 45 /* 46 * convert scatterlist to h/w link table format 47 * scatterlist must have been previously dma mapped 48 */ 49 static inline void sg_to_sec4_sg_last(struct scatterlist *sg, int sg_count, 50 struct sec4_sg_entry *sec4_sg_ptr, 51 u32 offset) 52 { 53 sec4_sg_ptr = sg_to_sec4_sg(sg, sg_count, sec4_sg_ptr, offset); 54 sec4_sg_ptr->len |= SEC4_SG_LEN_FIN; 55 } 56 57 static inline struct sec4_sg_entry *sg_to_sec4_sg_len( 58 struct scatterlist *sg, unsigned int total, 59 struct sec4_sg_entry *sec4_sg_ptr) 60 { 61 do { 62 unsigned int len = min(sg_dma_len(sg), total); 63 64 dma_to_sec4_sg_one(sec4_sg_ptr, sg_dma_address(sg), len, 0); 65 sec4_sg_ptr++; 66 sg = sg_next(sg); 67 total -= len; 68 } while (total); 69 return sec4_sg_ptr - 1; 70 } 71 72 /* count number of elements in scatterlist */ 73 static inline int __sg_count(struct scatterlist *sg_list, int nbytes, 74 bool *chained) 75 { 76 struct scatterlist *sg = sg_list; 77 int sg_nents = 0; 78 79 while (nbytes > 0) { 80 sg_nents++; 81 nbytes -= sg->length; 82 if (!sg_is_last(sg) && (sg + 1)->length == 0) 83 *chained = true; 84 sg = sg_next(sg); 85 } 86 87 return sg_nents; 88 } 89 90 /* derive number of elements in scatterlist, but return 0 for 1 */ 91 static inline int sg_count(struct scatterlist *sg_list, int nbytes, 92 bool *chained) 93 { 94 int sg_nents = __sg_count(sg_list, nbytes, chained); 95 96 if (likely(sg_nents == 1)) 97 return 0; 98 99 return sg_nents; 100 } 101 102 static inline void dma_unmap_sg_chained( 103 struct device *dev, struct scatterlist *sg, unsigned int nents, 104 enum dma_data_direction dir, bool chained) 105 { 106 if (unlikely(chained)) { 107 int i; 108 struct scatterlist *tsg = sg; 109 110 /* 111 * Use a local copy of the sg pointer to avoid moving the 112 * head of the list pointed to by sg as we walk the list. 113 */ 114 for (i = 0; i < nents; i++) { 115 dma_unmap_sg(dev, tsg, 1, dir); 116 tsg = sg_next(tsg); 117 } 118 } else if (nents) { 119 dma_unmap_sg(dev, sg, nents, dir); 120 } 121 } 122 123 static inline int dma_map_sg_chained( 124 struct device *dev, struct scatterlist *sg, unsigned int nents, 125 enum dma_data_direction dir, bool chained) 126 { 127 if (unlikely(chained)) { 128 int i; 129 struct scatterlist *tsg = sg; 130 131 /* 132 * Use a local copy of the sg pointer to avoid moving the 133 * head of the list pointed to by sg as we walk the list. 134 */ 135 for (i = 0; i < nents; i++) { 136 if (!dma_map_sg(dev, tsg, 1, dir)) { 137 dma_unmap_sg_chained(dev, sg, i, dir, 138 chained); 139 nents = 0; 140 break; 141 } 142 143 tsg = sg_next(tsg); 144 } 145 } else 146 nents = dma_map_sg(dev, sg, nents, dir); 147 148 return nents; 149 } 150