;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/

;/************************************************************************/
;/*									 */
;/* Driver Name: IBM2SCSI.ADD - Adapter Driver for ABIOS SCB Devices	 */
;/*		 ---------------------------------------------------	 */
;/*									 */
;/* Source File Name: IO.ASM						 */
;/*									 */
;/* Descriptive Name: I/O routines for accessing the adpaters.		 */
;/*									 */
;/* Function:								 */
;/*									 */
;/*									 */
;/*----------------------------------------------------------------------*/
;/*									 */
;/*									 */
;/* DISCLAIMER OF WARRANTIES.  The following [enclosed] code is 	 */
;/* provided to you solely for the purpose of assisting you in		 */
;/* the development of your applications. The code is provided		 */
;/* "AS IS", without warranty of any kind. IBM shall not be liable       */
;/* for any damages arising out of your use of this code, even if	 */
;/* they have been advised of the possibility of such damages.		 */
;/*									 */
;/*----------------------------------------------------------------------*/
;/*									 */
;/* Change Log								 */
;/*									 */
;/* Mark    Date      Programmer  Comment				 */
;/* ----    ----      ----------  -------				 */
;/* @nnnn   mm/dd/yy  NNN						 */
;/*									 */
;/*									 */
;/************************************************************************/

;
; Some local equates

BPTR	EQU	<byte ptr>
WPTR	EQU	<word ptr>
DPTR	EQU	<dword ptr>

CIR0	EQU	0   ; command interface register 0
CIR1	EQU	1   ; command interface register 1
CIR2	EQU	2   ; command interface register 2
CIR3	EQU	3   ; command interface register 3
AR	EQU	4   ; attention register
BCR	EQU	5   ; basic control reg
ISR	EQU	6   ; interrupt status register
BSR	EQU	7   ; basic status register

LONGSCB EQU   40h   ; long SCB attention request code
NORMSCB EQU   30h   ; normal SCB attention request code
IMMDCMD EQU   10h   ; immediate command



WAIT400 EQU    27   ; 27 15.625 intervals for 400 us wait (a little longer)
WAIT50	EQU	4   ; 4 intervals for 50 us wait



BUSY	EQU   01h   ; busy bit




	public	__StartIO
	public	_ReadReg
	public	_WriteReg
	public	_WriteAttn
	public	_ResetToggle


_TEXT	segment word public 'code'
	assume	cs:_TEXT

.286p


;/*************************************************************************/
;/*									  */
;/* StartIO - Start an SCB to the specified device.			  */
;/*									  */
;/*    This routine will start the SCB on the device passed.		  */
;/*									  */
;/*	  Entry: StartSCB(USHORT IOAddr,ULONG ppSCB, USHORT cmd);	  */
;/*									  */
;/*		    IOAddr - I/O base address of the card		  */
;/*		    ppSCB  - phys ptr to SCB or the immediate command	  */
;/*		    cmd    - command to write to the attention register.  */
;/*			     It includes the logical device number and	  */
;/*			     the request code.	Only the low byte is used */
;/*									  */
;/*	  Exit:  AX = 0 if no error, <> 0 if busy never cleared.	  */
;/*		 ints disabled if started (AX = 0)			  */
;/*									  */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr	    EQU     <(WPTR [bp+4])>	 ; IO base address
ppSCB	    EQU     <(DPTR [bp+6])>	 ; physical ptr to the SCB
cmd	    EQU     <(WPTR [bp+10])>	 ; command to the adapter.


__StartIO	proc	near

	enter	0,0

;
;	First wait for busy to be cleared

	push	dx
	push	cx

	mov	cx, WAIT400	; 400 us wait
	mov	dx, IOAddr	; card IO address
	call	wbsy		; go wait

	jc	bsy		; card is busy, something wrong

;
;	Card is not busy and ints are off now.	Program the Command interface
;	registers with the SCB address.

.386p
	mov	eax, ppSCB	; get the physical address
	mov	cx, 4		; loop 4 times
outl:	out	dx, al		; out the low byte
	inc	dx		; next CIR
	shr	eax, 8		; shift next byte into al
	loop	outl		; go loop

.286p

;
;	Now get the command code from the stack and write the low byte to
;	the attention register.

	mov	ax, cmd 	; get logical device number from stack
	mov	dx, IOAddr	; get base I/O address
	add	dx, AR		; attention register offset
	out	dx, al		; start the I/0
	mov	ax, 0		; no error
	jmp	exit		; leave

bsy:	mov	ax, 1		; signal some error

exit:	pop	cx		; restore stack
	pop	dx

	leave
	ret

__StartIO	endp


;/*************************************************************************/
;/*									  */
;/* ReadReg - read the specified adapter register.			  */
;/*									  */
;/*    This routine reads the register pointed to by IOAddr and returns   */
;/*    the value in AX. 						  */
;/*									  */
;/*	  Entry: ReadReg(USHORT IOAddr);				  */
;/*									  */
;/*	  Exit:  AL = register value					  */
;/*									  */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr	    EQU     <(WPTR [bp+4])>	 ; IO reg address

_ReadReg  proc	near

	enter	0,0

	push	dx	       ; save DX
	mov	dx, IOAddr     ; get register address
	in	al, dx	       ; read it
	pop	dx	       ; restore dx
	leave		       ; clear stack frame
	ret		       ; return to caller

_ReadReg  endp


