/*
	 * Macro:	PCI_WRITE_CONFIG_BYTE
	 * Arguments:	%eax address to write to (includes bus, device, function, &offset)
	 *              %dl byte to write
	 *
	 * Results:	none
	 *
	 * Trashed:	%eax, %edx
	 * Effects:	writes a single byte to pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *              And the tricks it does cannot scale beyond writing a single byte.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the data byte
	 *              in the high half of edx.
	 *
	 *              In %edx[3] it stores the byte to write.
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_WRITE_CONFIG_BYTE \
	shll $8,   %edx		; \
	movb %al,  %dl		; \
	andb $0x3, %dl		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	movb %dh,  %al		; \
	movb $0,   %dh		; \
	addl $0xcfc, %edx	; \
	outb %al,  %dx


	/*
	 * Macro:	PCI_WRITE_CONFIG_WORD
	 * Arguments:	%eax address to write to (includes bus, device, function, &offset)
	 *              %ecx word to write
	 *
	 * Results:	none
	 *
	 * Trashed:	%eax, %edx
	 * Preserved:   %ecx
	 * Effects:	writes a single byte to pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the least significant
	 *              bits of the address in the high half of edx.
	 *
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_WRITE_CONFIG_WORD \
	movb %al,  %dl		; \
	andl $0x3, %edx		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	movl %ecx, %eax		; \
	addl $0xcfc, %edx	; \
	outw %ax,  %dx



	/*
	 * Macro:	PCI_WRITE_CONFIG_DWORD
	 * Arguments:	%eax address to write to (includes bus, device, function, &offset)
	 *              %ecx dword to write
	 *
	 * Results:	none
	 *
	 * Trashed:	%eax, %edx
	 * Preserved:   %ecx
	 * Effects:	writes a single byte to pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the least significant
	 *              bits of the address in the high half of edx.
	 *
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_WRITE_CONFIG_DWORD \
	movb %al,  %dl		; \
	andl $0x3, %edx		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	movl %ecx, %eax		; \
	addl $0xcfc, %edx	; \
	outl %eax,  %dx



	
	/*
	 * Macro:	PCI_READ_CONFIG_BYTE
	 * Arguments:	%eax address to read from (includes bus, device, function, &offset)
	 *
	 * Results:	%al Byte read
	 *
	 * Trashed:	%eax, %edx
	 * Effects:	reads a single byte from pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the least significant
	 *              bits of the address in the high half of edx.
	 *
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_READ_CONFIG_BYTE \
	movb %al,  %dl		; \
	andl $0x3, %edx		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	addl $0xcfc, %edx	; \
	inb  %dx,  %al



	/*
	 * Macro:	PCI_READ_CONFIG_WORD
	 * Arguments:	%eax address to read from (includes bus, device, function, &offset)
	 *
	 * Results:	%ax word read
	 *
	 * Trashed:	%eax, %edx
	 * Effects:	reads a 2 bytes from pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the least significant
	 *              bits of the address in the high half of edx.
	 *
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_READ_CONFIG_WORD \
	movb %al,  %dl		; \
	andl $0x3, %edx		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	addl $0xcfc, %edx	; \
	inw  %dx,  %ax



	/*
	 * Macro:	PCI_READ_CONFIG_DWORD
	 * Arguments:	%eax address to read from (includes bus, device, function, &offset)
	 *
	 * Results:	%eax
	 *
	 * Trashed:	%edx
	 * Effects:	reads 4 bytes from pci config space
	 *
	 * Notes:	This routine is optimized for minimal register usage.
	 *               
	 *              What it does is almost simple.
	 *              It preserves %eax (baring special bits) until it is written
	 *              out to the appropriate port.  And hides the least significant
	 *              bits of the address in the high half of edx.
	 *
	 *              In %edx[2] it stores the lower three bits of the address.
	 */


#define PCI_READ_CONFIG_DWORD \
	movb %al,  %dl		; \
	andl $0x3, %edx		; \
	shll $16,  %edx		; \
	\
	orl  $0x80000000, %eax	; \
	andl $0xfffffffc, %eax	; \
	movw $0xcf8, %dx	; \
	outl %eax,  %dx		; \
	\
	shrl $16,  %edx		; \
	addl $0xcfc, %edx	; \
	inl  %dx,  %eax