]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - kernel/memremap.c
kbuild: drop FORCE from PHONY targets
[karo-tx-linux.git] / kernel / memremap.c
index 2c468dea60bc2e688c6a5271fa393ea82a1aacb4..a6d382312e6f3ff2cdb0bb526f8bbd2978ebffc1 100644 (file)
@@ -29,10 +29,10 @@ __weak void __iomem *ioremap_cache(resource_size_t offset, unsigned long size)
 
 static void *try_ram_remap(resource_size_t offset, size_t size)
 {
-       struct page *page = pfn_to_page(offset >> PAGE_SHIFT);
+       unsigned long pfn = PHYS_PFN(offset);
 
        /* In the simple case just return the existing linear address */
-       if (!PageHighMem(page))
+       if (pfn_valid(pfn) && !PageHighMem(pfn_to_page(pfn)))
                return __va(offset);
        return NULL; /* fallback to ioremap_cache */
 }
@@ -41,13 +41,15 @@ static void *try_ram_remap(resource_size_t offset, size_t size)
  * memremap() - remap an iomem_resource as cacheable memory
  * @offset: iomem resource start address
  * @size: size of remap
- * @flags: either MEMREMAP_WB or MEMREMAP_WT
+ * @flags: any of MEMREMAP_WB, MEMREMAP_WT and MEMREMAP_WC
  *
  * memremap() is "ioremap" for cases where it is known that the resource
  * being mapped does not have i/o side effects and the __iomem
- * annotation is not applicable.
+ * annotation is not applicable. In the case of multiple flags, the different
+ * mapping types will be attempted in the order listed below until one of
+ * them succeeds.
  *
- * MEMREMAP_WB - matches the default mapping for "System RAM" on
+ * MEMREMAP_WB - matches the default mapping for System RAM on
  * the architecture.  This is usually a read-allocate write-back cache.
  * Morever, if MEMREMAP_WB is specified and the requested remap region is RAM
  * memremap() will bypass establishing a new mapping and instead return
@@ -56,13 +58,21 @@ static void *try_ram_remap(resource_size_t offset, size_t size)
  * MEMREMAP_WT - establish a mapping whereby writes either bypass the
  * cache or are written through to memory and never exist in a
  * cache-dirty state with respect to program visibility.  Attempts to
