1 1.4 riastrad /* $NetBSD: linux_sgt.c,v 1.4 2021/12/24 15:08:31 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.4 riastrad __KERNEL_RCSID(0, "$NetBSD: linux_sgt.c,v 1.4 2021/12/24 15:08:31 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.4 riastrad KASSERT(sg->sg_npgs); 157 1.1 riastrad KASSERT(nents >= 1); 158 1.1 riastrad 159 1.1 riastrad switch (dir) { 160 1.1 riastrad case DMA_TO_DEVICE: 161 1.1 riastrad flags |= BUS_DMA_WRITE; 162 1.1 riastrad break; 163 1.1 riastrad case DMA_FROM_DEVICE: 164 1.1 riastrad flags |= BUS_DMA_READ; 165 1.1 riastrad break; 166 1.1 riastrad case DMA_BIDIRECTIONAL: 167 1.2 riastrad flags |= BUS_DMA_READ|BUS_DMA_WRITE; 168 1.1 riastrad break; 169 1.1 riastrad case DMA_NONE: 170 1.2 riastrad default: 171 1.1 riastrad panic("invalid DMA direction %d", dir); 172 1.1 riastrad } 173 1.1 riastrad 174 1.1 riastrad error = bus_dmamap_create(dmat, (bus_size_t)sg->sg_npgs << PAGE_SHIFT, 175 1.1 riastrad nents, PAGE_SIZE, 0, BUS_DMA_WAITOK, &sg->sg_dmamap); 176 1.1 riastrad if (error) 177 1.1 riastrad goto out; 178 1.1 riastrad KASSERT(sg->sg_dmamap); 179 1.1 riastrad 180 1.1 riastrad error = bus_dmamap_load_pages(dmat, sg->sg_dmamap, sg->sg_pgs, 181 1.1 riastrad (bus_size_t)sg->sg_npgs << PAGE_SHIFT, BUS_DMA_WAITOK|flags); 182 1.1 riastrad if (error) 183 1.1 riastrad goto out; 184 1.1 riastrad loaded = true; 185 1.1 riastrad 186 1.1 riastrad /* Success! */ 187 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs > 0); 188 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs <= nents); 189 1.3 riastrad sg->sg_dmat = dmat; 190 1.1 riastrad ret = sg->sg_dmamap->dm_nsegs; 191 1.2 riastrad error = 0; 192 1.1 riastrad 193 1.1 riastrad out: if (error) { 194 1.1 riastrad if (loaded) 195 1.1 riastrad bus_dmamap_unload(dmat, sg->sg_dmamap); 196 1.1 riastrad loaded = false; 197 1.1 riastrad if (sg->sg_dmamap) 198 1.1 riastrad bus_dmamap_destroy(dmat, sg->sg_dmamap); 199 1.1 riastrad sg->sg_dmamap = NULL; 200 1.1 riastrad ret = 0; 201 1.1 riastrad } 202 1.1 riastrad return ret; 203 1.1 riastrad } 204 1.1 riastrad 205 1.1 riastrad void 206 1.1 riastrad dma_unmap_sg(bus_dma_tag_t dmat, struct scatterlist *sg, int nents, int dir) 207 1.1 riastrad { 208 1.1 riastrad 209 1.1 riastrad dma_unmap_sg_attrs(dmat, sg, nents, dir, 0); 210 1.1 riastrad } 211 1.1 riastrad 212 1.1 riastrad void 213 1.1 riastrad dma_unmap_sg_attrs(bus_dma_tag_t dmat, struct scatterlist *sg, int nents, 214 1.1 riastrad int dir, int attrs) 215 1.1 riastrad { 216 1.1 riastrad 217 1.4 riastrad KASSERT(sg->sg_dmat == dmat); 218 1.4 riastrad 219 1.1 riastrad bus_dmamap_unload(dmat, sg->sg_dmamap); 220 1.1 riastrad bus_dmamap_destroy(dmat, sg->sg_dmamap); 221 1.1 riastrad sg->sg_dmamap = NULL; 222 1.1 riastrad } 223 1.1 riastrad 224 1.1 riastrad bus_addr_t 225 1.1 riastrad sg_dma_address(const struct scatterlist *sg) 226 1.1 riastrad { 227 1.1 riastrad 228 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs == 1); 229 1.1 riastrad return sg->sg_dmamap->dm_segs[0].ds_addr; 230 1.1 riastrad } 231 1.1 riastrad 232 1.1 riastrad bus_size_t 233 1.1 riastrad sg_dma_len(const struct scatterlist *sg) 234 1.1 riastrad { 235 1.1 riastrad 236 1.1 riastrad KASSERT(sg->sg_dmamap->dm_nsegs == 1); 237 1.1 riastrad return sg->sg_dmamap->dm_segs[0].ds_len; 238 1.1 riastrad } 239