;/*************************************************************************/
;/*									  */
;/* WriteReg - write adapter register.					  */
;/*									  */
;/*    This routine writes the low byte of the value on the stack to the  */
;/*    specified register.  This should not be used for writing to the	  */
;/*    attention register.						  */
;/*									  */
;/*	  Entry: WriteReg(USHORT IOAddr, USHORT val);			  */
;/*									  */
;/*	  Exit:  None.							  */
;/*									  */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr	    EQU     <(WPTR [bp+4])>	 ; IO reg address
val	    EQU     <(WPTR [bp+6])>	 ; value

_WriteReg  proc  near

	enter	0,0

	push	dx	       ; save DX
	push	ax	       ; save AX
	mov	ax, val        ; get value to write out (low byte only)
	mov	dx, IOAddr     ; get register address
	out	dx, al	       ; write the low byte out
	pop	ax	       ; restore ax
	pop	dx	       ; restore dx
	leave		       ; clear stack frame
	ret		       ; return to caller

_WriteReg  endp


;/*************************************************************************/
;/*									  */
;/* WriteAttn - write the attention register.				  */
;/*									  */
;/*    This routine writes the low byte of the value on the stack to the  */
;/*    attention register of the specified adapter.  It checks to make	  */
;/*    sure that busy is clear before doing the write.			  */
;/*									  */
;/*	  Entry: WriteReg(USHORT IOAddr, USHORT val);			  */
;/*									  */
;/*	  Exit:  0 = no error, <>0 = error				  */
;/*									  */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


IOAddr	    EQU     <(WPTR [bp+4])>	 ; base IO address
val	    EQU     <(WPTR [bp+6])>	 ; value

_WriteAttn  proc  near

	enter	0,0

;
;	First wait for busy to be cleared

	push	dx
	push	cx

	mov	cx, WAIT400	; 400 us wait
	mov	dx, IOAddr	; card IO address
	call	wbsy		; go wait

	jc	hosed		; card is busy, something wrong

	mov	ax, val 	; get value to write out (low byte only)
	mov	dx, IOAddr	; get the base IO address
	add	dx, AR		; get to the attention register
	out	dx, al		; write the low byte out
	mov	ax, 0		; no error
	jmp	allok

hosed:	mov	ax, 1		; error

allok:	pop	cx		; restore ax
	pop	dx		; restore dx
	sti
	leave			; clear stack frame
	ret			; return to caller

_WriteAttn  endp

;------ wbsy ---------------------------------------------------------------
; fixed time wait routine (memory refresh output used as reference)
;  as well as the busy indicator in the BSR
;
; entry:
;	cx	- count of 15.625 microsecond intervals to wait
;	dx	- base port value
;
; exit:
;	CFL	- 1, card busy, IFL unchanged
;		  0, card is idle and IFL disabled
;	cx	- undefined
;---------------------------------------------------------------------------
wbsy	proc	near			; 
	push	ax			; save reg
	push	dx			; save port base
	add	dx,BSR
wbsy1:					; 
	pushf				; save current IFL
	cli				; disable ints
	in	al,dx			; eyeball BSR for not busy condition
	test	al,BUSY 		; is card still busy?
	jz	not_bsy 		; nope - exit loop w/ IFL=0, CFL=0
	popf				; restore IFL
	in	al,61h			; read current refresh request output
	and	al,10h			; mask for refresh request bit
	cmp	al,ah			; did it just change
	je	wbsy1			; wait for a change
	mov	ah,al			; save new state
	loop	wbsy1			; decrement half cycles till count end
	stc				; time expired - CFL set means error
	jmp	short wb1		; exit

not_bsy:				; (CFL=0)
	pop	ax			; balance stack (throw away flags)
wb1:
	pop	dx			; restore base port
	pop	ax			; restore ax
	ret				;  exit - CFL is clear

wbsy	endp


;/*************************************************************************/
;/*									  */
;/* _ResetToggle							  */
;/*									  */
;/*    This routine will reset the specified adapter by toggling the	  */
;/*    sub-system reset bit in the basic control register.		  */
;/*									  */
;/*	  Entry: _ResetToggle (USHORT baseIO)				  */
;/*									  */
;/*	  Exit:  0 always						  */
;/*									  */
;/*************************************************************************/

;
; Stack frame, accounts for bp pushed on stack by enter instruction


baseIO	    EQU     <(WPTR [bp+4])>	 ; base IO address

_ResetToggle  proc  near

	enter	0,0

;
;	Get current BCR value, mask some bits off and save it

	mov	dx, baseIO		; base IO
	add	dx, BCR 		; pt to basic control reg
	in	al, dx			; get current value
	and	al, 0fh 		; mask off high nibble
	push	ax			; save value

;
;	Now write the reset bit on, wait 50 us and then turn it off.
;	Use memory refresh as a counter.

	mov	al, 80h
	out	dx, al			; reset bit on
	mov	cx, 4			; wait at least 50 us

@@:					; 
	in	al,61h			; read current refresh request output
	and	al,10h			; mask for refresh request bit
	cmp	al,ah			; did it just change
	je	@b			; wait for a change
	mov	ah,al			; save new state
	loop	@b			; decrement half cycles till count end

	mov	al, 00h
	out	dx, al			; now turn it off

;
;	Now program it back to the original value, minus a few bits.

	pop	ax			; original value
	out	dx, al			; write it back

	leave
	ret

_ResetToggle  endp


_TEXT	ends
	end
