/*	$NetBSD: ofw_subr.S,v 1.2.18.4 2008/02/27 08:36:23 yamt Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


	.local	firmstk
	.globl	openfirmware_entry
	.local	ofwsrsave
	.local	OF_buffer

	.data
GLOBAL(ofmsr)
	.long	0,0,0,0,0		/* msr & sprg[0-3] used in OF */

GLOBAL(ofwsprg0save)
	.long	0

GLOBAL(ofwreal_incharge)
	.long	0

	.comm	firmstk,NBPG,8
	.comm	OF_buffer,NBPG,4
	.comm	openfirmware_entry,4,4	/* openfirmware entry point */
	.comm	ofwsrsave,64,4		/* openfirmware SR savearea */

/*
 * Called by start to save the initial OFW state so we can restore it
 * when call back to OFW.
 */
ENTRY_NOPROFILE(ofwinit)
#ifdef	FIRMWORKSBUGS
	mfmsr	%r0
	andi.	%r0,%r0,PSL_IR|PSL_DR
	beq	1f

	li	%r8,1
	lis	%r9,ofwreal_incharge@ha
	stw	%r8,ofwreal_incharge@l(9)

	mflr	%r30
	bl	_C_LABEL(ofwr_init)
	mtlr	%r30
1:
#endif
	lis	%r8,openfirmware_entry@ha
	stw	%r5,openfirmware_entry@l(%r8) /* save client interface handler*/

	mfmsr	%r0
	lis	%r9,ofmsr@ha
	stwu	%r0,ofmsr@l(%r9)		/* save initial MSR value */

        mfsprg  %r0,0			/* save SPRGs */
       	stwu	%r0,4(%r9)
        mfsprg  %r0,1
       	stwu	%r0,4(%r9)
        mfsprg  %r0,2
       	stwu	%r0,4(%r9)
        mfsprg  %r0,3
       	stw	%r0,4(%r9)

	lis	%r8,OF_buffer@ha
	addi	%r8,%r8,OF_buffer@l
	lis	%r9,_C_LABEL(OF_buf)@ha
	stw	%r8,_C_LABEL(OF_buf)@l(%r9)

	blr

/*
 * OpenFirmware entry point
 */
	.text
ENTRY(openfirmware)
	mflr	%r0			/* save return address */
	stw	%r0,4(%r1)
	stwu	%r1,-16(%r1)		/* setup stack frame */

	lis	%r4,openfirmware_entry@ha	/* get firmware entry point */
	lwz	%r4,openfirmware_entry@l(%r4)
	mtlr	%r4

	mfsprg	%r5,0			/* save current sprg0 (curcpu) */
	lis	%r4,ofwsprg0save@ha
	addi	%r4,%r4,ofwsprg0save@l
	stw	%r5,0(%r4)

#ifdef FIRMWORKSBUGS
	lis	%r4,ofwreal_incharge@ha
	lwz	%r4,ofwreal_incharge@l(%r4)
	cmpwi	%r4,1
	bne	1f
	blrl
	b	4f
1:
#endif
	mfmsr	%r4			/* save msr */
	stw	%r4,8(%r1)

	li	%r0,0			/* clear battable translations */
#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
	mtdbatu	2,%r0
	mtdbatu	3,%r0
	mtibatu	2,%r0
	mtibatu	3,%r0
#endif /* PPC_OEA */

	lis	%r4,ofwsrsave@ha		/* save current SRs */
	addi	%r4,%r4,ofwsrsave@l
	li	%r5,0
1:	mfsrin	%r0,%r5
	stw	%r0,0(%r4)
	addi	%r4,%r4,4
	addis	%r5,%r5,0x10000000@h
	cmpwi	%r5,0
	bne	1b

	lis	%r4,_C_LABEL(ofw_pmap)@ha	/* load OFW SR */
	addi	%r4,%r4,_C_LABEL(ofw_pmap)@l
	lwz	%r0,PM_KERNELSR(%r4)
	cmpwi	%r0,0			/* pm_sr[KERNEL_SR] == 0? */
	beq	2f			/* then skip (not initialized yet) */
	li	%r5,0
