/*---------------------------------------------------------------------------*
  Project:  NitroSDK - CARD - libraries
  File:     card_common.h

  Copyright 2003-2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: card_common.h,v $
  Revision 1.10  2004/11/12 05:44:53  yosizaki
  add CARDi_CheckValidity().

  Revision 1.9  2004/08/23 10:38:23  yosizaki
  add write-command.
  add retry count on writing timeout.

  Revision 1.8  2004/08/18 03:24:03  yosizaki
  fix around PXI-INIT command.

  Revision 1.7  2004/07/28 13:18:14  yosizaki
  asynchronous mode support.

  Revision 1.6  2004/07/27 06:47:31  yosizaki
  small fix (warning "padding inserted")

  Revision 1.5  2004/07/24 08:23:55  yasu
  Change type of card owner ID (with error status) to s32.

  Revision 1.4  2004/07/23 08:28:23  yosizaki
  change backup-routine to ARM7 only.

  Revision 1.3  2004/07/22 11:33:28  yosizaki
  fix CARD access of ARM7.

  Revision 1.2  2004/07/19 13:14:32  yosizaki
  add task thread.

  Revision 1.1  2004/07/10 10:20:05  yosizaki
  (none)

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#ifndef __NITRO_CARD_COMMON_H__
#define __NITRO_CARD_COMMON_H__


#include <nitro.h>
#include <nitro/pxi.h>


/******************************************************************************/
/* constant */

/* Compile time check */
#define	SDK_ASSERT_ON_COMPILE(expr)	\
    static void sdk_assert_on_compile ## __LINE__ ( char is[(expr) ? +1 : -1] )

/* Obtain the number of elements in the array */
#define	COUNT_OF_(array)	(sizeof(array) / sizeof(*array))

/* Bit operation macros */
#define	BIT_MASK(n)	((1 << (n)) - 1)
#define	ALIGN_MASK(a)	((a) - 1)
#define	ALIGN_BYTE(n, a)	(((u32)(n) + ALIGN_MASK(a)) & ~ALIGN_MASK(a))
#define CARD_ALIGN_HI_BIT(n)     (((u32)(n)) & ~ALIGN_MASK(CARD_ROM_PAGE_SIZE))
#define CARD_ALIGN_LO_BIT(n)     (((u32)(n)) & ALIGN_MASK(CARD_ROM_PAGE_SIZE))


/* PXI interface */
enum {
    CARD_REQ_INIT	= 0,			/* initialize (setting command from ARM9) */
    CARD_REQ_ACK,					/* request done (acknowledge from ARM7) */
    CARD_REQ_IDENTIFY,				/* CARD_IdentifyBackup */
    CARD_REQ_READ_ID,				/* CARD_ReadRomID (TEG && ARM9) */
    CARD_REQ_READ_ROM,				/* CARD_ReadRom (TEG && ARM9) */
    CARD_REQ_WRITE_ROM,				/* (reserved) */
    CARD_REQ_READ_BACKUP,			/* CARD_ReadBackup */
    CARD_REQ_WRITE_BACKUP,			/* CARD_WriteBackup */
    CARD_REQ_PROGRAM_BACKUP,		/* CARD_ProgramBackup */
    CARD_REQ_VERIFY_BACKUP,			/* CARD_VerifyBackup */
    CARD_REQ_ERASE_PAGE_BACKUP,		/* CARD_EraseBackupPage */
    CARD_REQ_ERASE_SECTOR_BACKUP,	/* CARD_EraseBackupSector */
    CARD_REQ_ERASE_CHIP_BACKUP,		/* CARD_EraseBackupChip */
    CARD_REQ_MAX
} ;

/* CARD internal status */
enum {
    CARD_STAT_INIT		= (1 << 0),	/* Initialization complete */
    CARD_STAT_INIT_CMD	= (1 << 1),	/* Command PXI initialization complete */
    CARD_STAT_BUSY		= (1 << 2),	/* Read function itself is busy */
    CARD_STAT_TASK		= (1 << 3),	/* Shifting to task thread */
    CARD_STAT_RECV		= (1 << 4),	/* Receiving request via PXI */
    CARD_STAT_REQ		= (1 << 5)	/* Requesting via PXI in task processing */
} ;

typedef	enum {
    CARD_TARGET_NONE,
    CARD_TARGET_ROM,
    CARD_TARGET_BACKUP
} CARDTargetMode;

/* Maximum number of retries */
#define	CARD_RETRY_COUNT_MAX	10


typedef	s32  CARDiOwner;	// s32 for error status


#pragma	pack(push, 4)


/* PXI reception parameters */
typedef	struct	CARDiCommandArg
{
	CARDResult	result;	/* Return value */
	CARDBackupType	type;	/* Device type */
	u32	id;	/* Card ID */
	u32	src;	/* Transfer source */
	u32	dst;	/* Transfer destination */
	u32	len;	/* Transfer length */
	u32	reserved[2];	/* Padding */
} CARDiCommandArg;


