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