1:	lwz	%r0,0(%r4)
	mtsrin	%r0,%r5
	addi	%r4,%r4,4
	addis	%r5,%r5,0x10000000@h
	cmpwi	%r5,0
	bne	1b
2:
	lis	%r4,ofmsr@ha		/* Open Firmware msr + sprg[0-3] */
	lwzu	%r5,ofmsr+16@l(%r4)
	mtsprg	3,%r5
	lwzu	%r5,-4(%r4)
	mtsprg	2,%r5
	lwzu	%r5,-4(%r4)
	mtsprg	1,%r5
	lwzu	%r5,-4(%r4)
	mtsprg	0,%r5
	lwz	%r5,-4(%r4)
	mtmsr	%r5
	isync

	blrl				/* call Open Firmware */

	lis	%r4,ofwsrsave@ha	/* restore saved SRs */
	addi	%r4,%r4,ofwsrsave@l
	li	%r5,0
1:	lwz	%r0,0(%r4)
	mtsrin	%r0,%r5
	addi	%r4,%r4,4
	addis	%r5,%r5,0x10000000@h
	cmpwi	%r5,0
	bne	1b

	lwz	%r4,8(%r1)		/* restore msr */
	mtmsr	%r4
	isync
4:	
	lis	%r4,ofwsprg0save@ha	/* restore saved sprg0 (curcpu) */
	addi	%r4,%r4,ofwsprg0save@l
	lwz	%r5,0(%r4)
	mtsprg	0,%r5

	lwz	%r1,0(%r1)		/* and return */
	lwz	%r0,4(%r1)
	mtlr	%r0
	blr

/*
 * Switch to/from OpenFirmware real mode stack
 *
 * Note: has to be called as the very first thing in OpenFirmware interface
 * routines.
 * E.g.:
 * int
 * OF_xxx(arg1, arg2)
 * type arg1, arg2;
 * {
 *	static struct {
 *		char *name;
 *		int nargs;
 *		int nreturns;
 *		char *method;
 *		int arg1;
 *		int arg2;
 *		int ret;
 *	} args = {
 *		"xxx",
 *		2,
 *		1,
 *	};
 *
 *	ofw_stack();
 *	args.arg1 = arg1;
 *	args.arg2 = arg2;
 *	if (openfirmware(&args) < 0)
 *		return -1;
 *	return args.ret;
 * }
 */

ENTRY(ofw_stack)
	mfmsr	%r8			/* turn off interrupts */
	andi.	%r0,%r8,~(PSL_EE|PSL_RI)@l
	mtmsr	%r0
	stw	%r8,4(%r1)		/* abuse return address slot */

	lwz	%r5,0(%r1)		/* get length of stack frame */
	subf	%r5,%r1,%r5

	lis	%r7,firmstk+NBPG-8@ha
	addi	%r7,%r7,firmstk+NBPG-8@l
	lis	%r6,ofw_back@ha
	addi	%r6,%r6,ofw_back@l
	subf	%r4,%r5,%r7		/* make room for stack frame on
					   new stack */
	stw	%r6,-4(%r7)		/* setup return pointer */
	stwu	%r1,-8(%r7)

	stw	%r7,-8(%r4)

	addi	%r3,%r1,8
	addi	%r1,%r4,-8
	subi	%r5,%r5,8

	b	_C_LABEL(ofbcopy)	/* and copy it */

ofw_back:
	lwz	%r1,0(%r1)		/* get callers original stack pointer */

	lwz	%r0,4(%r1)		/* get saved msr from abused slot */
	mtmsr	%r0

	lwz	%r1,0(%r1)		/* return */
	lwz	%r0,4(%r1)
	mtlr	%r0
	blr
