diff options
Diffstat (limited to 'arch/powerpc/mm/mem.c')
| -rw-r--r-- | arch/powerpc/mm/mem.c | 211 | 
1 files changed, 198 insertions, 13 deletions
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index be941d382c8d..617c2777926f 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -31,6 +31,7 @@  #include <linux/slab.h>  #include <linux/vmalloc.h>  #include <linux/memremap.h> +#include <linux/dma-direct.h>  #include <asm/pgalloc.h>  #include <asm/prom.h> @@ -104,6 +105,27 @@ int __weak remove_section_mapping(unsigned long start, unsigned long end)  	return -ENODEV;  } +#define FLUSH_CHUNK_SIZE SZ_1G +/** + * flush_dcache_range_chunked(): Write any modified data cache blocks out to + * memory and invalidate them, in chunks of up to FLUSH_CHUNK_SIZE + * Does not invalidate the corresponding instruction cache blocks. + * + * @start: the start address + * @stop: the stop address (exclusive) + * @chunk: the max size of the chunks + */ +static void flush_dcache_range_chunked(unsigned long start, unsigned long stop, +				       unsigned long chunk) +{ +	unsigned long i; + +	for (i = start; i < stop; i += chunk) { +		flush_dcache_range(i, min(stop, i + chunk)); +		cond_resched(); +	} +} +  int __ref arch_add_memory(int nid, u64 start, u64 size,  			struct mhp_restrictions *restrictions)  { @@ -120,7 +142,6 @@ int __ref arch_add_memory(int nid, u64 start, u64 size,  			start, start + size, rc);  		return -EFAULT;  	} -	flush_dcache_range(start, start + size);  	return __add_pages(nid, start_pfn, nr_pages, restrictions);  } @@ -137,7 +158,8 @@ void __ref arch_remove_memory(int nid, u64 start, u64 size,  	/* Remove htab bolted mappings for this section of memory */  	start = (unsigned long)__va(start); -	flush_dcache_range(start, start + size); +	flush_dcache_range_chunked(start, start + size, FLUSH_CHUNK_SIZE); +  	ret = remove_section_mapping(start, start + size);  	WARN_ON_ONCE(ret); @@ -201,10 +223,10 @@ static int __init mark_nonram_nosave(void)   * everything else. GFP_DMA32 page allocations automatically fall back to   * ZONE_DMA.   * - * By using 31-bit unconditionally, we can exploit ARCH_ZONE_DMA_BITS to - * inform the generic DMA mapping code.  32-bit only devices (if not handled - * by an IOMMU anyway) will take a first dip into ZONE_NORMAL and get - * otherwise served by ZONE_DMA. + * By using 31-bit unconditionally, we can exploit zone_dma_bits to inform the + * generic DMA mapping code.  32-bit only devices (if not handled by an IOMMU + * anyway) will take a first dip into ZONE_NORMAL and get otherwise served by + * ZONE_DMA.   */  static unsigned long max_zone_pfns[MAX_NR_ZONES]; @@ -216,15 +238,13 @@ void __init paging_init(void)  	unsigned long long total_ram = memblock_phys_mem_size();  	phys_addr_t top_of_ram = memblock_end_of_DRAM(); -#ifdef CONFIG_PPC32 -	unsigned long v = __fix_to_virt(__end_of_fixed_addresses - 1); -	unsigned long end = __fix_to_virt(FIX_HOLE); +#ifdef CONFIG_HIGHMEM +	unsigned long v = __fix_to_virt(FIX_KMAP_END); +	unsigned long end = __fix_to_virt(FIX_KMAP_BEGIN);  	for (; v < end; v += PAGE_SIZE)  		map_kernel_page(v, 0, __pgprot(0)); /* XXX gross */ -#endif -#ifdef CONFIG_HIGHMEM  	map_kernel_page(PKMAP_BASE, 0, __pgprot(0));	/* XXX gross */  	pkmap_page_table = virt_to_kpte(PKMAP_BASE); @@ -237,9 +257,18 @@ void __init paging_init(void)  	printk(KERN_DEBUG "Memory hole size: %ldMB\n",  	       (long int)((top_of_ram - total_ram) >> 20)); +	/* +	 * Allow 30-bit DMA for very limited Broadcom wifi chips on many +	 * powerbooks. +	 */ +	if (IS_ENABLED(CONFIG_PPC32)) +		zone_dma_bits = 30; +	else +		zone_dma_bits = 31; +  #ifdef CONFIG_ZONE_DMA  	max_zone_pfns[ZONE_DMA]	= min(max_low_pfn, -				      1UL << (ARCH_ZONE_DMA_BITS - PAGE_SHIFT)); +				      1UL << (zone_dma_bits - PAGE_SHIFT));  #endif  	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;  #ifdef CONFIG_HIGHMEM @@ -260,6 +289,14 @@ void __init mem_init(void)  	BUILD_BUG_ON(MMU_PAGE_COUNT > 16);  #ifdef CONFIG_SWIOTLB +	/* +	 * Some platforms (e.g. 85xx) limit DMA-able memory way below +	 * 4G. We force memblock to bottom-up mode to ensure that the +	 * memory allocated in swiotlb_init() is DMA-able. +	 * As it's the last memblock allocation, no need to reset it +	 * back to to-down. +	 */ +	memblock_set_bottom_up(true);  	swiotlb_init(0);  #endif @@ -318,6 +355,120 @@ void free_initmem(void)  	free_initmem_default(POISON_FREE_INITMEM);  } +/** + * flush_coherent_icache() - if a CPU has a coherent icache, flush it + * @addr: The base address to use (can be any valid address, the whole cache will be flushed) + * Return true if the cache was flushed, false otherwise + */ +static inline bool flush_coherent_icache(unsigned long addr) +{ +	/* +	 * For a snooping icache, we still need a dummy icbi to purge all the +	 * prefetched instructions from the ifetch buffers. We also need a sync +	 * before the icbi to order the the actual stores to memory that might +	 * have modified instructions with the icbi. +	 */ +	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) { +		mb(); /* sync */ +		icbi((void *)addr); +		mb(); /* sync */ +		isync(); +		return true; +	} + +	return false; +} + +/** + * invalidate_icache_range() - Flush the icache by issuing icbi across an address range + * @start: the start address + * @stop: the stop address (exclusive) + */ +static void invalidate_icache_range(unsigned long start, unsigned long stop) +{ +	unsigned long shift = l1_icache_shift(); +	unsigned long bytes = l1_icache_bytes(); +	char *addr = (char *)(start & ~(bytes - 1)); +	unsigned long size = stop - (unsigned long)addr + (bytes - 1); +	unsigned long i; + +	for (i = 0; i < size >> shift; i++, addr += bytes) +		icbi(addr); + +	mb(); /* sync */ +	isync(); +} + +/** + * flush_icache_range: Write any modified data cache blocks out to memory + * and invalidate the corresponding blocks in the instruction cache + * + * Generic code will call this after writing memory, before executing from it. + * + * @start: the start address + * @stop: the stop address (exclusive) + */ +void flush_icache_range(unsigned long start, unsigned long stop) +{ +	if (flush_coherent_icache(start)) +		return; + +	clean_dcache_range(start, stop); + +	if (IS_ENABLED(CONFIG_44x)) { +		/* +		 * Flash invalidate on 44x because we are passed kmapped +		 * addresses and this doesn't work for userspace pages due to +		 * the virtually tagged icache. +		 */ +		iccci((void *)start); +		mb(); /* sync */ +		isync(); +	} else +		invalidate_icache_range(start, stop); +} +EXPORT_SYMBOL(flush_icache_range); + +#if !defined(CONFIG_PPC_8xx) && !defined(CONFIG_PPC64) +/** + * flush_dcache_icache_phys() - Flush a page by it's physical address + * @physaddr: the physical address of the page + */ +static void flush_dcache_icache_phys(unsigned long physaddr) +{ +	unsigned long bytes = l1_dcache_bytes(); +	unsigned long nb = PAGE_SIZE / bytes; +	unsigned long addr = physaddr & PAGE_MASK; +	unsigned long msr, msr0; +	unsigned long loop1 = addr, loop2 = addr; + +	msr0 = mfmsr(); +	msr = msr0 & ~MSR_DR; +	/* +	 * This must remain as ASM to prevent potential memory accesses +	 * while the data MMU is disabled +	 */ +	asm volatile( +		"   mtctr %2;\n" +		"   mtmsr %3;\n" +		"   isync;\n" +		"0: dcbst   0, %0;\n" +		"   addi    %0, %0, %4;\n" +		"   bdnz    0b;\n" +		"   sync;\n" +		"   mtctr %2;\n" +		"1: icbi    0, %1;\n" +		"   addi    %1, %1, %4;\n" +		"   bdnz    1b;\n" +		"   sync;\n" +		"   mtmsr %5;\n" +		"   isync;\n" +		: "+&r" (loop1), "+&r" (loop2) +		: "r" (nb), "r" (msr), "i" (bytes), "r" (msr0) +		: "ctr", "memory"); +} +#endif // !defined(CONFIG_PPC_8xx) && !defined(CONFIG_PPC64) +  /*   * This is called when a page has been modified by the kernel.   * It just marks the page as not i-cache clean.  We do the i-cache @@ -350,12 +501,46 @@ void flush_dcache_icache_page(struct page *page)  		__flush_dcache_icache(start);  		kunmap_atomic(start);  	} else { -		__flush_dcache_icache_phys(page_to_pfn(page) << PAGE_SHIFT); +		unsigned long addr = page_to_pfn(page) << PAGE_SHIFT; + +		if (flush_coherent_icache(addr)) +			return; +		flush_dcache_icache_phys(addr);  	}  #endif  }  EXPORT_SYMBOL(flush_dcache_icache_page); +/** + * __flush_dcache_icache(): Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * @page: the address of the page to flush + */ +void __flush_dcache_icache(void *p) +{ +	unsigned long addr = (unsigned long)p; + +	if (flush_coherent_icache(addr)) +		return; + +	clean_dcache_range(addr, addr + PAGE_SIZE); + +	/* +	 * We don't flush the icache on 44x. Those have a virtual icache and we +	 * don't have access to the virtual address here (it's not the page +	 * vaddr but where it's mapped in user space). The flushing of the +	 * icache on these is handled elsewhere, when a change in the address +	 * space occurs, before returning to user space. +	 */ + +	if (cpu_has_feature(MMU_FTR_TYPE_44x)) +		return; + +	invalidate_icache_range(addr, addr + PAGE_SIZE); +} +  void clear_user_page(void *page, unsigned long vaddr, struct page *pg)  {  	clear_page(page);  |