diff options
| -rw-r--r-- | arch/arm/mm/ioremap.c | 66 | ||||
| -rw-r--r-- | include/asm-arm/memory.h | 4 |
2 files changed, 65 insertions, 5 deletions
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 6aa13d59c858..7eac87f05180 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c | |||
| @@ -33,8 +33,8 @@ | |||
| 33 | #include <asm/sizes.h> | 33 | #include <asm/sizes.h> |
| 34 | 34 | ||
| 35 | /* | 35 | /* |
| 36 | * Used by ioremap() and iounmap() code to mark section-mapped I/O regions | 36 | * Used by ioremap() and iounmap() code to mark (super)section-mapped |
| 37 | * in vm_struct->flags field. | 37 | * I/O regions in vm_struct->flags field. |
| 38 | */ | 38 | */ |
| 39 | #define VM_ARM_SECTION_MAPPING 0x80000000 | 39 | #define VM_ARM_SECTION_MAPPING 0x80000000 |
| 40 | 40 | ||
| @@ -233,6 +233,54 @@ remap_area_sections(unsigned long virt, unsigned long pfn, | |||
| 233 | 233 | ||
| 234 | return 0; | 234 | return 0; |
| 235 | } | 235 | } |
| 236 | |||
| 237 | static int | ||
| 238 | remap_area_supersections(unsigned long virt, unsigned long pfn, | ||
| 239 | unsigned long size, unsigned long flags) | ||
| 240 | { | ||
| 241 | unsigned long prot, addr = virt, end = virt + size; | ||
| 242 | pgd_t *pgd; | ||
| 243 | |||
| 244 | /* | ||
| 245 | * Remove and free any PTE-based mapping, and | ||
| 246 | * sync the current kernel mapping. | ||
| 247 | */ | ||
| 248 | unmap_area_sections(virt, size); | ||
| 249 | |||
| 250 | prot = PMD_TYPE_SECT | PMD_SECT_SUPER | PMD_SECT_AP_WRITE | | ||
| 251 | PMD_DOMAIN(DOMAIN_IO) | | ||
| 252 | (flags & (L_PTE_CACHEABLE | L_PTE_BUFFERABLE)); | ||
| 253 | |||
| 254 | /* | ||
| 255 | * ARMv6 and above need XN set to prevent speculative prefetches | ||
| 256 | * hitting IO. | ||
| 257 | */ | ||
| 258 | if (cpu_architecture() >= CPU_ARCH_ARMv6) | ||
| 259 | prot |= PMD_SECT_XN; | ||
| 260 | |||
| 261 | pgd = pgd_offset_k(virt); | ||
| 262 | do { | ||
| 263 | unsigned long super_pmd_val, i; | ||
| 264 | |||
| 265 | super_pmd_val = __pfn_to_phys(pfn) | prot; | ||
| 266 | super_pmd_val |= ((pfn >> (32 - PAGE_SHIFT)) & 0xf) << 20; | ||
| 267 | |||
| 268 | for (i = 0; i < 8; i++) { | ||
| 269 | pmd_t *pmd = pmd_offset(pgd, addr); | ||
| 270 | |||
| 271 | pmd[0] = __pmd(super_pmd_val); | ||
| 272 | pmd[1] = __pmd(super_pmd_val); | ||
| 273 | flush_pmd_entry(pmd); | ||
| 274 | |||
| 275 | addr += PGDIR_SIZE; | ||
| 276 | pgd++; | ||
| 277 | } | ||
| 278 | |||
| 279 | pfn += SUPERSECTION_SIZE >> PAGE_SHIFT; | ||
| 280 | } while (addr < end); | ||
| 281 | |||
| 282 | return 0; | ||
| 283 | } | ||
| 236 | #endif | 284 | #endif |
| 237 | 285 | ||
| 238 | 286 | ||
| @@ -255,6 +303,13 @@ __ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, | |||
| 255 | int err; | 303 | int err; |
| 256 | unsigned long addr; | 304 | unsigned long addr; |
| 257 | struct vm_struct * area; | 305 | struct vm_struct * area; |
| 306 | unsigned int cr = get_cr(); | ||
| 307 | |||
| 308 | /* | ||
| 309 | * High mappings must be supersection aligned | ||
| 310 | */ | ||
| 311 | if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK)) | ||
| 312 | return NULL; | ||
| 258 | 313 | ||
| 259 | area = get_vm_area(size, VM_IOREMAP); | 314 | area = get_vm_area(size, VM_IOREMAP); |
| 260 | if (!area) | 315 | if (!area) |
| @@ -262,7 +317,12 @@ __ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, | |||
| 262 | addr = (unsigned long)area->addr; | 317 | addr = (unsigned long)area->addr; |
| 263 | 318 | ||
| 264 | #ifndef CONFIG_SMP | 319 | #ifndef CONFIG_SMP |
| 265 | if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) { | 320 | if ((((cpu_architecture() >= CPU_ARCH_ARMv6) && (cr & CR_XP)) || |
| 321 | cpu_is_xsc3()) && | ||
| 322 | !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) { | ||
| 323 | area->flags |= VM_ARM_SECTION_MAPPING; | ||
| 324 | err = remap_area_supersections(addr, pfn, size, flags); | ||
| 325 | } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) { | ||
| 266 | area->flags |= VM_ARM_SECTION_MAPPING; | 326 | area->flags |= VM_ARM_SECTION_MAPPING; |
| 267 | err = remap_area_sections(addr, pfn, size, flags); | 327 | err = remap_area_sections(addr, pfn, size, flags); |
| 268 | } else | 328 | } else |
diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h index 176a4fb04989..91d536c215d7 100644 --- a/include/asm-arm/memory.h +++ b/include/asm-arm/memory.h | |||
| @@ -69,9 +69,9 @@ | |||
| 69 | #define XIP_VIRT_ADDR(physaddr) (MODULE_START + ((physaddr) & 0x000fffff)) | 69 | #define XIP_VIRT_ADDR(physaddr) (MODULE_START + ((physaddr) & 0x000fffff)) |
| 70 | 70 | ||
| 71 | /* | 71 | /* |
| 72 | * Allow 2MB-aligned ioremap pages | 72 | * Allow 16MB-aligned ioremap pages |
| 73 | */ | 73 | */ |
| 74 | #define IOREMAP_MAX_ORDER 21 | 74 | #define IOREMAP_MAX_ORDER 24 |
| 75 | 75 | ||
| 76 | #else /* CONFIG_MMU */ | 76 | #else /* CONFIG_MMU */ |
| 77 | 77 | ||
