sh: Add fixed ioremap support
authorMatt Fleming <matt@console-pimps.org>
Thu, 5 Nov 2009 07:54:17 +0000 (07:54 +0000)
committerMatt Fleming <matt@console-pimps.org>
Sat, 16 Jan 2010 14:31:36 +0000 (14:31 +0000)
Some devices need to be ioremap'd and accessed very early in the boot
process. It is not possible to use the standard ioremap() function in
this case because that requires kmalloc()'ing some virtual address space
and kmalloc() may not be available so early in boot.

This patch provides fixmap mappings that allow physical address ranges
to be remapped into the kernel address space during the early boot
stages.

Signed-off-by: Matt Fleming <matt@console-pimps.org>
arch/sh/include/asm/fixmap.h
arch/sh/include/asm/io.h
arch/sh/kernel/setup.c
arch/sh/mm/Kconfig
arch/sh/mm/Makefile
arch/sh/mm/ioremap_fixed.c [new file with mode: 0644]

index 1566d3361ca4c1e180e79c6c62106bcb70bdca5b..38a1de866873b363046f6b5220bfb5247c2e8f14 100644 (file)
@@ -60,6 +60,14 @@ enum fixed_addresses {
        FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
        FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
 #endif
+       /*
+        * FIX_IOREMAP entries are useful for mapping physical address
+        * space before ioremap() is useable, e.g. really early in boot
+        * before kmalloc() is working.
+        */
+#define FIX_N_IOREMAPS 32
+       FIX_IOREMAP_BEGIN,
+       FIX_IOREMAP_END = FIX_IOREMAP_BEGIN + FIX_N_IOREMAPS,
        __end_of_fixed_addresses
 };
 
index f4314d8b05b8f4a7dab0df47e87acd8e52727461..bee5965e0a82a37022abf83cdff0c9519e802296 100644 (file)
@@ -237,6 +237,12 @@ void __iomem *__ioremap_caller(unsigned long offset, unsigned long size,
                               unsigned long flags, void *caller);
 void __iounmap(void __iomem *addr);
 
