1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef SCATTERLIST_H 3 #define SCATTERLIST_H 4 #include <linux/kernel.h> 5 6 struct scatterlist { 7 unsigned long page_link; 8 unsigned int offset; 9 unsigned int length; 10 dma_addr_t dma_address; 11 }; 12 13 /* Scatterlist helpers, stolen from linux/scatterlist.h */ 14 #define sg_is_chain(sg) ((sg)->page_link & 0x01) 15 #define sg_is_last(sg) ((sg)->page_link & 0x02) 16 #define sg_chain_ptr(sg) \ 17 ((struct scatterlist *) ((sg)->page_link & ~0x03)) 18 19 /** 20 * sg_assign_page - Assign a given page to an SG entry 21 * @sg: SG entry 22 * @page: The page 23 * 24 * Description: 25 * Assign page to sg entry. Also see sg_set_page(), the most commonly used 26 * variant. 27 * 28 **/ 29 static inline void sg_assign_page(struct scatterlist *sg, struct page *page) 30 { 31 unsigned long page_link = sg->page_link & 0x3; 32 33 /* 34 * In order for the low bit stealing approach to work, pages 35 * must be aligned at a 32-bit boundary as a minimum. 36 */ 37 BUG_ON((unsigned long) page & 0x03); 38 #ifdef CONFIG_DEBUG_SG 39 BUG_ON(sg_is_chain(sg)); 40 #endif 41 sg->page_link = page_link | (unsigned long) page; 42 } 43 44 /** 45 * sg_set_page - Set sg entry to point at given page 46 * @sg: SG entry 47 * @page: The page 48 * @len: Length of data 49 * @offset: Offset into page 50 * 51 * Description: 52 * Use this function to set an sg entry pointing at a page, never assign 53 * the page directly. We encode sg table information in the lower bits 54 * of the page pointer. See sg_page() for looking up the page belonging 55 * to an sg entry. 56 * 57 **/ 58 static inline void sg_set_page(struct scatterlist *sg, struct page *page, 59 unsigned int len, unsigned int offset) 60 { 61 sg_assign_page(sg, page); 62 sg->offset = offset; 63 sg->length = len; 64 } 65 66 static inline struct page *sg_page(struct scatterlist *sg) 67 { 68 #ifdef CONFIG_DEBUG_SG 69 BUG_ON(sg_is_chain(sg)); 70 #endif 71 return (struct page *)((sg)->page_link & ~0x3); 72 } 73 74 /* 75 * Loop over each sg element, following the pointer to a new list if necessary 76 */ 77 #define for_each_sg(sglist, sg, nr, __i) \ 78 for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) 79 80 /** 81 * sg_chain - Chain two sglists together 82 * @prv: First scatterlist 83 * @prv_nents: Number of entries in prv 84 * @sgl: Second scatterlist 85 * 86 * Description: 87 * Links @prv@ and @sgl@ together, to form a longer scatterlist. 88 * 89 **/ 90 static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, 91 struct scatterlist *sgl) 92 { 93 /* 94 * offset and length are unused for chain entry. Clear them. 95 */ 96 prv[prv_nents - 1].offset = 0; 97 prv[prv_nents - 1].length = 0; 98 99 /* 100 * Set lowest bit to indicate a link pointer, and make sure to clear 101 * the termination bit if it happens to be set. 102 */ 103 prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02; 104 } 105 106 /** 107 * sg_mark_end - Mark the end of the scatterlist 108 * @sg: SG entryScatterlist 109 * 110 * Description: 111 * Marks the passed in sg entry as the termination point for the sg 112 * table. A call to sg_next() on this entry will return NULL. 113 * 114 **/ 115 static inline void sg_mark_end(struct scatterlist *sg) 116 { 117 /* 118 * Set termination bit, clear potential chain bit 119 */ 120 sg->page_link |= 0x02; 121 sg->page_link &= ~0x01; 122 } 123 124 /** 125 * sg_unmark_end - Undo setting the end of the scatterlist 126 * @sg: SG entryScatterlist 127 * 128 * Description: 129 * Removes the termination marker from the given entry of the scatterlist. 130 * 131 **/ 132 static inline void sg_unmark_end(struct scatterlist *sg) 133 { 134 sg->page_link &= ~0x02; 135 } 136 137 static inline struct scatterlist *sg_next(struct scatterlist *sg) 138 { 139 if (sg_is_last(sg)) 140 return NULL; 141 142 sg++; 143 if (unlikely(sg_is_chain(sg))) 144 sg = sg_chain_ptr(sg); 145 146 return sg; 147 } 148 149 static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) 150 { 151 memset(sgl, 0, sizeof(*sgl) * nents); 152 sg_mark_end(&sgl[nents - 1]); 153 } 154 155 static inline dma_addr_t sg_phys(struct scatterlist *sg) 156 { 157 return page_to_phys(sg_page(sg)) + sg->offset; 158 } 159 160 static inline void sg_set_buf(struct scatterlist *sg, const void *buf, 161 unsigned int buflen) 162 { 163 sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); 164 } 165 166 static inline void sg_init_one(struct scatterlist *sg, 167 const void *buf, unsigned int buflen) 168 { 169 sg_init_table(sg, 1); 170 sg_set_buf(sg, buf, buflen); 171 } 172 #endif /* SCATTERLIST_H */ 173