• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/staging/tidspbridge/rmgr/
1/*
2 * rmm.c
3 *
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5 *
6 * Copyright (C) 2005-2006 Texas Instruments, Inc.
7 *
8 * This package is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
14 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
15 */
16
17/*
18 *  This memory manager provides general heap management and arbitrary
19 *  alignment for any number of memory segments.
20 *
21 *  Notes:
22 *
23 *  Memory blocks are allocated from the end of the first free memory
24 *  block large enough to satisfy the request.  Alignment requirements
25 *  are satisfied by "sliding" the block forward until its base satisfies
26 *  the alignment specification; if this is not possible then the next
27 *  free block large enough to hold the request is tried.
28 *
29 *  Since alignment can cause the creation of a new free block - the
30 *  unused memory formed between the start of the original free block
31 *  and the start of the allocated block - the memory manager must free
32 *  this memory to prevent a memory leak.
33 *
34 *  Overlay memory is managed by reserving through rmm_alloc, and freeing
35 *  it through rmm_free. The memory manager prevents DSP code/data that is
36 *  overlayed from being overwritten as long as the memory it runs at has
37 *  been allocated, and not yet freed.
38 */
39
40#include <linux/types.h>
41
42/*  ----------------------------------- DSP/BIOS Bridge */
43#include <dspbridge/dbdefs.h>
44
45/*  ----------------------------------- Trace & Debug */
46#include <dspbridge/dbc.h>
47
48/*  ----------------------------------- OS Adaptation Layer */
49#include <dspbridge/list.h>
50
51/*  ----------------------------------- This */
52#include <dspbridge/rmm.h>
53
54/*
55 *  ======== rmm_header ========
56 *  This header is used to maintain a list of free memory blocks.
57 */
58struct rmm_header {
59	struct rmm_header *next;	/* form a free memory link list */
60	u32 size;		/* size of the free memory */
61	u32 addr;		/* DSP address of memory block */
62};
63
64/*
65 *  ======== rmm_ovly_sect ========
66 *  Keeps track of memory occupied by overlay section.
67 */
68struct rmm_ovly_sect {
69	struct list_head list_elem;
70	u32 addr;		/* Start of memory section */
71	u32 size;		/* Length (target MAUs) of section */
72	s32 page;		/* Memory page */
73};
74
75/*
76 *  ======== rmm_target_obj ========
77 */
78struct rmm_target_obj {
79	struct rmm_segment *seg_tab;
80	struct rmm_header **free_list;
81	u32 num_segs;
82	struct lst_list *ovly_list;	/* List of overlay memory in use */
83};
84
85static u32 refs;		/* module reference count */
86
87static bool alloc_block(struct rmm_target_obj *target, u32 segid, u32 size,
88			u32 align, u32 *dsp_address);
89static bool free_block(struct rmm_target_obj *target, u32 segid, u32 addr,
90		       u32 size);
91
92/*
93 *  ======== rmm_alloc ========
94 */
95int rmm_alloc(struct rmm_target_obj *target, u32 segid, u32 size,
96		     u32 align, u32 *dsp_address, bool reserve)
97{
98	struct rmm_ovly_sect *sect;
99	struct rmm_ovly_sect *prev_sect = NULL;
100	struct rmm_ovly_sect *new_sect;
101	u32 addr;
102	int status = 0;
103
104	DBC_REQUIRE(target);
105	DBC_REQUIRE(dsp_address != NULL);
106	DBC_REQUIRE(size > 0);
107	DBC_REQUIRE(reserve || (target->num_segs > 0));
108	DBC_REQUIRE(refs > 0);
109
110	if (!reserve) {
111		if (!alloc_block(target, segid, size, align, dsp_address)) {
112			status = -ENOMEM;
113		} else {
114			/* Increment the number of allocated blocks in this
115			 * segment */
116			target->seg_tab[segid].number++;
117		}
118		goto func_end;
119	}
120	/* An overlay section - See if block is already in use. If not,
121	 * insert into the list in ascending address size. */
122	addr = *dsp_address;
123	sect = (struct rmm_ovly_sect *)lst_first(target->ovly_list);
124	/*  Find place to insert new list element. List is sorted from
125	 *  smallest to largest address. */
126	while (sect != NULL) {
127		if (addr <= sect->addr) {
128			/* Check for overlap with sect */
129			if ((addr + size > sect->addr) || (prev_sect &&
130							   (prev_sect->addr +
131							    prev_sect->size >
132							    addr))) {
133				status = -ENXIO;
134			}
135			break;
136		}
137		prev_sect = sect;
138		sect = (struct rmm_ovly_sect *)lst_next(target->ovly_list,
139							(struct list_head *)
140							sect);
141	}
142	if (!status) {
143		/* No overlap - allocate list element for new section. */
144		new_sect = kzalloc(sizeof(struct rmm_ovly_sect), GFP_KERNEL);
145		if (new_sect == NULL) {
146			status = -ENOMEM;
147		} else {
148			lst_init_elem((struct list_head *)new_sect);
149			new_sect->addr = addr;
150			new_sect->size = size;
151			new_sect->page = segid;
152			if (sect == NULL) {
153				/* Put new section at the end of the list */
154				lst_put_tail(target->ovly_list,
155					     (struct list_head *)new_sect);
156			} else {
157				/* Put new section just before sect */
158				lst_insert_before(target->ovly_list,
159						  (struct list_head *)new_sect,
160						  (struct list_head *)sect);
161			}
162		}
163	}
164func_end:
165	return status;
166}
167
168/*
169 *  ======== rmm_create ========
170 */
171int rmm_create(struct rmm_target_obj **target_obj,
172		      struct rmm_segment seg_tab[], u32 num_segs)
173{
174	struct rmm_header *hptr;
175	struct rmm_segment *sptr, *tmp;
176	struct rmm_target_obj *target;
177	s32 i;
178	int status = 0;
179
180	DBC_REQUIRE(target_obj != NULL);
181	DBC_REQUIRE(num_segs == 0 || seg_tab != NULL);
182
183	/* Allocate DBL target object */
184	target = kzalloc(sizeof(struct rmm_target_obj), GFP_KERNEL);
185
186	if (target == NULL)
187		status = -ENOMEM;
188
189	if (status)
190		goto func_cont;
191
192	target->num_segs = num_segs;
193	if (!(num_segs > 0))
194		goto func_cont;
195
196	/* Allocate the memory for freelist from host's memory */
197	target->free_list = kzalloc(num_segs * sizeof(struct rmm_header *),
198							GFP_KERNEL);
199	if (target->free_list == NULL) {
200		status = -ENOMEM;
201	} else {
202		/* Allocate headers for each element on the free list */
203		for (i = 0; i < (s32) num_segs; i++) {
204			target->free_list[i] =
205				kzalloc(sizeof(struct rmm_header), GFP_KERNEL);
206			if (target->free_list[i] == NULL) {
207				status = -ENOMEM;
208				break;
209			}
210		}
211		/* Allocate memory for initial segment table */
212		target->seg_tab = kzalloc(num_segs * sizeof(struct rmm_segment),
213								GFP_KERNEL);
214		if (target->seg_tab == NULL) {
215			status = -ENOMEM;
216		} else {
217			/* Initialize segment table and free list */
218			sptr = target->seg_tab;
219			for (i = 0, tmp = seg_tab; num_segs > 0;
220			     num_segs--, i++) {
221				*sptr = *tmp;
222				hptr = target->free_list[i];
223				hptr->addr = tmp->base;
224				hptr->size = tmp->length;
225				hptr->next = NULL;
226				tmp++;
227				sptr++;
228			}
229		}
230	}
231func_cont:
232	/* Initialize overlay memory list */
233	if (!status) {
234		target->ovly_list = kzalloc(sizeof(struct lst_list),
235							GFP_KERNEL);
236		if (target->ovly_list == NULL)
237			status = -ENOMEM;
238		else
239			INIT_LIST_HEAD(&target->ovly_list->head);
240	}
241
242	if (!status) {
243		*target_obj = target;
244	} else {
245		*target_obj = NULL;
246		if (target)
247			rmm_delete(target);
248
249	}
250
251	DBC_ENSURE((!status && *target_obj)
252		   || (status && *target_obj == NULL));
253
254	return status;
255}
256
257/*
258 *  ======== rmm_delete ========
259 */
260void rmm_delete(struct rmm_target_obj *target)
261{
262	struct rmm_ovly_sect *ovly_section;
263	struct rmm_header *hptr;
264	struct rmm_header *next;
265	u32 i;
266
267	DBC_REQUIRE(target);
268
269	kfree(target->seg_tab);
270
271	if (target->ovly_list) {
272		while ((ovly_section = (struct rmm_ovly_sect *)lst_get_head
273			(target->ovly_list))) {
274			kfree(ovly_section);
275		}
276		DBC_ASSERT(LST_IS_EMPTY(target->ovly_list));
277		kfree(target->ovly_list);
278	}
279
280	if (target->free_list != NULL) {
281		/* Free elements on freelist */
282		for (i = 0; i < target->num_segs; i++) {
283			hptr = next = target->free_list[i];
284			while (next) {
285				hptr = next;
286				next = hptr->next;
287				kfree(hptr);
288			}
289		}
290		kfree(target->free_list);
291	}
292
293	kfree(target);
294}
295
296/*
297 *  ======== rmm_exit ========
298 */
299void rmm_exit(void)
300{
301	DBC_REQUIRE(refs > 0);
302
303	refs--;
304
305	DBC_ENSURE(refs >= 0);
306}
307
308/*
309 *  ======== rmm_free ========
310 */
311bool rmm_free(struct rmm_target_obj *target, u32 segid, u32 dsp_addr, u32 size,
312	      bool reserved)
313{
314	struct rmm_ovly_sect *sect;
315	bool ret = true;
316
317	DBC_REQUIRE(target);
318
319	DBC_REQUIRE(reserved || segid < target->num_segs);
320	DBC_REQUIRE(reserved || (dsp_addr >= target->seg_tab[segid].base &&
321				 (dsp_addr + size) <= (target->seg_tab[segid].
322						   base +
323						   target->seg_tab[segid].
324						   length)));
325
326	/*
327	 *  Free or unreserve memory.
328	 */
329	if (!reserved) {
330		ret = free_block(target, segid, dsp_addr, size);
331		if (ret)
332			target->seg_tab[segid].number--;
333
334	} else {
335		/* Unreserve memory */
336		sect = (struct rmm_ovly_sect *)lst_first(target->ovly_list);
337		while (sect != NULL) {
338			if (dsp_addr == sect->addr) {
339				DBC_ASSERT(size == sect->size);
340				/* Remove from list */
341				lst_remove_elem(target->ovly_list,
342						(struct list_head *)sect);
343				kfree(sect);
344				break;
345			}
346			sect =
347			    (struct rmm_ovly_sect *)lst_next(target->ovly_list,
348							     (struct list_head
349							      *)sect);
350		}
351		if (sect == NULL)
352			ret = false;
353
354	}
355	return ret;
356}
357
358/*
359 *  ======== rmm_init ========
360 */
361bool rmm_init(void)
362{
363	DBC_REQUIRE(refs >= 0);
364
365	refs++;
366
367	return true;
368}
369
370/*
371 *  ======== rmm_stat ========
372 */
373bool rmm_stat(struct rmm_target_obj *target, enum dsp_memtype segid,
374	      struct dsp_memstat *mem_stat_buf)
375{
376	struct rmm_header *head;
377	bool ret = false;
378	u32 max_free_size = 0;
379	u32 total_free_size = 0;
380	u32 free_blocks = 0;
381
382	DBC_REQUIRE(mem_stat_buf != NULL);
383	DBC_ASSERT(target != NULL);
384
385	if ((u32) segid < target->num_segs) {
386		head = target->free_list[segid];
387
388		/* Collect data from free_list */
389		while (head != NULL) {
390			max_free_size = max(max_free_size, head->size);
391			total_free_size += head->size;
392			free_blocks++;
393			head = head->next;
394		}
395
396		/* ul_size */
397		mem_stat_buf->ul_size = target->seg_tab[segid].length;
398
399		/* ul_num_free_blocks */
400		mem_stat_buf->ul_num_free_blocks = free_blocks;
401
402		/* ul_total_free_size */
403		mem_stat_buf->ul_total_free_size = total_free_size;
404
405		/* ul_len_max_free_block */
406		mem_stat_buf->ul_len_max_free_block = max_free_size;
407
408		/* ul_num_alloc_blocks */
409		mem_stat_buf->ul_num_alloc_blocks =
410		    target->seg_tab[segid].number;
411
412		ret = true;
413	}
414
415	return ret;
416}
417
418/*
419 *  ======== balloc ========
420 *  This allocation function allocates memory from the lowest addresses
421 *  first.
422 */
423static bool alloc_block(struct rmm_target_obj *target, u32 segid, u32 size,
424			u32 align, u32 *dsp_address)
425{
426	struct rmm_header *head;
427	struct rmm_header *prevhead = NULL;
428	struct rmm_header *next;
429	u32 tmpalign;
430	u32 alignbytes;
431	u32 hsize;
432	u32 allocsize;
433	u32 addr;
434
435	alignbytes = (align == 0) ? 1 : align;
436	prevhead = NULL;
437	head = target->free_list[segid];
438
439	do {
440		hsize = head->size;
441		next = head->next;
442
443		addr = head->addr;	/* alloc from the bottom */
444
445		/* align allocation */
446		(tmpalign = (u32) addr % alignbytes);
447		if (tmpalign != 0)
448			tmpalign = alignbytes - tmpalign;
449
450		allocsize = size + tmpalign;
451
452		if (hsize >= allocsize) {	/* big enough */
453			if (hsize == allocsize && prevhead != NULL) {
454				prevhead->next = next;
455				kfree(head);
456			} else {
457				head->size = hsize - allocsize;
458				head->addr += allocsize;
459			}
460
461			/* free up any hole created by alignment */
462			if (tmpalign)
463				free_block(target, segid, addr, tmpalign);
464
465			*dsp_address = addr + tmpalign;
466			return true;
467		}
468
469		prevhead = head;
470		head = next;
471
472	} while (head != NULL);
473
474	return false;
475}
476
477/*
478 *  ======== free_block ========
479 *  TO DO: free_block() allocates memory, which could result in failure.
480 *  Could allocate an rmm_header in rmm_alloc(), to be kept in a pool.
481 *  free_block() could use an rmm_header from the pool, freeing as blocks
482 *  are coalesced.
483 */
484static bool free_block(struct rmm_target_obj *target, u32 segid, u32 addr,
485		       u32 size)
486{
487	struct rmm_header *head;
488	struct rmm_header *thead;
489	struct rmm_header *rhead;
490	bool ret = true;
491
492	/* Create a memory header to hold the newly free'd block. */
493	rhead = kzalloc(sizeof(struct rmm_header), GFP_KERNEL);
494	if (rhead == NULL) {
495		ret = false;
496	} else {
497		/* search down the free list to find the right place for addr */
498		head = target->free_list[segid];
499
500		if (addr >= head->addr) {
501			while (head->next != NULL && addr > head->next->addr)
502				head = head->next;
503
504			thead = head->next;
505
506			head->next = rhead;
507			rhead->next = thead;
508			rhead->addr = addr;
509			rhead->size = size;
510		} else {
511			*rhead = *head;
512			head->next = rhead;
513			head->addr = addr;
514			head->size = size;
515			thead = rhead->next;
516		}
517
518		/* join with upper block, if possible */
519		if (thead != NULL && (rhead->addr + rhead->size) ==
520		    thead->addr) {
521			head->next = rhead->next;
522			thead->size = size + thead->size;
523			thead->addr = addr;
524			kfree(rhead);
525			rhead = thead;
526		}
527
528		/* join with the lower block, if possible */
529		if ((head->addr + head->size) == rhead->addr) {
530			head->next = rhead->next;
531			head->size = head->size + rhead->size;
532			kfree(rhead);
533		}
534	}
535
536	return ret;
537}
538