/*
 * Copyright (C) 2000, 2001 Broadcom Corporation
 * Copyright (C) 2002 Ralf Baechle
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifndef __ASM_SIBYTE_64BIT_H
#define __ASM_SIBYTE_64BIT_H

#include <linux/config.h>
#include <linux/types.h>

#ifdef CONFIG_MIPS32

#include <asm/system.h>

/*
 * This is annoying...we can't actually write the 64-bit IO register properly
 * without having access to 64-bit registers...  which doesn't work by default
 * in o32 format...grrr...
 */
static inline void __out64(u64 val, unsigned long addr)
{
	u64 tmp;

	__asm__ __volatile__ (
		"	.set	mips3				\n"
		"	dsll32	%L0, %L0, 0	# __out64	\n"
		"	dsrl32	%L0, %L0, 0			\n"
		"	dsll32	%M0, %M0, 0			\n"
		"	or	%L0, %L0, %M0			\n"
		"	sd	%L0, (%2)			\n"
		"	.set	mips0				\n"
		: "=r" (tmp)
		: "0" (val), "r" (addr));
}

static inline void out64(u64 val, unsigned long addr)
{
	unsigned long flags;

	local_irq_save(flags);
	__out64(val, addr);
	local_irq_restore(flags);
}

static inline u64 __in64(unsigned long addr)
{
	u64 res;

	__asm__ __volatile__ (
		"	.set	mips3		# __in64	\n"
		"	ld	%L0, (%1)			\n"
		"	dsra32	%M0, %L0, 0			\n"
		"	sll	%L0, %L0, 0			\n"
		"	.set	mips0				\n"
		: "=r" (res)
		: "r" (addr));

	return res;
}

static inline u64 in64(unsigned long addr)
{
	unsigned long flags;
	u64 res;

	local_irq_save(flags);
	res = __in64(addr);
	local_irq_restore(flags);

	return res;
}

#endif /* CONFIG_MIPS32 */

#ifdef CONFIG_MIPS64

/*
 * These are provided so as to be able to use common
 * driver code for the 32-bit and 64-bit trees
 */
extern inline void out64(u64 val, unsigned long addr)
{
	*(volatile unsigned long *)addr = val;
}

extern inline u64 in64(unsigned long addr)
{
	return *(volatile unsigned long *)addr;
}

#define __in64(a)	in64(a)
#define __out64(v,a)	out64(v,a)

#endif /* CONFIG_MIPS64 */

/*
 * Avoid interrupt mucking, just adjust the address for 4-byte access.
 * Assume the addresses are 8-byte aligned.
 */

#ifdef __MIPSEB__
#define __CSR_32_ADJUST 4
#else
#define __CSR_32_ADJUST 0
#endif

#define csr_out32(v,a) (*(volatile u32 *)((unsigned long)(a) + __CSR_32_ADJUST) = (v))
#define csr_in32(a)    (*(volatile u32 *)((unsigned long)(a) + __CSR_32_ADJUST))

#endif /* __ASM_SIBYTE_64BIT_H */
