|
@@ -154,3 +154,169 @@ static int
|
|
remap_area_supersections(unsigned long virt, unsigned long pfn,
|
|
remap_area_supersections(unsigned long virt, unsigned long pfn,
|
|
size_t size, const struct mem_type *type)
|
|
size_t size, const struct mem_type *type)
|
|
{
|
|
{
|
|
|
|
+ unsigned long addr = virt, end = virt + size;
|
|
|
|
+ pgd_t *pgd;
|
|
|
|
+ pud_t *pud;
|
|
|
|
+ pmd_t *pmd;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Remove and free any PTE-based mapping, and
|
|
|
|
+ * sync the current kernel mapping.
|
|
|
|
+ */
|
|
|
|
+ unmap_area_sections(virt, size);
|
|
|
|
+
|
|
|
|
+ pgd = pgd_offset_k(virt);
|
|
|
|
+ pud = pud_offset(pgd, addr);
|
|
|
|
+ pmd = pmd_offset(pud, addr);
|
|
|
|
+ do {
|
|
|
|
+ unsigned long super_pmd_val, i;
|
|
|
|
+
|
|
|
|
+ super_pmd_val = __pfn_to_phys(pfn) | type->prot_sect |
|
|
|
|
+ PMD_SECT_SUPER;
|
|
|
|
+ super_pmd_val |= ((pfn >> (32 - PAGE_SHIFT)) & 0xf) << 20;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
|
+ pmd[0] = __pmd(super_pmd_val);
|
|
|
|
+ pmd[1] = __pmd(super_pmd_val);
|
|
|
|
+ flush_pmd_entry(pmd);
|
|
|
|
+
|
|
|
|
+ addr += PMD_SIZE;
|
|
|
|
+ pmd += 2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pfn += SUPERSECTION_SIZE >> PAGE_SHIFT;
|
|
|
|
+ } while (addr < end);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
|
|
|
|
+ unsigned long offset, size_t size, unsigned int mtype, void *caller)
|
|
|
|
+{
|
|
|
|
+ const struct mem_type *type;
|
|
|
|
+ int err;
|
|
|
|
+ unsigned long addr;
|
|
|
|
+ struct vm_struct * area;
|
|
|
|
+
|
|
|
|
+#ifndef CONFIG_ARM_LPAE
|
|
|
|
+ /*
|
|
|
|
+ * High mappings must be supersection aligned
|
|
|
|
+ */
|
|
|
|
+ if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
|
|
|
|
+ return NULL;
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ type = get_mem_type(mtype);
|
|
|
|
+ if (!type)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Page align the mapping size, taking account of any offset.
|
|
|
|
+ */
|
|
|
|
+ size = PAGE_ALIGN(offset + size);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Try to reuse one of the static mapping whenever possible.
|
|
|
|
+ */
|
|
|
|
+ read_lock(&vmlist_lock);
|
|
|
|
+ for (area = vmlist; area; area = area->next) {
|
|
|
|
+ if (!size || (sizeof(phys_addr_t) == 4 && pfn >= 0x100000))
|
|
|
|
+ break;
|
|
|
|
+ if (!(area->flags & VM_ARM_STATIC_MAPPING))
|
|
|
|
+ continue;
|
|
|
|
+ if ((area->flags & VM_ARM_MTYPE_MASK) != VM_ARM_MTYPE(mtype))
|
|
|
|
+ continue;
|
|
|
|
+ if (__phys_to_pfn(area->phys_addr) > pfn ||
|
|
|
|
+ __pfn_to_phys(pfn) + size-1 > area->phys_addr + area->size-1)
|
|
|
|
+ continue;
|
|
|
|
+ /* we can drop the lock here as we know *area is static */
|
|
|
|
+ read_unlock(&vmlist_lock);
|
|
|
|
+ addr = (unsigned long)area->addr;
|
|
|
|
+ addr += __pfn_to_phys(pfn) - area->phys_addr;
|
|
|
|
+ return (void __iomem *) (offset + addr);
|
|
|
|
+ }
|
|
|
|
+ read_unlock(&vmlist_lock);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Don't allow RAM to be mapped - this causes problems with ARMv6+
|
|
|
|
+ */
|
|
|
|
+ if (WARN_ON(pfn_valid(pfn)))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ area = get_vm_area_caller(size, VM_IOREMAP, caller);
|
|
|
|
+ if (!area)
|
|
|
|
+ return NULL;
|
|
|
|
+ addr = (unsigned long)area->addr;
|
|
|
|
+ area->phys_addr = __pfn_to_phys(pfn);
|
|
|
|
+
|
|
|
|
+#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
|
|
|
|
+ if (DOMAIN_IO == 0 &&
|
|
|
|
+ (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
|
|
|
|
+ cpu_is_xsc3()) && pfn >= 0x100000 &&
|
|
|
|
+ !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
|
|
|
|
+ area->flags |= VM_ARM_SECTION_MAPPING;
|
|
|
|
+ err = remap_area_supersections(addr, pfn, size, type);
|
|
|
|
+ } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {
|
|
|
|
+ area->flags |= VM_ARM_SECTION_MAPPING;
|
|
|
|
+ err = remap_area_sections(addr, pfn, size, type);
|
|
|
|
+ } else
|
|
|
|
+#endif
|
|
|
|
+ err = ioremap_page_range(addr, addr + size, __pfn_to_phys(pfn),
|
|
|
|
+ __pgprot(type->prot_pte));
|
|
|
|
+
|
|
|
|
+ if (err) {
|
|
|
|
+ vunmap((void *)addr);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ flush_cache_vmap(addr, addr + size);
|
|
|
|
+ return (void __iomem *) (offset + addr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size,
|
|
|
|
+ unsigned int mtype, void *caller)
|
|
|
|
+{
|
|
|
|
+ unsigned long last_addr;
|
|
|
|
+ unsigned long offset = phys_addr & ~PAGE_MASK;
|
|
|
|
+ unsigned long pfn = __phys_to_pfn(phys_addr);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Don't allow wraparound or zero size
|
|
|
|
+ */
|
|
|
|
+ last_addr = phys_addr + size - 1;
|
|
|
|
+ if (!size || last_addr < phys_addr)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,
|
|
|
|
+ caller);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 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 *
|
|
|
|
+__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
|
|
|
|
+ unsigned int mtype)
|
|
|
|
+{
|
|
|
|
+ return __arm_ioremap_pfn_caller(pfn, offset, size, mtype,
|
|
|
|
+ __builtin_return_address(0));
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(__arm_ioremap_pfn);
|
|
|
|
+
|
|
|
|
+void __iomem * (*arch_ioremap_caller)(unsigned long, size_t,
|
|
|
|
+ unsigned int, void *) =
|
|
|
|
+ __arm_ioremap_caller;
|
|
|
|
+
|
|
|
|
+void __iomem *
|
|
|
|
+__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
|
|
|
|
+{
|
|
|
|
+ return arch_ioremap_caller(phys_addr, size, mtype,
|
|
|
|
+ __builtin_return_address(0));
|
|
|
|
+}
|