/*---------------------------------------------------------------------------*\
 *   
 *   Copyright (C) 2006 AVM GmbH <fritzbox_info@avm.de>
 *
 *   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 version 2 of the License.
 *
 *   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 
\*---------------------------------------------------------------------------*/

#if !defined (_ahcilist_h_)
#define _ahcilist_h_

#include "ahcidef.h"
#include "ahcireg.h"

/*---------------------------------------------------------------------------*\
	List Control Register
\*---------------------------------------------------------------------------*/
AHCI_INLINE_PREFIX
void AHCI_WaitListReady (void) {
#	if defined (CONFIG_AHCI_DEBUG)
	unsigned count = 0;
#	endif

	/* busy waiting until no command */
	while ((AHCI_READ_REGISTER (pAHCI_ListControlReg) & AHCI_LISTCR_command_MASK) != 0) {
#		if defined (CONFIG_AHCI_DEBUG)
		count ++;
		
		PrintInfo ("Insert wait count %u, LCR is %08X\n", count, AHCI_READ_REGISTER (pAHCI_ListControlReg));
		info (!(AHCI_READ_REGISTER (pAHCI_InterruptStatusReg) & AHCI_ISR_hostHalted_FLAG));
		if (count > 1000) {
			break;
		}
		//to do
#		endif
	}
}

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
AHCI_INLINE_PREFIX
void AHCI_InsertDescriptor (AHCI_LIST_Type List, unsigned DescAdr) {
	AHCI_DESC_POS pos = DescAdr >> 4;

	//16 Byte aligned
	assert ((DescAdr & 0xF) == 0);
	assert (pos < (1 << 14));
	
	// List Control Register muss frei sein
	AHCI_WaitListReady ();
	assert ((AHCI_READ_REGISTER (pAHCI_ListControlReg) & AHCI_LISTCR_command_MASK) == 0);
	
	AHCI_WRITE_REGISTER (pAHCI_ListControlReg, (pos << AHCI_LISTCR_workingTD_SHIFT)|
										  (AHCI_LIST_insert << AHCI_LISTCR_command_SHIFT)|
										  (List << AHCI_LISTCR_list_SHIFT));
}


/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
AHCI_INLINE_PREFIX
void AHCI_AppendDescriptor (AHCI_LIST_Type List, unsigned OldDescAdr, unsigned DescAdr) {
	AHCI_DESC_POS pos = DescAdr >> 4;
	AHCI_DESC_POS oldpos = OldDescAdr >> 4;
	
	//16 Byte aligned
	assert ((DescAdr & 0xF) == 0);
	assert ((OldDescAdr & 0xF) == 0);
	
	assert (pos < (1 << 14));
	assert (oldpos < (1 << 14));
	assert (oldpos != 0);

	// List Control Register muss frei sein
	AHCI_WaitListReady ();
	assert ((AHCI_READ_REGISTER (pAHCI_ListControlReg) & AHCI_LISTCR_command_MASK) == 0);
	
	AHCI_WRITE_REGISTER (pAHCI_ListControlReg, (pos << AHCI_LISTCR_workingTD_SHIFT) |
										  (AHCI_LIST_append << AHCI_LISTCR_command_SHIFT) |
										  (oldpos << AHCI_LISTCR_existingTD_SHIFT) |
										  (List << AHCI_LISTCR_list_SHIFT));

}

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
AHCI_INLINE_PREFIX
void AHCI_RemoveDescriptor (unsigned DescAdr) {
	AHCI_DESC_POS pos = DescAdr >> 4;

	//16 Byte aligned
	assert ((DescAdr & 0xF) == 0);
	assert (pos < (1 << 14));

	// List Control Register muss frei sein
	AHCI_WaitListReady ();
	assert ((AHCI_READ_REGISTER (pAHCI_ListControlReg) & AHCI_LISTCR_command_MASK) == 0);

	AHCI_WRITE_REGISTER (pAHCI_ListControlReg, (pos << AHCI_LISTCR_workingTD_SHIFT) |
										  (AHCI_LIST_remove << AHCI_LISTCR_command_SHIFT));

}

#endif //_ahcilist_h_
