From: Ley Foon Tan <lftan@altera.com>
To: linux-arch@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org
Cc: Ley Foon Tan <lftan@altera.com>,
lftan.linux@gmail.com, cltang@codesourcery.com
Subject: [PATCH v3 07/29] nios2: I/O Mapping
Date: Mon, 8 Sep 2014 17:22:18 +0800 [thread overview]
Message-ID: <1410168160-3624-8-git-send-email-lftan@altera.com> (raw)
In-Reply-To: <1410168160-3624-1-git-send-email-lftan@altera.com>
This patch adds several definitions for I/O accessors and ioremap().
Signed-off-by: Ley Foon Tan <lftan@altera.com>
---
arch/nios2/include/asm/io.h | 84 ++++++++++++++++++++
arch/nios2/lib/io.c | 138 ++++++++++++++++++++++++++++++++
arch/nios2/mm/ioremap.c | 186 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 408 insertions(+)
create mode 100644 arch/nios2/include/asm/io.h
create mode 100644 arch/nios2/lib/io.c
create mode 100644 arch/nios2/mm/ioremap.c
diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h
new file mode 100644
index 0000000..c629226
--- /dev/null
+++ b/arch/nios2/include/asm/io.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_IO_H
+#define _ASM_NIOS2_IO_H
+
+#include <asm/pgtable-bits.h>
+
+/* PCI is not supported in nios2, set this to 0. */
+#define IO_SPACE_LIMIT 0
+
+extern void outsb(unsigned long addr, const void *buf, int len);
+extern void outsw(unsigned long addr, const void *buf, int len);
+extern void outsl(unsigned long addr, const void *buf, int len);
+extern void insb(unsigned long addr, void *buf, int len);
+extern void insw(unsigned long addr, void *buf, int len);
+extern void insl(unsigned long addr, void *buf, int len);
+#define outsb outsb
+#define outsw outsw
+#define outsl outsl
+#define insb insb
+#define insw insw
+#define insl insl
+
+#define readb_relaxed(addr) readb(addr)
+#define readw_relaxed(addr) readw(addr)
+#define readl_relaxed(addr) readl(addr)
+
+#define writeb_relaxed(x, addr) writeb(x, addr)
+#define writew_relaxed(x, addr) writew(x, addr)
+#define writel_relaxed(x, addr) writel(x, addr)
+
+#include <asm-generic/io.h>
+
+/*
+ * PCI is not enabled in nios2. Provide dummy implementation to get 'allyesconfig'
+ * to build successfully.
+ */
+#define inb(addr) ({ 0; })
+#define inw(addr) ({ 0; })
+#define inl(addr) ({ 0; })
+#define outb(x, addr) do { } while (0)
+#define outw(x, addr) do { } while (0)
+#define outl(x, addr) do { } while (0)
+
+extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size,
+ unsigned long cacheflag);
+extern void __iounmap(void __iomem *addr);
+
+static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void __iomem *ioremap_nocache(unsigned long physaddr,
+ unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void iounmap(void __iomem *addr)
+{
+ __iounmap(addr);
+}
+
+/* Pages to physical address... */
+# define page_to_phys(page) virt_to_phys(page_to_virt(page))
+# define page_to_bus(page) page_to_virt(page)
+
+/* Macros used for converting between virtual and physical mappings. */
+# define phys_to_virt(vaddr) \
+ ((void *)((unsigned long)(vaddr) | CONFIG_NIOS2_KERNEL_REGION_BASE))
+/* Clear top 3 bits */
+# define virt_to_phys(vaddr) \
+ ((unsigned long)((unsigned long)(vaddr) & ~0xE0000000))
+
+#endif /* _ASM_NIOS2_IO_H */
diff --git a/arch/nios2/lib/io.c b/arch/nios2/lib/io.c
new file mode 100644
index 0000000..94db593
--- /dev/null
+++ b/arch/nios2/lib/io.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/types.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE
+#define __IO_USE_DUFFS
+#endif
+
+#ifdef __IO_USE_DUFFS
+
+/* Use "Duff's Device" to unroll the loops. */
+#define __IO_OUT_LOOP(a, b, l) \
+ do { \
+ if (l > 0) { \
+ int _n = (l + 7) / 8; \
+ switch (l % 8) { \
+ case 0: \
+ do { \
+ *a = *b++; \
+ case 7: \
+ *a = *b++; \
+ case 6: \
+ *a = *b++; \
+ case 5: \
+ *a = *b++; \
+ case 4: \
+ *a = *b++; \
+ case 3: \
+ *a = *b++; \
+ case 2: \
+ *a = *b++; \
+ case 1: \
+ *a = *b++; \
+ } while (--_n > 0); \
+ } \
+ } \
+ } while (0)
+
+#define __IO_IN_LOOP(a, b, l) \
+ do { \
+ if (l > 0) { \
+ int _n = (l + 7) / 8; \
+ switch (l % 8) { \
+ case 0: \
+ do { \
+ *b++ = *a; \
+ case 7: \
+ *b++ = *a; \
+ case 6: \
+ *b++ = *a; \
+ case 5: \
+ *b++ = *a; \
+ case 4: \
+ *b++ = *a; \
+ case 3: \
+ *b++ = *a; \
+ case 2: \
+ *b++ = *a; \
+ case 1: \
+ *b++ = *a; \
+ } while (--_n > 0); \
+ } \
+ } \
+ } while (0)
+
+#else /* __IO_USE_DUFFS */
+
+/* Use simple loops. */
+#define __IO_OUT_LOOP(a, b, l) \
+ do { \
+ while (l--) \
+ *a = *b++; \
+ } while (0)
+
+#define __IO_IN_LOOP(a, b, l) \
+ do { \
+ while (l--) \
+ *b++ = *a; \
+ } while (0)
+
+#endif /* __IO_USE_DUFFS */
+
+void outsb(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned char *ap = (volatile unsigned char *)addr;
+ unsigned char *bp = (unsigned char *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsb);
+
+void outsw(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned short *ap = (volatile unsigned short *)addr;
+ unsigned short *bp = (unsigned short *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsw);
+
+void outsl(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned int *ap = (volatile unsigned int *)addr;
+ unsigned int *bp = (unsigned int *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsl);
+
+void insb(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned char *ap = (volatile unsigned char *)addr;
+ unsigned char *bp = (unsigned char *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insb);
+
+void insw(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned short *ap = (volatile unsigned short *)addr;
+ unsigned short *bp = (unsigned short *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insw);
+
+void insl(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned int *ap = (volatile unsigned int *)addr;
+ unsigned int *bp = (unsigned int *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insl);
diff --git a/arch/nios2/mm/ioremap.c b/arch/nios2/mm/ioremap.c
new file mode 100644
index 0000000..a46bccf
--- /dev/null
+++ b/arch/nios2/mm/ioremap.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+static inline void remap_area_pte(pte_t *pte, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+ unsigned long pfn;
+ pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ
+ | _PAGE_WRITE | flags);
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ if (address >= end)
+ BUG();
+ pfn = PFN_DOWN(phys_addr);
+ do {
+ if (!pte_none(*pte)) {
+ pr_err("remap_area_pte: page already exists\n");
+ BUG();
+ }
+ set_pte(pte, pfn_pte(pfn, pgprot));
+ address += PAGE_SIZE;
+ pfn++;
+ pte++;
+ } while (address && (address < end));
+}
+
+static inline int remap_area_pmd(pmd_t *pmd, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ phys_addr -= address;
+ if (address >= end)
+ BUG();
+ do {
+ pte_t *pte = pte_alloc_kernel(pmd, address);
+ if (!pte)
+ return -ENOMEM;
+ remap_area_pte(pte, address, end - address, address + phys_addr,
+ flags);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long phys_addr,
+ unsigned long size, unsigned long flags)
+{
+ int error;
+ pgd_t *dir;
+ unsigned long end = address + size;
+
+ phys_addr -= address;
+ dir = pgd_offset(&init_mm, address);
+ flush_cache_all();
+ if (address >= end)
+ BUG();
+ do {
+ pud_t *pud;
+ pmd_t *pmd;
+
+ error = -ENOMEM;
+ pud = pud_alloc(&init_mm, dir, address);
+ if (!pud)
+ break;
+ pmd = pmd_alloc(&init_mm, pud, address);
+ if (!pmd)
+ break;
+ if (remap_area_pmd(pmd, address, end - address,
+ phys_addr + address, flags))
+ break;
+ error = 0;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ } while (address && (address < end));
+ flush_tlb_all();
+ return error;
+}
+
+#define IS_MAPPABLE_UNCACHEABLE(addr) (addr < 0x20000000UL)
+
+/*
+ * Map some physical address range into the kernel address space.
+ */
+void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
+ unsigned long cacheflag)
+{
+ struct vm_struct *area;
+ unsigned long offset;
+ unsigned long last_addr;
+ void *addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /* Don't allow anybody to remap normal RAM that we're using */
+ if (phys_addr > PHYS_OFFSET && phys_addr < virt_to_phys(high_memory)) {
+ char *t_addr, *t_end;
+ struct page *page;
+
+ t_addr = __va(phys_addr);
+ t_end = t_addr + (size - 1);
+ for (page = virt_to_page(t_addr);
+ page <= virt_to_page(t_end); page++)
+ if (!PageReserved(page))
+ return NULL;
+ }
+
+ /*
+ * Map uncached objects in the low part of address space to
+ * CONFIG_NIOS2_IO_REGION_BASE
+ */
+ if (IS_MAPPABLE_UNCACHEABLE(phys_addr) &&
+ IS_MAPPABLE_UNCACHEABLE(last_addr) &&
+ !(cacheflag & _PAGE_CACHED))
+ return (void __iomem *)(CONFIG_NIOS2_IO_REGION_BASE + 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;
+
+ /* Ok, go for it */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (!area)
+ return NULL;
+ addr = area->addr;
+ if (remap_area_pages((unsigned long) addr, phys_addr, size,
+ cacheflag)) {
+ vunmap(addr);
+ return NULL;
+ }
+ return (void __iomem *) (offset + (char *)addr);
+}
+EXPORT_SYMBOL(__ioremap);
+
+/*
+ * __iounmap unmaps nearly everything, so be careful
+ * it doesn't free currently pointer/page tables anymore but it
+ * wasn't used anyway and might be added later.
+ */
+void __iounmap(void __iomem *addr)
+{
+ struct vm_struct *p;
+
+ if ((unsigned long) addr > CONFIG_NIOS2_IO_REGION_BASE)
+ return;
+
+ p = remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr));
+ if (!p)
+ pr_err("iounmap: bad address %p\n", addr);
+ kfree(p);
+}
+EXPORT_SYMBOL(__iounmap);
--
1.8.2.1
WARNING: multiple messages have this Message-ID (diff)
From: Ley Foon Tan <lftan@altera.com>
To: <linux-arch@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<linux-doc@vger.kernel.org>
Cc: Ley Foon Tan <lftan@altera.com>, <lftan.linux@gmail.com>,
<cltang@codesourcery.com>
Subject: [PATCH v3 07/29] nios2: I/O Mapping
Date: Mon, 8 Sep 2014 17:22:18 +0800 [thread overview]
Message-ID: <1410168160-3624-8-git-send-email-lftan@altera.com> (raw)
In-Reply-To: <1410168160-3624-1-git-send-email-lftan@altera.com>
This patch adds several definitions for I/O accessors and ioremap().
Signed-off-by: Ley Foon Tan <lftan@altera.com>
---
arch/nios2/include/asm/io.h | 84 ++++++++++++++++++++
arch/nios2/lib/io.c | 138 ++++++++++++++++++++++++++++++++
arch/nios2/mm/ioremap.c | 186 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 408 insertions(+)
create mode 100644 arch/nios2/include/asm/io.h
create mode 100644 arch/nios2/lib/io.c
create mode 100644 arch/nios2/mm/ioremap.c
diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h
new file mode 100644
index 0000000..c629226
--- /dev/null
+++ b/arch/nios2/include/asm/io.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_IO_H
+#define _ASM_NIOS2_IO_H
+
+#include <asm/pgtable-bits.h>
+
+/* PCI is not supported in nios2, set this to 0. */
+#define IO_SPACE_LIMIT 0
+
+extern void outsb(unsigned long addr, const void *buf, int len);
+extern void outsw(unsigned long addr, const void *buf, int len);
+extern void outsl(unsigned long addr, const void *buf, int len);
+extern void insb(unsigned long addr, void *buf, int len);
+extern void insw(unsigned long addr, void *buf, int len);
+extern void insl(unsigned long addr, void *buf, int len);
+#define outsb outsb
+#define outsw outsw
+#define outsl outsl
+#define insb insb
+#define insw insw
+#define insl insl
+
+#define readb_relaxed(addr) readb(addr)
+#define readw_relaxed(addr) readw(addr)
+#define readl_relaxed(addr) readl(addr)
+
+#define writeb_relaxed(x, addr) writeb(x, addr)
+#define writew_relaxed(x, addr) writew(x, addr)
+#define writel_relaxed(x, addr) writel(x, addr)
+
+#include <asm-generic/io.h>
+
+/*
+ * PCI is not enabled in nios2. Provide dummy implementation to get 'allyesconfig'
+ * to build successfully.
+ */
+#define inb(addr) ({ 0; })
+#define inw(addr) ({ 0; })
+#define inl(addr) ({ 0; })
+#define outb(x, addr) do { } while (0)
+#define outw(x, addr) do { } while (0)
+#define outl(x, addr) do { } while (0)
+
+extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size,
+ unsigned long cacheflag);
+extern void __iounmap(void __iomem *addr);
+
+static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void __iomem *ioremap_nocache(unsigned long physaddr,
+ unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void iounmap(void __iomem *addr)
+{
+ __iounmap(addr);
+}
+
+/* Pages to physical address... */
+# define page_to_phys(page) virt_to_phys(page_to_virt(page))
+# define page_to_bus(page) page_to_virt(page)
+
+/* Macros used for converting between virtual and physical mappings. */
+# define phys_to_virt(vaddr) \
+ ((void *)((unsigned long)(vaddr) | CONFIG_NIOS2_KERNEL_REGION_BASE))
+/* Clear top 3 bits */
+# define virt_to_phys(vaddr) \
+ ((unsigned long)((unsigned long)(vaddr) & ~0xE0000000))
+
+#endif /* _ASM_NIOS2_IO_H */
diff --git a/arch/nios2/lib/io.c b/arch/nios2/lib/io.c
new file mode 100644
index 0000000..94db593
--- /dev/null
+++ b/arch/nios2/lib/io.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/types.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE
+#define __IO_USE_DUFFS
+#endif
+
+#ifdef __IO_USE_DUFFS
+
+/* Use "Duff's Device" to unroll the loops. */
+#define __IO_OUT_LOOP(a, b, l) \
+ do { \
+ if (l > 0) { \
+ int _n = (l + 7) / 8; \
+ switch (l % 8) { \
+ case 0: \
+ do { \
+ *a = *b++; \
+ case 7: \
+ *a = *b++; \
+ case 6: \
+ *a = *b++; \
+ case 5: \
+ *a = *b++; \
+ case 4: \
+ *a = *b++; \
+ case 3: \
+ *a = *b++; \
+ case 2: \
+ *a = *b++; \
+ case 1: \
+ *a = *b++; \
+ } while (--_n > 0); \
+ } \
+ } \
+ } while (0)
+
+#define __IO_IN_LOOP(a, b, l) \
+ do { \
+ if (l > 0) { \
+ int _n = (l + 7) / 8; \
+ switch (l % 8) { \
+ case 0: \
+ do { \
+ *b++ = *a; \
+ case 7: \
+ *b++ = *a; \
+ case 6: \
+ *b++ = *a; \
+ case 5: \
+ *b++ = *a; \
+ case 4: \
+ *b++ = *a; \
+ case 3: \
+ *b++ = *a; \
+ case 2: \
+ *b++ = *a; \
+ case 1: \
+ *b++ = *a; \
+ } while (--_n > 0); \
+ } \
+ } \
+ } while (0)
+
+#else /* __IO_USE_DUFFS */
+
+/* Use simple loops. */
+#define __IO_OUT_LOOP(a, b, l) \
+ do { \
+ while (l--) \
+ *a = *b++; \
+ } while (0)
+
+#define __IO_IN_LOOP(a, b, l) \
+ do { \
+ while (l--) \
+ *b++ = *a; \
+ } while (0)
+
+#endif /* __IO_USE_DUFFS */
+
+void outsb(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned char *ap = (volatile unsigned char *)addr;
+ unsigned char *bp = (unsigned char *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsb);
+
+void outsw(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned short *ap = (volatile unsigned short *)addr;
+ unsigned short *bp = (unsigned short *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsw);
+
+void outsl(unsigned long addr, const void *buf, int len)
+{
+ volatile unsigned int *ap = (volatile unsigned int *)addr;
+ unsigned int *bp = (unsigned int *)buf;
+ __IO_OUT_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(outsl);
+
+void insb(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned char *ap = (volatile unsigned char *)addr;
+ unsigned char *bp = (unsigned char *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insb);
+
+void insw(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned short *ap = (volatile unsigned short *)addr;
+ unsigned short *bp = (unsigned short *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insw);
+
+void insl(unsigned long addr, void *buf, int len)
+{
+ volatile unsigned int *ap = (volatile unsigned int *)addr;
+ unsigned int *bp = (unsigned int *)buf;
+ __IO_IN_LOOP(ap, bp, len);
+}
+EXPORT_SYMBOL(insl);
diff --git a/arch/nios2/mm/ioremap.c b/arch/nios2/mm/ioremap.c
new file mode 100644
index 0000000..a46bccf
--- /dev/null
+++ b/arch/nios2/mm/ioremap.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+static inline void remap_area_pte(pte_t *pte, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+ unsigned long pfn;
+ pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ
+ | _PAGE_WRITE | flags);
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ if (address >= end)
+ BUG();
+ pfn = PFN_DOWN(phys_addr);
+ do {
+ if (!pte_none(*pte)) {
+ pr_err("remap_area_pte: page already exists\n");
+ BUG();
+ }
+ set_pte(pte, pfn_pte(pfn, pgprot));
+ address += PAGE_SIZE;
+ pfn++;
+ pte++;
+ } while (address && (address < end));
+}
+
+static inline int remap_area_pmd(pmd_t *pmd, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ phys_addr -= address;
+ if (address >= end)
+ BUG();
+ do {
+ pte_t *pte = pte_alloc_kernel(pmd, address);
+ if (!pte)
+ return -ENOMEM;
+ remap_area_pte(pte, address, end - address, address + phys_addr,
+ flags);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long phys_addr,
+ unsigned long size, unsigned long flags)
+{
+ int error;
+ pgd_t *dir;
+ unsigned long end = address + size;
+
+ phys_addr -= address;
+ dir = pgd_offset(&init_mm, address);
+ flush_cache_all();
+ if (address >= end)
+ BUG();
+ do {
+ pud_t *pud;
+ pmd_t *pmd;
+
+ error = -ENOMEM;
+ pud = pud_alloc(&init_mm, dir, address);
+ if (!pud)
+ break;
+ pmd = pmd_alloc(&init_mm, pud, address);
+ if (!pmd)
+ break;
+ if (remap_area_pmd(pmd, address, end - address,
+ phys_addr + address, flags))
+ break;
+ error = 0;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ } while (address && (address < end));
+ flush_tlb_all();
+ return error;
+}
+
+#define IS_MAPPABLE_UNCACHEABLE(addr) (addr < 0x20000000UL)
+
+/*
+ * Map some physical address range into the kernel address space.
+ */
+void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
+ unsigned long cacheflag)
+{
+ struct vm_struct *area;
+ unsigned long offset;
+ unsigned long last_addr;
+ void *addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /* Don't allow anybody to remap normal RAM that we're using */
+ if (phys_addr > PHYS_OFFSET && phys_addr < virt_to_phys(high_memory)) {
+ char *t_addr, *t_end;
+ struct page *page;
+
+ t_addr = __va(phys_addr);
+ t_end = t_addr + (size - 1);
+ for (page = virt_to_page(t_addr);
+ page <= virt_to_page(t_end); page++)
+ if (!PageReserved(page))
+ return NULL;
+ }
+
+ /*
+ * Map uncached objects in the low part of address space to
+ * CONFIG_NIOS2_IO_REGION_BASE
+ */
+ if (IS_MAPPABLE_UNCACHEABLE(phys_addr) &&
+ IS_MAPPABLE_UNCACHEABLE(last_addr) &&
+ !(cacheflag & _PAGE_CACHED))
+ return (void __iomem *)(CONFIG_NIOS2_IO_REGION_BASE + 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;
+
+ /* Ok, go for it */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (!area)
+ return NULL;
+ addr = area->addr;
+ if (remap_area_pages((unsigned long) addr, phys_addr, size,
+ cacheflag)) {
+ vunmap(addr);
+ return NULL;
+ }
+ return (void __iomem *) (offset + (char *)addr);
+}
+EXPORT_SYMBOL(__ioremap);
+
+/*
+ * __iounmap unmaps nearly everything, so be careful
+ * it doesn't free currently pointer/page tables anymore but it
+ * wasn't used anyway and might be added later.
+ */
+void __iounmap(void __iomem *addr)
+{
+ struct vm_struct *p;
+
+ if ((unsigned long) addr > CONFIG_NIOS2_IO_REGION_BASE)
+ return;
+
+ p = remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr));
+ if (!p)
+ pr_err("iounmap: bad address %p\n", addr);
+ kfree(p);
+}
+EXPORT_SYMBOL(__iounmap);
--
1.8.2.1
next prev parent reply other threads:[~2014-09-08 9:22 UTC|newest]
Thread overview: 85+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-09-08 9:22 [PATCH v3 00/29] nios2 Linux kernel port Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 01/29] asm-generic: add generic futex for !CONFIG_SMP Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-23 10:20 ` LF.Tan
2014-09-23 10:40 ` Arnd Bergmann
2014-09-24 10:18 ` Ley Foon Tan
2014-09-24 10:40 ` Arnd Bergmann
2014-09-24 10:57 ` Ley Foon Tan
2014-09-24 11:10 ` Arnd Bergmann
2014-09-25 8:33 ` Ley Foon Tan
2014-09-25 10:54 ` Arnd Bergmann
2014-09-26 2:22 ` Ley Foon Tan
2014-09-26 6:41 ` Geert Uytterhoeven
2014-09-26 8:30 ` Ley Foon Tan
2014-09-26 13:17 ` Tobias Klauser
2014-09-26 13:33 ` Arnd Bergmann
2014-09-23 21:47 ` Thomas Gleixner
2014-09-24 10:29 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 02/29] nios2: Assembly macros and definitions Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 03/29] nios2: Kernel booting and initialization Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 04/29] nios2: Exception handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 05/29] nios2: Traps exception handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 06/29] nios2: Memory management Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan [this message]
2014-09-08 9:22 ` [PATCH v3 07/29] nios2: I/O Mapping Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 08/29] nios2: MMU Fault handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 09/29] nios2: Page table management Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 10/29] nios2: Process management Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 11/29] nios2: Cache handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 12/29] nios2: TLB handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 13/29] nios2: Interrupt handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 14/29] nios2: DMA mapping API Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 15/29] Add ELF machine define for Nios2 Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 16/29] nios2: ELF definitions Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 17/29] nios2: System calls handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 18/29] nios2: Signal handling support Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 19/29] nios2: Library functions Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 20/29] nios2: Device tree support Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 21/29] nios2: Time keeping Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 22/29] nios2: Cpuinfo handling Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 23/29] nios2: Miscellaneous header files Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 24/29] nios2: Nios2 registers Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 25/29] nios2: Module support Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-09 21:25 ` Valdis.Kletnieks
2014-09-10 7:29 ` Chung-Lin Tang
2014-09-10 7:29 ` Chung-Lin Tang
2014-09-10 7:49 ` Tobias Klauser
2014-09-08 9:22 ` [PATCH v3 26/29] nios2: ptrace support Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-16 9:43 ` Tobias Klauser
2014-09-18 5:10 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 27/29] MAINTAINERS: Add nios2 maintainer Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 28/29] Documentation: Add documentation for Nios2 architecture Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-26 2:27 ` Ley Foon Tan
2014-09-26 8:31 ` Ley Foon Tan
2014-09-08 9:22 ` [PATCH v3 29/29] nios2: Build infrastructure Ley Foon Tan
2014-09-08 9:22 ` Ley Foon Tan
2014-09-08 12:13 ` [PATCH v3 00/29] nios2 Linux kernel port David Howells
2014-09-09 2:02 ` Al Viro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1410168160-3624-8-git-send-email-lftan@altera.com \
--to=lftan@altera.com \
--cc=cltang@codesourcery.com \
--cc=lftan.linux@gmail.com \
--cc=linux-arch@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.