ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / parisc / iommu-helpers.h
1 /**
2  * iommu_fill_pdir - Insert coalesced scatter/gather chunks into the I/O Pdir.
3  * @ioc: The I/O Controller.
4  * @startsg: The scatter/gather list of coalesced chunks.
5  * @nents: The number of entries in the scatter/gather list.
6  * @hint: The DMA Hint.
7  *
8  * This function inserts the coalesced scatter/gather list chunks into the
9  * I/O Controller's I/O Pdir.
10  */ 
11 static inline unsigned int
12 iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, 
13                 unsigned long hint,
14                 void (*iommu_io_pdir_entry)(u64 *, space_t, unsigned long,
15                                             unsigned long))
16 {
17         struct scatterlist *dma_sg = startsg;   /* pointer to current DMA */
18         unsigned int n_mappings = 0;
19         unsigned long dma_offset = 0, dma_len = 0;
20         u64 *pdirp = NULL;
21
22         /* Horrible hack.  For efficiency's sake, dma_sg starts one 
23          * entry below the true start (it is immediately incremented
24          * in the loop) */
25          dma_sg--;
26
27         while (nents-- > 0) {
28                 unsigned long vaddr;
29                 long size;
30
31                 DBG_RUN_SG(" %d : %08lx/%05x %08lx/%05x\n", nents,
32                            (unsigned long)sg_dma_address(startsg), cnt,
33                            sg_virt_addr(startsg), startsg->length
34                 );
35
36
37                 /*
38                 ** Look for the start of a new DMA stream
39                 */
40                 
41                 if (sg_dma_address(startsg) & PIDE_FLAG) {
42                         u32 pide = sg_dma_address(startsg) & ~PIDE_FLAG;
43
44                         BUG_ON(pdirp && (dma_len != sg_dma_len(dma_sg)));
45
46                         dma_sg++;
47
48                         dma_len = sg_dma_len(startsg);
49                         sg_dma_len(startsg) = 0;
50                         dma_offset = (unsigned long) pide & ~IOVP_MASK;
51                         n_mappings++;
52                         sg_dma_address(dma_sg) = pide;
53                         pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]);
54                         prefetchw(pdirp);
55                 }
56                 
57                 BUG_ON(pdirp == NULL);
58                 
59                 vaddr = sg_virt_addr(startsg);
60                 sg_dma_len(dma_sg) += startsg->length;
61                 size = startsg->length + dma_offset;
62                 dma_offset = 0;
63 #ifdef IOMMU_MAP_STATS
64                 ioc->msg_pages += startsg->length >> IOVP_SHIFT;
65 #endif
66                 do {
67                         iommu_io_pdir_entry(pdirp, KERNEL_SPACE, 
68                                             vaddr, hint);
69                         vaddr += IOVP_SIZE;
70                         size -= IOVP_SIZE;
71                         pdirp++;
72                 } while(unlikely(size > 0));
73                 startsg++;
74         }
75         return(n_mappings);
76 }
77
78
79 /*
80 ** First pass is to walk the SG list and determine where the breaks are
81 ** in the DMA stream. Allocates PDIR entries but does not fill them.
82 ** Returns the number of DMA chunks.
83 **
84 ** Doing the fill separate from the coalescing/allocation keeps the
85 ** code simpler. Future enhancement could make one pass through
86 ** the sglist do both.
87 */
88
89 static inline unsigned int
90 iommu_coalesce_chunks(struct ioc *ioc, struct scatterlist *startsg, int nents,
91                       int (*iommu_alloc_range)(struct ioc *, size_t))
92 {
93         struct scatterlist *contig_sg;     /* contig chunk head */
94         unsigned long dma_offset, dma_len; /* start/len of DMA stream */
95         unsigned int n_mappings = 0;
96
97         while (nents > 0) {
98
99                 /*
100                 ** Prepare for first/next DMA stream
101                 */
102                 contig_sg = startsg;
103                 dma_len = startsg->length;
104                 dma_offset = sg_virt_addr(startsg) & ~IOVP_MASK;
105
106                 /* PARANOID: clear entries */
107                 sg_dma_address(startsg) = 0;
108                 sg_dma_len(startsg) = 0;
109
110                 /*
111                 ** This loop terminates one iteration "early" since
112                 ** it's always looking one "ahead".
113                 */
114                 while(--nents > 0) {
115                         unsigned long prevstartsg_end, startsg_end;
116
117                         prevstartsg_end = sg_virt_addr(startsg) +
118                                 startsg->length;
119
120                         startsg++;
121                         startsg_end = sg_virt_addr(startsg) + 
122                                 startsg->length;
123
124                         /* PARANOID: clear entries */
125                         sg_dma_address(startsg) = 0;
126                         sg_dma_len(startsg) = 0;
127
128                         /*
129                         ** First make sure current dma stream won't
130                         ** exceed DMA_CHUNK_SIZE if we coalesce the
131                         ** next entry.
132                         */   
133                         if(unlikely(ROUNDUP(dma_len + dma_offset + startsg->length,
134                                             IOVP_SIZE) > DMA_CHUNK_SIZE))
135                                 break;
136
137                         /*
138                         ** Next see if we can append the next chunk (i.e.
139                         ** it must end on one page and begin on another
140                         */
141                         if (unlikely(((prevstartsg_end | sg_virt_addr(startsg)) & ~PAGE_MASK) != 0))
142                                 break;
143                         
144                         dma_len += startsg->length;
145                 }
146
147                 /*
148                 ** End of DMA Stream
149                 ** Terminate last VCONTIG block.
150                 ** Allocate space for DMA stream.
151                 */
152                 sg_dma_len(contig_sg) = dma_len;
153                 dma_len = ROUNDUP(dma_len + dma_offset, IOVP_SIZE);
154                 sg_dma_address(contig_sg) =
155                         PIDE_FLAG 
156                         | (iommu_alloc_range(ioc, dma_len) << IOVP_SHIFT)
157                         | dma_offset;
158                 n_mappings++;
159         }
160
161         return n_mappings;
162 }
163