/*
 * Copyright (C) 2005,2006,2007,2008 Imagination Technologies
 */

#ifndef __ASM_METAG_PROCESSOR_H
#define __ASM_METAG_PROCESSOR_H

#include <linux/atomic.h>

#include <asm/page.h>
#include <asm/ptrace.h>
#include <asm/metag_regs.h>

/*
 * Default implementation of macro that returns current
 * instruction pointer ("program counter").
 */
#define current_text_addr() ({ __label__ _l; _l: &&_l; })

/* The task stops where the kernel starts */
#define TASK_SIZE	PAGE_OFFSET
/* Add an extra page of padding at the top of the stack for the guard page. */
#define STACK_TOP	(TASK_SIZE - PAGE_SIZE)
#define STACK_TOP_MAX	STACK_TOP
/* Maximum virtual space for stack */
#define STACK_SIZE_MAX	(CONFIG_MAX_STACK_SIZE_MB*1024*1024)

/* This decides where the kernel will search for a free chunk of vm
 * space during mmap's.
 */
#define TASK_UNMAPPED_BASE	META_MEMORY_BASE

typedef struct {
	unsigned long seg;
} mm_segment_t;

#ifdef CONFIG_METAG_FPU
struct meta_fpu_context {
	TBICTXEXTFPU fpstate;
	union {
		struct {
			TBICTXEXTBB4 fx8_15;
			TBICTXEXTFPACC fpacc;
		} fx8_15;
		struct {
			TBICTXEXTFPACC fpacc;
			TBICTXEXTBB4 unused;
		} nofx8_15;
	} extfpstate;
	bool needs_restore;
};
#else
struct meta_fpu_context {};
#endif

#ifdef CONFIG_METAG_DSP
struct meta_ext_context {
	struct {
		TBIEXTCTX ctx;
		TBICTXEXTBB8 bb8;
		TBIDUAL ax[TBICTXEXTAXX_BYTES / sizeof(TBIDUAL)];
		TBICTXEXTHL2 hl2;
		TBICTXEXTTDPR ext;
		TBICTXEXTRP6 rp;
	} regs;

	/* DSPRAM A and B save areas. */
	void *ram[2];

	/* ECH encoded size of DSPRAM save areas. */
	unsigned int ram_sz[2];
};
#else
struct meta_ext_context {};
#endif

struct thread_struct {
	PTBICTX kernel_context;
	/* A copy of the user process Sig.SaveMask. */
	unsigned int user_flags;
	struct meta_fpu_context *fpu_context;
	void __user *tls_ptr;
	unsigned short int_depth;
	unsigned short txdefr_failure;
	struct meta_ext_context *dsp_context;
};

#define INIT_THREAD  { \
	NULL,			/* kernel_context */	\
	0,			/* user_flags */	\
	NULL,			/* fpu_context */	\
	NULL,			/* tls_ptr */		\
	1,			/* int_depth - we start in kernel */	\
	0,			/* txdefr_failure */	\
	NULL,			/* dsp_context */	\
}

/* Needed to make #define as we are referencing 'current', that is not visible
 * yet.
 *
 * Stack layout is as below.

      argc            argument counter (integer)
      argv[0]         program name (pointer)
      argv[1...N]     program args (pointers)
      argv[argc-1]    end of args (integer)
      NULL
      env[0...N]      environment variables (pointers)
      NULL

 */
#define start_thread(regs, pc, usp) do {				   \
	unsigned int *argc = (unsigned int *) bprm->exec;		   \
	current->thread.int_depth = 1;					   \
	/* Force this process down to user land */			   \
	regs->ctx.SaveMask = TBICTX_PRIV_BIT;				   \
	regs->ctx.CurrPC = pc;						   \
	regs->ctx.AX[0].U0 = usp;					   \
	regs->ctx.DX[3].U1 = *((int *)argc);			/* argc */ \
	regs->ctx.DX[3].U0 = (int)((int *)argc + 1);		/* argv */ \
	regs->ctx.DX[2].U1 = (int)((int *)argc +			   \
				   regs->ctx.DX[3].U1 + 2);	/* envp */ \
	regs->ctx.DX[2].U0 = 0;				   /* rtld_fini */ \
} while (0)

/* Forward declaration, a strange C thing */
struct task_struct;

/* Free all resources held by a thread. */
static inline void release_thread(struct task_struct *dead_task)
{
}

#define copy_segments(tsk, mm)		do { } while (0)
#define release_segments(mm)		do { } while (0)

/*
 * Return saved PC of a blocked thread.
 */
#define	thread_saved_pc(tsk)	\
	((unsigned long)(tsk)->thread.kernel_context->CurrPC)
#define thread_saved_sp(tsk)	\
	((unsigned long)(tsk)->thread.kernel_context->AX[0].U0)
#define thread_saved_fp(tsk)	\
	((unsigned long)(tsk)->thread.kernel_context->AX[1].U0)

unsigned long get_wchan(struct task_struct *p);

#define	KSTK_EIP(tsk)	(task_pt_regs(tsk)->ctx.CurrPC)
#define	KSTK_ESP(tsk)	(task_pt_regs(tsk)->ctx.AX[0].U0)

#define user_stack_pointer(regs)        ((regs)->ctx.AX[0].U0)

#define cpu_relax()     barrier()
#define cpu_relax_lowlatency()  cpu_relax()

extern void setup_priv(void);

static inline unsigned int hard_processor_id(void)
{
	unsigned int id;

	asm volatile ("MOV	%0, TXENABLE\n"
		      "AND	%0, %0, %1\n"
		      "LSR	%0, %0, %2\n"
		      : "=&d" (id)
		      : "I" (TXENABLE_THREAD_BITS),
			"K" (TXENABLE_THREAD_S)
		      );

	return id;
}

#define OP3_EXIT	0

#define HALT_OK		0
#define HALT_PANIC	-1

/*
 * Halt (stop) the hardware thread. This instruction sequence is the
 * standard way to cause a Meta hardware thread to exit. The exit code
 * is pushed onto the stack which is interpreted by the debug adapter.
 */
static inline void hard_processor_halt(int exit_code)
{
	asm volatile ("MOV	D1Ar1, %0\n"
		      "MOV	D0Ar6, %1\n"
		      "MSETL	[A0StP],D0Ar6,D0Ar4,D0Ar2\n"
		      "1:\n"
		      "SWITCH	#0xC30006\n"
		      "B		1b\n"
		      : : "r" (exit_code), "K" (OP3_EXIT));
}

/* Set these hooks to call SoC specific code to restart/halt/power off. */
extern void (*soc_restart)(char *cmd);
extern void (*soc_halt)(void);

extern void show_trace(struct task_struct *tsk, unsigned long *sp,
		       struct pt_regs *regs);

extern const struct seq_operations cpuinfo_op;

#endif
