MIPS: PowerTV: Use O(1) algorthm for phys_to_dma/dma_to_phys
[pandora-kernel.git] / arch / mips / powertv / memory.c
index f49eb3d..73880ad 100644 (file)
 #include <asm/sections.h>
 
 #include <asm/mips-boards/prom.h>
+#include <asm/mach-powertv/asic.h>
+#include <asm/mach-powertv/ioremap.h>
 
 #include "init.h"
 
 /* Memory constants */
 #define KIBIBYTE(n)            ((n) * 1024)    /* Number of kibibytes */
 #define MEBIBYTE(n)            ((n) * KIBIBYTE(1024)) /* Number of mebibytes */
-#define DEFAULT_MEMSIZE                MEBIBYTE(256)   /* If no memsize provided */
-#define LOW_MEM_MAX            MEBIBYTE(252)   /* Max usable low mem */
-#define RES_BOOTLDR_MEMSIZE    MEBIBYTE(1)     /* Memory reserved for bldr */
-#define BOOT_MEM_SIZE          KIBIBYTE(256)   /* Memory reserved for bldr */
-#define PHYS_MEM_START         0x10000000      /* Start of physical memory */
+#define DEFAULT_MEMSIZE                MEBIBYTE(128)   /* If no memsize provided */
 
-char __initdata cmdline[COMMAND_LINE_SIZE];
+#define BLDR_SIZE      KIBIBYTE(256)           /* Memory reserved for bldr */
+#define RV_SIZE                MEBIBYTE(4)             /* Size of reset vector */
 
-void __init prom_meminit(void)
+#define LOW_MEM_END    0x20000000              /* Highest low memory address */
+#define BLDR_ALIAS     0x10000000              /* Bootloader address */
+#define RV_PHYS                0x1fc00000              /* Reset vector address */
+#define LOW_RAM_END    RV_PHYS                 /* End of real RAM in low mem */
+
+/*
+ * Very low-level conversion from processor physical address to device
+ * DMA address for the first bank of memory.
+ */
+#define PHYS_TO_DMA(paddr)     ((paddr) + (CONFIG_LOW_RAM_DMA - LOW_RAM_ALIAS))
+
+unsigned long ptv_memsize;
+
+/*
+ * struct low_mem_reserved - Items in low memmory that are reserved
+ * @start:     Physical address of item
+ * @size:      Size, in bytes, of this item
+ * @is_aliased:        True if this is RAM aliased from another location. If false,
+ *             it is something other than aliased RAM and the RAM in the
+ *             unaliased address is still visible outside of low memory.
+ */
+struct low_mem_reserved {
+       phys_addr_t     start;
+       phys_addr_t     size;
+       bool            is_aliased;
+};
+
+/*
+ * Must be in ascending address order
+ */
+struct low_mem_reserved low_mem_reserved[] = {
+       {BLDR_ALIAS, BLDR_SIZE, true},  /* Bootloader RAM */
+       {RV_PHYS, RV_SIZE, false},      /* Reset vector */
+};
+
+/*
+ * struct mem_layout - layout of a piece of the system RAM
+ * @phys:      Physical address of the start of this piece of RAM. This is the
+ *             address at which both the processor and I/O devices see the
+ *             RAM.
+ * @alias:     Alias of this piece of memory in order to make it appear in
+ *             the low memory part of the processor's address space. I/O
+ *             devices don't see anything here.
+ * @size:      Size, in bytes, of this piece of RAM
+ */
+struct mem_layout {
+       phys_addr_t     phys;
+       phys_addr_t     alias;
+       phys_addr_t     size;
+};
+
+/*
+ * struct mem_layout_list - list descriptor for layouts of system RAM pieces
+ * @family:    Specifies the family being described
+ * @n:         Number of &struct mem_layout elements
+ * @layout:    Pointer to the list of &mem_layout structures
+ */
+struct mem_layout_list {
+       enum family_type        family;
+       size_t                  n;
+       struct mem_layout       *layout;
+};
+
+static struct mem_layout f1500_layout[] = {
+       {0x20000000, 0x10000000, MEBIBYTE(256)},
+};
+
+static struct mem_layout f4500_layout[] = {
+       {0x40000000, 0x10000000, MEBIBYTE(256)},
+       {0x20000000, 0x20000000, MEBIBYTE(32)},
+};
+
+static struct mem_layout f8500_layout[] = {
+       {0x40000000, 0x10000000, MEBIBYTE(256)},
+       {0x20000000, 0x20000000, MEBIBYTE(32)},
+       {0x30000000, 0x30000000, MEBIBYTE(32)},
+};
+
+static struct mem_layout fx600_layout[] = {
+       {0x20000000, 0x10000000, MEBIBYTE(256)},
+       {0x60000000, 0x60000000, MEBIBYTE(128)},
+};
+
+static struct mem_layout_list layout_list[] = {
+       {FAMILY_1500, ARRAY_SIZE(f1500_layout), f1500_layout},
+       {FAMILY_1500VZE, ARRAY_SIZE(f1500_layout), f1500_layout},
+       {FAMILY_1500VZF, ARRAY_SIZE(f1500_layout), f1500_layout},
+       {FAMILY_4500, ARRAY_SIZE(f4500_layout), f4500_layout},
+       {FAMILY_8500, ARRAY_SIZE(f8500_layout), f8500_layout},
+       {FAMILY_8500RNG, ARRAY_SIZE(f8500_layout), f8500_layout},
+       {FAMILY_4600, ARRAY_SIZE(fx600_layout), fx600_layout},
+       {FAMILY_4600VZA, ARRAY_SIZE(fx600_layout), fx600_layout},
+       {FAMILY_8600, ARRAY_SIZE(fx600_layout), fx600_layout},
+       {FAMILY_8600VZB, ARRAY_SIZE(fx600_layout), fx600_layout},
+};
+
+/* If we can't determine the layout, use this */
+static struct mem_layout default_layout[] = {
+       {0x20000000, 0x10000000, MEBIBYTE(128)},
+};
+
+/**
+ * register_non_ram - register low memory not available for RAM usage
+ */
+static __init void register_non_ram(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(low_mem_reserved); i++)
+               add_memory_region(low_mem_reserved[i].start,
+                       low_mem_reserved[i].size, BOOT_MEM_RESERVED);
+}
+
+/**
+ * get_memsize - get the size of memory as a single bank
+ */
+static phys_addr_t get_memsize(void)
 {
+       static char cmdline[COMMAND_LINE_SIZE] __initdata;
+       phys_addr_t memsize = 0;
        char *memsize_str;
-       unsigned long memsize = 0;
-       unsigned int physend;
        char *ptr;
-       int low_mem;
-       int high_mem;
 
        /* Check the command line first for a memsize directive */
        strcpy(cmdline, arcs_cmdline);
@@ -73,96 +186,156 @@ void __init prom_meminit(void)
                if (memsize == 0) {
                        if (_prom_memsize != 0) {
                                memsize = _prom_memsize;
-                               pr_info("_prom_memsize = 0x%lx\n", memsize);
+                               pr_info("_prom_memsize = 0x%x\n", memsize);
                                /* add in memory that the bootloader doesn't
                                 * report */
-                               memsize += BOOT_MEM_SIZE;
+                               memsize += BLDR_SIZE;
                        } else {
                                memsize = DEFAULT_MEMSIZE;
                                pr_info("Memsize not passed by bootloader, "
-                                       "defaulting to 0x%lx\n", memsize);
+                                       "defaulting to 0x%x\n", memsize);
                        }
                }
        }
 
