/*
 *  linux/include/asm-arm/cpu-multi32.h
 *
 *  Copyright (C) 2000 Russell King
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#ifndef __ASSEMBLY__

#include <asm/memory.h>
#include <asm/page.h>

struct mm_struct;

/*
 * Don't change this structure - ASM code
 * relies on it.
 */
extern struct processor {
	/* MISC
	 * get data abort address/flags
	 */
	void (*_data_abort)(unsigned long pc);
	/*
	 * check for any bugs
	 */
	void (*_check_bugs)(void);
	/*
	 * Set up any processor specifics
	 */
	void (*_proc_init)(void);
	/*
	 * Disable any processor specifics
	 */
	void (*_proc_fin)(void);
	/*
	 * Special stuff for a reset
	 */
	volatile void (*reset)(unsigned long addr);
	/*
	 * Idle the processor
	 */
	int (*_do_idle)(void);
	/*
	 * Processor architecture specific
	 */
	struct {	/* CACHE */
		/*
		 * flush all caches
		 */
		void (*clean_invalidate_all)(void);
		/*
		 * flush a specific page or pages
		 */
		void (*clean_invalidate_range)(unsigned long address, unsigned long end, int flags);
	} cache;

	struct {	/* D-cache */
		/*
		 * invalidate the specified data range
		 */
		void (*invalidate_range)(unsigned long start, unsigned long end);
		/*
		 * clean specified data range
		 */
		void (*clean_range)(unsigned long start, unsigned long end);
		/*
		 * obsolete flush cache entry
		 */
		void (*clean_page)(void *virt_page);
		/*
		 * clean a virtual address range from the
		 * D-cache without flushing the cache.
		 */
		void (*clean_entry)(unsigned long start);
	} dcache;

	struct {	/* I-cache */
		/*
		 * invalidate the I-cache for the specified range
		 */
		void (*invalidate_range)(unsigned long start, unsigned long end);
		/*
		 * invalidate the I-cache for the specified virtual page
		 */
		void (*invalidate_page)(void *virt_page);
	} icache;

	struct {	/* PageTable */
		/*
		 * Set the page table
		 */
		void (*set_pgd)(unsigned long pgd_phys, struct mm_struct *mm);
		/*
		 * Set a PMD (handling IMP bit 4)
		 */
		void (*flush_pmd)(pmd_t *pmdp);
		/*
		 * Set a PTE
		 */
		void (*set_pte)(pte_t *ptep, pte_t pte);
	} pgtable;
} processor;

extern const struct processor arm6_processor_functions;
extern const struct processor arm7_processor_functions;
extern const struct processor sa110_processor_functions;

#define cpu_check_bugs()			processor._check_bugs()
#define cpu_proc_init()				processor._proc_init()
#define cpu_proc_fin()				processor._proc_fin()
#define cpu_reset(addr)				processor.reset(addr)
#define cpu_do_idle()				processor._do_idle()

#define cpu_cache_clean_invalidate_all()	processor.cache.clean_invalidate_all()
#define cpu_cache_clean_invalidate_range(s,e,f)	processor.cache.clean_invalidate_range(s,e,f)

#define cpu_dcache_clean_page(vp)		processor.dcache.clean_page(vp)
#define cpu_dcache_clean_entry(addr)		processor.dcache.clean_entry(addr)
#define cpu_dcache_clean_range(s,e)		processor.dcache.clean_range(s,e)
#define cpu_dcache_invalidate_range(s,e)	processor.dcache.invalidate_range(s,e)

#define cpu_icache_invalidate_range(s,e)	processor.icache.invalidate_range(s,e)
#define cpu_icache_invalidate_page(vp)		processor.icache.invalidate_page(vp)

#define cpu_set_pgd(pgd,mm)			processor.pgtable.set_pgd(pgd,mm)
#define cpu_flush_pmd(pmdp)			processor.pgtable.flush_pmd(pmdp)
#define cpu_set_pte(ptep, pte)			processor.pgtable.set_pte(ptep, pte)

#define cpu_switch_mm(pgd,mm)			cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)),mm)

#define cpu_get_pgd()	\
	({						\
		unsigned long pg;			\
		__asm__("mrc p15, 0, %0, c2, c0, 0"	\
			 : "=r" (pg));			\
		pg &= ~0x3fff;				\
		(pgd_t *)phys_to_virt(pg);		\
	})

#endif
