linux_sgt.c revision 1.3 1 1.3 riastrad /* $NetBSD: linux_sgt.c,v 1.3 2021/12/19 12:10:42 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad #include <sys/cdefs.h>
30 1.3 riastrad __KERNEL_RCSID(0, "$NetBSD: linux_sgt.c,v 1.3 2021/12/19 12:10:42 riastradh Exp $");
31 1.1 riastrad
32 1.1 riastrad #include <sys/bus.h>
33 1.1 riastrad #include <sys/errno.h>
34 1.1 riastrad
35 1.1 riastrad #include <drm/bus_dma_hacks.h>
36 1.1 riastrad
37 1.1 riastrad #include <linux/dma-mapping.h>
38 1.1 riastrad #include <linux/gfp.h>
39 1.1 riastrad #include <linux/mm_types.h>
40 1.1 riastrad #include <linux/scatterlist.h>
41 1.1 riastrad #include <linux/slab.h>
42 1.1 riastrad
43 1.1 riastrad int
44 1.1 riastrad sg_alloc_table(struct sg_table *sgt, unsigned npgs, gfp_t gfp)
45 1.1 riastrad {
46 1.1 riastrad
47 1.1 riastrad sgt->sgl->sg_pgs = kcalloc(npgs, sizeof(sgt->sgl->sg_pgs[0]), gfp);
48 1.1 riastrad if (sgt->sgl->sg_pgs == NULL)
49 1.1 riastrad return -ENOMEM;
50 1.1 riastrad sgt->sgl->sg_npgs = sgt->nents = npgs;
51 1.1 riastrad sgt->sgl->sg_dmamap = NULL;
52 1.1 riastrad
53 1.1 riastrad return 0;
54 1.1 riastrad }
55 1.1 riastrad
56 1.1 riastrad int
57 1.1 riastrad __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pgs,
58 1.1 riastrad unsigned npgs, bus_size_t offset, bus_size_t size, unsigned maxseg,
59 1.1 riastrad gfp_t gfp)
60 1.1 riastrad {
61 1.1 riastrad unsigned i;
62 1.1 riastrad int ret;
63 1.1 riastrad
64 1.1 riastrad KASSERT(offset == 0);
65 1.1 riastrad KASSERT(size == (bus_size_t)npgs << PAGE_SHIFT);
66 1.1 riastrad
67 1.1 riastrad ret = sg_alloc_table(sgt, npgs, gfp);
68 1.1 riastrad if (ret)
69 1.1 riastrad return ret;
70 1.1 riastrad
71 1.1 riastrad for (i = 0; i < npgs; i++)
72 1.1 riastrad sgt->sgl->sg_pgs[i] = pgs[i];
73 1.1 riastrad
74 1.1 riastrad return 0;
75 1.1 riastrad }
76 1.1 riastrad
77 1.1 riastrad int
78 1.1 riastrad sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pgs,
79 1.1 riastrad unsigned npgs, bus_size_t offset, bus_size_t size, gfp_t gfp)
80 1.1 riastrad {
81 1.1 riastrad
82 1.1 riastrad return __sg_alloc_table_from_pages(sgt, pgs, npgs, offset, size,
83 1.1 riastrad -1, gfp);
84 1.1 riastrad }
85 1.1 riastrad
86 1.1 riastrad int
87 1.1 riastrad sg_alloc_table_from_bus_dmamem(struct sg_table *sgt, bus_dma_tag_t dmat,
88 1.1 riastrad const bus_dma_segment_t *seg, int nseg, gfp_t gfp)
89 1.1 riastrad {
90 1.1 riastrad int i, npgs = 0;
91 1.1 riastrad int ret;
92 1.1 riastrad
93 1.1 riastrad KASSERT(nseg >= 1);
94 1.1 riastrad
95 1.1 riastrad /*
96 1.1 riastrad * Count the number of pages. Some segments may span multiple
97 1.1 riastrad * contiguous pages.
98 1.1 riastrad */
99 1.1 riastrad for (i = 0; i < nseg; i++) {
100 1.1 riastrad bus_size_t len = seg[i].ds_len;
101 1.1 riastrad for (; len >= PAGE_SIZE; len -= PAGE_SIZE, npgs++) {
102 1.1 riastrad if (npgs == INT_MAX)
103 1.1 riastrad return -ENOMEM;
104 1.1 riastrad }
105 1.1 riastrad KASSERTMSG(len == 0, "misaligned segment length: %ju\n",
106 1.1 riastrad (uintmax_t)seg[i].ds_len);
107 1.1 riastrad }
108 1.1 riastrad
109 1.1 riastrad ret = sg_alloc_table(sgt, npgs, gfp);
110 1.1 riastrad if (ret)
111 1.1 riastrad return ret;
112 1.1 riastrad
113 1.1 riastrad /* XXX errno NetBSD->Linux */
114 1.1 riastrad ret = -bus_dmamem_export_pages(dmat, seg, nseg, sgt->sgl->sg_pgs,
115 1.1 riastrad sgt->sgl->sg_npgs);
116 1.1 riastrad if (ret)
117 1.1 riastrad goto out;
118 1.1 riastrad
119 1.1 riastrad /* Success! */
120 1.1 riastrad ret = 0;
121 1.1 riastrad
122 1.1 riastrad out: if (ret)
123 1.1 riastrad sg_free_table(sgt);
124 1.1 riastrad return ret;
125 1.1 riastrad }
126 1.1 riastrad
127 1.1 riastrad void
128 1.1 riastrad sg_free_table(struct sg_table *sgt)
129 1.1 riastrad {
130 1.1 riastrad
131 1.3 riastrad if (sgt->sgl->sg_dmamap) {
132 1.3 riastrad KASSERT(sgt->sgl->sg_dmat);
133 1.3 riastrad bus_dmamap_destroy(sgt->sgl->sg_dmat, sgt->sgl->sg_dmamap);
134 1.3 riastrad }
135 1.1 riastrad kfree(sgt->sgl->sg_pgs);
136 1.1 riastrad sgt->sgl->sg_pgs = NULL;
137 1.1 riastrad sgt->sgl->sg_npgs = 0;
138 1.1 riastrad }
139 1.1 riastrad
140 1.1 riastrad int
141 1.1 riastrad dma_map_sg(bus_dma_tag_t dmat, struct scatterlist *sg, int nents, int dir)
142 1.1 riastrad {
143 1.1 riastrad
144 1.1 riastrad return dma_map_sg_attrs(dmat, sg, nents, dir, 0);
145 1.1 riastrad }
146 1.1 riastrad
147 1.1 riastrad int
148 1.1 riastrad dma_map_sg_attrs(bus_dma_tag_t dmat, struct scatterlist *sg, int nents,
149 1.1 riastrad int dir, int attrs)
150 1.1 riastrad {
151 1.1 riastrad int flags = 0;
152 1.1 riastrad bool loaded = false;
153 1.1 riastrad int ret, error = 0;
154 1.1 riastrad
155 1.1 riastrad KASSERT(sg->sg_dmamap == NULL);
156 1.1 riastrad KASSERT(nents >= 1);
157 1.1 riastrad
158 1.1 riastrad switch (dir) {
159 1.1 riastrad case DMA_TO_DEVICE:
160 1.1 riastrad flags |= BUS_DMA_WRITE;
161 1.1 riastrad break;
162 1.1 riastrad case DMA_FROM_DEVICE:
163 1.1 riastrad flags |= BUS_DMA_READ;
164 1.1 riastrad break;
165 1.1 riastrad case DMA_BIDIRECTIONAL:
166 1.2 riastrad flags |= BUS_DMA_READ|BUS_DMA_WRITE;
167 1.1 riastrad break;
168 1.1 riastrad case DMA_NONE:
169 1.2 riastrad default:
170 1.1 riastrad panic("invalid DMA direction %d", dir);
171 1.1 riastrad }
172 1.1 riastrad
173 1.1 riastrad error = bus_dmamap_create(dmat, (bus_size_t)sg->sg_npgs << PAGE_SHIFT,
174 1.1 riastrad nents, PAGE_SIZE, 0, BUS_DMA_WAITOK, &sg->sg_dmamap);
175 1.1 riastrad if (error)
176 1.1 riastrad goto out;
177 1.1 riastrad KASSERT(sg->sg_dmamap);
178 1.1 riastrad
179 1.1 riastrad error = bus_dmamap_load_pages(dmat, sg->sg_dmamap, sg->sg_pgs,
180 1.1 riastrad (bus_size_t)sg->sg_npgs << PAGE_SHIFT, BUS_DMA_WAITOK|flags);
181 1.1 riastrad if (error)
182 1.1 riastrad goto out;
183 1.1 riastrad loaded = true;
184 1.1 riastrad
185 1.1 riastrad /* Success! */
186 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs > 0);
187 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs <= nents);
188 1.3 riastrad sg->sg_dmat = dmat;
189 1.1 riastrad ret = sg->sg_dmamap->dm_nsegs;
190 1.2 riastrad error = 0;
191 1.1 riastrad
192 1.1 riastrad out: if (error) {
193 1.1 riastrad if (loaded)
194 1.1 riastrad bus_dmamap_unload(dmat, sg->sg_dmamap);
195 1.1 riastrad loaded = false;
196 1.1 riastrad if (sg->sg_dmamap)
197 1.1 riastrad bus_dmamap_destroy(dmat, sg->sg_dmamap);
198 1.1 riastrad sg->sg_dmamap = NULL;
199 1.1 riastrad ret = 0;
200 1.1 riastrad }
201 1.1 riastrad return ret;
202 1.1 riastrad }
203 1.1 riastrad
204 1.1 riastrad void
205 1.1 riastrad dma_unmap_sg(bus_dma_tag_t dmat, struct scatterlist *sg, int nents, int dir)
206 1.1 riastrad {
207 1.1 riastrad
208 1.1 riastrad dma_unmap_sg_attrs(dmat, sg, nents, dir, 0);
209 1.1 riastrad }
210 1.1 riastrad
211 1.1 riastrad void
212 1.1 riastrad dma_unmap_sg_attrs(bus_dma_tag_t dmat, struct scatterlist *sg, int nents,
213 1.1 riastrad int dir, int attrs)
214 1.1 riastrad {
215 1.1 riastrad
216 1.1 riastrad bus_dmamap_unload(dmat, sg->sg_dmamap);
217 1.1 riastrad bus_dmamap_destroy(dmat, sg->sg_dmamap);
218 1.1 riastrad sg->sg_dmamap = NULL;
219 1.1 riastrad }
220 1.1 riastrad
221 1.1 riastrad bus_addr_t
222 1.1 riastrad sg_dma_address(const struct scatterlist *sg)
223 1.1 riastrad {
224 1.1 riastrad
225 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs == 1);
226 1.1 riastrad return sg->sg_dmamap->dm_segs[0].ds_addr;
227 1.1 riastrad }
228 1.1 riastrad
229 1.1 riastrad bus_size_t
230 1.1 riastrad sg_dma_len(const struct scatterlist *sg)
231 1.1 riastrad {
232 1.1 riastrad
233 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs == 1);
234 1.1 riastrad return sg->sg_dmamap->dm_segs[0].ds_len;
235 1.1 riastrad }
236