-       physend = PFN_ALIGN(&_end) - 0x80000000;
-       if (memsize > LOW_MEM_MAX) {
-               low_mem = LOW_MEM_MAX;
-               high_mem = memsize - low_mem;
-       } else {
-               low_mem = memsize;
-               high_mem = 0;
+       return memsize;
+}
+
+/**
+ * register_low_ram - register an aliased section of RAM
+ * @p:         Alias address of memory
+ * @n:         Number of bytes in this section of memory
+ *
+ * Returns the number of bytes registered
+ *
+ */
+static __init phys_addr_t register_low_ram(phys_addr_t p, phys_addr_t n)
+{
+       phys_addr_t s;
+       int i;
+       phys_addr_t orig_n;
+
+       orig_n = n;
+
+       BUG_ON(p + n > RV_PHYS);
+
+       for (i = 0; n != 0 && i < ARRAY_SIZE(low_mem_reserved); i++) {
+               phys_addr_t start;
+               phys_addr_t size;
+
+               start = low_mem_reserved[i].start;
+               size = low_mem_reserved[i].size;
+
+               /* Handle memory before this low memory section */
+               if (p < start) {
+                       phys_addr_t s;
+                       s = min(n, start - p);
+                       add_memory_region(p, s, BOOT_MEM_RAM);
+                       p += s;
+                       n -= s;
+               }
+
+               /* Handle the low memory section itself. If it's aliased,
+                * we reduce the number of byes left, but if not, the RAM
+                * is available elsewhere and we don't reduce the number of
+                * bytes remaining. */
+               if (p == start) {
+                       if (low_mem_reserved[i].is_aliased) {
+                               s = min(n, size);
+                               n -= s;
+                               p += s;
+                       } else
+                               p += n;
+               }
        }
 
+       return orig_n - n;
+}
+
 /*
- * TODO: We will use the hard code for memory configuration until
- * the bootloader releases their device tree to us.
+ * register_ram - register real RAM
+ * @p: Address of memory as seen by devices
+ * @alias:     If the memory is seen at an additional address by the processor,
+ *             this will be the address, otherwise it is the same as @p.
+ * @n:         Number of bytes in this section of memory
  */
