/* unaligned.h: unaligned access handler
 *
 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#ifndef _ASM_UNALIGNED_H
#define _ASM_UNALIGNED_H

/*
 * Unaligned accesses on uClinux can't be performed in a fault handler - the
 * CPU detects them as imprecise exceptions making this impossible.
 *
 * With the FR451, however, they are precise, and so we used to fix them up in
 * the memory access fault handler.  However, instruction bundling make this
 * impractical.  So, now we fall back to using memcpy.
 */
#ifdef CONFIG_MMU

/*
 * The asm statement in the macros below is a way to get GCC to copy a
 * value from one variable to another without having any clue it's
 * actually doing so, so that it won't have any idea that the values
 * in the two variables are related.
 */

#define get_unaligned(ptr) ({				\
	typeof((*(ptr))) __x;				\
	void *__ptrcopy;				\
	asm("" : "=r" (__ptrcopy) : "0" (ptr));		\
	memcpy(&__x, __ptrcopy, sizeof(*(ptr)));	\
	__x;						\
})

#define put_unaligned(val, ptr) ({			\
	typeof((*(ptr))) __x = (val);			\
	void *__ptrcopy;				\
	asm("" : "=r" (__ptrcopy) : "0" (ptr));		\
	memcpy(__ptrcopy, &__x, sizeof(*(ptr)));	\
})

extern int handle_misalignment(unsigned long esr0, unsigned long ear0, unsigned long epcr0);

#else

#define get_unaligned(ptr)							\
({										\
	typeof(*(ptr)) x;							\
	const char *__p = (const char *) (ptr);					\
										\
	switch (sizeof(x)) {							\
	case 1:									\
		x = *(ptr);							\
		break;								\
	case 2:									\
	{									\
		uint8_t a;							\
		asm("	ldub%I2		%M2,%0		\n"			\
		    "	ldub%I3.p	%M3,%1		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%1,%0	\n"			\
		    : "=&r"(x), "=&r"(a)					\
		    : "m"(__p[0]),  "m"(__p[1])					\
		    );								\
		break;								\
	}									\
										\
	case 4:									\
	{									\
		uint8_t a;							\
		asm("	ldub%I2		%M2,%0		\n"			\
		    "	ldub%I3.p	%M3,%1		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%1,%0	\n"			\
		    "	ldub%I4.p	%M4,%1		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%1,%0	\n"			\
		    "	ldub%I5.p	%M5,%1		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%1,%0	\n"			\
		    : "=&r"(x), "=&r"(a)					\
		    : "m"(__p[0]),  "m"(__p[1]), "m"(__p[2]), "m"(__p[3])	\
		    );								\
		break;								\
	}									\
										\
	case 8:									\
	{									\
		union { uint64_t x; __u32 y[2]; } z;				\
		uint8_t a;							\
		asm("	ldub%I3		%M3,%0		\n"			\
		    "	ldub%I4.p	%M4,%2		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%2,%0	\n"			\
		    "	ldub%I5.p	%M5,%2		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%2,%0	\n"			\
		    "	ldub%I6.p	%M6,%2		\n"			\
		    "	slli		%0,#8,%0	\n"			\
		    "	or		%0,%2,%0	\n"			\
		    "	ldub%I7		%M7,%1		\n"			\
		    "	ldub%I8.p	%M8,%2		\n"			\
		    "	slli		%1,#8,%1	\n"			\
		    "	or		%1,%2,%1	\n"			\
		    "	ldub%I9.p	%M9,%2		\n"			\
		    "	slli		%1,#8,%1	\n"			\
		    "	or		%1,%2,%1	\n"			\
		    "	ldub%I10.p	%M10,%2		\n"			\
		    "	slli		%1,#8,%1	\n"			\
		    "	or		%1,%2,%1	\n"			\
		    : "=&r"(z.y[0]), "=&r"(z.y[1]), "=&r"(a)			\
		    : "m"(__p[0]), "m"(__p[1]), "m"(__p[2]), "m"(__p[3]),	\
		      "m"(__p[4]), "m"(__p[5]), "m"(__p[6]), "m"(__p[7])	\
		    );								\
		x = z.x;							\
		break;								\
	}									\
										\
	default:								\
		x = 0;								\
		BUG();								\
		break;								\
	}									\
										\
	x;									\
})

#define put_unaligned(val, ptr)								\
do {											\
	char *__p = (char *) (ptr);							\
	int x;										\
											\
	switch (sizeof(*ptr)) {								\
	case 2:										\
	{										\
		asm("	stb%I1.p	%0,%M1		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I2		%0,%M2		\n"				\
		    : "=r"(x), "=m"(__p[1]),  "=m"(__p[0])				\
		    : "0"(val)								\
		    );									\
		break;									\
	}										\
											\
	case 4:										\
	{										\
		asm("	stb%I1.p	%0,%M1		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I2.p	%0,%M2		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I3.p	%0,%M3		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I4		%0,%M4		\n"				\
		    : "=r"(x), "=m"(__p[3]),  "=m"(__p[2]), "=m"(__p[1]), "=m"(__p[0])	\
		    : "0"(val)								\
		    );									\
		break;									\
	}										\
											\
	case 8:										\
	{										\
		uint32_t __high, __low;							\
		__high = (uint64_t)val >> 32;						\
		__low = val & 0xffffffff;						\
		asm("	stb%I2.p	%0,%M2		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I3.p	%0,%M3		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I4.p	%0,%M4		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I5.p	%0,%M5		\n"				\
		    "	srli		%0,#8,%0	\n"				\
		    "	stb%I6.p	%1,%M6		\n"				\
		    "	srli		%1,#8,%1	\n"				\
		    "	stb%I7.p	%1,%M7		\n"				\
		    "	srli		%1,#8,%1	\n"				\
		    "	stb%I8.p	%1,%M8		\n"				\
		    "	srli		%1,#8,%1	\n"				\
		    "	stb%I9		%1,%M9		\n"				\
		    : "=&r"(__low), "=&r"(__high), "=m"(__p[7]), "=m"(__p[6]), 		\
		      "=m"(__p[5]), "=m"(__p[4]), "=m"(__p[3]), "=m"(__p[2]), 		\
		      "=m"(__p[1]), "=m"(__p[0])					\
		    : "0"(__low), "1"(__high)						\
		    );									\
		break;									\
	}										\
											\
        default:									\
		*(ptr) = (val);								\
		break;									\
	}										\
} while(0)

#endif

#endif
