diff options
Diffstat (limited to 'drivers/scsi/sym53c8xx_2/sym_malloc.c')
-rw-r--r-- | drivers/scsi/sym53c8xx_2/sym_malloc.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/drivers/scsi/sym53c8xx_2/sym_malloc.c b/drivers/scsi/sym53c8xx_2/sym_malloc.c new file mode 100644 index 000000000000..a34d403ccc6c --- /dev/null +++ b/drivers/scsi/sym53c8xx_2/sym_malloc.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family | ||
3 | * of PCI-SCSI IO processors. | ||
4 | * | ||
5 | * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr> | ||
6 | * | ||
7 | * This driver is derived from the Linux sym53c8xx driver. | ||
8 | * Copyright (C) 1998-2000 Gerard Roudier | ||
9 | * | ||
10 | * The sym53c8xx driver is derived from the ncr53c8xx driver that had been | ||
11 | * a port of the FreeBSD ncr driver to Linux-1.2.13. | ||
12 | * | ||
13 | * The original ncr driver has been written for 386bsd and FreeBSD by | ||
14 | * Wolfgang Stanglmeier <wolf@cologne.de> | ||
15 | * Stefan Esser <se@mi.Uni-Koeln.de> | ||
16 | * Copyright (C) 1994 Wolfgang Stanglmeier | ||
17 | * | ||
18 | * Other major contributions: | ||
19 | * | ||
20 | * NVRAM detection and reading. | ||
21 | * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk> | ||
22 | * | ||
23 | *----------------------------------------------------------------------------- | ||
24 | * | ||
25 | * This program is free software; you can redistribute it and/or modify | ||
26 | * it under the terms of the GNU General Public License as published by | ||
27 | * the Free Software Foundation; either version 2 of the License, or | ||
28 | * (at your option) any later version. | ||
29 | * | ||
30 | * This program is distributed in the hope that it will be useful, | ||
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
33 | * GNU General Public License for more details. | ||
34 | * | ||
35 | * You should have received a copy of the GNU General Public License | ||
36 | * along with this program; if not, write to the Free Software | ||
37 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
38 | */ | ||
39 | |||
40 | #ifdef __FreeBSD__ | ||
41 | #include <dev/sym/sym_glue.h> | ||
42 | #else | ||
43 | #include "sym_glue.h" | ||
44 | #endif | ||
45 | |||
46 | /* | ||
47 | * Simple power of two buddy-like generic allocator. | ||
48 | * Provides naturally aligned memory chunks. | ||
49 | * | ||
50 | * This simple code is not intended to be fast, but to | ||
51 | * provide power of 2 aligned memory allocations. | ||
52 | * Since the SCRIPTS processor only supplies 8 bit arithmetic, | ||
53 | * this allocator allows simple and fast address calculations | ||
54 | * from the SCRIPTS code. In addition, cache line alignment | ||
55 | * is guaranteed for power of 2 cache line size. | ||
56 | * | ||
57 | * This allocator has been developped for the Linux sym53c8xx | ||
58 | * driver, since this O/S does not provide naturally aligned | ||
59 | * allocations. | ||
60 | * It has the advantage of allowing the driver to use private | ||
61 | * pages of memory that will be useful if we ever need to deal | ||
62 | * with IO MMUs for PCI. | ||
63 | */ | ||
64 | static void *___sym_malloc(m_pool_p mp, int size) | ||
65 | { | ||
66 | int i = 0; | ||
67 | int s = (1 << SYM_MEM_SHIFT); | ||
68 | int j; | ||
69 | void *a; | ||
70 | m_link_p h = mp->h; | ||
71 | |||
72 | if (size > SYM_MEM_CLUSTER_SIZE) | ||
73 | return NULL; | ||
74 | |||
75 | while (size > s) { | ||
76 | s <<= 1; | ||
77 | ++i; | ||
78 | } | ||
79 | |||
80 | j = i; | ||
81 | while (!h[j].next) { | ||
82 | if (s == SYM_MEM_CLUSTER_SIZE) { | ||
83 | h[j].next = (m_link_p) M_GET_MEM_CLUSTER(); | ||
84 | if (h[j].next) | ||
85 | h[j].next->next = NULL; | ||
86 | break; | ||
87 | } | ||
88 | ++j; | ||
89 | s <<= 1; | ||
90 | } | ||
91 | a = h[j].next; | ||
92 | if (a) { | ||
93 | h[j].next = h[j].next->next; | ||
94 | while (j > i) { | ||
95 | j -= 1; | ||
96 | s >>= 1; | ||
97 | h[j].next = (m_link_p) (a+s); | ||
98 | h[j].next->next = NULL; | ||
99 | } | ||
100 | } | ||
101 | #ifdef DEBUG | ||
102 | printf("___sym_malloc(%d) = %p\n", size, (void *) a); | ||
103 | #endif | ||
104 | return a; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Counter-part of the generic allocator. | ||
109 | */ | ||
110 | static void ___sym_mfree(m_pool_p mp, void *ptr, int size) | ||
111 | { | ||
112 | int i = 0; | ||
113 | int s = (1 << SYM_MEM_SHIFT); | ||
114 | m_link_p q; | ||
115 | unsigned long a, b; | ||
116 | m_link_p h = mp->h; | ||
117 | |||
118 | #ifdef DEBUG | ||
119 | printf("___sym_mfree(%p, %d)\n", ptr, size); | ||
120 | #endif | ||
121 | |||
122 | if (size > SYM_MEM_CLUSTER_SIZE) | ||
123 | return; | ||
124 | |||
125 | while (size > s) { | ||
126 | s <<= 1; | ||
127 | ++i; | ||
128 | } | ||
129 | |||
130 | a = (unsigned long)ptr; | ||
131 | |||
132 | while (1) { | ||
133 | if (s == SYM_MEM_CLUSTER_SIZE) { | ||
134 | #ifdef SYM_MEM_FREE_UNUSED | ||
135 | M_FREE_MEM_CLUSTER((void *)a); | ||
136 | #else | ||
137 | ((m_link_p) a)->next = h[i].next; | ||
138 | h[i].next = (m_link_p) a; | ||
139 | #endif | ||
140 | break; | ||
141 | } | ||
142 | b = a ^ s; | ||
143 | q = &h[i]; | ||
144 | while (q->next && q->next != (m_link_p) b) { | ||
145 | q = q->next; | ||
146 | } | ||
147 | if (!q->next) { | ||
148 | ((m_link_p) a)->next = h[i].next; | ||
149 | h[i].next = (m_link_p) a; | ||
150 | break; | ||
151 | } | ||
152 | q->next = q->next->next; | ||
153 | a = a & b; | ||
154 | s <<= 1; | ||
155 | ++i; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Verbose and zeroing allocator that wrapps to the generic allocator. | ||
161 | */ | ||
162 | static void *__sym_calloc2(m_pool_p mp, int size, char *name, int uflags) | ||
163 | { | ||
164 | void *p; | ||
165 | |||
166 | p = ___sym_malloc(mp, size); | ||
167 | |||
168 | if (DEBUG_FLAGS & DEBUG_ALLOC) { | ||
169 | printf ("new %-10s[%4d] @%p.\n", name, size, p); | ||
170 | } | ||
171 | |||
172 | if (p) | ||
173 | memset(p, 0, size); | ||
174 | else if (uflags & SYM_MEM_WARN) | ||
175 | printf ("__sym_calloc2: failed to allocate %s[%d]\n", name, size); | ||
176 | return p; | ||
177 | } | ||
178 | #define __sym_calloc(mp, s, n) __sym_calloc2(mp, s, n, SYM_MEM_WARN) | ||
179 | |||
180 | /* | ||
181 | * Its counter-part. | ||
182 | */ | ||
183 | static void __sym_mfree(m_pool_p mp, void *ptr, int size, char *name) | ||
184 | { | ||
185 | if (DEBUG_FLAGS & DEBUG_ALLOC) | ||
186 | printf ("freeing %-10s[%4d] @%p.\n", name, size, ptr); | ||
187 | |||
188 | ___sym_mfree(mp, ptr, size); | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Default memory pool we donnot need to involve in DMA. | ||
193 | * | ||
194 | * With DMA abstraction, we use functions (methods), to | ||
195 | * distinguish between non DMAable memory and DMAable memory. | ||
196 | */ | ||
197 | static void *___mp0_get_mem_cluster(m_pool_p mp) | ||
198 | { | ||
199 | void *m = sym_get_mem_cluster(); | ||
200 | if (m) | ||
201 | ++mp->nump; | ||
202 | return m; | ||
203 | } | ||
204 | |||
205 | #ifdef SYM_MEM_FREE_UNUSED | ||
206 | static void ___mp0_free_mem_cluster(m_pool_p mp, void *m) | ||
207 | { | ||
208 | sym_free_mem_cluster(m); | ||
209 | --mp->nump; | ||
210 | } | ||
211 | #else | ||
212 | #define ___mp0_free_mem_cluster NULL | ||
213 | #endif | ||
214 | |||
215 | static struct sym_m_pool mp0 = { | ||
216 | NULL, | ||
217 | ___mp0_get_mem_cluster, | ||
218 | ___mp0_free_mem_cluster | ||
219 | }; | ||
220 | |||
221 | /* | ||
222 | * Methods that maintains DMAable pools according to user allocations. | ||
223 | * New pools are created on the fly when a new pool id is provided. | ||
224 | * They are deleted on the fly when they get emptied. | ||
225 | */ | ||
226 | /* Get a memory cluster that matches the DMA constraints of a given pool */ | ||
227 | static void * ___get_dma_mem_cluster(m_pool_p mp) | ||
228 | { | ||
229 | m_vtob_p vbp; | ||
230 | void *vaddr; | ||
231 | |||
232 | vbp = __sym_calloc(&mp0, sizeof(*vbp), "VTOB"); | ||
233 | if (!vbp) | ||
234 | goto out_err; | ||
235 | |||
236 | vaddr = sym_m_get_dma_mem_cluster(mp, vbp); | ||
237 | if (vaddr) { | ||
238 | int hc = VTOB_HASH_CODE(vaddr); | ||
239 | vbp->next = mp->vtob[hc]; | ||
240 | mp->vtob[hc] = vbp; | ||
241 | ++mp->nump; | ||
242 | } | ||
243 | return vaddr; | ||
244 | out_err: | ||
245 | return NULL; | ||
246 | } | ||
247 | |||
248 | #ifdef SYM_MEM_FREE_UNUSED | ||
249 | /* Free a memory cluster and associated resources for DMA */ | ||
250 | static void ___free_dma_mem_cluster(m_pool_p mp, void *m) | ||
251 | { | ||
252 | m_vtob_p *vbpp, vbp; | ||
253 | int hc = VTOB_HASH_CODE(m); | ||
254 | |||
255 | vbpp = &mp->vtob[hc]; | ||
256 | while (*vbpp && (*vbpp)->vaddr != m) | ||
257 | vbpp = &(*vbpp)->next; | ||
258 | if (*vbpp) { | ||
259 | vbp = *vbpp; | ||
260 | *vbpp = (*vbpp)->next; | ||
261 | sym_m_free_dma_mem_cluster(mp, vbp); | ||
262 | __sym_mfree(&mp0, vbp, sizeof(*vbp), "VTOB"); | ||
263 | --mp->nump; | ||
264 | } | ||
265 | } | ||
266 | #endif | ||
267 | |||
268 | /* Fetch the memory pool for a given pool id (i.e. DMA constraints) */ | ||
269 | static __inline m_pool_p ___get_dma_pool(m_pool_ident_t dev_dmat) | ||
270 | { | ||
271 | m_pool_p mp; | ||
272 | for (mp = mp0.next; | ||
273 | mp && !sym_m_pool_match(mp->dev_dmat, dev_dmat); | ||
274 | mp = mp->next); | ||
275 | return mp; | ||
276 | } | ||
277 | |||
278 | /* Create a new memory DMAable pool (when fetch failed) */ | ||
279 | static m_pool_p ___cre_dma_pool(m_pool_ident_t dev_dmat) | ||
280 | { | ||
281 | m_pool_p mp = __sym_calloc(&mp0, sizeof(*mp), "MPOOL"); | ||
282 | if (mp) { | ||
283 | mp->dev_dmat = dev_dmat; | ||
284 | mp->get_mem_cluster = ___get_dma_mem_cluster; | ||
285 | #ifdef SYM_MEM_FREE_UNUSED | ||
286 | mp->free_mem_cluster = ___free_dma_mem_cluster; | ||
287 | #endif | ||
288 | mp->next = mp0.next; | ||
289 | mp0.next = mp; | ||
290 | return mp; | ||
291 | } | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | #ifdef SYM_MEM_FREE_UNUSED | ||
296 | /* Destroy a DMAable memory pool (when got emptied) */ | ||
297 | static void ___del_dma_pool(m_pool_p p) | ||
298 | { | ||
299 | m_pool_p *pp = &mp0.next; | ||
300 | |||
301 | while (*pp && *pp != p) | ||
302 | pp = &(*pp)->next; | ||
303 | if (*pp) { | ||
304 | *pp = (*pp)->next; | ||
305 | __sym_mfree(&mp0, p, sizeof(*p), "MPOOL"); | ||
306 | } | ||
307 | } | ||
308 | #endif | ||
309 | |||
310 | /* This lock protects only the memory allocation/free. */ | ||
311 | static DEFINE_SPINLOCK(sym53c8xx_lock); | ||
312 | |||
313 | /* | ||
314 | * Actual allocator for DMAable memory. | ||
315 | */ | ||
316 | void *__sym_calloc_dma(m_pool_ident_t dev_dmat, int size, char *name) | ||
317 | { | ||
318 | unsigned long flags; | ||
319 | m_pool_p mp; | ||
320 | void *m = NULL; | ||
321 | |||
322 | spin_lock_irqsave(&sym53c8xx_lock, flags); | ||
323 | mp = ___get_dma_pool(dev_dmat); | ||
324 | if (!mp) | ||
325 | mp = ___cre_dma_pool(dev_dmat); | ||
326 | if (!mp) | ||
327 | goto out; | ||
328 | m = __sym_calloc(mp, size, name); | ||
329 | #ifdef SYM_MEM_FREE_UNUSED | ||
330 | if (!mp->nump) | ||
331 | ___del_dma_pool(mp); | ||
332 | #endif | ||
333 | |||
334 | out: | ||
335 | spin_unlock_irqrestore(&sym53c8xx_lock, flags); | ||
336 | return m; | ||
337 | } | ||
338 | |||
339 | void __sym_mfree_dma(m_pool_ident_t dev_dmat, void *m, int size, char *name) | ||
340 | { | ||
341 | unsigned long flags; | ||
342 | m_pool_p mp; | ||
343 | |||
344 | spin_lock_irqsave(&sym53c8xx_lock, flags); | ||
345 | mp = ___get_dma_pool(dev_dmat); | ||
346 | if (!mp) | ||
347 | goto out; | ||
348 | __sym_mfree(mp, m, size, name); | ||
349 | #ifdef SYM_MEM_FREE_UNUSED | ||
350 | if (!mp->nump) | ||
351 | ___del_dma_pool(mp); | ||
352 | #endif | ||
353 | out: | ||
354 | spin_unlock_irqrestore(&sym53c8xx_lock, flags); | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * Actual virtual to bus physical address translator | ||
359 | * for 32 bit addressable DMAable memory. | ||
360 | */ | ||
361 | dma_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m) | ||
362 | { | ||
363 | unsigned long flags; | ||
364 | m_pool_p mp; | ||
365 | int hc = VTOB_HASH_CODE(m); | ||
366 | m_vtob_p vp = NULL; | ||
367 | void *a = (void *)((unsigned long)m & ~SYM_MEM_CLUSTER_MASK); | ||
368 | dma_addr_t b; | ||
369 | |||
370 | spin_lock_irqsave(&sym53c8xx_lock, flags); | ||
371 | mp = ___get_dma_pool(dev_dmat); | ||
372 | if (mp) { | ||
373 | vp = mp->vtob[hc]; | ||
374 | while (vp && vp->vaddr != a) | ||
375 | vp = vp->next; | ||
376 | } | ||
377 | if (!vp) | ||
378 | panic("sym: VTOBUS FAILED!\n"); | ||
379 | b = vp->baddr + (m - a); | ||
380 | spin_unlock_irqrestore(&sym53c8xx_lock, flags); | ||
381 | return b; | ||
382 | } | ||