+static __init void register_ram(phys_addr_t p, phys_addr_t alias,
+       phys_addr_t n)
+{
        /*
-        * Add the memory reserved for use by the bootloader to the
-        * memory map.
-        */
-       add_memory_region(PHYS_MEM_START, RES_BOOTLDR_MEMSIZE,
-               BOOT_MEM_RESERVED);
-#ifdef CONFIG_HIGHMEM_256_128
-       /*
-        * Add memory in low for general use by the kernel and its friends
-        * (like drivers, applications, etc).
-        */
-       add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
-               LOW_MEM_MAX - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
-       /*
-        * Add the memory reserved for reset vector.
-        */
-       add_memory_region(0x1fc00000, MEBIBYTE(4), BOOT_MEM_RESERVED);
-       /*
-        * Add the memory reserved.
-        */
-       add_memory_region(0x20000000, MEBIBYTE(1024 + 75), BOOT_MEM_RESERVED);
-       /*
-        * Add memory in high for general use by the kernel and its friends
-        * (like drivers, applications, etc).
-        *
-        * 75MB is reserved for devices which are using the memory in high.
-        */
-       add_memory_region(0x60000000 + MEBIBYTE(75), MEBIBYTE(128 - 75),
-               BOOT_MEM_RAM);
-#elif defined CONFIG_HIGHMEM_128_128
-       /*
-        * Add memory in low for general use by the kernel and its friends
-        * (like drivers, applications, etc).
-        */
-       add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
-               MEBIBYTE(128) - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
-       /*
-        * Add the memory reserved.
-        */
-       add_memory_region(PHYS_MEM_START + MEBIBYTE(128),
-               MEBIBYTE(128 + 1024 + 75), BOOT_MEM_RESERVED);
-       /*
-        * Add memory in high for general use by the kernel and its friends
-        * (like drivers, applications, etc).
-        *
-        * 75MB is reserved for devices which are using the memory in high.
-        */
-       add_memory_region(0x60000000 + MEBIBYTE(75), MEBIBYTE(128 - 75),
-               BOOT_MEM_RAM);
-#else
-       /* Add low memory regions for either:
-        *   - no-highmemory configuration case -OR-
-        *   - highmemory "HIGHMEM_LOWBANK_ONLY" case
-        */
-       /*
-        * Add memory for general use by the kernel and its friends
-        * (like drivers, applications, etc).
+        * If some or all of this memory has an alias, break it into the
+        * aliased and non-aliased portion.
         */
-       add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
-               low_mem - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
+       if (p != alias) {
+               phys_addr_t alias_size;
+               phys_addr_t registered;
+
+               alias_size = min(n, LOW_RAM_END - alias);
+               registered = register_low_ram(alias, alias_size);
+               ioremap_add_map(alias, p, n);
+               n -= registered;
+               p += registered;
+       }
+
+#ifdef CONFIG_HIGHMEM
+       if (n != 0) {
+               add_memory_region(p, n, BOOT_MEM_RAM);
+               ioremap_add_map(p, p, n);
+       }
+#endif
+}
+
+/**
+ * register_address_space - register things in the address space
+ * @memsize:   Number of bytes of RAM installed
+ *
+ * Takes the given number of bytes of RAM and registers as many of the regions,
+ * or partial regions, as it can. So, the default configuration might have
+ * two regions with 256 MiB each. If the memsize passed in on the command line
+ * is 384 MiB, it will register the first region with 256 MiB and the second
+ * with 128 MiB.
+ */
+static __init void register_address_space(phys_addr_t memsize)
+{
+       int i;
+       phys_addr_t size;
+       size_t n;
+       struct mem_layout *layout;
+       enum family_type family;
+
        /*
-        * Add the memory reserved for reset vector.
+        * Register all of the things that aren't available to the kernel as
+        * memory.
         */
-       add_memory_region(0x1fc00000, MEBIBYTE(4), BOOT_MEM_RESERVED);
-#endif
+       register_non_ram();
+
+       /* Find the appropriate memory description */
+       family = platform_get_family();
+
+       for (i = 0; i < ARRAY_SIZE(layout_list); i++) {
+               if (layout_list[i].family == family)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(layout_list)) {
+               n = ARRAY_SIZE(default_layout);
+               layout = default_layout;
+       } else {
+               n = layout_list[i].n;
+               layout = layout_list[i].layout;
+       }
+
+       for (i = 0; memsize != 0 && i < n; i++) {
+               size = min(memsize, layout[i].size);
+               register_ram(layout[i].phys, layout[i].alias, size);
+               memsize -= size;
+       }
+}
+
+void __init prom_meminit(void)
+{
+       ptv_memsize = get_memsize();
+       register_address_space(ptv_memsize);
 }
 
 void __init prom_free_prom_memory(void)