- Kai Germaschewski: ISDN update (including Makefiles)
[davej-history.git] / arch / sparc64 / kernel / iommu_common.c
blobacb3b4192f4455b7ce3d918befe191baea52f713
1 /* $Id: iommu_common.c,v 1.4 2000/06/04 21:50:23 anton Exp $
2 * iommu_common.c: UltraSparc SBUS/PCI common iommu code.
4 * Copyright (C) 1999 David S. Miller (davem@redhat.com)
5 */
7 #include "iommu_common.h"
9 /* You are _strongly_ advised to enable the following debugging code
10 * any time you make changes to the sg code below, run it for a while
11 * with filesystems mounted read-only before buying the farm... -DaveM
14 #ifdef VERIFY_SG
15 int verify_lengths(struct scatterlist *sg, int nents, int npages)
17 int sg_len, dma_len;
18 int i, pgcount;
20 sg_len = 0;
21 for (i = 0; i < nents; i++)
22 sg_len += sg[i].length;
24 dma_len = 0;
25 for (i = 0; i < nents && sg[i].dvma_length; i++)
26 dma_len += sg[i].dvma_length;
28 if (sg_len != dma_len) {
29 printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
30 sg_len, dma_len);
31 return -1;
34 pgcount = 0;
35 for (i = 0; i < nents && sg[i].dvma_length; i++) {
36 unsigned long start, end;
38 start = sg[i].dvma_address;
39 start = start & PAGE_MASK;
41 end = sg[i].dvma_address + sg[i].dvma_length;
42 end = (end + (PAGE_SIZE - 1)) & PAGE_MASK;
44 pgcount += ((end - start) >> PAGE_SHIFT);
47 if (pgcount != npages) {
48 printk("verify_lengths: Error, page count wrong, "
49 "npages[%d] pgcount[%d]\n",
50 npages, pgcount);
51 return -1;
54 /* This test passes... */
55 return 0;
58 int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte)
60 struct scatterlist *sg = *__sg;
61 iopte_t *iopte = *__iopte;
62 u32 dlen = dma_sg->dvma_length;
63 u32 daddr = dma_sg->dvma_address;
64 unsigned int sglen;
65 unsigned long sgaddr;
67 sglen = sg->length;
68 sgaddr = (unsigned long) sg->address;
69 while (dlen > 0) {
70 unsigned long paddr;
72 /* SG and DMA_SG must begin at the same sub-page boundary. */
73 if ((sgaddr & ~PAGE_MASK) != (daddr & ~PAGE_MASK)) {
74 printk("verify_one_map: Wrong start offset "
75 "sg[%08lx] dma[%08x]\n",
76 sgaddr, daddr);
77 nents = -1;
78 goto out;
81 /* Verify the IOPTE points to the right page. */
82 paddr = iopte_val(*iopte) & IOPTE_PAGE;
83 if ((paddr + PAGE_OFFSET) != (sgaddr & PAGE_MASK)) {
84 printk("verify_one_map: IOPTE[%08lx] maps the "
85 "wrong page, should be [%08lx]\n",
86 iopte_val(*iopte), (sgaddr & PAGE_MASK) - PAGE_OFFSET);
87 nents = -1;
88 goto out;
91 /* If this SG crosses a page, adjust to that next page
92 * boundary and loop.
94 if ((sgaddr & PAGE_MASK) ^ ((sgaddr + sglen - 1) & PAGE_MASK)) {
95 unsigned long next_page, diff;
97 next_page = (sgaddr + PAGE_SIZE) & PAGE_MASK;
98 diff = next_page - sgaddr;
99 sgaddr += diff;
100 daddr += diff;
101 sglen -= diff;
102 dlen -= diff;
103 if (dlen > 0)
104 iopte++;
105 continue;
108 /* SG wholly consumed within this page. */
109 daddr += sglen;
110 dlen -= sglen;
112 if (dlen > 0 && ((daddr & ~PAGE_MASK) == 0))
113 iopte++;
115 sg++;
116 if (--nents <= 0)
117 break;
118 sgaddr = (unsigned long) sg->address;
119 sglen = sg->length;
121 if (dlen < 0) {
122 /* Transfer overrun, big problems. */
123 printk("verify_one_map: Transfer overrun by %d bytes.\n",
124 -dlen);
125 nents = -1;
126 } else {
127 /* Advance to next dma_sg implies that the next iopte will
128 * begin it.
130 iopte++;
133 out:
134 *__sg = sg;
135 *__iopte = iopte;
136 return nents;
139 int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte)
141 struct scatterlist *dma_sg = sg;
142 struct scatterlist *orig_dma_sg = dma_sg;
143 int orig_nents = nents;
145 for (;;) {
146 nents = verify_one_map(dma_sg, &sg, nents, &iopte);
147 if (nents <= 0)
148 break;
149 dma_sg++;
150 if (dma_sg->dvma_length == 0)
151 break;
154 if (nents > 0) {
155 printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
156 nents);
157 return -1;
160 if (nents < 0) {
161 printk("verify_maps: Error, messed up mappings, "
162 "at sg %d dma_sg %d\n",
163 (int) (orig_nents + nents), (int) (dma_sg - orig_dma_sg));
164 return -1;
167 /* This test passes... */
168 return 0;
171 void verify_sglist(struct scatterlist *sg, int nents, iopte_t *iopte, int npages)
173 if (verify_lengths(sg, nents, npages) < 0 ||
174 verify_maps(sg, nents, iopte) < 0) {
175 int i;
177 printk("verify_sglist: Crap, messed up mappings, dumping, iodma at %08x.\n",
178 (u32) (sg->dvma_address & PAGE_MASK));
179 for (i = 0; i < nents; i++) {
180 printk("sg(%d): address(%p) length(%x) "
181 "dma_address[%08x] dma_length[%08x]\n",
183 sg[i].address, sg[i].length,
184 sg[i].dvma_address, sg[i].dvma_length);
188 /* Seems to be ok */
190 #endif
192 /* Two addresses are "virtually contiguous" if and only if:
193 * 1) They are equal, or...
194 * 2) They are both on a page boundry
196 #define VCONTIG(__X, __Y) (((__X) == (__Y)) || \
197 (((__X) | (__Y)) << (64UL - PAGE_SHIFT)) == 0UL)
199 unsigned long prepare_sg(struct scatterlist *sg, int nents)
201 struct scatterlist *dma_sg = sg;
202 unsigned long prev;
203 u32 dent_addr, dent_len;
205 prev = (unsigned long) sg->address;
206 prev += (unsigned long) (dent_len = sg->length);
207 dent_addr = (u32) ((unsigned long)sg->address & (PAGE_SIZE - 1UL));
208 while (--nents) {
209 unsigned long addr;
211 sg++;
212 addr = (unsigned long) sg->address;
213 if (! VCONTIG(prev, addr)) {
214 dma_sg->dvma_address = dent_addr;
215 dma_sg->dvma_length = dent_len;
216 dma_sg++;
218 dent_addr = ((dent_addr +
219 dent_len +
220 (PAGE_SIZE - 1UL)) >> PAGE_SHIFT);
221 dent_addr <<= PAGE_SHIFT;
222 dent_addr += addr & (PAGE_SIZE - 1UL);
223 dent_len = 0;
225 dent_len += sg->length;
226 prev = addr + sg->length;
228 dma_sg->dvma_address = dent_addr;
229 dma_sg->dvma_length = dent_len;
231 return ((unsigned long) dent_addr +
232 (unsigned long) dent_len +
233 (PAGE_SIZE - 1UL)) >> PAGE_SHIFT;