/*
 * APM (Advanced Power Management) BIOS Device Driver
 *
 * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
 *
 * This software may be used, modified, copied, and distributed, in
 * both source and binary form provided that the above copyright and
 * these terms are retained. Under no circumstances is the author 
 * responsible for the proper functioning of this software, nor does 
 * the author assume any responsibility for damages incurred with its 
 * use.
 *
 * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
 *
 *	$Id$
 */

/*
 * If you want to know the specification of APM BIOS, see the following
 * documentations,
 *
 * [1] Intel Corporation and Microsoft Corporation, "Advanced Power 
 *     Management, The Next Generation, Version 1.0", Feb.,1992.
 *
 * [2] Intel Corporation and Microsoft Corporation, "Advanced Power
 *     Management (APM) BIOS Interface Specification Revision 1.1",
 *     Sep.,1993, Intel Order Number: 241704-001, Microsoft Part
 *     Number: 781-110-X01
 *
 * or contact
 *
 * APM Support Desk (Intel Corporation, US)
 *   TEL: (800)628-8686 
 *   FAX: (916)356-6100.
 */

	.file	"apm_init.S"

#define ASM

#include "real_prot.h"
#include "apm_bios.h"
#include "apm_segments.h"

/*
 * APM BIOS initializer
 *
 * Return value:
 *	%eax	0xffffffff	Can't find APM BIOS
 *		0xfffffffe	Don't support 32bit connection
 *		0xfffffffd	Connection error
 *		otherwise	APM version (16bit BCD format)
 *	%ebx			APM cs entry offset (32bit)
 *	%ecx	lower 16bit	APM 16bit cs base (real mode segment)
 *		upper 16bit	APM 32bit cs base (real mode segment)
 *	%edx	lower 16bit	APM ds limit (real mode segment)
 *		upper 16bit	[Reserved]
 *	%esi	lower 16bit	APM cs limit (APM 1.1 or later)
 *		upper 16bit	APM ds limit (APM 1.1 or later)
 *	%edi	bit0 = 1	16bit protected mode interface supported
 *		bit1 = 1	32bit protected mode interface supported
 *		bit2 = 1	"CPU idle" call slows processor clock speed
 *		bit3 = 1	APM BIOS Power Management disabled
 *		bit4 = 1	APM BIOS Power Management disengaged
 */

	.text
ENTRY(apm_init)
	cli				/* disable interrupt */
	pushl	%ebp			/* save original base pointer */
					/* ebp is used as a register variable */
	/* 
	 * save old data segments: We assume that %ds == %es && %ds == %ss
	 */
	pushl	%fs
	movw	%ds, %ax
	movw	%ax, %fs
	movw	$(APM_INIT_DS_SEL), %ax	/* initializer data segment */
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss
	movl	%esp, old_esp		/* save original stack pointer */
	movl	$0x10000, %esp		/* setup temporary stack */
					/* (note that it isn't 0x00000000) */

	sidt	EXT(Idtr_prot)		/* save current IDT */
	call	EXT(prot_to_real)	/* return to real mode */

	/*
	 * APM installation check
	 */
	movb	$(APM_BIOS), %ah
	movb	$(APM_INSTCHECK), %al
	data32
	movl	$(PMDV_APMBIOS), %ebx
	sti
	int	$(SYSTEM_BIOS)		/* call system BIOS */
	cli

	jnc	1f			/* if found, goto 1f */
	
	data32
	call	EXT(real_to_prot)	/* come back again to protected mode */
	movl	$(APMINI_CANTFIND), apm_version
					/* can't find APM BIOS */
	jmp	finish

1:
	movl	%eax, %edx		/* actually, movw %ax, %dx */
					/* save the value of %ax */
	data32
	call	EXT(real_to_prot)	/* come back again to protected mode */
	cmpw	$0x504d, %bx		/* "PM" signature? */
	jz	1f

	movl	$(APMINI_CANTFIND), apm_version
					/* can't find APM BIOS */
	jmp	finish

1:
	testl	$(APM_32BIT_SUPPORT), %ecx
					/* supports 32bit connection? */
	jnz	1f

	movl	$(APMINI_NOT32BIT), apm_version
					/* don't support 32bit connection */
	jmp	finish
1:
	andl	$0x0000ffff, %edx
	movl	%edx, apm_version
	andl	$0x0000ffff, %ecx
	movl	%ecx, apm_flags

	/*
	 * APM Protected Mode 32-bit Interface Connect
	 */
	call	EXT(prot_to_real)	/* return to real mode */

	/* Disconnect, just in case */
	movb	$(APM_BIOS), %ah
	movb	$(APM_DISCONNECT), %al
	data32
	movl	$(PMDV_APMBIOS), %ebx
	sti
	int	$(SYSTEM_BIOS)
	cli

	/* Ignore return code, but we can now connect safely */
	movb	$(APM_BIOS), %ah
	movb	$(APM_PROT32CONNECT), %al
	data32
	movl	$(PMDV_APMBIOS), %ebx
	sti
	int	$(SYSTEM_BIOS)
	cli
	jnc	1f			/* if successed, go to 1f */
	data32
	call	EXT(real_to_prot)
	movl	$(APMINI_CONNECTERR), apm_version
					/* connection error */
	jmp	finish
1:	
	/* save PM 32bit code segment into %bp */
	movl	%eax, %ebp		/* actually, movw %ax, %bp */
	data32
	call	EXT(real_to_prot)
	movl	$0x0000ffff, %eax
	andl	%eax, %ebp		/* 32bit cs base */
	andl	%eax, %ecx		/* 16bit cs base */
	andl	%eax, %edx		/* ds base */
	andl	%eax, %esi		/* cs length (APM 1.1 or later) */
	andl	%eax, %edi		/* ds length (APM 1.1 or later) */
					/* %ebx is code offset */
	/* pack 32bit cs and 16bit cs into %ecx */
	shll	$16, %ebp
	orl	%ebp, %ecx
	/* pack cs length and ds length into %esi */
	shll	$16, %edi
	orl	%edi, %esi
finish:
	cli
	lidt	EXT(Idtr_prot)		/* restore old IDTR */
	movl	old_esp, %esp		/* restore old stack pointer */
	movl	apm_version, %ebp	/* stored to %eax later */
	movl	apm_flags, %edi
	movw	%fs, %ax
	movw	%ax, %ss
	movw	%ax, %es
	movw	%ax, %ds
	movl	%ebp, %eax
	popl	%fs
	popl	%ebp			/* restore old base pointer */
	lret				/* restore old code segment */

	.data

	.globl	EXT(ouraddr)
LEXT(ouraddr)
	.long	APM_OURADDR

old_esp:
	.long	0
apm_version:
	.long	0
apm_flags:
	.long	0
old_ds:
	.word	0
old_es:
	.word	0
old_ss:
	.word	0
