/*
 * Copyright (c) 2006-2008, Technologic Systems
 * All rights reserved.
 */

#ifndef _SDCORE_H_
#define _SDCORE_H_


/* These structs should start intialized to all 0's (bzero()'ed).  Proper
 * operation can be assured by setting sd_regstart, and the os_delay
 * callback.  sdreset() should be called to initialize the core, then
 * sdread() and sdwrite() can be used.
 */
struct sdcore {
	/* virtual address of SD block register start, to be filled in
	 * by client code before calling any sdcore functions.
	 */
	unsigned int sd_regstart; 

	/* public bits for sd_state bitfield, can be read from client code.
	 * Do not write!  Other bits are used internally.
	 */
	#define SDDAT_RX	(1<<0)
	#define SDDAT_TX	(1<<1)
	#define SDCMD_RX	(1<<2)
	#define SDCMD_TX	(1<<3)
	unsigned int sd_state; 

	/* Erase hint for subsequent sdwrite() call, used to optimize 
	 * write throughput on multi-sector writes by pre-erasing this
	 * many sectors.
	 */
	unsigned int sd_erasehint;

	/* Following this comment are 5 function pointer declarations to 
	 * OS helper functions.  The 'os_arg' member is passed as the 
	 * first argument to the helpers and should be set by
	 * client code before issueing sdreset()
	 *
	 * os_dmastream(os_arg, buf, buflen)
	 * This function should look at sd_state and set up and run an
	 * appropriate DMA transfer.  If buf is NULL, callee doesn't care 
	 * about the actual data sent/received and helper function 
	 * can do whatever it wants.  Should return 0 when DMA transfer was 
	 * run and completed successfully.  If this function pointer is
	 * NULL, PIO methods of transfer will be used instead of DMA.
	 *
	 * os_dmaprep(os_arg, buf, buflen)
	 * This function is used to prepare an area of memory for a possible 
	 * DMA transfer.  This function is called once per distinct buffer
	 * passed in.  After this function is called, os_dmastream() may be
	 * called one or more times (for sequential addresses) on subregions
	 * of the address range passed here.  Should write-back or invalidate 
	 * L1 cache lines and possibly look up physical addresses for buf 
	 * passed in if I/O buffers.  If 'os_dmaprep' is set to NULL, function
	 * call will not happen. (though os_dmastream() calls may still)
	 *
	 * os_delay(os_arg, microseconds)
	 * This function is supposed to delay or stall the processor for
	 * the passed in value number of microseconds.
	 *
	 * os_irqwait(os_arg, type)
	 * Called at certain times to request to be put to sleep/block until
	 * an SD interrupt occurs.  It is not critical to set this function.
	 * When NULL, the sdcore routines simply busy-wait.
	 *
	 * os_powerok(os_arg)
	 * Experimental callback function -- set to NULL for now.
	 */
	void *os_arg;
	int (*os_dmastream)(void *, unsigned char *, unsigned int);
	void (*os_dmaprep)(void *, unsigned char *, unsigned int);
	void (*os_delay)(void *, unsigned int);
	void (*os_irqwait)(void *, unsigned int);
	int (*os_powerok)(void *);

	/* If the SD card last successfully reset is write protected, this
	 * member will be non-zero.
	 */
	unsigned int sd_wprot;

	/* If this card may have been already initialized by TS-SDBOOT, place
	 * the magic token it placed in the EP93xx SYSCON ScratchReg1 here
	 * before calling sdreset() to avoid re-initialization.
	 */
	unsigned int sdboot_token;

	/* CRC hint for subsequent sdwrite() call, used to optimize
	 * write throughput while using DMA by pre-calculating CRC's for
	 * next write.  NULL means no hint supplied.
	 */
	unsigned char *sd_crchint;

	/* The block size of the memory device.  Normally 512, but can be 1024
	 * for larger cards.  Read-only member and actually not very useful.
	 */
	unsigned int sd_blocksize;

	/* Password for auto-unlocking in sdreset()
	 */
	unsigned char *sd_pwd;
	
	/* If the SD card was password locked, this will be non-zero after
	 * unsuccessful sdreset(). 
	 */
	unsigned int sd_locked;

	/* Whether or not writes can be parked.  Definitely should be set to 1
	 * as writes are very slow without it.
	 */
	unsigned int sd_writeparking;

	/* Logical unit number.  Some SD cores will have multiple card slots.
	 * LUN #0 is the first.
	 */
	unsigned int sd_lun;

	/* The rest of these members are for private internal use and should
	 * not be of interest to client code.
	 */
	unsigned int reserved[47];
};

/* For sdreadv() / sdwritev() */
struct sdiov {
	unsigned char *sdiov_base;
	unsigned int sdiov_nsect;
};

int sdreset(struct sdcore *);
int sdread(struct sdcore *, unsigned int, unsigned char *, int);
int sdwrite(struct sdcore *, unsigned int, unsigned char *, int);
int sdreadv(struct sdcore *, unsigned int, struct sdiov *, int);
int sdwritev(struct sdcore *, unsigned int, struct sdiov *, int);
int sdsetwprot(struct sdcore *, unsigned int);
#define SDLOCK_UNLOCK	0
#define SDLOCK_SETPWD	1
#define SDLOCK_CLRPWD	2
#define SDLOCK_ERASE	8
int sdlockctl(struct sdcore *, unsigned int, unsigned char *, unsigned char *);

#endif