- * map "System RAM" with this mapping type will fail.
+ * map System RAM with this mapping type will fail.
+ *
+ * MEMREMAP_WC - establish a writecombine mapping, whereby writes may
+ * be coalesced together (e.g. in the CPU's write buffers), but is otherwise
+ * uncached. Attempts to map System RAM with this mapping type will fail.
  */
 void *memremap(resource_size_t offset, size_t size, unsigned long flags)
 {
-       int is_ram = region_intersects(offset, size, "System RAM");
+       int is_ram = region_intersects(offset, size,
+                                      IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE);
        void *addr = NULL;
 
+       if (!flags)
+               return NULL;
+
        if (is_ram == REGION_MIXED) {
                WARN_ONCE(1, "memremap attempted on mixed range %pa size: %#lx\n",
                                &offset, (unsigned long) size);
@@ -71,12 +81,11 @@ void *memremap(resource_size_t offset, size_t size, unsigned long flags)
 
        /* Try all mapping types requested until one returns non-NULL */
        if (flags & MEMREMAP_WB) {
-               flags &= ~MEMREMAP_WB;
                /*
                 * MEMREMAP_WB is special in that it can be satisifed
                 * from the direct map.  Some archs depend on the
                 * capability of memremap() to autodetect cases where
-                * the requested range is potentially in "System RAM"
+                * the requested range is potentially in System RAM.
                 */
                if (is_ram == REGION_INTERSECTS)
                        addr = try_ram_remap(offset, size);
@@ -85,21 +94,22 @@ void *memremap(resource_size_t offset, size_t size, unsigned long flags)
        }
 
        /*
-        * If we don't have a mapping yet and more request flags are
-        * pending then we will be attempting to establish a new virtual
+        * If we don't have a mapping yet and other request flags are
+        * present then we will be attempting to establish a new virtual
         * address mapping.  Enforce that this mapping is not aliasing
-        * "System RAM"
+        * System RAM.
         */
-       if (!addr && is_ram == REGION_INTERSECTS && flags) {
+       if (!addr && is_ram == REGION_INTERSECTS && flags != MEMREMAP_WB) {
                WARN_ONCE(1, "memremap attempted on ram %pa size: %#lx\n",
                                &offset, (unsigned long) size);
                return NULL;
        }
 
-       if (!addr && (flags & MEMREMAP_WT)) {
-               flags &= ~MEMREMAP_WT;
+       if (!addr && (flags & MEMREMAP_WT))
                addr = ioremap_wt(offset, size);
-       }
+
+       if (!addr && (flags & MEMREMAP_WC))
+               addr = ioremap_wc(offset, size);
 
        return addr;
 }
@@ -114,7 +124,7 @@ EXPORT_SYMBOL(memunmap);
 
 static void devm_memremap_release(struct device *dev, void *res)
 {
-       memunmap(res);
+       memunmap(*(void **)res);
 }
 
 static int devm_memremap_match(struct device *dev, void *res, void *match_data)
@@ -136,8 +146,10 @@ void *devm_memremap(struct device *dev, resource_size_t offset,
        if (addr) {
                *ptr = addr;
                devres_add(dev, ptr);
-       } else
+       } else {
                devres_free(ptr);
+               return ERR_PTR(-ENXIO);
+       }
 
        return addr;
 }
@@ -268,13 +280,17 @@ struct dev_pagemap *find_dev_pagemap(resource_size_t phys)
 void *devm_memremap_pages(struct device *dev, struct resource *res,
                struct percpu_ref *ref, struct vmem_altmap *altmap)
 {
-       int is_ram = region_intersects(res->start, resource_size(res),
-                       "System RAM");
        resource_size_t key, align_start, align_size, align_end;
        struct dev_pagemap *pgmap;
        struct page_map *page_map;
+       int error, nid, is_ram;
        unsigned long pfn;
-       int error, nid;
+
+       align_start = res->start & ~(SECTION_SIZE - 1);
+       align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE)
+               - align_start;
+       is_ram = region_intersects(align_start, align_size,
+               IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE);
 
        if (is_ram == REGION_MIXED) {
                WARN_ONCE(1, "%s attempted on mixed region %pr\n",
@@ -312,8 +328,6 @@ void *devm_memremap_pages(struct device *dev, struct resource *res,
 
        mutex_lock(&pgmap_lock);
        error = 0;
-       align_start = res->start & ~(SECTION_SIZE - 1);
-       align_size = ALIGN(resource_size(res), SECTION_SIZE);
        align_end = align_start + align_size - 1;
        for (key = align_start; key <= align_end; key += SECTION_SIZE) {
                struct dev_pagemap *dup;
@@ -349,8 +363,13 @@ void *devm_memremap_pages(struct device *dev, struct resource *res,
        for_each_device_pfn(pfn, page_map) {
                struct page *page = pfn_to_page(pfn);
 
-               /* ZONE_DEVICE pages must never appear on a slab lru */
-               list_force_poison(&page->lru);
+               /*
+                * ZONE_DEVICE pages union ->lru with a ->pgmap back
+                * pointer.  It is a bug if a ZONE_DEVICE page is ever
+                * freed or placed on a driver-private list.  Seed the
+                * storage with LIST_POISON* values.
+                */
+               list_del(&page->lru);
                page->pgmap = pgmap;
        }
        devres_add(dev, page_map);
@@ -381,7 +400,7 @@ struct vmem_altmap *to_vmem_altmap(unsigned long memmap_start)
        /*
         * 'memmap_start' is the virtual address for the first "struct
         * page" in this range of the vmemmap array.  In the case of
-        * CONFIG_SPARSE_VMEMMAP a page_to_pfn conversion is simple
+        * CONFIG_SPARSEMEM_VMEMMAP a page_to_pfn conversion is simple
         * pointer arithmetic, so we can perform this to_vmem_altmap()
         * conversion without concern for the initialization state of
         * the struct page fields.
@@ -390,7 +409,7 @@ struct vmem_altmap *to_vmem_altmap(unsigned long memmap_start)
        struct dev_pagemap *pgmap;
 
        /*
-        * Uncoditionally retrieve a dev_pagemap associated with the
+        * Unconditionally retrieve a dev_pagemap associated with the
         * given physical address, this is only for use in the
         * arch_{add|remove}_memory() for setting up and tearing down
         * the memmap.