18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _M68K_CACHEFLUSH_H
38c2ecf20Sopenharmony_ci#define _M68K_CACHEFLUSH_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/mm.h>
68c2ecf20Sopenharmony_ci#ifdef CONFIG_COLDFIRE
78c2ecf20Sopenharmony_ci#include <asm/mcfsim.h>
88c2ecf20Sopenharmony_ci#endif
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/* cache code */
118c2ecf20Sopenharmony_ci#define FLUSH_I_AND_D	(0x00000808)
128c2ecf20Sopenharmony_ci#define FLUSH_I		(0x00000008)
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#ifndef ICACHE_MAX_ADDR
158c2ecf20Sopenharmony_ci#define ICACHE_MAX_ADDR	0
168c2ecf20Sopenharmony_ci#define ICACHE_SET_MASK	0
178c2ecf20Sopenharmony_ci#define DCACHE_MAX_ADDR	0
188c2ecf20Sopenharmony_ci#define DCACHE_SETMASK	0
198c2ecf20Sopenharmony_ci#endif
208c2ecf20Sopenharmony_ci#ifndef CACHE_MODE
218c2ecf20Sopenharmony_ci#define	CACHE_MODE	0
228c2ecf20Sopenharmony_ci#define	CACR_ICINVA	0
238c2ecf20Sopenharmony_ci#define	CACR_DCINVA	0
248c2ecf20Sopenharmony_ci#define	CACR_BCINVA	0
258c2ecf20Sopenharmony_ci#endif
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * ColdFire architecture has no way to clear individual cache lines, so we
298c2ecf20Sopenharmony_ci * are stuck invalidating all the cache entries when we want a clear operation.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic inline void clear_cf_icache(unsigned long start, unsigned long end)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
348c2ecf20Sopenharmony_ci		"movec	%0,%%cacr\n\t"
358c2ecf20Sopenharmony_ci		"nop"
368c2ecf20Sopenharmony_ci		:
378c2ecf20Sopenharmony_ci		: "r" (CACHE_MODE | CACR_ICINVA | CACR_BCINVA));
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline void clear_cf_dcache(unsigned long start, unsigned long end)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
438c2ecf20Sopenharmony_ci		"movec	%0,%%cacr\n\t"
448c2ecf20Sopenharmony_ci		"nop"
458c2ecf20Sopenharmony_ci		:
468c2ecf20Sopenharmony_ci		: "r" (CACHE_MODE | CACR_DCINVA));
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic inline void clear_cf_bcache(unsigned long start, unsigned long end)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
528c2ecf20Sopenharmony_ci		"movec	%0,%%cacr\n\t"
538c2ecf20Sopenharmony_ci		"nop"
548c2ecf20Sopenharmony_ci		:
558c2ecf20Sopenharmony_ci		: "r" (CACHE_MODE | CACR_ICINVA | CACR_BCINVA | CACR_DCINVA));
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Use the ColdFire cpushl instruction to push (and invalidate) cache lines.
608c2ecf20Sopenharmony_ci * The start and end addresses are cache line numbers not memory addresses.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic inline void flush_cf_icache(unsigned long start, unsigned long end)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned long set;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	for (set = start; set <= end; set += (0x10 - 3)) {
678c2ecf20Sopenharmony_ci		__asm__ __volatile__ (
688c2ecf20Sopenharmony_ci			"cpushl %%ic,(%0)\n\t"
698c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
708c2ecf20Sopenharmony_ci			"cpushl %%ic,(%0)\n\t"
718c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
728c2ecf20Sopenharmony_ci			"cpushl %%ic,(%0)\n\t"
738c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
748c2ecf20Sopenharmony_ci			"cpushl %%ic,(%0)"
758c2ecf20Sopenharmony_ci			: "=a" (set)
768c2ecf20Sopenharmony_ci			: "a" (set));
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic inline void flush_cf_dcache(unsigned long start, unsigned long end)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	unsigned long set;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	for (set = start; set <= end; set += (0x10 - 3)) {
858c2ecf20Sopenharmony_ci		__asm__ __volatile__ (
868c2ecf20Sopenharmony_ci			"cpushl %%dc,(%0)\n\t"
878c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
888c2ecf20Sopenharmony_ci			"cpushl %%dc,(%0)\n\t"
898c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
908c2ecf20Sopenharmony_ci			"cpushl %%dc,(%0)\n\t"
918c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
928c2ecf20Sopenharmony_ci			"cpushl %%dc,(%0)"
938c2ecf20Sopenharmony_ci			: "=a" (set)
948c2ecf20Sopenharmony_ci			: "a" (set));
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline void flush_cf_bcache(unsigned long start, unsigned long end)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	unsigned long set;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	for (set = start; set <= end; set += (0x10 - 3)) {
1038c2ecf20Sopenharmony_ci		__asm__ __volatile__ (
1048c2ecf20Sopenharmony_ci			"cpushl %%bc,(%0)\n\t"
1058c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
1068c2ecf20Sopenharmony_ci			"cpushl %%bc,(%0)\n\t"
1078c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
1088c2ecf20Sopenharmony_ci			"cpushl %%bc,(%0)\n\t"
1098c2ecf20Sopenharmony_ci			"addq%.l #1,%0\n\t"
1108c2ecf20Sopenharmony_ci			"cpushl %%bc,(%0)"
1118c2ecf20Sopenharmony_ci			: "=a" (set)
1128c2ecf20Sopenharmony_ci			: "a" (set));
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * Cache handling functions
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic inline void flush_icache(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	if (CPU_IS_COLDFIRE) {
1238c2ecf20Sopenharmony_ci		flush_cf_icache(0, ICACHE_MAX_ADDR);
1248c2ecf20Sopenharmony_ci	} else if (CPU_IS_040_OR_060) {
1258c2ecf20Sopenharmony_ci		asm volatile (	"nop\n"
1268c2ecf20Sopenharmony_ci			"	.chip	68040\n"
1278c2ecf20Sopenharmony_ci			"	cpusha	%bc\n"
1288c2ecf20Sopenharmony_ci			"	.chip	68k");
1298c2ecf20Sopenharmony_ci	} else {
1308c2ecf20Sopenharmony_ci		unsigned long tmp;
1318c2ecf20Sopenharmony_ci		asm volatile (	"movec	%%cacr,%0\n"
1328c2ecf20Sopenharmony_ci			"	or.w	%1,%0\n"
1338c2ecf20Sopenharmony_ci			"	movec	%0,%%cacr"
1348c2ecf20Sopenharmony_ci			: "=&d" (tmp)
1358c2ecf20Sopenharmony_ci			: "id" (FLUSH_I));
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * invalidate the cache for the specified memory range.
1418c2ecf20Sopenharmony_ci * It starts at the physical address specified for
1428c2ecf20Sopenharmony_ci * the given number of bytes.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_ciextern void cache_clear(unsigned long paddr, int len);
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * push any dirty cache in the specified memory range.
1478c2ecf20Sopenharmony_ci * It starts at the physical address specified for
1488c2ecf20Sopenharmony_ci * the given number of bytes.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_ciextern void cache_push(unsigned long paddr, int len);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * push and invalidate pages in the specified user virtual
1548c2ecf20Sopenharmony_ci * memory range.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_ciextern void cache_push_v(unsigned long vaddr, int len);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* This is needed whenever the virtual mapping of the current
1598c2ecf20Sopenharmony_ci   process changes.  */
1608c2ecf20Sopenharmony_ci#define __flush_cache_all()					\
1618c2ecf20Sopenharmony_ci({								\
1628c2ecf20Sopenharmony_ci	if (CPU_IS_COLDFIRE) {					\
1638c2ecf20Sopenharmony_ci		flush_cf_dcache(0, DCACHE_MAX_ADDR);		\
1648c2ecf20Sopenharmony_ci	} else if (CPU_IS_040_OR_060) {				\
1658c2ecf20Sopenharmony_ci		__asm__ __volatile__("nop\n\t"			\
1668c2ecf20Sopenharmony_ci				     ".chip 68040\n\t"		\
1678c2ecf20Sopenharmony_ci				     "cpusha %dc\n\t"		\
1688c2ecf20Sopenharmony_ci				     ".chip 68k");		\
1698c2ecf20Sopenharmony_ci	} else {						\
1708c2ecf20Sopenharmony_ci		unsigned long _tmp;				\
1718c2ecf20Sopenharmony_ci		__asm__ __volatile__("movec %%cacr,%0\n\t"	\
1728c2ecf20Sopenharmony_ci				     "orw %1,%0\n\t"		\
1738c2ecf20Sopenharmony_ci				     "movec %0,%%cacr"		\
1748c2ecf20Sopenharmony_ci				     : "=&d" (_tmp)		\
1758c2ecf20Sopenharmony_ci				     : "di" (FLUSH_I_AND_D));	\
1768c2ecf20Sopenharmony_ci	}							\
1778c2ecf20Sopenharmony_ci})
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define __flush_cache_030()					\
1808c2ecf20Sopenharmony_ci({								\
1818c2ecf20Sopenharmony_ci	if (CPU_IS_020_OR_030) {				\
1828c2ecf20Sopenharmony_ci		unsigned long _tmp;				\
1838c2ecf20Sopenharmony_ci		__asm__ __volatile__("movec %%cacr,%0\n\t"	\
1848c2ecf20Sopenharmony_ci				     "orw %1,%0\n\t"		\
1858c2ecf20Sopenharmony_ci				     "movec %0,%%cacr"		\
1868c2ecf20Sopenharmony_ci				     : "=&d" (_tmp)		\
1878c2ecf20Sopenharmony_ci				     : "di" (FLUSH_I_AND_D));	\
1888c2ecf20Sopenharmony_ci	}							\
1898c2ecf20Sopenharmony_ci})
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci#define flush_cache_all() __flush_cache_all()
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci#define flush_cache_vmap(start, end)		flush_cache_all()
1948c2ecf20Sopenharmony_ci#define flush_cache_vunmap(start, end)		flush_cache_all()
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic inline void flush_cache_mm(struct mm_struct *mm)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	if (mm == current->mm)
1998c2ecf20Sopenharmony_ci		__flush_cache_030();
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci#define flush_cache_dup_mm(mm)			flush_cache_mm(mm)
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* flush_cache_range/flush_cache_page must be macros to avoid
2058c2ecf20Sopenharmony_ci   a dependency on linux/mm.h, which includes this file... */
2068c2ecf20Sopenharmony_cistatic inline void flush_cache_range(struct vm_area_struct *vma,
2078c2ecf20Sopenharmony_ci				     unsigned long start,
2088c2ecf20Sopenharmony_ci				     unsigned long end)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (vma->vm_mm == current->mm)
2118c2ecf20Sopenharmony_ci	        __flush_cache_030();
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic inline void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	if (vma->vm_mm == current->mm)
2178c2ecf20Sopenharmony_ci	        __flush_cache_030();
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* Push the page at kernel virtual address and clear the icache */
2228c2ecf20Sopenharmony_ci/* RZ: use cpush %bc instead of cpush %dc, cinv %ic */
2238c2ecf20Sopenharmony_cistatic inline void __flush_page_to_ram(void *vaddr)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	if (CPU_IS_COLDFIRE) {
2268c2ecf20Sopenharmony_ci		unsigned long addr, start, end;
2278c2ecf20Sopenharmony_ci		addr = ((unsigned long) vaddr) & ~(PAGE_SIZE - 1);
2288c2ecf20Sopenharmony_ci		start = addr & ICACHE_SET_MASK;
2298c2ecf20Sopenharmony_ci		end = (addr + PAGE_SIZE - 1) & ICACHE_SET_MASK;
2308c2ecf20Sopenharmony_ci		if (start > end) {
2318c2ecf20Sopenharmony_ci			flush_cf_bcache(0, end);
2328c2ecf20Sopenharmony_ci			end = ICACHE_MAX_ADDR;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci		flush_cf_bcache(start, end);
2358c2ecf20Sopenharmony_ci	} else if (CPU_IS_040_OR_060) {
2368c2ecf20Sopenharmony_ci		__asm__ __volatile__("nop\n\t"
2378c2ecf20Sopenharmony_ci				     ".chip 68040\n\t"
2388c2ecf20Sopenharmony_ci				     "cpushp %%bc,(%0)\n\t"
2398c2ecf20Sopenharmony_ci				     ".chip 68k"
2408c2ecf20Sopenharmony_ci				     : : "a" (__pa(vaddr)));
2418c2ecf20Sopenharmony_ci	} else {
2428c2ecf20Sopenharmony_ci		unsigned long _tmp;
2438c2ecf20Sopenharmony_ci		__asm__ __volatile__("movec %%cacr,%0\n\t"
2448c2ecf20Sopenharmony_ci				     "orw %1,%0\n\t"
2458c2ecf20Sopenharmony_ci				     "movec %0,%%cacr"
2468c2ecf20Sopenharmony_ci				     : "=&d" (_tmp)
2478c2ecf20Sopenharmony_ci				     : "di" (FLUSH_I));
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
2528c2ecf20Sopenharmony_ci#define flush_dcache_page(page)		__flush_page_to_ram(page_address(page))
2538c2ecf20Sopenharmony_ci#define flush_dcache_mmap_lock(mapping)		do { } while (0)
2548c2ecf20Sopenharmony_ci#define flush_dcache_mmap_unlock(mapping)	do { } while (0)
2558c2ecf20Sopenharmony_ci#define flush_icache_page(vma, page)	__flush_page_to_ram(page_address(page))
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ciextern void flush_icache_user_page(struct vm_area_struct *vma, struct page *page,
2588c2ecf20Sopenharmony_ci				    unsigned long addr, int len);
2598c2ecf20Sopenharmony_ciextern void flush_icache_range(unsigned long address, unsigned long endaddr);
2608c2ecf20Sopenharmony_ciextern void flush_icache_user_range(unsigned long address,
2618c2ecf20Sopenharmony_ci		unsigned long endaddr);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic inline void copy_to_user_page(struct vm_area_struct *vma,
2648c2ecf20Sopenharmony_ci				     struct page *page, unsigned long vaddr,
2658c2ecf20Sopenharmony_ci				     void *dst, void *src, int len)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	flush_cache_page(vma, vaddr, page_to_pfn(page));
2688c2ecf20Sopenharmony_ci	memcpy(dst, src, len);
2698c2ecf20Sopenharmony_ci	flush_icache_user_page(vma, page, vaddr, len);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_cistatic inline void copy_from_user_page(struct vm_area_struct *vma,
2728c2ecf20Sopenharmony_ci				       struct page *page, unsigned long vaddr,
2738c2ecf20Sopenharmony_ci				       void *dst, void *src, int len)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	flush_cache_page(vma, vaddr, page_to_pfn(page));
2768c2ecf20Sopenharmony_ci	memcpy(dst, src, len);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci#endif /* _M68K_CACHEFLUSH_H */
280