+#ifdef CONFIG_IOREMAP_FIXED
+extern void __iomem *ioremap_fixed(resource_size_t, unsigned long, pgprot_t);
+extern void iounmap_fixed(void __iomem *);
+extern void ioremap_fixed_init(void);
+#endif
+
 static inline void __iomem *
 __ioremap(unsigned long offset, unsigned long size, unsigned long flags)
 {
index f79ebe32a24aa2d7f4ae7c718dc9096997b6a351..e187750dd3193440f5d6c21ff2abbcfc9dd85c63 100644 (file)
@@ -449,14 +449,15 @@ void __init setup_arch(char **cmdline_p)
 #ifdef CONFIG_DUMMY_CONSOLE
        conswitchp = &dummy_con;
 #endif
+       paging_init();
+       pmb_init();
+
+       ioremap_fixed_init();
 
        /* Perform the machine specific initialisation */
        if (likely(sh_mv.mv_setup))
                sh_mv.mv_setup(cmdline_p);
 
-       paging_init();
-       pmb_init();
-
 #ifdef CONFIG_SMP
        plat_smp_setup();
 #endif
index 7a4ebc8cbaddf8b4df3fcdcefbbd1eeaf9810576..b89075256b70ea0dbdac7f9fd5247191a54503ec 100644 (file)
@@ -169,6 +169,10 @@ config ARCH_MEMORY_PROBE
        def_bool y
        depends on MEMORY_HOTPLUG
 
+config IOREMAP_FIXED
+       def_bool y
+       depends on X2TLB || SUPERH64
+
 choice
        prompt "Kernel page size"
        default PAGE_SIZE_4KB
index edde8bdd681d474aef5cc3293a34477e3492e9b2..89ba56c20ade06b7788b0ce24c5b7acfc0e551e9 100644 (file)
@@ -35,6 +35,7 @@ endif
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
 obj-$(CONFIG_PMB)              += pmb.o
 obj-$(CONFIG_NUMA)             += numa.o
+obj-$(CONFIG_IOREMAP_FIXED)    += ioremap_fixed.o
 
 # Special flags for fault_64.o.  This puts restrictions on the number of
 # caller-save registers that the compiler can target when building this file.
diff --git a/arch/sh/mm/ioremap_fixed.c b/arch/sh/mm/ioremap_fixed.c
new file mode 100644 (file)
index 0000000..3a9d3d8
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Re-map IO memory to kernel address space so that we can access it.
+ *
+ * These functions should only be used when it is necessary to map a
+ * physical address space into the kernel address space before ioremap()
+ * can be used, e.g. early in boot before paging_init().
+ *
+ * Copyright (C) 2009  Matt Fleming
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/bootmem.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <asm/fixmap.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/addrspace.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+
+struct ioremap_map {
+       void __iomem *addr;
+       unsigned long size;
+       unsigned long fixmap_addr;
+};
+
+static struct ioremap_map ioremap_maps[FIX_N_IOREMAPS];
+
+void __init ioremap_fixed_init(void)
+{
+       struct ioremap_map *map;
+       int i;
+
+       for (i = 0; i < FIX_N_IOREMAPS; i++) {
+               map = &ioremap_maps[i];
+               map->fixmap_addr = __fix_to_virt(FIX_IOREMAP_BEGIN + i);
+       }
+}
+
+void __init __iomem *
+ioremap_fixed(resource_size_t phys_addr, unsigned long size, pgprot_t prot)
+{
+       enum fixed_addresses idx0, idx;
+       resource_size_t last_addr;
+       struct ioremap_map *map;
+       unsigned long offset;
+       unsigned int nrpages;
+       int i, slot;
+
+       slot = -1;
+       for (i = 0; i < FIX_N_IOREMAPS; i++) {
+               map = &ioremap_maps[i];
+               if (!map->addr) {
+                       map->size = size;
+                       slot = i;
+                       break;
+               }
+       }
+
+       if (slot < 0)
+               return NULL;
+
+       /* Don't allow wraparound or zero size */
+       last_addr = phys_addr + size - 1;
+       if (!size || last_addr < phys_addr)
+               return NULL;
+
+       /*
+        * Fixmap mappings have to be page-aligned
+        */
+       offset = phys_addr & ~PAGE_MASK;
+       phys_addr &= PAGE_MASK;
+       size = PAGE_ALIGN(last_addr + 1) - phys_addr;
+
+       /*
+        * Mappings have to fit in the FIX_IOREMAP area.
+        */
+       nrpages = size >> PAGE_SHIFT;
+       if (nrpages > FIX_N_IOREMAPS)
+               return NULL;
+
+       /*
+        * Ok, go for it..
+        */
+       idx0 = FIX_IOREMAP_BEGIN + slot;
+       idx = idx0;
+       while (nrpages > 0) {
+               pgprot_val(prot) |= _PAGE_WIRED;
+               __set_fixmap(idx, phys_addr, prot);
+               phys_addr += PAGE_SIZE;
+               idx++;
+               --nrpages;
+       }
+
+       map->addr = (void __iomem *)(offset + map->fixmap_addr);
+       return map->addr;
+}
+
+void __init iounmap_fixed(void __iomem *addr)
+{
+       enum fixed_addresses idx;
+       unsigned long virt_addr;
+       struct ioremap_map *map;
+       unsigned long offset;
+       unsigned int nrpages;
+       int i, slot;
+       pgprot_t prot;
+
+       slot = -1;
+       for (i = 0; i < FIX_N_IOREMAPS; i++) {
+               map = &ioremap_maps[i];
+               if (map->addr == addr) {
+                       slot = i;
+                       break;
+               }
+       }
+
+       if (slot < 0)
+               return;
+
+       virt_addr = (unsigned long)addr;
+
+       offset = virt_addr & ~PAGE_MASK;
+       nrpages = PAGE_ALIGN(offset + map->size - 1) >> PAGE_SHIFT;
+
+       pgprot_val(prot) = _PAGE_WIRED;
+
+       idx = FIX_IOREMAP_BEGIN + slot + nrpages;
+       while (nrpages > 0) {
+               __clear_fixmap(idx, prot);
+               --idx;
+               --nrpages;
+       }
+
+       map->size = 0;
+       map->addr = NULL;
+}