uio: Support physical addresses >32 bits on 32-bit systems
authorKai Jiang <Kai.Jiang@freescale.com>
Mon, 17 Oct 2011 18:50:20 +0000 (20:50 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 18 Oct 2011 18:18:57 +0000 (11:18 -0700)
To support >32-bit physical addresses for UIO_MEM_PHYS type we need to
extend the width of 'addr' in struct uio_mem.  Numerous platforms like
embedded PPC, ARM, and X86 have support for systems with larger physical
address than logical.

Since 'addr' may contain a physical, logical, or virtual address the
easiest solution is to just change the type to 'phys_addr_t' which
should always be greater than or equal to the sizeof(void *) such that
it can properly hold any of the address types.

For physical address we can support up to a 44-bit physical address on a
typical 32-bit system as we utilize remap_pfn_range() for the mapping of
the memory region and pfn's are represnted by shifting the address by
the page size (typically 4k).

Signed-off-by: Kai Jiang <Kai.Jiang@freescale.com>
Signed-off-by: Minghuan Lian <Minghuan.Lian@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Hans J. Koch <hjk@hansjkoch.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Documentation/DocBook/uio-howto.tmpl
drivers/uio/uio.c
include/linux/uio_driver.h

index 7c4b514..54883de 100644 (file)
@@ -529,7 +529,7 @@ memory (e.g. allocated with <function>kmalloc()</function>). There's also
 </para></listitem>
 
 <listitem><para>
-<varname>unsigned long addr</varname>: Required if the mapping is used.
+<varname>phys_addr_t addr</varname>: Required if the mapping is used.
 Fill in the address of your memory block. This address is the one that
 appears in sysfs.
 </para></listitem>
index c89f12a..a783d53 100644 (file)
@@ -69,7 +69,7 @@ static ssize_t map_name_show(struct uio_mem *mem, char *buf)
 
 static ssize_t map_addr_show(struct uio_mem *mem, char *buf)
 {
-       return sprintf(buf, "0x%lx\n", mem->addr);
+       return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr);
 }
 
 static ssize_t map_size_show(struct uio_mem *mem, char *buf)
@@ -79,7 +79,7 @@ static ssize_t map_size_show(struct uio_mem *mem, char *buf)
 
 static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
 {
-       return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
+       return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr & ~PAGE_MASK);
 }
 
 struct map_sysfs_entry {
@@ -634,8 +634,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
                page = virt_to_page(idev->info->mem[mi].addr + offset);
        else
-               page = vmalloc_to_page((void *)idev->info->mem[mi].addr
-                                                       + offset);
+               page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset);
        get_page(page);
        vmf->page = page;
        return 0;
index 665517c..fd99ff9 100644 (file)
@@ -23,7 +23,10 @@ struct uio_map;
 /**
  * struct uio_mem - description of a UIO memory region
  * @name:              name of the memory region for identification
- * @addr:              address of the device's memory
+ * @addr:              address of the device's memory (phys_addr is used since
+ *                     addr can be logical, virtual, or physical & phys_addr_t
+ *                     should always be large enough to handle any of the
+ *                     address types)
  * @size:              size of IO
  * @memtype:           type of memory addr points to
  * @internal_addr:     ioremap-ped version of addr, for driver internal use
@@ -31,7 +34,7 @@ struct uio_map;
  */
 struct uio_mem {
        const char              *name;
-       unsigned long           addr;
+       phys_addr_t             addr;
        unsigned long           size;
        int                     memtype;
        void __iomem            *internal_addr;