Logo Search packages:      
Sourcecode: linux version File versions

intmem.c

/*
 * Simple allocator for internal RAM in ETRAX FS
 *
 * Copyright (c) 2004 Axis Communications AB.
 */

#include <linux/list.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <memmap.h>

#define STATUS_FREE 0
#define STATUS_ALLOCATED 1

#ifdef CONFIG_ETRAX_L2CACHE
#define RESERVED_SIZE 66*1024
#else
#define RESERVED_SIZE 0
#endif

struct intmem_allocation {
      struct list_head entry;
      unsigned int size;
      unsigned offset;
      char status;
};


static struct list_head intmem_allocations;
static void* intmem_virtual;

static void crisv32_intmem_init(void)
{
      static int initiated = 0;
      if (!initiated) {
            struct intmem_allocation* alloc =
              (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL);
            INIT_LIST_HEAD(&intmem_allocations);
            intmem_virtual = ioremap(MEM_INTMEM_START + RESERVED_SIZE,
                               MEM_INTMEM_SIZE - RESERVED_SIZE);
            initiated = 1;
            alloc->size = MEM_INTMEM_SIZE - RESERVED_SIZE;
            alloc->offset = 0;
            alloc->status = STATUS_FREE;
            list_add_tail(&alloc->entry, &intmem_allocations);
      }
}

void* crisv32_intmem_alloc(unsigned size, unsigned align)
{
      struct intmem_allocation* allocation;
      struct intmem_allocation* tmp;
      void* ret = NULL;

      preempt_disable();
      crisv32_intmem_init();

      list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
            int alignment = allocation->offset % align;
            alignment = alignment ? align - alignment : alignment;

            if (allocation->status == STATUS_FREE &&
                allocation->size >= size + alignment) {
                  if (allocation->size > size + alignment) {
                        struct intmem_allocation* alloc =
                              (struct intmem_allocation*)
                              kmalloc(sizeof *alloc, GFP_ATOMIC);
                        alloc->status = STATUS_FREE;
                        alloc->size = allocation->size - size -
                              alignment;
                        alloc->offset = allocation->offset + size +
                              alignment;
                        list_add(&alloc->entry, &allocation->entry);

                        if (alignment) {
                              struct intmem_allocation *tmp;
                              tmp = (struct intmem_allocation *)
                                    kmalloc(sizeof *tmp,
                                          GFP_ATOMIC);
                              tmp->offset = allocation->offset;
                              tmp->size = alignment;
                              tmp->status = STATUS_FREE;
                              allocation->offset += alignment;
                              list_add_tail(&tmp->entry,
                                    &allocation->entry);
                        }
                  }
                  allocation->status = STATUS_ALLOCATED;
                  allocation->size = size;
                  ret = (void*)((int)intmem_virtual + allocation->offset);
            }
      }
      preempt_enable();
      return ret;
}

void crisv32_intmem_free(void* addr)
{
      struct intmem_allocation* allocation;
      struct intmem_allocation* tmp;

      if (addr == NULL)
            return;

      preempt_disable();
      crisv32_intmem_init();

      list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
            if (allocation->offset == (int)(addr - intmem_virtual)) {
                  struct intmem_allocation *prev =
                    list_entry(allocation->entry.prev,
                               struct intmem_allocation, entry);
                  struct intmem_allocation *next =
                    list_entry(allocation->entry.next,
                             struct intmem_allocation, entry);

                  allocation->status = STATUS_FREE;
                  /* Join with prev and/or next if also free */
                  if ((prev != &intmem_allocations) &&
                              (prev->status == STATUS_FREE)) {
                        prev->size += allocation->size;
                        list_del(&allocation->entry);
                        kfree(allocation);
                        allocation = prev;
                  }
                  if ((next != &intmem_allocations) &&
                              (next->status == STATUS_FREE)) {
                        allocation->size += next->size;
                        list_del(&next->entry);
                        kfree(next);
                  }
                  preempt_enable();
                  return;
            }
      }
      preempt_enable();
}

void* crisv32_intmem_phys_to_virt(unsigned long addr)
{
      return (void *)(addr - (MEM_INTMEM_START + RESERVED_SIZE) +
            (unsigned long)intmem_virtual);
}

unsigned long crisv32_intmem_virt_to_phys(void* addr)
{
      return (unsigned long)((unsigned long )addr -
            (unsigned long)intmem_virtual + MEM_INTMEM_START +
            RESERVED_SIZE);
}

module_init(crisv32_intmem_init);


Generated by  Doxygen 1.6.0   Back to index