/* Shared parameters for card */
typedef	struct	CARDiCommon
{
	/* Shared memory for command arguments */
	CARDiCommandArg	*cmd;

	int	command;	/* Current command */
#if	defined(SDK_ARM7)
	u32	recv_step;	/* Number of received steps */
#endif

	/*
	 * Card access rights management.
	 * Take exclusive control of card/backup in processor.
	 * This is necessary because control cannot be passed
	 * using only OS_LockCard() during backup due to request made to the ARM7.
	 *
	 *  This follows an ID, as OSMutex follows a thread.
	 */
	volatile CARDiOwner	lock_owner;	// ==s32 with Error status
	volatile int	lock_ref;
	OSThreadQueue	lock_queue[4 / sizeof(OSThreadQueue)];
	CARDTargetMode	lock_target;

	/* API task parameters */
	u32	src;	/* Transfer source */
	u32	dst;	/* Transfer destination */
	u32	len;	/* Transfer size */
	u32	dma;	/* DMA channel */
	MIDmaCallback	callback;	/* Callback */
	void	*callback_arg;	/* Callback argument */
	void	(*task_func)(struct CARDiCommon*);	/* Task */

	/* Status flag */
	volatile u32	flag;

	/* Task thread */
	u32	priority;
	OSThread	thread[1];			/* Thread body */
	OSThread	*cur_th;			/* Thread that is handling task.
									   Can also be user thread */
	OSThreadQueue	busy_q[4 / sizeof(OSThreadQueue)];		/* Thread waiting for completion of task */

	/* padding */
#if	defined(SDK_ARM9)
	u32	dummy[2];
#else
	u32	dummy[8];
#endif

	/* Cache of last-transferred backup page  */
	u8	backup_cache_page_buf[256];

} CARDiCommon;


#pragma	pack(pop)


SDK_ASSERT_ON_COMPILE(sizeof(CARDiCommandArg) % 32 == 0);

SDK_ASSERT_ON_COMPILE(sizeof(CARDiCommon) % 32 == 0);



/******************************************************************************/
/* Variables */

extern	CARDiCommon	cardi_common;

/* Request table */
extern	void	(*const(cardi_request_table[]))(CARDiCommon *);


/******************************************************************************/
/* Functions */

/* FIFO word transmission  */
static inline void	CARDi_SendPxi(u32 data)
{
    while(PXI_SendWordByFifo(PXI_FIFO_TAG_FS, data, TRUE) < 0)
		;
}

/* Get current lock target */
static inline CARDTargetMode	CARDi_GetTargetMode(void)
{
    return	cardi_common.lock_target;
}

/* Wait for turn (assuming that specified bus is locked) */
static inline void	CARDi_WaitTask(CARDiCommon *p)
{
    OSIntrMode	bak_psr = OS_DisableInterrupts();
    while((p->flag & CARD_STAT_BUSY) != 0)
		OS_SleepThread(p->busy_q);
    p->flag |= CARD_STAT_BUSY;
    (void)OS_RestoreInterrupts(bak_psr);
}

/* Acknowledge completion of task */
static inline void	CARDi_EndTask(CARDiCommon *p)
{
    OSIntrMode	bak_psr = OS_DisableInterrupts();
    p->flag &= ~(CARD_STAT_BUSY | CARD_STAT_TASK);
    OS_WakeupThread(p->busy_q);
    if(p->flag & CARD_STAT_RECV)
		OS_WakeupThreadDirect(p->thread);
    (void)OS_RestoreInterrupts(bak_psr);
}

/* Initialize card's common parts  */
void	CARDi_InitCommon(void);

/* Assign a task to the task thread (task is already exclusively controlled) */
void	CARDi_SetTask(void (*task)(CARDiCommon*));

/* Common processing waiting for asynchronous completion */
BOOL	CARDi_WaitAsync(void);
BOOL	CARDi_TryWaitAsync(void);

#if	defined(SDK_TS) || defined(SDK_ARM7)

/* Read the card ID */
u32	CARDi_ReadRomIDCore(void);

#endif

#if	defined(SDK_ARM7)

/* Card request */
void	CARDi_ReadRomCore(u32 dma, const void *src, void *dst, u32 len);

#endif


#if	defined(SDK_ARM9)

/*
 * Send request and block completion at that location
 * If the result is a timeout, will retry up to the specified number of times.
 */
BOOL	CARDi_Request(CARDiCommon *p, int command, int retry_max);


#endif	/* SDK_ARM9 */

/* Determine whether a CARD is a proper environment for access. (For details see implementation section) */
BOOL	CARDi_CheckValidity(void);

#define	CARD_VALIDITY_ASSERTION()	\
if(!CARDi_CheckValidity()) {	\
		OS_Panic(	\
			"NITRO-CARD is not available!(wireless-downloaded program)"	\
		);	\
}


#endif	/*__NITRO_CARD_SPI_H__*/
