aboutsummaryrefslogtreecommitdiffstats
path: root/arch/metag/mm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/metag/mm')
-rw-r--r--arch/metag/mm/ioremap.c89
-rw-r--r--arch/metag/mm/maccess.c68
2 files changed, 157 insertions, 0 deletions
diff --git a/arch/metag/mm/ioremap.c b/arch/metag/mm/ioremap.c
new file mode 100644
index 000000000000..a136a435fdaa
--- /dev/null
+++ b/arch/metag/mm/ioremap.c
@@ -0,0 +1,89 @@
+/*
+ * Re-map IO memory to kernel address space so that we can access it.
+ * Needed for memory-mapped I/O devices mapped outside our normal DRAM
+ * window (that is, all memory-mapped I/O devices).
+ *
+ * Copyright (C) 1995,1996 Linus Torvalds
+ *
+ * Meta port based on CRIS-port by Axis Communications AB
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <asm/pgtable.h>
+
+/*
+ * Remap an arbitrary physical address space into the kernel virtual
+ * address space. Needed when the kernel wants to access high addresses
+ * directly.
+ *
+ * NOTE! We need to allow non-page-aligned mappings too: we will obviously
+ * have to convert them into an offset in a page-aligned mapping, but the
+ * caller shouldn't need to know that small detail.
+ */
+void __iomem *__ioremap(unsigned long phys_addr, size_t size,
+ unsigned long flags)
+{
+ unsigned long addr;
+ struct vm_struct *area;
+ unsigned long offset, last_addr;
+ pgprot_t prot;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /* Custom region addresses are accessible and uncached by default. */
+ if (phys_addr >= LINSYSCUSTOM_BASE &&
+ phys_addr < (LINSYSCUSTOM_BASE + LINSYSCUSTOM_LIMIT))
+ return (__force void __iomem *) phys_addr;
+
+ /*
+ * Mappings have to be page-aligned
+ */
+ offset = phys_addr & ~PAGE_MASK;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr+1) - phys_addr;
+ prot = __pgprot(_PAGE_PRESENT | _PAGE_WRITE | _PAGE_DIRTY |
+ _PAGE_ACCESSED | _PAGE_KERNEL | _PAGE_CACHE_WIN0 |
+ flags);
+
+ /*
+ * Ok, go for it..
+ */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (!area)
+ return NULL;
+ area->phys_addr = phys_addr;
+ addr = (unsigned long) area->addr;
+ if (ioremap_page_range(addr, addr + size, phys_addr, prot)) {
+ vunmap((void *) addr);
+ return NULL;
+ }
+ return (__force void __iomem *) (offset + (char *)addr);
+}
+EXPORT_SYMBOL(__ioremap);
+
+void __iounmap(void __iomem *addr)
+{
+ struct vm_struct *p;
+
+ if ((__force unsigned long)addr >= LINSYSCUSTOM_BASE &&
+ (__force unsigned long)addr < (LINSYSCUSTOM_BASE +
+ LINSYSCUSTOM_LIMIT))
+ return;
+
+ p = remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr));
+ if (unlikely(!p)) {
+ pr_err("iounmap: bad address %p\n", addr);
+ return;
+ }
+
+ kfree(p);
+}
+EXPORT_SYMBOL(__iounmap);
diff --git a/arch/metag/mm/maccess.c b/arch/metag/mm/maccess.c
new file mode 100644
index 000000000000..eba2cfc935b1
--- /dev/null
+++ b/arch/metag/mm/maccess.c
@@ -0,0 +1,68 @@
+/*
+ * safe read and write memory routines callable while atomic
+ *
+ * Copyright 2012 Imagination Technologies
+ */
+
+#include <linux/uaccess.h>
+#include <asm/io.h>
+
+/*
+ * The generic probe_kernel_write() uses the user copy code which can split the
+ * writes if the source is unaligned, and repeats writes to make exceptions
+ * precise. We override it here to avoid these things happening to memory mapped
+ * IO memory where they could have undesired effects.
+ * Due to the use of CACHERD instruction this only works on Meta2 onwards.
+ */
+#ifdef CONFIG_METAG_META21
+long probe_kernel_write(void *dst, const void *src, size_t size)
+{
+ unsigned long ldst = (unsigned long)dst;
+ void __iomem *iodst = (void __iomem *)dst;
+ unsigned long lsrc = (unsigned long)src;
+ const u8 *psrc = (u8 *)src;
+ unsigned int pte, i;
+ u8 bounce[8] __aligned(8);
+
+ if (!size)
+ return 0;
+
+ /* Use the write combine bit to decide is the destination is MMIO. */
+ pte = __builtin_meta2_cacherd(dst);
+
+ /* Check the mapping is valid and writeable. */
+ if ((pte & (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
+ != (MMCU_ENTRY_WR_BIT | MMCU_ENTRY_VAL_BIT))
+ return -EFAULT;
+
+ /* Fall back to generic version for cases we're not interested in. */
+ if (pte & MMCU_ENTRY_WRC_BIT || /* write combined memory */
+ (ldst & (size - 1)) || /* destination unaligned */
+ size > 8 || /* more than max write size */
+ (size & (size - 1))) /* non power of 2 size */
+ return __probe_kernel_write(dst, src, size);
+
+ /* If src is unaligned, copy to the aligned bounce buffer first. */
+ if (lsrc & (size - 1)) {
+ for (i = 0; i < size; ++i)
+ bounce[i] = psrc[i];
+ psrc = bounce;
+ }
+
+ switch (size) {
+ case 1:
+ writeb(*psrc, iodst);
+ break;
+ case 2:
+ writew(*(const u16 *)psrc, iodst);
+ break;
+ case 4:
+ writel(*(const u32 *)psrc, iodst);
+ break;
+ case 8:
+ writeq(*(const u64 *)psrc, iodst);
+ break;
+ }
+ return 0;
+}
+#endif