Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[pandora-kernel.git] / arch / sparc64 / kernel / iommu_common.c
1 /* $Id: iommu_common.c,v 1.9 2001/12/17 07:05:09 davem Exp $
2  * iommu_common.c: UltraSparc SBUS/PCI common iommu code.
3  *
4  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
5  */
6
7 #include "iommu_common.h"
8
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
12  */
13
14 #ifdef VERIFY_SG
15 static int verify_lengths(struct scatterlist *sglist, int nents, int npages)
16 {
17         int sg_len, dma_len;
18         int i, pgcount;
19         struct scatterlist *sg;
20
21         sg_len = 0;
22         for_each_sg(sglist, sg, nents, i)
23                 sg_len += sg->length;
24
25         dma_len = 0;
26         for_each_sg(sglist, sg, nents, i) {
27                 if (!sg->dma_length)
28                         break;
29                 dma_len += sg->dma_length;
30         }
31
32         if (sg_len != dma_len) {
33                 printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
34                        sg_len, dma_len);
35                 return -1;
36         }
37
38         pgcount = 0;
39         for_each_sg(sglist, sg, nents, i) {
40                 unsigned long start, end;
41
42                 if (!sg->dma_length)
43                         break;
44
45                 start = sg->dma_address;
46                 start = start & IO_PAGE_MASK;
47
48                 end = sg->dma_address + sg->dma_length;
49                 end = (end + (IO_PAGE_SIZE - 1)) & IO_PAGE_MASK;
50
51                 pgcount += ((end - start) >> IO_PAGE_SHIFT);
52         }
53
54         if (pgcount != npages) {
55                 printk("verify_lengths: Error, page count wrong, "
56                        "npages[%d] pgcount[%d]\n",
57                        npages, pgcount);
58                 return -1;
59         }
60
61         /* This test passes... */
62         return 0;
63 }
64
65 static int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte)
66 {
67         struct scatterlist *sg = *__sg;
68         iopte_t *iopte = *__iopte;
69         u32 dlen = dma_sg->dma_length;
70         u32 daddr;
71         unsigned int sglen;
72         unsigned long sgaddr;
73
74         daddr = dma_sg->dma_address;
75         sglen = sg->length;
76         sgaddr = (unsigned long) sg_virt(sg);
77         while (dlen > 0) {
78                 unsigned long paddr;
79
80                 /* SG and DMA_SG must begin at the same sub-page boundary. */
81                 if ((sgaddr & ~IO_PAGE_MASK) != (daddr & ~IO_PAGE_MASK)) {
82                         printk("verify_one_map: Wrong start offset "
83                                "sg[%08lx] dma[%08x]\n",
84                                sgaddr, daddr);
85                         nents = -1;
86                         goto out;
87                 }
88
89                 /* Verify the IOPTE points to the right page. */
90                 paddr = iopte_val(*iopte) & IOPTE_PAGE;
91                 if ((paddr + PAGE_OFFSET) != (sgaddr & IO_PAGE_MASK)) {
92                         printk("verify_one_map: IOPTE[%08lx] maps the "
93                                "wrong page, should be [%08lx]\n",
94                                iopte_val(*iopte), (sgaddr & IO_PAGE_MASK) - PAGE_OFFSET);
95                         nents = -1;
96                         goto out;
97                 }
98
99                 /* If this SG crosses a page, adjust to that next page
100                  * boundary and loop.
101                  */
102                 if ((sgaddr & IO_PAGE_MASK) ^ ((sgaddr + sglen - 1) & IO_PAGE_MASK)) {
103                         unsigned long next_page, diff;
104
105                         next_page = (sgaddr + IO_PAGE_SIZE) & IO_PAGE_MASK;
106                         diff = next_page - sgaddr;
107                         sgaddr += diff;
108                         daddr += diff;
109                         sglen -= diff;
110                         dlen -= diff;
111                         if (dlen > 0)
112                                 iopte++;
113                         continue;
114                 }
115
116                 /* SG wholly consumed within this page. */
117                 daddr += sglen;
118                 dlen -= sglen;
119
120                 if (dlen > 0 && ((daddr & ~IO_PAGE_MASK) == 0))
121                         iopte++;
122
123                 sg = sg_next(sg);
124                 if (--nents <= 0)
125                         break;
126                 sgaddr = (unsigned long) sg_virt(sg);
127                 sglen = sg->length;
128         }
129         if (dlen < 0) {
130                 /* Transfer overrun, big problems. */
131                 printk("verify_one_map: Transfer overrun by %d bytes.\n",
132                        -dlen);
133                 nents = -1;
134         } else {
135                 /* Advance to next dma_sg implies that the next iopte will
136                  * begin it.
137                  */
138                 iopte++;
139         }
140
141 out:
142         *__sg = sg;
143         *__iopte = iopte;
144         return nents;
145 }
146
147 static int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte)
148 {
149         struct scatterlist *dma_sg = sg;
150         struct scatterlist *orig_dma_sg = dma_sg;
151         int orig_nents = nents;
152
153         for (;;) {
154                 nents = verify_one_map(dma_sg, &sg, nents, &iopte);
155                 if (nents <= 0)
156                         break;
157                 dma_sg = sg_next(dma_sg);
158                 if (dma_sg->dma_length == 0)
159                         break;
160         }
161
162         if (nents > 0) {
163                 printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
164                        nents);
165                 return -1;
166         }
167
168         if (nents < 0) {
169                 printk("verify_maps: Error, messed up mappings, "
170                        "at sg %d dma_sg %d\n",
171                        (int) (orig_nents + nents), (int) (dma_sg - orig_dma_sg));
172                 return -1;
173         }
174
175         /* This test passes... */
176         return 0;
177 }
178
179 void verify_sglist(struct scatterlist *sglist, int nents, iopte_t *iopte, int npages)
180 {
181         struct scatterlist *sg;
182
183         if (verify_lengths(sglist, nents, npages) < 0 ||
184             verify_maps(sglist, nents, iopte) < 0) {
185                 int i;
186
187                 printk("verify_sglist: Crap, messed up mappings, dumping, iodma at ");
188                 printk("%016lx.\n", sglist->dma_address & IO_PAGE_MASK);
189
190                 for_each_sg(sglist, sg, nents, i) {
191                         printk("sg(%d): page_addr(%p) off(%x) length(%x) "
192                                "dma_address[%016x] dma_length[%016x]\n",
193                                i,
194                                page_address(sg_page(sg)), sg->offset,
195                                sg->length,
196                                sg->dma_address, sg->dma_length);
197                 }
198         }
199
200         /* Seems to be ok */
201 }
202 #endif
203
204 unsigned long prepare_sg(struct scatterlist *sg, int nents)
205 {
206         struct scatterlist *dma_sg = sg;
207         unsigned long prev;
208         u32 dent_addr, dent_len;
209
210         prev  = (unsigned long) sg_virt(sg);
211         prev += (unsigned long) (dent_len = sg->length);
212         dent_addr = (u32) ((unsigned long)(sg_virt(sg)) & (IO_PAGE_SIZE - 1UL));
213         while (--nents) {
214                 unsigned long addr;
215
216                 sg = sg_next(sg);
217                 addr = (unsigned long) sg_virt(sg);
218                 if (! VCONTIG(prev, addr)) {
219                         dma_sg->dma_address = dent_addr;
220                         dma_sg->dma_length = dent_len;
221                         dma_sg = sg_next(dma_sg);
222
223                         dent_addr = ((dent_addr +
224                                       dent_len +
225                                       (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT);
226                         dent_addr <<= IO_PAGE_SHIFT;
227                         dent_addr += addr & (IO_PAGE_SIZE - 1UL);
228                         dent_len = 0;
229                 }
230                 dent_len += sg->length;
231                 prev = addr + sg->length;
232         }
233         dma_sg->dma_address = dent_addr;
234         dma_sg->dma_length = dent_len;
235
236         if (dma_sg != sg) {
237                 dma_sg = next_sg(dma_sg);
238                 dma_sg->dma_length = 0;
239         }
240
241         return ((unsigned long) dent_addr +
242                 (unsigned long) dent_len +
243                 (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT;
244 }