~ekangmonyet/bare-pizero

803c555d8810ecfea174ac08d9613bb9aa995b14 — ekangmonyet 16 days ago 2f31efc
Add SD setup with EMMC
7 files changed, 3005 insertions(+), 0 deletions(-)

A sd/Makefile
A sd/README
A sd/SDCard.c
A sd/hal.h
A sd/loader.s
A sd/memmap
A sd/sd.c
A sd/Makefile => sd/Makefile +27 -0
@@ 0,0 1,27 @@
ARMGNU=	arm-none-eabi-
CFLAGS=	-Wall -Werror -O0 -nostdlib -nostartfiles -ffreestanding
PROJNAME=sd

all: $(PROJNAME).bin $(PROJNAME).hex

$(PROJNAME).bin:	$(PROJNAME).elf
	$(ARMGNU)objcopy $^ -O binary $@

$(PROJNAME).hex:	$(PROJNAME).elf
	$(ARMGNU)objcopy $^ -O ihex $@

$(PROJNAME).elf:	memmap $(PROJNAME).o loader.o
	$(ARMGNU)ld loader.o $(PROJNAME).o -T memmap -o $@

loader.o:	loader.s
	$(ARMGNU)as $^ -o $@

$(PROJNAME).o:		$(PROJNAME).c hal.h
	$(ARMGNU)gcc $(CFLAGS) -c $(PROJNAME).c -o $@

clean :
	rm -f *.o
	rm -f *.bin
	rm -f *.elf
	rm -f *.hex


A sd/README => sd/README +45 -0
@@ 0,0 1,45 @@
This experiment is about reading data from SD card, and also FAT32 file system
 handling.  The EMMC on BCM is merely a middleman, hence it's mostly dealing
 with the SD card controller.  SD is a proprietary format, it's lucky enough
 that the information required for basic IO is provided.  It's the same case
 for FAT32.  The ideology behind these technologies are of course not
 appreciated.  However, it's what Pi supports natively so..
I'm fortunate enough to come across this nice implementation by LdB-ECM, you
 should read it up for a more complete SD handling.  Note that the degree of
 abstraction is much higher compared to mine.  It's somehow necessary when
 you're building something bigger, but it's not the best to follow.

You can compile the program by:

    make


Outcomes:
    - Configuring EMMC
    - Configuring SD (SDHC)
    - SD Reading
    - FAT32 Reading


Useful Resources:

SD (and FAT32, but not referenced much) implementation:

    https://github.com/LdB-ECM/Raspberry-Pi/tree/master/SD_FAT32

SD Part 1 PHY LAYER Simplified Spec:

    https://www.sdcard.org/downloads/pls

FAT Explanation (good visualization):

    https://www.pjrc.com/tech/8051/ide/fat32.html

FAT SPEC

    http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc

OSDEV FAT ENTRY

    https://wiki.osdev.org/FAT


A sd/SDCard.c => sd/SDCard.c +2496 -0
@@ 0,0 1,2496 @@
#include <stdbool.h>							// Needed for bool and true/false
#include <stdint.h>								// Needed for uint8_t, uint32_t, uint64_t etc
#include <ctype.h>								// Needed for toupper for wildcard string match
#include <wchar.h>								// Needed for UTF for long file name support
#include <string.h>								// Needed for string copy
#include "rpi-smartstart.h"						// Provides all basic hardware access
#include "emb-stdio.h"							// Provides printf functionality
#include "SDCard.h"								// This units header

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{																			}
{       Filename: SDCard.c													}
{       Copyright(c): Leon de Boer(LdB) 2017								}
{       Version: 1.01														}
{       Original start code from hldswrth use from the Pi Forum             }
{																			}
{***************[ THIS CODE IS FREEWARE UNDER CC Attribution]***************}
{																            }
{     This sourcecode is released for the purpose to promote programming    }
{  on the Raspberry Pi. You may redistribute it and/or modify with the      }
{  following disclaimer and condition.                                      }
{																            }
{      The SOURCE CODE is distributed "AS IS" WITHOUT WARRANTIES AS TO      }
{   PERFORMANCE OF MERCHANTABILITY WHETHER EXPRESSED OR IMPLIED.            }
{   Redistributions of source code must retain the copyright notices to     }
{   maintain the author credit (attribution) .								}
{																			}
{***************************************************************************/

/*--------------------------------------------------------------------------}
{    These are SmartStart functions we use. If you wish to port this code   }
{  you will need to provide equivalent 3 functions on any new system.       }
{--------------------------------------------------------------------------*/
#define waitMicro timer_wait							// Waits a number of microseconds
#define TICKCOUNT timer_getTickCount					// Gets current system clock value (1Mhz accuracy)
#define TIMEDIFF  tick_difference						// Given two TICKCOUNT values calculates microseconds between them.
/* Smartstart also defines a function pointer ....  int (*printhandler) (const char *fmt, ...); */
printhandler LOG_ERROR = NULL;							// LOG_ERROR is a function pointer of that printhandler format

/*--------------------------------------------------------------------------}
{  This controls if debugging code is compiled or removed at compile time   }
{--------------------------------------------------------------------------*/
#define DEBUG_INFO 0									// Compile debugging message code .... set to 1 and other value means no compilation 

/*--------------------------------------------------------------------------}
{  The working part of the DEBUG_INFO macro to make compilation on or off   }
{--------------------------------------------------------------------------*/
#if DEBUG_INFO == 1
#define LOG_DEBUG(...) printf( __VA_ARGS__ )			// This displays debugging messages to function given
#else
#define LOG_DEBUG(...)									// This just swallows debug code, it doesn't even get compiled
#endif

/***************************************************************************}
{    PRIVATE INTERNAL SD HOST REGISTER STRUCTURES AS PER BCM2835 MANUAL     }
****************************************************************************/

/*--------------------------------------------------------------------------}
{      EMMC BLKSIZECNT register - BCM2835.PDF Manual Section 5 page 68      }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regBLKSIZECNT {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned BLKSIZE : 10;				// @0-9		Block size in bytes
			unsigned reserved : 6;						// @10-15	Write as zero read as don't care
			volatile unsigned BLKCNT : 16;				// @16-31	Number of blocks to be transferred 
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CMDTM register - BCM2835.PDF Manual Section 5 pages 69-70       }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCMDTM {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 1;						// @0		Write as zero read as don't care
			volatile unsigned TM_BLKCNT_EN : 1;			// @1		Enable the block counter for multiple block transfers
			volatile enum {	TM_NO_COMMAND = 0,			//			no command 
							TM_CMD12 = 1,				//			command CMD12 
							TM_CMD23 = 2,				//			command CMD23
							TM_RESERVED = 3,
						  } TM_AUTO_CMD_EN : 2;			// @2-3		Select the command to be send after completion of a data transfer
			volatile unsigned TM_DAT_DIR : 1;			// @4		Direction of data transfer (0 = host to card , 1 = card to host )
			volatile unsigned TM_MULTI_BLOCK : 1;		// @5		Type of data transfer (0 = single block, 1 = muli block)
			unsigned reserved1 : 10;					// @6-15	Write as zero read as don't care
			volatile enum {	CMD_NO_RESP = 0,			//			no response
							CMD_136BIT_RESP = 1,		//			136 bits response 
							CMD_48BIT_RESP = 2,			//			48 bits response 
							CMD_BUSY48BIT_RESP = 3,		//			48 bits response using busy 
						  } CMD_RSPNS_TYPE : 2;			// @16-17
			unsigned reserved2 : 1;						// @18		Write as zero read as don't care
			volatile unsigned CMD_CRCCHK_EN : 1;		// @19		Check the responses CRC (0=disabled, 1= enabled)
			volatile unsigned CMD_IXCHK_EN : 1;			// @20		Check that response has same index as command (0=disabled, 1= enabled)
			volatile unsigned CMD_ISDATA : 1;			// @21		Command involves data transfer (0=disabled, 1= enabled)
			volatile enum {	CMD_TYPE_NORMAL = 0,		//			normal command
							CMD_TYPE_SUSPEND = 1,		//			suspend command 
							CMD_TYPE_RESUME = 2,		//			resume command 
							CMD_TYPE_ABORT = 3,			//			abort command 
						  } CMD_TYPE : 2;				// @22-23 
			volatile unsigned CMD_INDEX : 6;			// @24-29
			unsigned reserved3 : 2;						// @30-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{        EMMC STATUS register - BCM2835.PDF Manual Section 5 page 72        }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSTATUS {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_INHIBIT : 1;			// @0		Command line still used by previous command
			volatile unsigned DAT_INHIBIT : 1;			// @1		Data lines still used by previous data transfer
			volatile unsigned DAT_ACTIVE : 1;			// @2		At least one data line is active
			unsigned reserved : 5;						// @3-7		Write as zero read as don't care
			volatile unsigned WRITE_TRANSFER : 1;		// @8		New data can be written to EMMC
			volatile unsigned READ_TRANSFER : 1;		// @9		New data can be read from EMMC
			unsigned reserved1 : 10;					// @10-19	Write as zero read as don't care
			volatile unsigned DAT_LEVEL0 : 4;			// @20-23	Value of data lines DAT3 to DAT0
			volatile unsigned CMD_LEVEL : 1;			// @24		Value of command line CMD 
			volatile unsigned DAT_LEVEL1 : 4;			// @25-28	Value of data lines DAT7 to DAT4
			unsigned reserved2 : 3;						// @29-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CONTROL0 register - BCM2835.PDF Manual Section 5 page 73/74     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL0 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 1;						// @0		Write as zero read as don't care
			volatile unsigned HCTL_DWIDTH : 1;			// @1		Use 4 data lines (true = enable)
			volatile unsigned HCTL_HS_EN : 1;			// @2		Select high speed mode (true = enable)
			unsigned reserved1 : 2;						// @3-4		Write as zero read as don't care
			volatile unsigned HCTL_8BIT : 1;			// @5		Use 8 data lines (true = enable)
			unsigned reserved2 : 10;					// @6-15	Write as zero read as don't care
			volatile unsigned GAP_STOP : 1;				// @16		Stop the current transaction at the next block gap
			volatile unsigned GAP_RESTART : 1;			// @17		Restart a transaction last stopped using the GAP_STOP
			volatile unsigned READWAIT_EN : 1;			// @18		Use DAT2 read-wait protocol for cards supporting this
			volatile unsigned GAP_IEN : 1;				// @19		Enable SDIO interrupt at block gap 
			volatile unsigned SPI_MODE : 1;				// @20		SPI mode enable
			volatile unsigned BOOT_EN : 1;				// @21		Boot mode access
			volatile unsigned ALT_BOOT_EN : 1;			// @22		Enable alternate boot mode access
			unsigned reserved3 : 9;						// @23-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CONTROL1 register - BCM2835.PDF Manual Section 5 page 74/75     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL1 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CLK_INTLEN : 1;			// @0		Clock enable for internal EMMC clocks for power saving
			volatile const unsigned CLK_STABLE : 1;		// @1		SD clock stable  0=No 1=yes   **read only
			volatile unsigned CLK_EN : 1;				// @2		SD clock enable  0=disable 1=enable
			unsigned reserved : 2;						// @3-4		Write as zero read as don't care
			volatile unsigned CLK_GENSEL : 1;			// @5		Mode of clock generation (0=Divided, 1=Programmable)
			volatile unsigned CLK_FREQ_MS2 : 2;			// @6-7		SD clock base divider MSBs (Version3+ only)
			volatile unsigned CLK_FREQ8 : 8;			// @8-15	SD clock base divider LSBs
			volatile unsigned DATA_TOUNIT : 4;			// @16-19	Data timeout unit exponent
			unsigned reserved1 : 4;						// @20-23	Write as zero read as don't care
			volatile unsigned SRST_HC : 1;				// @24		Reset the complete host circuit
			volatile unsigned SRST_CMD : 1;				// @25		Reset the command handling circuit
			volatile unsigned SRST_DATA : 1;			// @26		Reset the data handling circuit
			unsigned reserved2 : 5;						// @27-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{    EMMC CONTROL2 register - BCM2835.PDF Manual Section 5 pages 81-84      }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL2 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile const unsigned ACNOX_ERR : 1;		// @0		Auto command not executed due to an error **read only
			volatile const unsigned ACTO_ERR : 1;		// @1		Timeout occurred during auto command execution **read only
			volatile const unsigned ACCRC_ERR : 1;		// @2		Command CRC error occurred during auto command execution **read only
			volatile const unsigned ACEND_ERR : 1;		// @3		End bit is not 1 during auto command execution **read only
			volatile const unsigned ACBAD_ERR : 1;		// @4		Command index error occurred during auto command execution **read only
			unsigned reserved : 2;						// @5-6		Write as zero read as don't care
			volatile const unsigned NOTC12_ERR : 1;		// @7		Error occurred during auto command CMD12 execution **read only
			unsigned reserved1 : 8;						// @8-15	Write as zero read as don't care			
			volatile enum { SDR12 = 0,
							SDR25 = 1, 
							SDR50 = 2,
							SDR104 = 3,
							DDR50 = 4,
						  } UHSMODE : 3;				// @16-18	Select the speed mode of the SD card (SDR12, SDR25 etc)
			unsigned reserved2 : 3;						// @19-21	Write as zero read as don't care
			volatile unsigned TUNEON : 1;				// @22		Start tuning the SD clock
			volatile unsigned TUNED : 1;				// @23		Tuned clock is used for sampling data
			unsigned reserved3 : 8;						// @24-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{     EMMC INTERRUPT register - BCM2835.PDF Manual Section 5 pages 75-77    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regINTERRUPT {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{     EMMC IRPT_MASK register - BCM2835.PDF Manual Section 5 pages 77-79    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regIRPT_MASK {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC IRPT_EN register - BCM2835.PDF Manual Section 5 pages 79-71     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regIRPT_EN {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{       EMMC TUNE_STEP  register - BCM2835.PDF Manual Section 5 page 86     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regTUNE_STEP {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile enum {	TUNE_DELAY_200ps  = 0,
							TUNE_DELAY_400ps  = 1,
							TUNE_DELAY_400psA = 2,		// I dont understand the duplicate value???
							TUNE_DELAY_600ps  = 3,
							TUNE_DELAY_700ps  = 4,
							TUNE_DELAY_900ps  = 5,
							TUNE_DELAY_900psA = 6,		// I dont understand the duplicate value??
							TUNE_DELAY_1100ps = 7,
						   } DELAY : 3;					// @0-2		Select the speed mode of the SD card (SDR12, SDR25 etc)
			unsigned reserved : 29;						// @3-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{    EMMC SLOTISR_VER register - BCM2835.PDF Manual Section 5 pages 87-88   }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSLOTISR_VER {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned SLOT_STATUS : 8;			// @0-7		Logical OR of interrupt and wakeup signal for each slot
			unsigned reserved : 8;						// @8-15	Write as zero read as don't care
			volatile unsigned SDVERSION : 8;			// @16-23	Host Controller specification version 
			volatile unsigned VENDOR : 8;				// @24-31	Vendor Version Number 
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};


/***************************************************************************}
{         PRIVATE POINTERS TO ALL THE BCM2835 EMMC HOST REGISTERS           }
****************************************************************************/
#define EMMC_ARG2			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300000))
#define EMMC_BLKSIZECNT		((volatile struct __attribute__((aligned(4))) regBLKSIZECNT*)(uintptr_t)(RPi_IO_Base_Addr + 0x300004))
#define EMMC_ARG1			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300008))
#define EMMC_CMDTM			((volatile struct __attribute__((aligned(4))) regCMDTM*)(uintptr_t)(RPi_IO_Base_Addr + 0x30000c))
#define EMMC_RESP0			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300010))
#define EMMC_RESP1			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300014))
#define EMMC_RESP2			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300018))
#define EMMC_RESP3			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x30001C))
#define EMMC_DATA			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300020))
#define EMMC_STATUS			((volatile struct __attribute__((aligned(4))) regSTATUS*)(uintptr_t)(RPi_IO_Base_Addr + 0x300024))
#define EMMC_CONTROL0		((volatile struct __attribute__((aligned(4))) regCONTROL0*)(uintptr_t)(RPi_IO_Base_Addr + 0x300028))
#define EMMC_CONTROL1		((volatile struct __attribute__((aligned(4))) regCONTROL1*)(uintptr_t)(RPi_IO_Base_Addr + 0x30002C))
#define EMMC_INTERRUPT		((volatile struct __attribute__((aligned(4))) regINTERRUPT*)(uintptr_t)(RPi_IO_Base_Addr + 0x300030))
#define EMMC_IRPT_MASK		((volatile struct __attribute__((aligned(4))) regIRPT_MASK*)(uintptr_t)(RPi_IO_Base_Addr + 0x300034))
#define EMMC_IRPT_EN		((volatile struct __attribute__((aligned(4))) regIRPT_EN*)(uintptr_t)(RPi_IO_Base_Addr + 0x300038))
#define EMMC_CONTROL2		((volatile struct __attribute__((aligned(4))) regCONTROL2*)(uintptr_t)(RPi_IO_Base_Addr + 0x30003C))
#define EMMC_TUNE_STEP 		((volatile struct __attribute__((aligned(4))) regTUNE_STEP*)(uintptr_t)(RPi_IO_Base_Addr + 0x300088))
#define EMMC_SLOTISR_VER	((volatile struct __attribute__((aligned(4))) regSLOTISR_VER*)(uintptr_t)(RPi_IO_Base_Addr + 0x3000fC))


/***************************************************************************}
{   PRIVATE INTERNAL SD CARD REGISTER STRUCTURES AS PER SD CARD STANDARD    }
****************************************************************************/

/*--------------------------------------------------------------------------}
{						   SD CARD OCR register							    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regOCR {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 15;						// @0-14	Write as zero read as don't care
			unsigned voltage2v7to2v8 : 1;				// @15		Voltage window 2.7v to 2.8v
			unsigned voltage2v8to2v9 : 1;				// @16		Voltage window 2.8v to 2.9v
			unsigned voltage2v9to3v0 : 1;				// @17		Voltage window 2.9v to 3.0v
			unsigned voltage3v0to3v1 : 1;				// @18		Voltage window 3.0v to 3.1v
			unsigned voltage3v1to3v2 : 1;				// @19		Voltage window 3.1v to 3.2v
			unsigned voltage3v2to3v3 : 1;				// @20		Voltage window 3.2v to 3.3v
			unsigned voltage3v3to3v4 : 1;				// @21		Voltage window 3.3v to 3.4v
			unsigned voltage3v4to3v5 : 1;				// @22		Voltage window 3.4v to 3.5v
			unsigned voltage3v5to3v6 : 1;				// @23		Voltage window 3.5v to 3.6v
			unsigned reserved1 : 6;						// @24-29	Write as zero read as don't care
			unsigned card_capacity : 1;					// @30		Card Capacity status
			unsigned card_power_up_busy : 1;			// @31		Card power up status (busy)
		};
		volatile uint32_t Raw32;						// @0-31	Union to access 32 bits as a uint32_t		
	};
};

/*--------------------------------------------------------------------------}
{						   SD CARD SCR register							    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSCR {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile enum { SD_SPEC_1_101 = 0,			// ..enum..	Version 1.0-1.01 
							SD_SPEC_11 = 1,				// ..enum..	Version 1.10 
							SD_SPEC_2_3 = 2,			// ..enum..	Version 2.00 or Version 3.00 (check bit SD_SPEC3)
						  } SD_SPEC : 4;				// @0-3		SD Memory Card Physical Layer Specification version
			volatile enum {	SCR_VER_1 = 0,				// ..enum..	SCR version 1.0
						  } SCR_STRUCT : 4;				// @4-7		SCR structure version
			volatile enum { BUS_WIDTH_1 = 1,			// ..enum..	Card supports bus width 1
							BUS_WIDTH_4 = 4,			// ..enum.. Card supports bus width 4
						   } BUS_WIDTH : 4;				// @8-11	SD Bus width
			volatile enum { SD_SEC_NONE = 0,			// ..enum..	No Security
							SD_SEC_NOT_USED = 1,		// ..enum..	Security Not Used
							SD_SEC_101 = 2,				// ..enum..	SDSC Card (Security Version 1.01)
							SD_SEC_2 = 3,				// ..enum..	SDHC Card (Security Version 2.00)
							SD_SEC_3 = 4,				// ..enum..	SDXC Card (Security Version 3.xx)
						  } SD_SECURITY : 3;			// @12-14	Card security in use
			volatile unsigned DATA_AFTER_ERASE : 1;		// @15		Defines the data status after erase, whether it is ‘0’ or ‘1
			unsigned reserved : 3;						// @16-18	Write as zero read as don't care
			volatile enum {	EX_SEC_NONE = 0,			// ..enum..	No extended Security
						  } EX_SECURITY : 4;			// @19-22	Extended security
			volatile unsigned SD_SPEC3 : 1;				// @23		Spec. Version 3.00 or higher
			volatile enum { CMD_SUPP_SPEED_CLASS = 1,
							CMD_SUPP_SET_BLKCNT = 2,
						   } CMD_SUPPORT : 2;			// @24-25	CMD support
			unsigned reserved1 : 6;						// @26-63	Write as zero read as don't care
		};
		volatile uint32_t Raw32_Lo;						// @0-31	Union to access low 32 bits as a uint32_t		
	};
	volatile uint32_t Raw32_Hi;							// @32-63	Access upper 32 bits as a uint32_t
};

/*--------------------------------------------------------------------------}
{						  PI SD CARD CID register						    }
{--------------------------------------------------------------------------*/
/* The CID is Big Endian and secondly the Pi butchers it by not having CRC */
/*  So the CID appears shifted 8 bits right with first 8 bits reading zero */
struct __attribute__((__packed__, aligned(4))) regCID {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile uint8_t OID_Lo;					
			volatile uint8_t OID_Hi;					// @0-15	Identifies the card OEM. The OID is assigned by the SD-3C, LLC
			volatile uint8_t MID;						// @16-23	Manufacturer ID, assigned by the SD-3C, LLC
			unsigned reserved : 8;						// @24-31	PI butcher with CRC removed these bits end up empty
		};
		volatile uint32_t Raw32_0;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile char ProdName4 : 8;				// @0-7		Product name character four
			volatile char ProdName3 : 8;				// @8-15	Product name character three
			volatile char ProdName2 : 8;				// @16-23	Product name character two
			volatile char ProdName1 : 8;				// @24-31	Product name character one	
		};
		volatile uint32_t Raw32_1;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned SerialNumHi : 16;			// @0-15	Serial number upper 16 bits
			volatile unsigned ProdRevLo : 4;			// @16-19	Product revision low value in BCD
			volatile unsigned ProdRevHi : 4;			// @20-23	Product revision high value in BCD
			volatile char ProdName5 : 8;				// @24-31	Product name character five
		};
		volatile uint32_t Raw32_2;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned ManufactureMonth : 4;		// @0-3		Manufacturing date month (1=Jan, 2=Feb, 3=Mar etc)
			volatile unsigned ManufactureYear : 8;		// @4-11	Manufacturing date–year (offset from 2000 .. 1=2001,2=2002,3=2003 etc)
			unsigned reserved1 : 4;						// @12-15 	Write as zero read as don't care
			volatile unsigned SerialNumLo : 16;			// @16-23	Serial number lower 16 bits
		};
		volatile uint32_t Raw32_3;						// @0-31	Union to access 32 bits as a uint32_t		
	};
};

/***************************************************************************}
{			 PRIVATE INTERNAL SD CARD REGISTER STRUCTURE CHECKS             }
****************************************************************************/
/*--------------------------------------------------------------------------}
{					 INTERNAL STRUCTURE COMPILE TIME CHECKS		            }
{--------------------------------------------------------------------------*/
/* GIVEN THE AMOUNT OF PRECISE PACKING OF THESE STRUCTURES .. IT'S PRUDENT */
/* TO CHECK THEM AT COMPILE TIME. USE IS POINTLESS IF THE SIZES ARE WRONG. */
/*-------------------------------------------------------------------------*/
/* If you have never seen compile time assertions it's worth google search */
/* on "Compile Time Assertions". It is part of the C11++ specification and */
/* all compilers that support the standard will have them (GCC, MSC inc)   */
/* This actually produces no code it only executes as a compile time check */
/*-------------------------------------------------------------------------*/
#include <assert.h>								// Need for compile time static_assert

/* Check the main register section group sizes */
static_assert(sizeof(struct regBLKSIZECNT) == 0x04, "EMMC register BLKSIZECNT should be 0x04 bytes in size");
static_assert(sizeof(struct regCMDTM) == 0x04, "EMMC register CMDTM should be 0x04 bytes in size");
static_assert(sizeof(struct regSTATUS) == 0x04, "EMMC register STATUS should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL0) == 0x04, "EMMC register CONTROL0 should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL1) == 0x04, "EMMC register CONTROL1 should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL2) == 0x04, "EMMC register CONTROL2 should be 0x04 bytes in size");
static_assert(sizeof(struct regINTERRUPT) == 0x04, "EMMC register INTERRUPT should be 0x04 bytes in size");
static_assert(sizeof(struct regIRPT_MASK) == 0x04, "EMMC register IRPT_MASK should be 0x04 bytes in size");
static_assert(sizeof(struct regIRPT_EN) == 0x04, "EMMC register IRPT_EN should be 0x04 bytes in size");
static_assert(sizeof(struct regTUNE_STEP) == 0x04, "EMMC register TUNE_STEP should be 0x04 bytes in size");
static_assert(sizeof(struct regSLOTISR_VER) == 0x04, "EMMC register SLOTISR_VER should be 0x04 bytes in size");

static_assert(sizeof(struct regOCR) == 0x04, "EMMC register OCR should be 0x04 bytes in size");
static_assert(sizeof(struct regSCR) == 0x08, "EMMC register SCR should be 0x08 bytes in size");
static_assert(sizeof(struct regCID) == 0x10, "EMMC register CID should be 0x10 bytes in size");

/*--------------------------------------------------------------------------}
{			  INTERRUPT REGISTER TURN TO MASK BIT DEFINITIONS			    }
{--------------------------------------------------------------------------*/
#define INT_AUTO_ERROR   0x01000000									// ACMD_ERR bit in register
#define INT_DATA_END_ERR 0x00400000									// DEND_ERR bit in register
#define INT_DATA_CRC_ERR 0x00200000									// DCRC_ERR bit in register
#define INT_DATA_TIMEOUT 0x00100000									// DTO_ERR bit in register
#define INT_INDEX_ERROR  0x00080000									// CBAD_ERR bit in register
#define INT_END_ERROR    0x00040000									// CEND_ERR bit in register
#define INT_CRC_ERROR    0x00020000									// CCRC_ERR bit in register
#define INT_CMD_TIMEOUT  0x00010000									// CTO_ERR bit in register
#define INT_ERR          0x00008000									// ERR bit in register
#define INT_ENDBOOT      0x00004000									// ENDBOOT bit in register
#define INT_BOOTACK      0x00002000									// BOOTACK bit in register
#define INT_RETUNE       0x00001000									// RETUNE bit in register
#define INT_CARD         0x00000100									// CARD bit in register
#define INT_READ_RDY     0x00000020									// READ_RDY bit in register
#define INT_WRITE_RDY    0x00000010									// WRITE_RDY bit in register
#define INT_BLOCK_GAP    0x00000004									// BLOCK_GAP bit in register
#define INT_DATA_DONE    0x00000002									// DATA_DONE bit in register
#define INT_CMD_DONE     0x00000001									// CMD_DONE bit in register
#define INT_ERROR_MASK   (INT_CRC_ERROR|INT_END_ERROR|INT_INDEX_ERROR| \
                          INT_DATA_TIMEOUT|INT_DATA_CRC_ERR|INT_DATA_END_ERR| \
                          INT_ERR|INT_AUTO_ERROR)
#define INT_ALL_MASK     (INT_CMD_DONE|INT_DATA_DONE|INT_READ_RDY|INT_WRITE_RDY|INT_ERROR_MASK)


/*--------------------------------------------------------------------------}
{						  SD CARD FREQUENCIES							    }
{--------------------------------------------------------------------------*/
#define FREQ_SETUP				400000  // 400 Khz
#define FREQ_NORMAL			  25000000  // 25 Mhz

/*--------------------------------------------------------------------------}
{						  CMD 41 BIT SELECTIONS							    }
{--------------------------------------------------------------------------*/
#define ACMD41_HCS           0x40000000
#define ACMD41_SDXC_POWER    0x10000000
#define ACMD41_S18R          0x04000000
#define ACMD41_VOLTAGE       0x00ff8000
/* PI DOES NOT SUPPORT VOLTAGE SWITCH */
#define ACMD41_ARG_HC        (ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE)//(ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE|ACMD41_S18R)
#define ACMD41_ARG_SC        (ACMD41_VOLTAGE)   //(ACMD41_VOLTAGE|ACMD41_S18R)

/*--------------------------------------------------------------------------}
{						   SD CARD COMMAND RECORD						    }
{--------------------------------------------------------------------------*/
typedef struct EMMCCommand
{
	const char cmd_name[16];
	struct regCMDTM code;
	struct __attribute__((__packed__)) {
		unsigned use_rca : 1;										// @0		Command uses rca										
		unsigned reserved : 15;										// @1-15	Write as zero read as don't care
		uint16_t delay;												// @16-31	Delay to apply after command
	};
} EMMCCommand;

/*--------------------------------------------------------------------------}
{						SD CARD COMMAND INDEX DEFINITIONS				    }
{--------------------------------------------------------------------------*/
#define IX_GO_IDLE_STATE    0
#define IX_ALL_SEND_CID     1
#define IX_SEND_REL_ADDR    2
#define IX_SET_DSR          3
#define IX_SWITCH_FUNC      4
#define IX_CARD_SELECT      5
#define IX_SEND_IF_COND     6
#define IX_SEND_CSD         7
#define IX_SEND_CID         8
#define IX_VOLTAGE_SWITCH   9
#define IX_STOP_TRANS       10
#define IX_SEND_STATUS      11
#define IX_GO_INACTIVE      12
#define IX_SET_BLOCKLEN     13
#define IX_READ_SINGLE      14
#define IX_READ_MULTI       15
#define IX_SEND_TUNING      16
#define IX_SPEED_CLASS      17
#define IX_SET_BLOCKCNT     18
#define IX_WRITE_SINGLE     19
#define IX_WRITE_MULTI      20
#define IX_PROGRAM_CSD      21
#define IX_SET_WRITE_PR     22
#define IX_CLR_WRITE_PR     23
#define IX_SND_WRITE_PR     24
#define IX_ERASE_WR_ST      25
#define IX_ERASE_WR_END     26
#define IX_ERASE            27
#define IX_LOCK_UNLOCK      28
#define IX_APP_CMD          29
#define IX_APP_CMD_RCA      30
#define IX_GEN_CMD          31

// Commands hereafter require APP_CMD.
#define IX_APP_CMD_START    32
#define IX_SET_BUS_WIDTH    32
#define IX_SD_STATUS        33
#define IX_SEND_NUM_WRBL    34
#define IX_SEND_NUM_ERS     35
#define IX_APP_SEND_OP_COND 36
#define IX_SET_CLR_DET      37
#define IX_SEND_SCR         38

/*--------------------------------------------------------------------------}
{							  SD CARD COMMAND TABLE						    }
{--------------------------------------------------------------------------*/
static EMMCCommand sdCommandTable[IX_SEND_SCR + 1] =  {
	[IX_GO_IDLE_STATE] =	{ "GO_IDLE_STATE", .code.CMD_INDEX = 0x00, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 0},
	[IX_ALL_SEND_CID] =		{ "ALL_SEND_CID" , .code.CMD_INDEX = 0x02, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 0 , .delay = 0},
	[IX_SEND_REL_ADDR] =	{ "SEND_REL_ADDR", .code.CMD_INDEX = 0x03, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SET_DSR] =			{ "SET_DSR"      , .code.CMD_INDEX = 0x04, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 0},
	[IX_SWITCH_FUNC] =		{ "SWITCH_FUNC"  , .code.CMD_INDEX = 0x06, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_CARD_SELECT] =		{ "CARD_SELECT"  , .code.CMD_INDEX = 0x07, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 1 , .delay = 0},
	[IX_SEND_IF_COND] = 	{ "SEND_IF_COND" , .code.CMD_INDEX = 0x08, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 100},
	[IX_SEND_CSD] =			{ "SEND_CSD"     , .code.CMD_INDEX = 0x09, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 1 , .delay = 0},
	[IX_SEND_CID] =			{ "SEND_CID"     , .code.CMD_INDEX = 0x0A, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 1 , .delay = 0},
	[IX_VOLTAGE_SWITCH] =	{ "VOLT_SWITCH"  , .code.CMD_INDEX = 0x0B, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_STOP_TRANS] =		{ "STOP_TRANS"   , .code.CMD_INDEX = 0x0C, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SEND_STATUS] =		{ "SEND_STATUS"  , .code.CMD_INDEX = 0x0D, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_GO_INACTIVE] =		{ "GO_INACTIVE"  , .code.CMD_INDEX = 0x0F, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 1 , .delay = 0},
	[IX_SET_BLOCKLEN] =		{ "SET_BLOCKLEN" , .code.CMD_INDEX = 0x10, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_READ_SINGLE] =		{ "READ_SINGLE"  , .code.CMD_INDEX = 0x11, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
	                                           .code.CMD_ISDATA = 1  , .code.TM_DAT_DIR = 1,					   .use_rca = 0 , .delay = 0},
	[IX_READ_MULTI] =		{ "READ_MULTI"   , .code.CMD_INDEX = 0x12, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1 ,  .code.TM_DAT_DIR = 1,
											   .code.TM_BLKCNT_EN =1 , .code.TM_MULTI_BLOCK = 1,                   .use_rca = 0 , .delay = 0},
	[IX_SEND_TUNING] =		{ "SEND_TUNING"  , .code.CMD_INDEX = 0x13, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SPEED_CLASS] =		{ "SPEED_CLASS"  , .code.CMD_INDEX = 0x14, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SET_BLOCKCNT] =		{ "SET_BLOCKCNT" , .code.CMD_INDEX = 0x17, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_WRITE_SINGLE] =		{ "WRITE_SINGLE" , .code.CMD_INDEX = 0x18, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  ,											   .use_rca = 0 , .delay = 0},
	[IX_WRITE_MULTI] =		{ "WRITE_MULTI"  , .code.CMD_INDEX = 0x19, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  ,  
											   .code.TM_BLKCNT_EN = 1, .code.TM_MULTI_BLOCK = 1,				   .use_rca = 0 , .delay = 0},
	[IX_PROGRAM_CSD] =		{ "PROGRAM_CSD"  , .code.CMD_INDEX = 0x1B, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SET_WRITE_PR] =		{ "SET_WRITE_PR" , .code.CMD_INDEX = 0x1C, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_CLR_WRITE_PR] =		{ "CLR_WRITE_PR" , .code.CMD_INDEX = 0x1D, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SND_WRITE_PR] =		{ "SND_WRITE_PR" , .code.CMD_INDEX = 0x1E, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE_WR_ST] =		{ "ERASE_WR_ST"  , .code.CMD_INDEX = 0x20, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE_WR_END] =		{ "ERASE_WR_END" , .code.CMD_INDEX = 0x21, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE] =			{ "ERASE"        , .code.CMD_INDEX = 0x26, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_LOCK_UNLOCK] =		{ "LOCK_UNLOCK"  , .code.CMD_INDEX = 0x2A, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_APP_CMD] =			{ "APP_CMD"      , .code.CMD_INDEX = 0x37, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 100},
	[IX_APP_CMD_RCA] =		{ "APP_CMD"      , .code.CMD_INDEX = 0x37, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_GEN_CMD] =			{ "GEN_CMD"      , .code.CMD_INDEX = 0x38, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},

	// APP commands must be prefixed by an APP_CMD.
	[IX_SET_BUS_WIDTH] =	{ "SET_BUS_WIDTH", .code.CMD_INDEX = 0x06, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SD_STATUS] =		{ "SD_STATUS"    , .code.CMD_INDEX = 0x0D, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_SEND_NUM_WRBL] =	{ "SEND_NUM_WRBL", .code.CMD_INDEX = 0x16, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SEND_NUM_ERS] =		{ "SEND_NUM_ERS" , .code.CMD_INDEX = 0x17, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_APP_SEND_OP_COND] =	{ "SD_SENDOPCOND", .code.CMD_INDEX = 0x29, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 1000},
	[IX_SET_CLR_DET] =		{ "SET_CLR_DET"  , .code.CMD_INDEX = 0x2A, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SEND_SCR] =			{ "SEND_SCR"     , .code.CMD_INDEX = 0x33, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  , .code.TM_DAT_DIR = 1,					   .use_rca = 0 , .delay = 0},
};

static const char* SD_TYPE_NAME[] = { "Unknown", "MMC", "Type 1", "Type 2 SC", "Type 2 HC" };

/*--------------------------------------------------------------------------}
{						  SD CARD DESCRIPTION RECORD					    }
{--------------------------------------------------------------------------*/
typedef struct SDDescriptor {
	struct regCID cid;							// Card cid
	struct CSD csd;								// Card csd
	struct regSCR scr;							// Card scr
	uint64_t CardCapacity;						// Card capacity expanded .. calculated from card details
	SDCARD_TYPE type;							// Card type
	uint32_t rca;								// Card rca
	struct regOCR ocr;							// Card ocr
	uint32_t status;							// Card last status

	EMMCCommand* lastCmd;

	struct {
		uint32_t rootCluster;					// Active partition rootCluster
		uint32_t sectorPerCluster;				// Active partition sectors per cluster
		uint32_t bytesPerSector;				// Active partition bytes per sector
		uint32_t firstDataSector;				// Active partition first data sector
		uint32_t dataSectors;					// Active partition data sectors
		uint32_t unusedSectors;					// Active partition unused sectors
		uint32_t reservedSectorCount;			// Active partition reserved sectors
	} partition;

	SFN_NAME partitionLabe1;					// Partition label
} SDDescriptor;

/*--------------------------------------------------------------------------}
{					    CURRENT SD CARD DATA STORAGE					    }
{--------------------------------------------------------------------------*/
static SDDescriptor sdCard = { 0 };


//**************************************************************************
// SD Card PUBLIC functions.
//**************************************************************************

static int sdDebugResponse( int resp ) 
{
	LOG_DEBUG("EMMC: Status: %08x, control1: %08x, interrupt: %08x\n", 
		(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_CONTROL1->Raw32, 
		(unsigned int)EMMC_INTERRUPT->Raw32);
	LOG_DEBUG("EMMC: Command %s resp %08x: %08x %08x %08x %08x\n",
		sdCard.lastCmd->cmd_name, (unsigned int)resp,(unsigned int)*EMMC_RESP3, 
		(unsigned int)*EMMC_RESP2, (unsigned int)*EMMC_RESP1, 
		(unsigned int)*EMMC_RESP0);
	return resp;
}

/*-[INTERNAL: sdWaitForInterrupt]-------------------------------------------}
. Given an interrupt mask the routine loops polling for the condition for up
. to 1 second.
. RETURN: SD_TIMEOUT - the condition mask flags where not met in 1 second
.		  SD_ERROR - an identifiable error occurred
.		  SD_OK - the wait completed with a mask state as requested
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForInterrupt (uint32_t mask ) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	uint32_t tMask = mask | INT_ERROR_MASK;							// Add fatal error masks to mask provided
	while (!(EMMC_INTERRUPT->Raw32 & tMask) && (td < 1000000)) {
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	uint32_t ival = EMMC_INTERRUPT->Raw32;							// Fetch all the interrupt flags
	if( td >= 1000000 ||											// No reponse timeout occurred
		(ival & INT_CMD_TIMEOUT) ||									// Command timeout occurred 
		(ival & INT_DATA_TIMEOUT) )									// Data timeout occurred
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for interrupt %08x timeout: %08x %08x %08x\n", 
			(unsigned int)mask, (unsigned int)EMMC_STATUS->Raw32, 
			(unsigned int)ival, (unsigned int)*EMMC_RESP0);			// Log any error if requested

		// Clear the interrupt register completely.
		EMMC_INTERRUPT->Raw32 = ival;								// Clear any interrupt that occured

		return SD_TIMEOUT;											// Return SD_TIMEOUT
	} else if ( ival & INT_ERROR_MASK ) {
		if (LOG_ERROR) LOG_ERROR("EMMC: Error waiting for interrupt: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)ival, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested

		// Clear the interrupt register completely.
		EMMC_INTERRUPT->Raw32 = ival;								// Clear any interrupt that occured

		return SD_ERROR;											// Return SD_ERROR
    }

	// Clear the interrupt we were waiting for, leaving any other (non-error) interrupts.
	EMMC_INTERRUPT->Raw32 = mask;									// Clear any interrupt we are waiting on

	return SD_OK;													// Return SD_OK
}


/*-[INTERNAL: sdWaitForCommand]---------------------------------------------}
. Waits for up to 1 second for any command that may be in progress.
. RETURN: SD_BUSY - the command was not completed within 1 second period
.		  SD_OK - the wait completed sucessfully
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForCommand (void) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	while ((EMMC_STATUS->CMD_INHIBIT) &&							// Command inhibit signal
		  !(EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) &&				// No error occurred
		   (td < 1000000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// Get start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start and now
	}
	if( (td >= 1000000) || (EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) )// Error occurred or it timed out
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for command aborted: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested
		return SD_BUSY;												// return SD_BUSY
    }

	return SD_OK;													// return SD_OK
}


/*-[INTERNAL: sdWaitForData]------------------------------------------------}
. Waits for up to 1 second for any data transfer that may be in progress.
. RETURN: SD_BUSY - the transfer was not completed within 1 second period
.		  SD_OK - the transfer completed sucessfully
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForData (void) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	while ((EMMC_STATUS->DAT_INHIBIT) &&							// Data inhibit signal
		  !(EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) &&				// Some error occurred
		   (td < 500000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if ( (td >= 500000) || (EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) )
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for data aborted: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested
		return SD_BUSY;												// return SD_BUSY
    }
	return SD_OK;													// return SD_OK
}


/*-[INTERNAL: unpack_csd]---------------------------------------------------}
. Unpacks a CSD in Resp0,1,2,3 into the proper CSD structure we defined.
. 16Aug17 LdB
.--------------------------------------------------------------------------*/
static void unpack_csd(struct CSD* csd)
{
	uint8_t buf[16] = { 0 };

	/* Fill buffer CSD comes IN MSB so I will invert so its sort of right way up so I can debug it */
	__attribute__((aligned(4))) uint32_t* p;
	p = (uint32_t*)&buf[12];
	*p = *EMMC_RESP0;
	p = (uint32_t*)&buf[8];
	*p = *EMMC_RESP1;
	p = (uint32_t*)&buf[4];
	*p = *EMMC_RESP2;
	p = (uint32_t*)&buf[0];
	*p = *EMMC_RESP3;

	/* Display raw CSD - values of my SANDISK ultra 16GB shown under each */
	LOG_DEBUG("CSD Contents : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
		buf[2], buf[1], buf[0], buf[7], buf[6], buf[5], buf[4],
		/*    40       e0      00     32        5b     59     00               */
		buf[11], buf[10], buf[9], buf[8], buf[15], buf[14], buf[13], buf[12]);
	/*    00       73       a7     7f       80       0a       40       00 */

	/* Populate CSD structure */
	csd->csd_structure = (buf[2] & 0xc0) >> 6;								// @126-127 ** correct 
	csd->spec_vers = buf[2] & 0x3F;											// @120-125 ** correct
	csd->taac = buf[1];														// @112-119 ** correct
	csd->nsac = buf[0];														// @104-111 ** correct
	csd->tran_speed = buf[7];												// @96-103  ** correct
	csd->ccc = (((uint16_t)buf[6]) << 4) | ((buf[5] & 0xf0) >> 4);			// @84-95   ** correct
	csd->read_bl_len = buf[5] & 0x0f;										// @80-83   ** correct
	csd->read_bl_partial = (buf[4] & 0x80) ? 1 : 0;							// @79		** correct
	csd->write_blk_misalign = (buf[4] & 0x40) ? 1 : 0;						// @78      ** correct
	csd->read_blk_misalign = (buf[4] & 0x20) ? 1 : 0;						// @77		** correct
	csd->dsr_imp = (buf[4] & 0x10) ? 1 : 0;									// @76		** correct

	if (csd->csd_structure == 0x1) {										// CSD VERSION 2.0 
		/* Basically absorbs bottom of buf[4] to align to next byte */		// @@75-70  ** Correct
		csd->ver2_c_size = (uint32_t)(buf[11] & 0x3F) << 16;				// @69-64
		csd->ver2_c_size += (uint32_t)buf[10] << 8;							// @63-56
		csd->ver2_c_size += (uint32_t)buf[9];								// @55-48
		sdCard.CardCapacity = csd->ver2_c_size;
		sdCard.CardCapacity *= (512 * 1024);								// Calculate Card capacity
	}
	else {																	// CSD VERSION 1.0
		csd->c_size = (uint32_t)(buf[4] & 0x03) << 8;
		csd->c_size += (uint32_t)buf[11];
		csd->c_size <<= 2;
		csd->c_size += (buf[10] & 0xc0) >> 6;								// @62-73
		csd->vdd_r_curr_min = (buf[10] & 0x38) >> 3;						// @59-61
		csd->vdd_r_curr_max = buf[10] & 0x07;								// @56-58
		csd->vdd_w_curr_min = (buf[9] & 0xe0) >> 5;							// @53-55
		csd->vdd_w_curr_max = (buf[9] & 0x1c) >> 2;							// @50-52	
		csd->c_size_mult = ((buf[9] & 0x03) << 1) | ((buf[8] & 0x80) >> 7);	// @47-49
		sdCard.CardCapacity = (csd->c_size + 1) * (1 << (csd->c_size_mult + 2)) * (1 << csd->read_bl_len);
	}

	csd->erase_blk_en = (buf[8] & 0x40) >> 6;								// @46
	csd->sector_size = ((buf[15] & 0x80) >> 1) | (buf[8] & 0x3F);			// @39-45
	csd->wp_grp_size = buf[15] & 0x7f;										// @32-38
	csd->wp_grp_enable = (buf[14] & 0x80) ? 1 : 0;							// @31  
	csd->default_ecc = (buf[14] & 0x60) >> 5;								// @29-30
	csd->r2w_factor = (buf[14] & 0x1c) >> 2;								// @26-28   ** correct
	csd->write_bl_len = ((buf[14] & 0x03) << 2) | ((buf[13] & 0xc0) >> 6);  // @22-25   **correct
	csd->write_bl_partial = (buf[13] & 0x20) ? 1 : 0;						// @21 
																			// @16-20 are reserved
	csd->file_format_grp = (buf[12] & 0x80) ? 1 : 0;						// @15
	csd->copy = (buf[12] & 0x40) ? 1 : 0;									// @14
	csd->perm_write_protect = (buf[12] & 0x20) ? 1 : 0;						// @13
	csd->tmp_write_protect = (buf[12] & 0x10) ? 1 : 0;						// @12
	csd->file_format = (buf[12] & 0x0c) >> 2;								// @10-11    **correct
	csd->ecc = buf[12] & 0x03;												// @8-9      **corrrect   

	LOG_DEBUG("  csd_structure=%d\t  spec_vers=%d\t  taac=%02x\t nsac=%02x\t  tran_speed=%02x\t  ccc=%04x\n"
		"  read_bl_len=%d\t  read_bl_partial=%d\t  write_blk_misalign=%d\t  read_blk_misalign=%d\n"
		"  dsr_imp=%d\t  sector_size =%d\t  erase_blk_en=%d\n",
		csd->csd_structure, csd->spec_vers, csd->taac, csd->nsac, csd->tran_speed, csd->ccc,
		csd->read_bl_len, csd->read_bl_partial, csd->write_blk_misalign, csd->read_blk_misalign,
		csd->dsr_imp, csd->sector_size, csd->erase_blk_en);

	if (csd->csd_structure == 0x1) {
		LOG_DEBUG("CSD 2.0: ver2_c_size = %d\t  card capacity: %lu\n",
			csd->ver2_c_size, sdCard.CardCapacity);
	}
	else {
		LOG_DEBUG("CSD 1.0: c_size = %d\t  c_size_mult=%d\t card capacity: %lu\n"
			"  vdd_r_curr_min = %d\t  vdd_r_curr_max=%d\t  vdd_w_curr_min = %d\t  vdd_w_curr_max=%d\n",
			csd->c_size, csd->c_size_mult, sdCard.CardCapacity,
			csd->vdd_r_curr_min, csd->vdd_r_curr_max, csd->vdd_w_curr_min, csd->vdd_w_curr_max);
	}

	LOG_DEBUG("  wp_grp_size=%d\t  wp_grp_enable=%d\t  default_ecc=%d\t  r2w_factor=%d\n"
		"  write_bl_len=%d\t  write_bl_partial=%d\t  file_format_grp=%d\t  copy=%d\n"
		"  perm_write_protect=%d\t  tmp_write_protect=%d\t  file_format=%d\t  ecc=%d\n",
		csd->wp_grp_size, csd->wp_grp_enable, csd->default_ecc, csd->r2w_factor,
		csd->write_bl_len, csd->write_bl_partial, csd->file_format_grp, csd->copy,
		csd->perm_write_protect, csd->tmp_write_protect, csd->file_format, csd->ecc);
}

/*-[INTERNAL: sdSendCommandP]-----------------------------------------------}
. Send command and handle response.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
#define R1_ERRORS_MASK       0xfff9c004
static int sdSendCommandP( EMMCCommand* cmd, uint32_t arg )
{
	SDRESULT res;

	/* Check for command in progress */
	if ( sdWaitForCommand() != SD_OK ) return SD_BUSY;				// Check command wait

	LOG_DEBUG("EMMC: Sending command %s code %08x arg %08x\n",
		cmd->cmd_name, (unsigned int)cmd->code.CMD_INDEX, (unsigned int)arg);
	sdCard.lastCmd = cmd;

	/* Clear interrupt flags.  This is done by setting the ones that are currently set */
	EMMC_INTERRUPT->Raw32 = EMMC_INTERRUPT->Raw32;					// Clear interrupts

	/* Set the argument and the command code, Some commands require a delay before reading the response */
	*EMMC_ARG1 = arg;												// Set argument to SD card
	*EMMC_CMDTM = cmd->code;										// Send command to SD card								
	if ( cmd->delay ) waitMicro(cmd->delay);						// Wait for required delay

	/* Wait until command complete interrupt */
	if ( (res = sdWaitForInterrupt(INT_CMD_DONE))) return res;		// In non zero return result 

	/* Get response from RESP0 */
	uint32_t resp0 = *EMMC_RESP0;									// Fetch SD card response 0 to command

	/* Handle response types for command */
	switch ( cmd->code.CMD_RSPNS_TYPE) {
		// no response
		case CMD_NO_RESP:
			return SD_OK;											// Return okay then

		case CMD_BUSY48BIT_RESP:
			sdCard.status = resp0;
			// Store the card state.  Note that this is the state the card was in before the
			// command was accepted, not the new state.
			//sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
			return resp0 & R1_ERRORS_MASK;

		// RESP0 contains card status, no other data from the RESP* registers.
		// Return value non-zero if any error flag in the status value.
		case CMD_48BIT_RESP:
			switch (cmd->code.CMD_INDEX) {
				case 0x03:											// SEND_REL_ADDR command
					// RESP0 contains RCA and status bits 23,22,19,12:0
					sdCard.rca = resp0 & 0xffff0000;				// RCA[31:16] of response
					sdCard.status = ((resp0 & 0x00001fff)) |		// 12:0 map directly to status 12:0
						((resp0 & 0x00002000) << 6) |				// 13 maps to status 19 ERROR
						((resp0 & 0x00004000) << 8) |				// 14 maps to status 22 ILLEGAL_COMMAND
						((resp0 & 0x00008000) << 8);				// 15 maps to status 23 COM_CRC_ERROR
					// Store the card state.  Note that this is the state the card was in before the
					// command was accepted, not the new state.
					// sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
					return sdCard.status & R1_ERRORS_MASK;
				case 0x08:											// SEND_IF_COND command
					// RESP0 contains voltage acceptance and check pattern, which should match
					// the argument.
					sdCard.status = 0;
					return resp0 == arg ? SD_OK : SD_ERROR;
					// RESP0 contains OCR register
					// TODO: What is the correct time to wait for this?
				case 0x29:											// SD_SENDOPCOND command
					sdCard.status = 0;
					sdCard.ocr.Raw32 = resp0;
					return SD_OK;
				default:
					sdCard.status = resp0;
					// Store the card state.  Note that this is the state the card was in before the
					// command was accepted, not the new state.
					//sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
					return resp0 & R1_ERRORS_MASK;
			}
		// RESP0..3 contains 128 bit CID or CSD shifted down by 8 bits as no CRC
		// Note: highest bits are in RESP3.
		case CMD_136BIT_RESP:		
			sdCard.status = 0;
			if (cmd->code.CMD_INDEX == 0x09) {
				unpack_csd(&sdCard.csd);
			} else {
				uint32_t* data = (uint32_t*)(uintptr_t)&sdCard.cid;
				data[3] = resp0;
				data[2] = *EMMC_RESP1;
				data[1] = *EMMC_RESP2;
				data[0] = *EMMC_RESP3;
			}
			return SD_OK;
    }

	return SD_ERROR;
}


/*-[INTERNAL: sdSendAppCommand]---------------------------------------------}
. Send APP command and handle response.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
#define ST_APP_CMD           0x00000020
static SDRESULT sdSendAppCommand (void)
{
	SDRESULT resp;
	// If no RCA, send the APP_CMD and don't look for a response.
	if ( !sdCard.rca )
		sdSendCommandP(&sdCommandTable[IX_APP_CMD], 0x00000000);
	// If there is an RCA, include that in APP_CMD and check card accepted it.
	else {
		if( (resp = sdSendCommandP(&sdCommandTable[IX_APP_CMD_RCA], sdCard.rca)) ) 
			return sdDebugResponse(resp);
		// Debug - check that status indicates APP_CMD accepted.
		if( !(sdCard.status & ST_APP_CMD) ) return SD_ERROR;
	}
	return SD_OK;
}

 /*-[INTERNAL: sdSendCommand]------------------------------------------------}
 . Send a command with no argument. RCA automatically added if required.
 . APP_CMD sent automatically if required.
 . 10Aug17 LdB
 .--------------------------------------------------------------------------*/
static SDRESULT sdSendCommand ( int index )
{
	// Issue APP_CMD if needed.
	SDRESULT resp;
	if ( index >= IX_APP_CMD_START && (resp = sdSendAppCommand()) )
		return sdDebugResponse(resp);

	// Get the command and set RCA if required.
	EMMCCommand* cmd = &sdCommandTable[index];
	uint32_t arg = 0;
	if( cmd->use_rca == 1 ) arg = sdCard.rca;

	if( (resp = sdSendCommandP(cmd, arg)) ) return resp;

	// Check that APP_CMD was correctly interpreted.
	if( index >= IX_APP_CMD_START && sdCard.rca && !(sdCard.status & ST_APP_CMD) )
		return SD_ERROR_APP_CMD;

	return resp;
}

 /*-[INTERNAL: sdSendCommandA]-----------------------------------------------}
 . Send a command with argument. APP_CMD sent automatically if required.
 . APP_CMD sent automatically if required.
 . 10Aug17 LdB
 .--------------------------------------------------------------------------*/
static SDRESULT sdSendCommandA ( int index, uint32_t arg )
{
	// Issue APP_CMD if needed.
	SDRESULT resp;
	if( index >= IX_APP_CMD_START && (resp = sdSendAppCommand()) )
		return sdDebugResponse(resp);

	// Get the command and pass the argument through.
	if( (resp = sdSendCommandP(&sdCommandTable[index],arg)) ) return resp;

	// Check that APP_CMD was correctly interpreted.
	if( index >= IX_APP_CMD_START && sdCard.rca && !(sdCard.status & ST_APP_CMD) )
		return SD_ERROR_APP_CMD;

	return resp;
}


/*-[INTERNAL: sdReadSCR]----------------------------------------------------}
. Read card's SCR
. APP_CMD sent automatically if required.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdReadSCR (void)
{
	// SEND_SCR command is like a READ_SINGLE but for a block of 8 bytes.
	// Ensure that any data operation has completed before reading the block.
	if( sdWaitForData() ) return SD_TIMEOUT;

	// Set BLKSIZECNT to 1 block of 8 bytes, send SEND_SCR command
	EMMC_BLKSIZECNT->BLKCNT = 1;
	EMMC_BLKSIZECNT->BLKSIZE = 8;
	int resp;
	if( (resp = sdSendCommand(IX_SEND_SCR)) ) return sdDebugResponse(resp);

	// Wait for READ_RDY interrupt.
	if( (resp = sdWaitForInterrupt(INT_READ_RDY)) )
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for ready to read\n");
		return sdDebugResponse(resp);
	}

	// Allow maximum of 100ms for the read operation.
	int numRead = 0, count = 100000;
	while( numRead < 2 )  {
		if (EMMC_STATUS->READ_TRANSFER) {
			//sdCard.scr[numRead++] = *EMMC_DATA;
			if (numRead == 0) sdCard.scr.Raw32_Lo = *EMMC_DATA;
				else sdCard.scr.Raw32_Hi = *EMMC_DATA;
			numRead++;
		} else {
			waitMicro(1);
			if( --count == 0 ) break;
		}
	}

	// If SCR not fully read, the operation timed out.
	if( numRead != 2 )
	{
		if (LOG_ERROR) {
			LOG_ERROR("EMMC: SEND_SCR ERR: %08x %08x %08x\n", 
				(unsigned int)EMMC_STATUS->Raw32, 
				(unsigned int)EMMC_INTERRUPT->Raw32, 
				(unsigned int)*EMMC_RESP0);
			LOG_ERROR("EMMC: Reading SCR, only read %d words\n", numRead);
		}
		return SD_TIMEOUT;
	}

	return SD_OK;
}


/*-[INTERNAL: fls_uint32_t]-------------------------------------------------}
. Find Last Set bit in given uint32_t value. That is find the bit index of 
. the MSB that is set in the value. 
. RETURN: 0 - 32 are only possible answers given uint32_t is 32 bits.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static uint_fast8_t fls_uint32_t (uint32_t x) 
{
	uint_fast8_t r = 32;											// Start at 32
	if (!x)  return 0;												// If x is zero answer must be zero
	if (!(x & 0xffff0000u)) {										// If none of the upper word bits are set
		x <<= 16;													// We can roll it up 16 bits
		r -= 16;													// Reduce r by 16
	}
	if (!(x & 0xff000000u)) {										// If none of uppermost byte bits are set
		x <<= 8;													// We can roll it up by 8 bits
		r -= 8;														// Reduce r by 8
	}
	if (!(x & 0xf0000000u)) {										// If none of the uppermost 4 bits are set
		x <<= 4;													// We can roll it up by 4 bits
		r -= 4;														// Reduce r by 4
	}
	if (!(x & 0xc0000000u)) {										// If none of the uppermost 2 bits are set
		x <<= 2;													// We can roll it up by 2 bits
		r -= 2;														// Reduce r by 2
	}
	if (!(x & 0x80000000u)) {										// If the uppermost bit is not set
		x <<= 1;													// We can roll it up by 1 bit
		r -= 1;														// Reduce r by 1
	}
	return r;														// Return the number of the uppermost set bit
}

/*-[INTERNAL: sdGetClockDivider]--------------------------------------------}
. Get clock divider for the given requested frequency. This is calculated 
. relative to the SD base clock of 41.66667Mhz
. RETURN: 3 - 0x3FF are only possible answers for the divisor
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static uint32_t sdGetClockDivider (uint32_t freq) 
{
	uint32_t divisor = (41666667 + freq - 1) / freq;				// Pi SD frequency is always 41.66667Mhz on baremetal
	if (divisor > 0x3FF) divisor = 0x3FF;							// Constrain divisor to max 0x3FF
	if (EMMC_SLOTISR_VER->SDVERSION < 2) {							// Any version less than HOST SPECIFICATION 3 (Aka numeric 2)						
		uint_fast8_t shiftcount = fls_uint32_t(divisor);			// Only 8 bits and set pwr2 div on Hosts specs 1 & 2
		if (shiftcount > 0) shiftcount--;							// Note the offset of shift by 1 (look at the spec)
		if (shiftcount > 7) shiftcount = 7;							// It's only 8 bits maximum on HOST_SPEC_V2
		divisor = ((uint32_t)1 << shiftcount);						// Version 1,2 take power 2
	} else if (divisor < 3) divisor = 4;							// Set minimum divisor limit
	LOG_DEBUG("Divisor = %i, Freq Set = %i\n", (int)divisor, (int)(41666667/divisor));
	return divisor;													// Return divisor that would be required
}

/*-[INTERNAL: sdGetClockDivider]--------------------------------------------}
. Set the SD clock to the given frequency from the 41.66667Mhz base clock
. RETURN: SD_ERROR_CLOCK - A fatal error occurred setting the clock
.		  SD_OK - the clock change to given frequency
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdSetClock (uint32_t freq)
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	LOG_DEBUG("EMMC: setting clock speed to %i.\n", (unsigned int)freq);
	while ((EMMC_STATUS->CMD_INHIBIT || EMMC_STATUS->DAT_INHIBIT)	// Data inhibit signal
  		   && (td < 100000))										// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for inghibit flags
		if (LOG_ERROR) LOG_ERROR("EMMC: Set clock: timeout waiting for inhibit flags. Status %08x.\n",
			(unsigned int)EMMC_STATUS->Raw32);
		return SD_ERROR_CLOCK;										// Return clock error
	}

	/* Switch clock off */
	EMMC_CONTROL1->CLK_EN = 0;										// Disable clock
	waitMicro(10);													// We must now wait 10 microseconds

	/* Request the divisor for new clock setting */
	uint_fast32_t cdiv = sdGetClockDivider(freq);					// Fetch divisor for new frequency
	uint_fast32_t divlo = (cdiv & 0xff) << 8;						// Create divisor low bits value
	uint_fast32_t divhi = ((cdiv & 0x300) >> 2);					// Create divisor high bits value

	/* Set new clock frequency by setting new divisor */
	EMMC_CONTROL1->Raw32 = (EMMC_CONTROL1->Raw32 & 0xffff001f) | divlo | divhi;
	waitMicro(10);													// We must now wait 10 microseconds

	/* Enable the clock. */
	EMMC_CONTROL1->CLK_EN = 1;										// Enable the clock

	/* Wait for clock to be stablize */
	td = 0;															// Zero time difference
	start_time = 0;													// Zero start time
	while (!(EMMC_CONTROL1->CLK_STABLE)								// Clock not stable yet
		&& (td < 100000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for stability flag
		if (LOG_ERROR) LOG_ERROR("EMMC: ERROR: failed to get stable clock.\n");
		return SD_ERROR_CLOCK;										// Return clock error
	}
	return SD_OK;													// Clock frequency set worked
}

/*-[INTERNAL: sdResetCard]--------------------------------------------------}
. Reset the SD Card
. RETURN: SD_ERROR_RESET - A fatal error occurred resetting the SD Card
.		  SD_OK - SD Card reset correctly
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdResetCard (void)
{
	SDRESULT resp;
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time


	/* Send reset host controller and wait for complete */
	EMMC_CONTROL0->Raw32 = 0;										// Zero control0 register
	EMMC_CONTROL1->Raw32 = 0;										// Zero control1 register
	EMMC_CONTROL1->SRST_HC = 1;										// Reset the complete host circuit
  	//EMMC_CONTROL2->UHSMODE = SDR12;
	waitMicro(10);													// Wait 10 microseconds
	LOG_DEBUG("EMMC: reset card.\n");
	while ((EMMC_CONTROL1->SRST_HC)									// Host circuit reset not clear
			&& (td < 100000))										// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for reset flag
		if (LOG_ERROR) LOG_ERROR("EMMC: ERROR: failed to reset.\n");
		return SD_ERROR_RESET;										// Return reset SD Card error
    }

	/* Enable internal clock and set data timeout */
	EMMC_CONTROL1->DATA_TOUNIT = 0xE;								// Maximum timeout value
	EMMC_CONTROL1->CLK_INTLEN = 1;									// Enable internal clock
	waitMicro(10);													// Wait 10 microseconds

	/* Set clock to setup frequency */
	if ( (resp = sdSetClock(FREQ_SETUP)) ) return resp;				// Set low speed setup frequency (400Khz)

	/* Enable interrupts for command completion values */
	EMMC_IRPT_EN->Raw32   = 0xffffffff;
	EMMC_IRPT_MASK->Raw32 = 0xffffffff;

	/* Reset our card structure entries */
	sdCard.rca = 0;													// Zero rca
	sdCard.ocr.Raw32 = 0;											// Zero ocr
	sdCard.lastCmd = 0;												// Zero lastCmd
	sdCard.status = 0;												// Zero status
	sdCard.type = SD_TYPE_UNKNOWN;									// Set card type unknown

	/* Send GO_IDLE_STATE to card */
	resp = sdSendCommand(IX_GO_IDLE_STATE);							// Send GO idle state

	return resp;													// Return response
}

 /*-[INTERNAL: sdAppSendOpCond]---------------------------------------------}
 . Common routine for APP_SEND_OP_COND
 . This is used for both SC and HC cards based on the parameter
 . 10Aug17 LdB
 .-------------------------------------------------------------------------*/
static SDRESULT sdAppSendOpCond (uint32_t arg )
{
	// Send APP_SEND_OP_COND with the given argument (for SC or HC cards).
	// Note: The host shall set ACMD41 timeout more than 1 second to abort repeat of issuing ACMD41
	SDRESULT  resp;
	if( (resp = sdSendCommandA(IX_APP_SEND_OP_COND,arg)) && resp != SD_TIMEOUT )
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: ACMD41 returned non-timeout error %d\n",resp);
			return resp;
    }
	int count = 6;
	while( (sdCard.ocr.card_power_up_busy == 0) && count-- )
	{
		//scPrintf("EMMC: Retrying ACMD SEND_OP_COND status %08x\n",*EMMC_STATUS);
		waitMicro(400000);
		if( (resp = sdSendCommandA(IX_APP_SEND_OP_COND,arg)) && resp != SD_TIMEOUT )
		{
			if (LOG_ERROR) LOG_ERROR("EMMC: ACMD41 returned non-timeout error %d\n",resp);
			return resp;
		}
	}

	// Return timeout error if still not busy.
	if( sdCard.ocr.card_power_up_busy == 0 )
	return SD_TIMEOUT;

	// Pi is 3.3v SD only so check that one voltage values around 3.3v was returned.
	if(  sdCard.ocr.voltage3v2to3v3 == 0  && sdCard.ocr.voltage3v3to3v4 == 0)
	return SD_ERROR_VOLTAGE;

	return SD_OK;
}

/*-[sdTransferBlocks]-------------------------------------------------------}
. Transfer the count blocks starting at given block to/from SD Card.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdTransferBlocks (uint32_t startBlock, uint32_t numBlocks, uint8_t* buffer, bool write )
{
	if ( sdCard.type == SD_TYPE_UNKNOWN ) return SD_NO_RESP;		// If card not known return error
	if ( sdWaitForData() ) return SD_TIMEOUT;						// Ensure any data operation has completed before doing the transfer.

	// Work out the status, interrupt and command values for the transfer.
	int readyInt = write ? INT_WRITE_RDY : INT_READ_RDY;

	int transferCmd = write ? ( numBlocks == 1 ? IX_WRITE_SINGLE : IX_WRITE_MULTI) :
							( numBlocks == 1 ? IX_READ_SINGLE : IX_READ_MULTI);

	// If more than one block to transfer, and the card supports it,
	// send SET_BLOCK_COUNT command to indicate the number of blocks to transfer.
	SDRESULT resp;
	if ( numBlocks > 1 &&
		(sdCard.scr.CMD_SUPPORT == CMD_SUPP_SET_BLKCNT) &&
		(resp = sdSendCommandA(IX_SET_BLOCKCNT, numBlocks)) ) return sdDebugResponse(resp);

	// Address is different depending on the card type.
	// HC pass address as block # so just pass it thru.
	// SC pass address so need to multiply by 512 which is shift left 9.
	uint32_t blockAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)(startBlock << 9) : (uint32_t)startBlock;

	// Set BLKSIZECNT to number of blocks * 512 bytes, send the read or write command.
	// Once the data transfer has started and the TM_BLKCNT_EN bit in the CMDTM register is
	// set the EMMC module automatically decreases the BLKCNT value as the data blocks
	// are transferred and stops the transfer once BLKCNT reaches 0.
	// TODO: TM_AUTO_CMD12 - is this needed?  What effect does it have?
	EMMC_BLKSIZECNT->BLKCNT = numBlocks;
	EMMC_BLKSIZECNT->BLKSIZE = 512;
	if ((resp = sdSendCommandA(transferCmd, blockAddress))) {
		return sdDebugResponse(resp);
	}

	// Transfer all blocks.
	uint_fast32_t blocksDone = 0;
	while ( blocksDone < numBlocks )
    {
		// Wait for ready interrupt for the next block.
		if( (resp = sdWaitForInterrupt(readyInt)) )
		{
			if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for ready to read\n");
			return sdDebugResponse(resp);
		}

		// Handle non-word-aligned buffers byte-by-byte.
		// Note: the entire block is sent without looking at status registers.
		if ((uintptr_t)buffer & 0x03) {
			for (uint_fast16_t i = 0; i < 512; i++ ) {
				if ( write ) {
					uint32_t data = (buffer[i]      );
					data |=    (buffer[i+1] << 8 );
					data |=    (buffer[i+2] << 16);
					data |=    (buffer[i+3] << 24);
					*EMMC_DATA = data;
				} else {
					uint32_t data = *EMMC_DATA;
					buffer[i] =   (data      ) & 0xff;
					buffer[i+1] = (data >> 8 ) & 0xff;
					buffer[i+2] = (data >> 16) & 0xff;
					buffer[i+3] = (data >> 24) & 0xff;
				}
			}
		}
	    // Handle word-aligned buffers more efficiently.
		// Hopefully people smart enough to privide aligned data buffer
		else {
			uint32_t* intbuff = (uint32_t*)buffer;
			for (uint_fast16_t i = 0; i < 128; i++ ) {
				if ( write ) *EMMC_DATA = intbuff[i];
					else intbuff[i] = *EMMC_DATA;
			}
		}

		blocksDone++;
		buffer += 512;
	}

	// If not all bytes were read, the operation timed out.
	if( blocksDone != numBlocks ) {
		if (LOG_ERROR) LOG_ERROR("EMMC: Transfer error only done %d/%d blocks\n",blocksDone,numBlocks);
		LOG_DEBUG("EMMC: Transfer: %08x %08x %08x %08x\n", (unsigned int)EMMC_STATUS->Raw32, 
			(unsigned int)EMMC_INTERRUPT->Raw32, (unsigned int)*EMMC_RESP0, 
			(unsigned int)EMMC_BLKSIZECNT->Raw32);
		if( !write && numBlocks > 1 && (resp = sdSendCommand(IX_STOP_TRANS)) )
			LOG_DEBUG("EMMC: Error response from stop transmission: %d\n",resp);

		return SD_TIMEOUT;
    }

	// For a write operation, ensure DATA_DONE interrupt before we stop transmission.
	if( write && (resp = sdWaitForInterrupt(INT_DATA_DONE)) )
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for data done\n");
		return sdDebugResponse(resp);
	}

	// For a multi-block operation, if SET_BLOCKCNT is not supported, we need to indicate
	// that there are no more blocks to be transferred.
	if( (numBlocks > 1) && (sdCard.scr.CMD_SUPPORT != CMD_SUPP_SET_BLKCNT) &&
		(resp = sdSendCommand(IX_STOP_TRANS)) ) return sdDebugResponse(resp);

	return SD_OK;
}

/*-[sdClearBlocks]----------------------------------------------------------}
. Clears the count blocks starting at given block from SD Card.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdClearBlocks(uint32_t startBlock , uint32_t numBlocks)
{
	if (sdCard.type == SD_TYPE_UNKNOWN) return SD_NO_RESP;

	// Ensure that any data operation has completed before doing the transfer.
	if ( sdWaitForData() ) return SD_TIMEOUT;

	// Address is different depending on the card type.
	// HC pass address as block # which is just address/512.
	// SC pass address straight through.
	uint32_t startAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)(startBlock << 9) : (uint32_t)startBlock;
	uint32_t endAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)( (startBlock +numBlocks) << 9) : (uint32_t)(startBlock + numBlocks);
	SDRESULT resp;
	LOG_DEBUG("EMMC: erasing blocks from %d to %d\n", startAddress, endAddress);
	if ( (resp = sdSendCommandA(IX_ERASE_WR_ST,startAddress)) ) return sdDebugResponse(resp);
	if ( (resp = sdSendCommandA(IX_ERASE_WR_END,endAddress)) ) return sdDebugResponse(resp);
	if ( (resp = sdSendCommand(IX_ERASE)) ) return sdDebugResponse(resp);

	// Wait for data inhibit status to drop.
	int count = 1000000;
	while( EMMC_STATUS->DAT_INHIBIT )
	{
	if ( --count == 0 )
		{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for erase: %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32);
		return SD_TIMEOUT;
		}

		waitMicro(10);
	}

	//scPrintf("EMMC: completed erase command int %08x\n",*EMMC_INTERRUPT);

	return SD_OK;
}

/*==========================================================================}
{				      FAT32 STRUCTURES AND ROUTINES							}
{==========================================================================*/

/*--------------------------------------------------------------------------}
{                        FAT12/16 SPECIFIC STRUCTURE		 	            }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT16/12 table entry varies this defines it
struct __attribute__((packed, aligned(1))) BPBFAT1612_struct {
	uint8_t			BS_DriveNumber;			// 36       ( As used in INT 13)
	uint8_t			BS_Reserved1;           // 37
	uint8_t			BS_BootSig;             // 38		(0x29)
	uint32_t		BS_VolumeID;            // 42
	uint8_t			BS_VolumeLabel[11];     // 43-53	(Label or  "NO NAME    ")
	char			BS_FileSystemType[8];   // 54-61    ("FAT16   ", "FAT     ", or all zero.)
};

/*--------------------------------------------------------------------------}
{                         FAT32 SPECIFIC STRUCTURE				            }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT32 table entry varies this defines it
struct __attribute__((packed, aligned(1))) BPBFAT32_struct {
	uint32_t		FATSize32;				// 36-39   (Number of sectors per FAT on FAT32)
	uint16_t		ExtFlags;				// 40-41   (Mirror flags Bits 0-3: number of active FAT (if bit 7 is 1) Bit 7: 1 = single active FAT; zero: all FATs are updated at runtime; Bits 4-6 & 8-15 : reserved)
	uint16_t		FSVersion;				// 42-43
	uint32_t		RootCluster;			// 44-47   (usually 2)
	uint16_t		FSInfo;					// 48-49   (usually 1)
	uint16_t		BkBootSec;				// 50-51   (usually 6)
	uint8_t			Reserved[12];			// 52-63
	uint8_t			BS_DriveNumber;			// 64
	uint8_t			BS_Reserved1;           // 65
	uint8_t		    BS_BootSig;				// 66      (0x29: extend signature .. Early FAT32 stop here ) 
	uint32_t		BS_VolumeID;			// 67-70
	char			BS_VolumeLabel[11];     // 71-81   (Label or  "NO NAME    ")
	char			BS_FileSystemType[8];   // 82-89   ("FAT32   ", "FAT     ", or all zero.)
};

/*--------------------------------------------------------------------------}
{          FAT12/16/32 UNIONIZED BIOS PARAMETER BLOCK STRUCTURE		        }
{--------------------------------------------------------------------------*/
// Full unionized BPB struct valid for 12/16/32 bit
struct __attribute__((packed, aligned(4))) FAT_BPB_struct {			// OFFSET LOCATIONS
	uint8_t			BS_JmpBoot[3];			// 0-2   (eb xx 90, or e9 xx xx)
	char			BS_OEMName[8];			// 3-10
	uint16_t		BytesPerSector;			// 11-12 (valid 512, 1024, 2048, 4096)
	uint8_t			SectorsPerCluster;		// 13    (valid 1, 2, 4, 8, 16, 32, 64, 128, 32768, 65536)
	uint16_t		ReservedSectorCount;	// 14-15 
	uint8_t			NumFATs;				// 16    (2)
	uint16_t		RootEntryCount;			// 17-18 (224 = FAT12, 512 = FAT16, 0 = FAT 32**)
	uint16_t		TotalSectors16;			// 19-20
	uint8_t			Media;					// 21    (Media descriptor f0: 1.4 MB floppy, f8 : hard disk; etc)
	uint16_t		FATSize16;				// 22-23 (Number of sectors per FAT .. 0 on FAT32**)
	uint16_t		SectorsPerTrack;		// 24-25
	uint16_t		NumberOfHeads;			// 26-27
	uint32_t		HiddenSectors;			// 28-31
	uint32_t		TotalSectors32;			// 32-35
	union {									// Unionize the two structures at offset 36
		struct BPBFAT1612_struct fat1612;	// Fat 16/12 structure
		struct BPBFAT32_struct	 fat32;		// Fat 32 structure
	} FSTypeData;
};

/*--------------------------------------------------------------------------}
{				    PARTITION DECRIPTION BLOCK STRUCTURE				    }
{--------------------------------------------------------------------------*/
//Structure to access info of a partition on the disk
struct __attribute__((packed, aligned(2))) partition_info {
	uint8_t		status;							// 0x80 - active partition
	uint8_t		headStart;						// starting head
	uint16_t	cylSectStart;					// starting cylinder and sector
	uint8_t		type;							// partition type (01h = 12bit FAT, 04h = 16bit FAT, 05h = Ex MSDOS, 06h = 16bit FAT (>32Mb), 0Bh = 32bit FAT (<2048GB))
	uint8_t		headEnd;						// ending head of the partition
	uint16_t	cylSectEnd;						// ending cylinder and sector
	uint32_t	firstSector;					// total sectors between MBR & the first sector of the partition
	uint32_t	sectorsTotal;					// size of this partition in sectors
};

/*--------------------------------------------------------------------------}
{				     MASTER BOOT RECORD BLOCK STRUCTURE					    }
{--------------------------------------------------------------------------*/
// Structure to access Master Boot Record for getting info about partitions
struct __attribute__((packed, aligned(4))) MBR_info {
	uint8_t					nothing[446];		// Filler the gap in the structure
	struct partition_info partitionData[4];		// partition records (16x4)
	uint16_t				signature;			// 0xaa55
};


/*--------------------------------------------------------------------------}
{				  FAT 12/16/32 (8:3) DIRECTORY ENTRY STRUCTURE			    }
{--------------------------------------------------------------------------*/
//Structure to access Directory Entry in the FAT
struct __attribute__((packed, aligned(1))) dir_Structure {
	SFN_NAME	name;						// 8.3 filename
	uint8_t		attrib;						// file attributes
	uint8_t		NTreserved;					// always 0
	uint8_t		timeTenth;					// tenths of seconds, set to 0 here
	uint16_t	writeTime;					// time file was last written
	uint16_t	writeDate;					// date file was last written
	uint16_t	lastAccessDate;				// date file was last accessed
	uint16_t	firstClusterHI;				// higher word of the first cluster number
	uint16_t	createTime;					// time file was created
	uint16_t	createDate;					// date file was created
	uint16_t	firstClusterLO;				// lower word of the first cluster number
	uint32_t	fileSize;					// size of file in bytes
};

/*--------------------------------------------------------------------------}
{				  FAT 12/16/32 (LFN) DIRECTORY ENTRY STRUCTURE			    }
{--------------------------------------------------------------------------*/
//Structure to access FAT long filename Directory Entry in the FAT
struct __attribute__((packed, aligned(1))) dir_LFN_Structure {
	uint8_t		LDIR_SeqNum;				// Long directory order		
	uint8_t		LDIR_Name1[10];				// Characters 1-5 of long name (UTF 16) but misaligned again
	uint8_t		LDIR_Attr;					// Long directory attribute .. always 0x0f	
	uint8_t		LDIR_Type;					// Long directory type ... always 0x00
	uint8_t		LDIR_ChkSum;				// Long directory checksum
	uint16_t	LDIR_Name2[6];				// Characters 6-11 of long name (UTF 16)
	uint16_t	LDIR_firstClusterLO;		// First cluster lo
	uint16_t	LDIR_Name3[2];				// Characters 12-13 of long name (UTF 16)
};

/*-[INTERNAL: LoadDrivePartition]-------------------------------------------}
. Attempts to load the partition on the SD Card. This involves detecting the
. type of partiton and saving values that will be needed for IO access.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static bool LoadDrivePartition (printhandler prn_basic) {
	uint32_t partition_totalClusters;
	uint8_t buffer[512] __attribute__((aligned(4)));

	if (sdTransferBlocks(0, 1, (uint8_t*)&buffer[0], false) != SD_OK) return false;
	struct FAT_BPB_struct* bpb = (struct FAT_BPB_struct *)buffer;
	if (bpb->BS_JmpBoot[0] != 0xE9 && bpb->BS_JmpBoot[0] != 0xEB) { // Check if it is boot sector
		struct MBR_info* mbr = (struct MBR_info*)&buffer[0];		// if it is not boot sector, it must be MBR
		if (mbr->signature != 0xaa55) return false;					// if it is not even MBR then it's not FAT
		struct partition_info* pd = &mbr->partitionData[0];			// First partition
		sdCard.partition.unusedSectors = pd->firstSector;			// FAT16 needs this value so hold it
		sdTransferBlocks(pd->firstSector, 1, &buffer[0], false);	// Read first sector of partition
		if (bpb->BS_JmpBoot[0] != 0xE9 && bpb->BS_JmpBoot[0] != 0xEB)  
			return false;											// Not an MBR
	}
	sdCard.partition.bytesPerSector = bpb->BytesPerSector;			// Bytes per sector on partition
	sdCard.partition.sectorPerCluster = bpb->SectorsPerCluster;		// Hold the sector per cluster count
	sdCard.partition.reservedSectorCount = bpb->ReservedSectorCount;// Hold the reserved sector count
	if ((bpb->FATSize16 == 0) && (bpb->RootEntryCount == 0)) {		// Check if FAT16/FAT32
		// FAT32
		sdCard.partition.rootCluster = bpb->FSTypeData.fat32.RootCluster;// Hold partition root cluster
		sdCard.partition.firstDataSector = bpb->ReservedSectorCount + bpb->HiddenSectors + (bpb->FSTypeData.fat32.FATSize32 * bpb->NumFATs);
		// data sectors x sectorsize = capacity ... I have check this on PC and it gives right calc
		sdCard.partition.dataSectors = bpb->TotalSectors32 - bpb->ReservedSectorCount - (bpb->FSTypeData.fat32.FATSize32 * bpb->NumFATs);
		if (prn_basic) prn_basic("FAT32 Volume Label: %s, ID: %08x\n", 
			bpb->FSTypeData.fat32.BS_VolumeLabel, 
			(unsigned int)bpb->FSTypeData.fat32.BS_VolumeID);		// Basic detail print if requested
	}
	else {
		// FAT16
		sdCard.partition.rootCluster = 2;							// Hold partition root cluster, FAT16 always start at 2
		sdCard.partition.firstDataSector = sdCard.partition.unusedSectors + (bpb->NumFATs * bpb->FATSize16) + 1;
		// data sectors x sectorsize = capacity ... I have check this on PC and gives right calc
		sdCard.partition.dataSectors = bpb->TotalSectors32 - (bpb->NumFATs * bpb->FATSize16) - 33;  // -1 see above +1 and 32 fixed sectors 
		if (bpb->FSTypeData.fat1612.BS_BootSig == 0x29) {
			if (prn_basic) prn_basic("FAT12/16 Volume Label: %s, Volume ID %08x\n", 
				bpb->FSTypeData.fat1612.BS_VolumeLabel, 
				(unsigned int)bpb->FSTypeData.fat1612.BS_VolumeID);	// Basic detail print if requested
		}
	}
	
	// total clusters *  clustersize = capacity  ... see data sectors above ... another way to say same thing 
	partition_totalClusters = sdCard.partition.dataSectors / sdCard.partition.sectorPerCluster;
	if (prn_basic) prn_basic("First Sector: %lu, Data Sectors: %lu, TotalClusters: %lu, RootCluster: %lu\n",
		(unsigned long)sdCard.partition.firstDataSector, (unsigned long)sdCard.partition.dataSectors, 
		(unsigned long)partition_totalClusters, (unsigned long) sdCard.partition.rootCluster);
	return true;
}


/*-[INTERNAL: getFirstSector]-----------------------------------------------}
. Calculate first sector address of any given cluster number on a partition.
. beside the cluster number you need the sectorPerCluster and FirstDataSector
. of the partition on which you wish to calculate.
. 12Feb17 LdB
.--------------------------------------------------------------------------*/
static uint32_t getFirstSector (uint32_t clusterNumber, uint32_t sectorPerCluster, uint32_t firstDataSector) {
	return (((clusterNumber - 2) * sectorPerCluster) + firstDataSector);
}

/*-getSetNextCluster---------------------------------------------------------
Simply gets or sets next cluster entry value of the FAT chain
12Feb17 LdB
--------------------------------------------------------------------------*/
uint32_t getSetNextCluster (uint32_t clusterNumber, bool set, uint32_t clusterEntry) {
	uint32_t FATEntrySector, FATEntryOffset;
	uint32_t* FATEntryValue;
	uint8_t buffer[512];
	FATEntrySector = sdCard.partition.unusedSectors + sdCard.partition.reservedSectorCount +
		((clusterNumber * 4) / sdCard.partition.bytesPerSector);	// Get sector number of the cluster entry in the FAT
	FATEntryOffset = ((clusterNumber * 4) % sdCard.partition.bytesPerSector); // Get the offset address in that sector number
	if (sdTransferBlocks(FATEntrySector, 1, (uint8_t*)&buffer[0], false) != SD_OK)
	{
		return 0xFFFFFFFF;											// Sector read failed twice so fail exit
	}
	FATEntryValue = (uint32_t*)&buffer[FATEntryOffset];				// Get the cluster address from the buffer .. fortunately always aligned
	if (set == false) return (*FATEntryValue);						// If not setting exit with the retrieved value
	*FATEntryValue = clusterEntry;									// Setting new value in cluster entry in FAT
	if (sdTransferBlocks(FATEntrySector, 1, (uint8_t*)&buffer[0], true) != SD_OK)
	{
		return 0xFFFFFFFF;											// Sector write failed twice so fail exit
	}
	return (0);														// return zero
}


/*==========================================================================}
{				      PRIVATE SEARCH STRUCTURES AND ROUTINES				}
{==========================================================================*/

/*--------------------------------------------------------------------------}
{                         PRIVATE SEARCH DATA STRUCTURE			            }
{--------------------------------------------------------------------------*/
struct __attribute__((packed, aligned(4))) PRIV_SEARCH_DATA {
	uint32_t cluster;												// Current cluster
	uint32_t firstSector;											// First sector
	uint32_t sector;												// Current sector
	uint32_t bPos;													// Buffer position
	uint8_t buffer[512] __attribute__((aligned(4)));				// Data buffer
	LFN_NAME searchPattern;											// Search pattern find was started with
};

/*--------------------------------------------------------------------------}
{               INTERNAL SEARCHS THAT ARE CURRENTLY RUNNING		            }
{--------------------------------------------------------------------------*/
#define MAX_SEARCH_RECORDS 4										// Current limit is 4 (if you need more simultaneous searches increase)
static struct PRIV_SEARCH_DATA __attribute__((aligned(4))) intSearchRecord[MAX_SEARCH_RECORDS] = { 0 };

/*-[INTERNAL: SetFindDataFromFATEntry]--------------------------------------}
. Transfers the normal directory entry values to a FIND_DATA structure ptr.
. If both pointers it will transfer the various time, size and name records.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
static void SetFindDataFromFATEntry (struct dir_Structure* dir, FIND_DATA* lpFD) {
	if ((dir) && (lpFD)) {
		lpFD->dwFileAttributes = dir->attrib;						// Transfer file attributes	
		lpFD->CreateDT.tm_year = ((dir->createDate & 0xFE00) >> 9) + 80;// Looks weird but file date is from 1980 and C standard from 1900 so add 80
		lpFD->CreateDT.tm_mon = ((dir->createDate & 0x01E0) >> 5) - 1;// file date is 1-12 .. C standard is 0-11
		lpFD->CreateDT.tm_mday = (dir->createDate & 0x001F);		// day is strangely correct both 1-31 
		lpFD->CreateDT.tm_hour = ((dir->createTime & 0xF800) >> 11);// Hour when file was created
		lpFD->CreateDT.tm_min = ((dir->createTime & 0x07E0) >> 5);	// Minute when file was created
		lpFD->CreateDT.tm_sec = ((dir->createTime & 0x001F) << 1);	// Seconds when file was created

		lpFD->LastAccessDate.tm_year = ((dir->lastAccessDate & 0xFE00) >> 9) + 80; // Looks weird but file date is from 1980 and C standard from 1900
		lpFD->LastAccessDate.tm_mon = ((dir->lastAccessDate & 0x01E0) >> 5) - 1; // file date is 1-12 .. C standard is 0-11
		lpFD->LastAccessDate.tm_mday = (dir->lastAccessDate & 0x001F);	// day is strangely correct both 1-31 
		

		lpFD->WriteDT.tm_year = ((dir->writeDate & 0xFE00) >> 9) + 80;// Looks weird but file date is from 1980 and C standard from 1900
		lpFD->WriteDT.tm_mon = ((dir->writeDate & 0x01E0) >> 5) - 1;// file date is 1-12 .. C standard is 0-11
		lpFD->WriteDT.tm_mday = (dir->writeDate & 0x001F);			// day is strangely correct both 1-31 
		lpFD->WriteDT.tm_hour = ((dir->writeTime & 0xF800) >> 11);	// Hour of last file write
		lpFD->WriteDT.tm_min = ((dir->writeTime & 0x07E0) >> 5);	// Minute of last file write
		lpFD->WriteDT.tm_sec = ((dir->writeTime & 0x001F) << 1);	// Second of last file write

		lpFD->nFileSizeLow = dir->fileSize;							// Low 32 bits of file size
		lpFD->nFileSizeHigh = 0;									// Zero high 32 bits as not supported

		for (int i = 0; i < sizeof(SFN_NAME); i++)					// This is all unaligned so has to be manhandled
			lpFD->cAlternateFileName[i] = dir->name[i];				// Transfer short name (8:3)
	}
}

/*-[INTERNAL: ReadLFNEntry]-------------------------------------------------}
. Transfers up to 13 characters from the LFN directory entry to the buffer
. and returns the count of characters placed in the buffer. The first five
. characters in Name1 have alignment issues for the ARM 7/8, care needed.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
static uint8_t ReadLFNEntry (struct dir_LFN_Structure* LFdir, char LFNtext[14]) {
	uint8_t utf1, utf2;
	uint16_t utf, j;
	bool lfn_done = false;
	uint8_t LFN_blockcount = 0;
	if (LFdir) {													// Check the directory pointer valid
		for (j = 0; ((j < 5) && (lfn_done == false)); j++) {		// For each of the first 5 characters
			/* Alignment problems on this 1st block .. please leave alone */
			utf1 = LFdir->LDIR_Name1[j * 2];						// Load the low byte  ***unaligned access
			utf2 = LFdir->LDIR_Name1[j * 2 + 1];					// Load the high byte
			utf = (utf2 << 8) | utf1;								// Roll and join to make word
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 6) && (lfn_done == false)); j++) {		// For the next six characters
			utf = LFdir->LDIR_Name2[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 2) && (lfn_done == false)); j++) {		// For the last two characters
			utf = LFdir->LDIR_Name3[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
	}
	LFNtext[LFN_blockcount] = '\0';									// Make ascizz incase it needs to be used as string									
	return LFN_blockcount;											// Return characters in buffer 0-13 is valid range
}


/*-[INTERNAL: WildcardMatch ]-----------------------------------------------}
. Given a pattern string which may include wildcard characters "*","?" and
. sets the given text string is compared for a match. It is the classic code
. that checks if a given filename matches the provided pattern. 
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
bool WildcardMatch (const char * pattern, const char * text)
{
	const char * retryPat = NULL;
	const char * retryText = NULL;
	int	ch;
	bool found;
	while (toupper(*text) || toupper(*pattern)) {					// While neither string terminated
		ch = *pattern++;											// Fetch next pattern character
		switch (ch)
		{
			case '*':												// If character is wildcard
				retryPat = pattern;									// Hold pattern for retry
				retryText = text;									// Hold text for retry
				break;
			case '[':												// Multi character select wildcard open
				found = false;										// found = false
				while ((ch = *pattern++) != ']')					// While not close multicharacter
				{
					if (ch == '\\')	ch = *pattern++;				// Literal request
					if (ch == '\0')	return false;					// Pattern terminated abnormally without match
					if (*text == ch) found = false;					// character not found
				}
				if (!found)											// If not found
				{
					pattern = retryPat;								// Reset pattern to retry string
					text = ++retryText;								// Set text string to next character on from retry
				}
				/* fall into next case */
			case '?':												// Single character wildcard
				if (*text++ == '\0') return false;					// text string ended so no match
				break;
			case '\\':												// Literal slash
				ch = *pattern++;									// Load next character
				if (ch == '\0')	return false;						// Pattern terminated abnormally without match
				/* fall into next case */
			default:
				if (toupper(*text) == toupper(ch))					// If pattern character matches text character
				{
					if (*text) text++;								// Move the text pointer forward
					break;
				}
				if (*text)											// If end of text string
				{
					pattern = retryPat;								// Reset pattern to retry point
					text = ++retryText;								// Set text string to next character on from retry
					break;
				}
				return false;										// No match
		}
		if (pattern == NULL) return false;							// No pattern provided
	}
	return true;													// Text string matches pattern
}

/*--------------------------------------------------------------------------}
{						  FAT SYSTEM ERROR CONSTANTS		 	            }
{--------------------------------------------------------------------------*/
#define FAT_RESULT_OK				0								// FAT function success
#define FAT_END_REACHED				1								// FAT table end reached (did not find requested before)
#define FAT_INVALID_DATAPTR			2								// FAT function given invalid pointer
#define FAT_READSECTOR_FAIL			3								// FAT sector read failed
#define FAT_WRITESECTOR_FAIL		4								// FAT sector write failed

/*-[INTERNAL: LocateFATEntry]-----------------------------------------------}
. Return the next diretory entry on the file system. It will return NULL if
. an error occurs and if you provide an ErrorID pointer it will set value
. to a value to help identify the error.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
#define FILE_EMPTY					0x00							// File empty
#define FILE_DELETED				0xE5							// File was deleted
#define FILE_ATTRIBUTE_LONGNAME		0x0F							// Long filename

static struct dir_Structure* LocateFATEntry (const char* searchPat, struct PRIV_SEARCH_DATA* priv, bool dirChange, LFN_NAME LFN_Name, uint32_t* ErrorID) {
	struct dir_Structure* dir;
	uint32_t LFN_count = 0;
	while ((priv->cluster < 0x0ffffff6)	&& (priv->cluster != 0)) {	// Check cluster valid and read successful	
		while (priv->sector < sdCard.partition.sectorPerCluster) {
			while (priv->bPos <  512) {								// While not a buffer end
				dir = (struct dir_Structure *) &priv->buffer[priv->bPos];// Transfer buffer position to pointer				
				if (dir->name[0] == FILE_EMPTY) {
					if (ErrorID) *ErrorID = FAT_END_REACHED;		// End of FAT entry chain reached
					return NULL;									// Return null pointer
				}
				if (dir->name[0] != FILE_DELETED) {					// If entry not deleted
					if (dir->attrib == FILE_ATTRIBUTE_LABEL) {		// FAT LABEL ENTRY
						/*printf("LABEL: %c%c%c%c%c%c%c%c%c%c%c\n",
							dir->name[0], dir->name[1], dir->name[2], dir->name[3],
							dir->name[4], dir->name[5], dir->name[6], dir->name[7],
							dir->name[8], dir->name[9], dir->name[10]);*/
					} else if (dir->attrib == FILE_ATTRIBUTE_LONGNAME)// LFN block precede the normal block
					{
						uint8_t LFN_blockcount;									
						char LFNtext[14] = { 0 };					// Each block can hold max 13 characters						
						// Read the LFN
						LFN_blockcount = ReadLFNEntry((struct dir_LFN_Structure*)&priv->buffer[priv->bPos], LFNtext);
						// Transfer the max 13 characters to front of reverse growing string
						for (int j = 0; j < LFN_blockcount; j++)
							LFN_Name[255 - LFN_count - LFN_blockcount + j] = LFNtext[j];
						LFN_count += LFN_blockcount;				// Increment long filename count
					} else {
						uint_fast32_t dotadded = 0;					// Track if we add a dot .. it starts zero
						if (LFN_count != 0) {						// Filename is LFN
							bool hasdot = false;					// Track if filname has dot in it, start false
							for (int j = 0; j < LFN_count; j++) {	// For each character in LFN
								LFN_Name[j] = LFN_Name[255 - LFN_count + j]; // Transfer it from back to front
								if (LFN_Name[j] == '.')				// If we see a dot 
									hasdot = true;					// Mark it as true
							}
							if ((!hasdot) &&						// If do not have a dot
							   (dirChange == false))				// This is not a directory change call
							{							
								dotadded = LFN_count;				// Set where we are going to add dot
								LFN_Name[LFN_count++] = '.';		// Add dot as req for wildcard name check
							}
							LFN_Name[LFN_count] = '\0';				// Make asciiz
							LFN_count = 0;							// Make sure we clear the internal LFN_count
						} else {
							int j = 0;								// Start on zero
							while (j < 8 && dir->name[j] != 0 && dir->name[j] != ' ') {
								LFN_Name[j] = dir->name[j];			// From SFN string to LFN
								j++;
							}
							if (dirChange == false) {
								LFN_Name[j++] = '.';				// Add a "." at end of name
								if (dir->attrib != FILE_ATTRIBUTE_DIRECTORY) {
									int k = 0;						// Start on zero
									while (k < 3 && dir->name[8 + k] != 0 && dir->name[8 + k] != ' ') {
										LFN_Name[j++] = dir->name[8 + k];// From SFN string to LFN
										k++;						// Next ext character
									}
								} else  dotadded = j - 1;
							}
							LFN_Name[j] = '\0';						// Make LFN asciiz
						}
						if ((dir->attrib != FILE_ATTRIBUTE_LABEL) &&// If not a label 
						   (WildcardMatch(searchPat, &LFN_Name[0])))// And LFN matches search pattern
						{
							if (dotadded) LFN_Name[dotadded] = '\0';// Make asciiz
							priv->bPos += sizeof(struct dir_Structure);// Move pointer down 	
							if (ErrorID) *ErrorID = FAT_RESULT_OK;	// Return result of FAT_RESULT_OK if requested
							return dir;								// Return directory pointer	
						}
						/*printf("Rejected match %s vs %s\n", searchPat, &LFN_Name[0]);*/
					}
				}
				priv->bPos += sizeof(struct dir_Structure);			// Buffer position moves forward
			}
			priv->sector++;											// Increment sector
			if (priv->sector < sdCard.partition.sectorPerCluster) { // Next sector valid do disk read
				priv->bPos = 0;										// Reset buffer position to top of buffer
				if (sdTransferBlocks(priv->firstSector + priv->sector, 
					1,	&priv->buffer[0], false) != SD_OK)
				{
					if (ErrorID) *ErrorID = FAT_READSECTOR_FAIL;	// Check for read failure
					return NULL;									// Return null pointer
				}
			}
		}
		priv->cluster = getSetNextCluster(priv->cluster, false, 0); // Fetch the next cluster
		priv->firstSector = getFirstSector(priv->cluster, 
			sdCard.partition.sectorPerCluster, 
			sdCard.partition.firstDataSector);						// Hold the first sector of this new cluster		
		priv->sector = 0;											// Zero the sector count of this new cluster 
		priv->bPos = 0;												// Reset buffer position to top of buffer
		if (sdTransferBlocks(priv->firstSector + priv->sector, 
			1,	&priv->buffer[0], false) != SD_OK) 
		{
			if (ErrorID) *ErrorID = FAT_READSECTOR_FAIL;			// Check for read failure
			return NULL;											// Return null pointer
		}
	}
	if (ErrorID) *ErrorID = FAT_INVALID_DATAPTR;					// If valid pointer return error id
	return NULL;													// Find FAT entry failed
}

/*-[CopyUnAlignedString]----------------------------------------------------}
. Copy a string byte by byte allowing for unaligned allocation
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
static bool CopyUnAlignedString (char* Dest, const char* Src) {
	if ((Src) && (Dest)) {
		while (*Src) *Dest++ = *Src++;								// Transfer source byte to dest and increment pointers
		*Dest = '\0';												// Make sure Dest is terminated
		return true;												// Return success
	}
	return false;													// One of the pointers was invalid
}


/*==========================================================================}
{						 PUBLIC FILE SEARCH ROUTINES						}
{==========================================================================*/

/*-[sdFindFirstFile]--------------------------------------------------------}
. This is an exact replica of Windows FindFirst function but restricted to
. this SD card.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdFindFirstFile (const char* lpFileName, FIND_DATA* lpFFData) 
{												
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	int i = 0;
	LFN_NAME tempName;												// We will cut and chop name so we need local copy
	if ((lpFileName == 0) || (lpFFData == 0)) return 0;				// One of the pointers is invalid so fail
	CopyUnAlignedString(&tempName[0], lpFileName);					// Copy the name string as we will chop and change it
	const char* searchStr = &tempName[0];							// Search string starts as lpFilename
	if (searchStr[0] == '\\') searchStr++;							// We sometimes write root directory with leadslash .. remove it
	while ((intSearchRecord[i].firstSector != 0) && i < MAX_SEARCH_RECORDS) i++;
	if (i != MAX_SEARCH_RECORDS) {									// Empty search record found
		intSearchRecord[i].cluster = sdCard.partition.rootCluster;	// We are going to start on FAT which starts at rootCluster
		intSearchRecord[i].firstSector = getFirstSector(
			sdCard.partition.rootCluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector of FAT32
		intSearchRecord[i].sector = 0;								// Zero sector count
		intSearchRecord[i].bPos = 0;								// Zero buffer position
		if (sdTransferBlocks(intSearchRecord[i].firstSector, 1,
			&intSearchRecord[i].buffer[0], false) == SD_OK) {
			char* p;
			do {
				p = strchr(searchStr, '\\');						// Check for sub-directory slash
				if (p) {											// Directory slash found	
					*p = '\0';										// Turn the slash into a terminate
					dir = LocateFATEntry(searchStr, &intSearchRecord[i],
						true, lpFFData->cFileName, &errID);			// Locate directory FAT entry
					if (dir == NULL) return 0;						// Did not find subdirectory
					intSearchRecord[i].cluster = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
					intSearchRecord[i].firstSector = getFirstSector(
						intSearchRecord[i].cluster,
						sdCard.partition.sectorPerCluster,
						sdCard.partition.firstDataSector);			// Hold the first sector
					intSearchRecord[i].sector = 0;					// Zero sector count
					intSearchRecord[i].bPos = 0;					// Buffer pos starts at top of buffer
					if (sdTransferBlocks(intSearchRecord[i].firstSector, 1,
						&intSearchRecord[i].buffer[0], false) != SD_OK)
						return 0;									// Some read error occured
					searchStr = p + 1;								// Search string now starts after directory
				}
			} while (p != NULL);
			dir = LocateFATEntry(searchStr, &intSearchRecord[i],
				false, lpFFData->cFileName, &errID);				// Locate first FAT entry
			if ((errID == FAT_RESULT_OK) && (dir)) {				// First FAT entry returned		
				int j = 0;
				while (searchStr[j] != 0) {
					intSearchRecord[i].searchPattern[j] = searchStr[j];// Hold the search pattern
					j++;
				}
				SetFindDataFromFATEntry(dir, lpFFData);				// Set all the find data fields from FAT entry
				return (i+1);										// Return internal search record index as handle
			}
		}
		intSearchRecord[i].firstSector = 0;							// Clear first sector which is used to mark in use
	}
	return 0;														// Return the handle and its error
}

/*-[sdFindNextFile]---------------------------------------------------------}
. Continues a file search from a previous call to the FindFirstFile.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdFindNextFile (HANDLE hFindFile, FIND_DATA* lpFFData)
{
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	if ((hFindFile > 0) && (hFindFile <= MAX_SEARCH_RECORDS) &&
		(intSearchRecord[hFindFile-1].firstSector != 0)) {			// File handle maps to an internal search record in use 
		dir = LocateFATEntry(&intSearchRecord[hFindFile-1].searchPattern[0],
			&intSearchRecord[hFindFile-1],
			false, lpFFData->cFileName, &errID);					// Locate first FAT entry
		if ((errID == FAT_RESULT_OK) && (dir)) {					// First FAT entry returned
			SetFindDataFromFATEntry(dir, lpFFData);					// Set all the find data fields from FAT entry
			return (hFindFile);										// Return internal serach record index as handle
		}
		intSearchRecord[hFindFile - 1].firstSector = 0;				// Clear first sector which is used to mark in use
	}
	return 0;														// Return the handle and its error
}

/*-[sdFindClose]------------------------------------------------------------}
. Closes and releases a file search handle opened by the FindFirstFile.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdFindClose (HANDLE hFindFile)
{
	if ((hFindFile > 0) && (hFindFile <= MAX_SEARCH_RECORDS) &&
		(intSearchRecord[hFindFile - 1].firstSector != 0)) {		// File handle maps to an internal search record in use 
		intSearchRecord[hFindFile - 1].firstSector = 0;				// Clear first sector which is used to mark in use
		return true;
	}
	return false;
}

/*==========================================================================}
{						 PUBLIC FILE IO ROUTINES							}
{==========================================================================*/


/*--------------------------------------------------------------------------}
{                        PRIVATE FILE IO DATA STRUCTURE		            }
{--------------------------------------------------------------------------*/
struct __attribute__((packed, aligned(4))) PRIV_FILE_IO_DATA {
	struct PRIV_SEARCH_DATA srec;									// Search record
	uint32_t fileStart;												// File start sector
	uint32_t filePos;												// Current file position
	uint32_t fileSize;												// Current file size
};

/*--------------------------------------------------------------------------}
{               INTERNAL FILE IO THAT ARE CURRENTLY RUNNING		            }
{--------------------------------------------------------------------------*/
#define MAX_FILE_IO_RECORDS 8										// Maximum number of file IO that can occur at same time
static struct PRIV_FILE_IO_DATA __attribute__((aligned(4))) intFileIORecord[MAX_FILE_IO_RECORDS] = { 0 };

/*-[sdCreateFile]-----------------------------------------------------------}
. Creates or opens a file or I/O device. The function returns a handle that 
. can be used to access the file for followup I/O operations. The file or 
. device will be openned with flags and attributes specified.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdCreateFile (const char* lpFileName,						// Filename or device to open
					 uint32_t	dwDesiredAccess,					// GENERIC_READ or GENERIC_WRITE
					 uint32_t	dwShareMode,						// Currently not supported (use 0)
					 uint32_t	lpSecurityAttributes,				// Currently not supported (use 0)
					 uint32_t	dwCreationDisposition,				// CREATE_ALWAYS, CREATE_NEW, OPEN_EXISTING, OPEN_ALWAYS
					 uint32_t	dwFlagsAndAttributes,				// Standard file attributes
					 HANDLE hTemplateFile)							// Currently not supported (use 0)
{
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	LFN_NAME openName;
	int i = 0;
	LFN_NAME tempName;												// We will cut and chop name so we need local copy
	if (lpFileName == 0) return 0;									// Name pointer is invalid so fail
	CopyUnAlignedString(&tempName[0], lpFileName);					// Make local copy of name as we will chop and change it
	const char* searchStr = &tempName[0];							// Search string starts as lpFilename
	if (searchStr[0] == '\\') searchStr++;							// We sometimes write root directory with lead slash .. remove it
	while ((intFileIORecord[i].srec.firstSector != 0) && i < MAX_FILE_IO_RECORDS) i++;
	if (i != MAX_FILE_IO_RECORDS) {									// Empty search record found
		intFileIORecord[i].srec.cluster = sdCard.partition.rootCluster;
		intFileIORecord[i].srec.firstSector = getFirstSector(
			sdCard.partition.rootCluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector
		intFileIORecord[i].srec.sector = 0;							// Zero sector count
		intFileIORecord[i].srec.bPos = 0;							// Zero buffer position
		if (sdTransferBlocks(intFileIORecord[i].srec.firstSector, 1,
			&intFileIORecord[i].srec.buffer[0], false) == SD_OK)	// Read a sector of data
		{
			char* p;
			do {
				p = strchr(searchStr, '\\');						// Check for sub-directory slash
				if (p) {											// Directory slash found	
					*p = '\0';										// Turn the slash into a terminate
					dir = LocateFATEntry(searchStr, &intFileIORecord[i].srec,
						true, openName, &errID);					// Locate directory FAT entry
					if (dir == NULL) return 0;						// Did not find subdirectory
					intFileIORecord[i].srec.cluster = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
					intFileIORecord[i].srec.firstSector = getFirstSector(
						intFileIORecord[i].srec.cluster,
						sdCard.partition.sectorPerCluster,
						sdCard.partition.firstDataSector);			// Hold the first sector
					intFileIORecord[i].srec.sector = 0;				// Zero sector count
					intFileIORecord[i].srec.bPos = 0;				// Buffer pos starts at top of buffer
					if (sdTransferBlocks(intFileIORecord[i].srec.firstSector, 1,
						&intFileIORecord[i].srec.buffer[0], false) != SD_OK)
						return 0;									// Some read error occured
					searchStr = p + 1;								// Search string now starts after directory
				}
			} while (p != NULL);
			dir = LocateFATEntry(searchStr, &intFileIORecord[i].srec,
				false, openName, &errID);							// Locate file first FAT entry
			if ((errID == FAT_RESULT_OK) && (dir)) {				// First FAT matching entry returned
				intFileIORecord[i].fileStart = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
				intFileIORecord[i].filePos = 0;						// Zero file position
				intFileIORecord[i].fileSize = dir->fileSize;		// Transfer file size
				intFileIORecord[i].srec.cluster = intFileIORecord[i].fileStart;
				intFileIORecord[i].srec.firstSector = getFirstSector(
					intFileIORecord[i].srec.cluster,
					sdCard.partition.sectorPerCluster,
					sdCard.partition.firstDataSector);				// Hold the first sector
				intFileIORecord[i].srec.sector = 0;					// Zero sector count
				intFileIORecord[i].srec.bPos = 0;					// Buffer pos starts at top of buffer		
				if (sdTransferBlocks(intFileIORecord[i].srec.firstSector,
					1, &intFileIORecord[i].srec.buffer[0], false) == SD_OK) 
				{
					return (i + 1);									// Return internal file io record index as handle
				}
			}
		}
	}
	return 0;														// Create file failed
}


/*-[sdReadFile]-------------------------------------------------------------}
. Reads data from the specified file or input/output (I/O) device. The file
. must have been opened with CreateFile and the handle is returned from that
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdReadFile (HANDLE hFile,										// Handle as returned from CreateFile
				 void* lpBuffer,									// Pointer to buffer read data will be placed
				 uint32_t nNumberOfBytesToRead,						// Number of bytes requested to read
				 uint32_t* lpNumberOfBytesRead,						// Provide a pointer to a value which updated to bytes actually placed in buffer
				 void* lpOverlapped)								// Currently not supported (use 0)
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 
		uint32_t bytesRead = 0;										// Zero bytes read
		uint32_t retErrId = FAT_RESULT_OK;							// Preset error id clear

		while ((retErrId == FAT_RESULT_OK) && (intFileIORecord[hFile - 1].srec.cluster < 0x0ffffff6)
			&& (intFileIORecord[hFile - 1].srec.cluster != 0)) {	// Check cluster valid and read successful	

			while (	(nNumberOfBytesToRead > bytesRead) &&			// Still more bytes to read
				(intFileIORecord[hFile - 1].filePos < intFileIORecord[hFile - 1].fileSize) && // Still inside fileSize
				(intFileIORecord[hFile - 1].srec.bPos <  512) )		// While not a buffer end
			{
				((uint8_t*)lpBuffer)[bytesRead] = intFileIORecord[hFile - 1].srec.buffer[intFileIORecord[hFile - 1].srec.bPos++]; // Transfer byte and increment pointer
				bytesRead++;										// Increment bytes read	
				intFileIORecord[hFile - 1].filePos++;				// Increment file position
			}
			if (nNumberOfBytesToRead == bytesRead) {				// Finished
				if (lpNumberOfBytesRead) *lpNumberOfBytesRead = bytesRead;
				return true;										// Read completed successfully
			}
			if (intFileIORecord[hFile - 1].filePos >= intFileIORecord[hFile - 1].fileSize) 
			{														// File end reached
				if (lpNumberOfBytesRead) *lpNumberOfBytesRead = bytesRead;
				return false;										// Read ended as file ran out of data
			}

			intFileIORecord[hFile - 1].srec.sector++;				// Increment sector
			if (intFileIORecord[hFile - 1].srec.sector < sdCard.partition.sectorPerCluster) // Next sector still in current cluster
			{
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return false;									// Return read failure
				}
			} else {												// Need to move to next cluster
				intFileIORecord[hFile - 1].srec.cluster = getSetNextCluster(
					intFileIORecord[hFile - 1].srec.cluster, false, 0);// Fetch the next cluster
				intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
					intFileIORecord[hFile - 1].srec.cluster,
					sdCard.partition.sectorPerCluster,
					sdCard.partition.firstDataSector);				// Hold the first sector of this new cluster
				intFileIORecord[hFile - 1].srec.sector = 0;			// Zero the sector count 
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return false;									// Return read failure
				}
			}
		}
	}
	return false;													// Return read failure
}
	
/*-[sdCloseHandle]----------------------------------------------------------}
. Closes file or device as per the handle that was given when it opened.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdCloseHandle (HANDLE hFile)
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 
		intFileIORecord[hFile - 1].srec.firstSector = 0;			// Clear first sector which is used to mark in use
		return true;												// Return handle successfully closed
	}
	return false;													// Return handle did not close
}

/*-[sdSetFilePointer]-------------------------------------------------------}
. The function stores the file pointer in two LONG values. To work with file
. pointers that are larger than a single LONG value, it is easier to use the
. SetFilePointerEx function.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
uint32_t sdSetFilePointer (HANDLE hFile,							// Handle as returned from CreateFile
						   uint32_t lDistanceToMove,				// low order 32-bit signed value of bytes to move the file pointer
						   uint32_t* lpDistanceToMoveHigh,			// A pointer to high order 32-bits of the signed 64-bit distance to move
						   uint32_t dwMoveMethod)					// FILE_BEGIN, FILE_CURRENT, FILE_END
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 

		switch (dwMoveMethod) {
			case FILE_CURRENT:										// Movement from current position
				lDistanceToMove += intFileIORecord[hFile - 1].filePos;// So simply add current position
				break;
			case FILE_END:											// Movement from end of file
				lDistanceToMove += intFileIORecord[hFile - 1].fileSize;// So simply add filesize
				break;
			default:												// Default will be file begin and do nothing
				break;
		}

		if (lDistanceToMove > intFileIORecord[hFile - 1].fileSize)	// You cant request a move larger than filesize
			return INVALID_SET_FILE_POINTER;						// Return set file position failure

		if (lDistanceToMove == intFileIORecord[hFile - 1].filePos)  // Request move is where we already are
			return (intFileIORecord[hFile - 1].filePos);			// So return position as if we did something

		intFileIORecord[hFile - 1].srec.cluster = intFileIORecord[hFile - 1].fileStart;
		intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
			intFileIORecord[hFile - 1].srec.cluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector
		intFileIORecord[hFile - 1].srec.sector = 0;					// Zero sector count
		intFileIORecord[hFile - 1].srec.bPos = 0;					// Buffer pos starts at top of buffer	
		intFileIORecord[hFile - 1].filePos = 0;						// Zero file position
		if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector,
			1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
			return INVALID_SET_FILE_POINTER;						// Return set file position failure
		}

		while (lDistanceToMove > 512) {								// Position not in current sector
			intFileIORecord[hFile - 1].srec.sector++;				// Increment sector
			if (intFileIORecord[hFile - 1].srec.sector < sdCard.partition.sectorPerCluster) // Next sector still in current cluster
			{
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return INVALID_SET_FILE_POINTER;				// Return set file position failure
				}
			} else {												// Need to move to next cluster
				intFileIORecord[hFile - 1].srec.cluster = getSetNextCluster(
					intFileIORecord[hFile - 1].srec.cluster, false, 0);  // Fetch the next cluster
				intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
					intFileIORecord[hFile - 1].srec.cluster, 
					sdCard.partition.sectorPerCluster, 
					sdCard.partition.firstDataSector);				// Hold the first sector of this new cluster
				intFileIORecord[hFile - 1].srec.sector = 0;			// Zero the sector count 
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return INVALID_SET_FILE_POINTER;				// Return set file position failure
				}
			}
			lDistanceToMove -= 512;									// We have moved 512 bytes
			intFileIORecord[hFile - 1].filePos += 512;				// Increment file position
		}
		intFileIORecord[hFile - 1].srec.bPos = lDistanceToMove;		// Buffer position is whatever is left below 512
		intFileIORecord[hFile - 1].filePos += lDistanceToMove;		// Add left over movement to file position
		if (lpDistanceToMoveHigh) *lpDistanceToMoveHigh = 0;		// We don't use this
		return (intFileIORecord[hFile - 1].filePos);				// Return the file position
	}
	return INVALID_SET_FILE_POINTER;								// Return set file position failure
}


/*-[sdGetFileSize]----------------------------------------------------------}
. Retrieves the size of the specified file, in bytes. As we don't support
. individual file size beyond 4GB lpFileSizeHigh is unused.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
uint32_t sdGetFileSize (HANDLE  hFile, uint32_t* lpFileSizeHigh) {
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) 		// File handle maps to an internal file io record in use
	{
		return(intFileIORecord[hFile - 1].fileSize);				// Return the file size
	}
	return 0;														// Function failed
}


/*-[sdInitCard]-------------------------------------------------------------}
. Attempts to initializes current SD Card and returns success/error status.
. This call should be done before any attempt to do anything with a SD card.
. RETURN: SD_OK indicates the current card successfully initialized.
.         !SD_OK if card initialize failed with code identifying error.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdInitCard (printhandler prn_basic, printhandler prn_error, bool mount)
{
	SDRESULT resp;

	// First ensure log error handler set
	LOG_ERROR = prn_error;											// Hold the provided print error handler

	// Reset the card.
	if( (resp = sdResetCard()) ) return resp;						// Reset SD card

	// Send SEND_IF_COND,0x000001AA (CMD8) voltage range 0x1 check pattern 0xAA
	// If voltage range and check pattern don't match, look for older card.
	resp = sdSendCommandA(IX_SEND_IF_COND,0x000001AA);
	if( resp == SD_OK )
	{
	// Card responded with voltage and check pattern.
	// Resolve voltage and check for high capacity card.
	if( (resp = sdAppSendOpCond(ACMD41_ARG_HC)) ) return sdDebugResponse(resp);

	// Check for high or standard capacity.
	if( sdCard.ocr.card_capacity )
		sdCard.type = SD_TYPE_2_HC;
	else
		sdCard.type = SD_TYPE_2_SC;
	}
	else if( resp == SD_BUSY ) return resp;
	// No response to SEND_IF_COND, treat as an old card.
	else
    {
		// If there appears to be a command in progress, reset the card.
		if( (EMMC_STATUS->CMD_INHIBIT) &&
			(resp = sdResetCard()) )
			return resp;

		// wait(50);
		// Resolve voltage.
		if( (resp = sdAppSendOpCond(ACMD41_ARG_SC)) ) return sdDebugResponse(resp);

		sdCard.type = SD_TYPE_1;
    }

	// Send ALL_SEND_CID (CMD2)
	if( (resp = sdSendCommand(IX_ALL_SEND_CID)) ) return sdDebugResponse(resp);

	// Send SEND_REL_ADDR (CMD3)
	// TODO: In theory, loop back to SEND_IF_COND to find additional cards.
	if( (resp = sdSendCommand(IX_SEND_REL_ADDR)) ) return sdDebugResponse(resp);

	// From now on the card should be in standby state.
	// Actually cards seem to respond in identify state at this point.
	// Check this with a SEND_STATUS (CMD13)
	//if( (resp = sdSendCommand(IX_SEND_STATUS)) ) return sdDebugResponse(resp);
	//printf("Card current state: %08x %s\n",sdCard.status,STATUS_NAME[sdCard.cardState]);

	// Send SEND_CSD (CMD9) and parse the result.
	if( (resp = sdSendCommand(IX_SEND_CSD)) ) return sdDebugResponse(resp);

	// At this point, set the clock to full speed.
	if( (resp = sdSetClock(FREQ_NORMAL)) ) return sdDebugResponse(resp);

	// Send CARD_SELECT  (CMD7)
	// TODO: Check card_is_locked status in the R1 response from CMD7 [bit 25], if so, use CMD42 to unlock
	// CMD42 structure [4.3.7] same as a single block write; data block includes
	// PWD setting mode, PWD len, PWD data.
	if( (resp = sdSendCommand(IX_CARD_SELECT)) ) return sdDebugResponse(resp);

	// Get the SCR as well.
	// Need to do this before sending ACMD6 so that allowed bus widths are known.
	if( (resp = sdReadSCR()) ) return sdDebugResponse(resp);

	// Send APP_SET_BUS_WIDTH (ACMD6)
	// If supported, set 4 bit bus width and update the CONTROL0 register.
	if (sdCard.scr.BUS_WIDTH == BUS_WIDTH_4)
	{
		if( (resp = sdSendCommandA(IX_SET_BUS_WIDTH, sdCard.rca | 2)) ) 
			return sdDebugResponse(resp);
		EMMC_CONTROL0->HCTL_DWIDTH = 1;
		LOG_DEBUG("EMMC: Bus width set to 4\n");
	}

	// Send SET_BLOCKLEN (CMD16)
	if( (resp = sdSendCommandA(IX_SET_BLOCKLEN,512)) ) return sdDebugResponse(resp);

	// Print out the CID having got this far.
	unsigned int serial = sdCard.cid.SerialNumHi;
	serial <<= 16;
	serial |= sdCard.cid.SerialNumLo;
	if (prn_basic) prn_basic("EMMC: SD Card %s %dMb mfr %d '%c%c:%c%c%c%c%c' r%d.%d %d/%d, #%08x RCA %04x\n",
		SD_TYPE_NAME[sdCard.type], (int)(sdCard.CardCapacity >> 20),
		sdCard.cid.MID, sdCard.cid.OID_Hi, sdCard.cid.OID_Lo,
		sdCard.cid.ProdName1, sdCard.cid.ProdName2, sdCard.cid.ProdName3, sdCard.cid.ProdName4, sdCard.cid.ProdName5,
		sdCard.cid.ProdRevHi, sdCard.cid.ProdRevLo, sdCard.cid.ManufactureMonth, 2000+sdCard.cid.ManufactureYear, serial,
		sdCard.rca >> 16);

	if (mount) if (!LoadDrivePartition(prn_basic)) return SD_MOUNT_FAIL;
	return SD_OK;
}

/*-[sdCardCSD]--------------------------------------------------------------}
. Returns the pointer to the CSD structure for the current SD Card.
. RETURN: Valid CSD pointer if the current card successfully initialized
.         NULL if current card initialize not called or failed.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
struct CSD* sdCardCSD (void) {
	if (sdCard.type != SD_TYPE_UNKNOWN) return (&sdCard.csd);		// Card success so return structure pointer
		else return NULL;											// Return fail result of null
}

A sd/hal.h => sd/hal.h +145 -0
@@ 0,0 1,145 @@
#ifndef __INC_HAL_H
#define __INC_HAL_H

#include "stdint.h"

/* ------------------------- loader.s -*/
extern void PUT32(uint32_t, uint32_t);
extern uint32_t GET32(uint32_t);
extern void dummy(uint32_t);

/* ------------------------- GPIO -----*/
#define GPFSEL0         0x20200000
#define GPPUD           0x20200094
#define GPPUDCLK0       0x20200098
#define GPPUDCLK1       0x2020009C
#define GPIO_FUNC_IN    0
#define GPIO_FUNC_OUT   1
#define GPIO_FUNC_ALT0  4
#define GPIO_FUNC_ALT1  5
#define GPIO_FUNC_ALT2  6
#define GPIO_FUNC_ALT3  7
#define GPIO_FUNC_ALT4  3
#define GPIO_FUNC_ALT5  2
#define GPIO_PULL_OFF   0
#define GPIO_PULL_DOWN  1
#define GPIO_PULL UP    2

static inline void HAL_GPIO_FSEL(uint8_t pin, uint8_t function) {
    uint32_t gpfsel = GPFSEL0 + ((pin / 10) * 4);
    uint32_t ra = GET32(gpfsel);
    ra &= ~(7 << ((pin % 10) * 3));
    ra |= function << ((pin % 10) * 3);
    PUT32(gpfsel, ra);
}
static inline void HAL_GPIO_SET_PULL(uint8_t pin, uint8_t pull) {
    uint8_t ra;
    uint32_t gppudclk = (pin < 32) ? GPPUDCLK0 : GPPUDCLK1;
    PUT32(GPPUD,0);
    for(ra=0;ra<150;ra++) dummy(ra);
    PUT32(gppudclk, (pull << (pin % 32)));
    for(ra=0;ra<150;ra++) dummy(ra);
    PUT32(GPPUDCLK0,0);
}

#define AUX_ENABLES     0x20215004
#define AUX_MU_IO_REG   0x20215040
#define AUX_MU_IER_REG  0x20215044
#define AUX_MU_IIR_REG  0x20215048
#define AUX_MU_LCR_REG  0x2021504C
#define AUX_MU_MCR_REG  0x20215050
#define AUX_MU_LSR_REG  0x20215054
#define AUX_MU_MSR_REG  0x20215058
#define AUX_MU_CNTL_REG 0x20215060
#define AUX_MU_BAUD_REG 0x20215068

/* ------------------------- UART -----*/
static inline void HAL_UART_PUTC(uint8_t c) {
    while(1)
        if(GET32(AUX_MU_LSR_REG)&0x20) break;
    PUT32(AUX_MU_IO_REG, c);
}
static inline uint32_t HAL_UART_GETC() {
    while(1)
        if(GET32(AUX_MU_LSR_REG)&0x01) break;
    return GET32(AUX_MU_IO_REG);
}

static inline void uart_puts(const char* s) {
/*
 * This helper function is not directly related to hardware abstraction,
 * hence the HAL naming is not used.
 */
    for (; *s; s++)
        HAL_UART_PUTC((uint8_t)*s);
    HAL_UART_PUTC('\r');
    HAL_UART_PUTC('\n');
}

static inline void HAL_UART_INIT() {
    //GPIO14  TXD0 and TXD1
    //GPIO15  RXD0 and RXD1
    //alt function 5 for uart1
    //alt function 0 for uart0
    //((250,000,000/115200)/8)-1 = 270

    PUT32(AUX_ENABLES,1);
    PUT32(AUX_MU_IER_REG,0); // Disable RxTx interrupts
    PUT32(AUX_MU_CNTL_REG,0); // Disable RxTx (and various CTS RTS config, probably irrelevant)
    PUT32(AUX_MU_LCR_REG,3); // 8-bits mode (7-bits is fine but has to reconfig com)
    PUT32(AUX_MU_MCR_REG,0); // modem RTS high
    PUT32(AUX_MU_IIR_REG,0xC6); // Clear RxTx FIFO
    PUT32(AUX_MU_BAUD_REG,270); // Set baud rate


    // Set GPIO14 and GPIO15 to use ALT5 (mini UART)
    HAL_GPIO_FSEL(14, GPIO_FUNC_ALT5);
    HAL_GPIO_FSEL(15, GPIO_FUNC_ALT5);

    // Procedure to disable pulls on GPIO14 and GPIO15 (p.101)
    HAL_GPIO_SET_PULL(14, GPIO_PULL_DOWN);
    HAL_GPIO_SET_PULL(15, GPIO_PULL_DOWN);

    PUT32(AUX_MU_CNTL_REG, 3); // Enable RxTx
}

/* ------------------------- TIMER -----*/
#define ARM_TIMER_LOD   0x2000B400
#define ARM_TIMER_VAL   0x2000B404
#define ARM_TIMER_CTL   0x2000B408
#define ARM_TIMER_CLI   0x2000B40C
#define ARM_TIMER_RIS   0x2000B410
#define ARM_TIMER_MIS   0x2000B414
#define ARM_TIMER_RLD   0x2000B418
#define ARM_TIMER_DIV   0x2000B41C
#define ARM_TIMER_CNT   0x2000B420

static inline void HAL_TIMER_INIT(uint32_t timeout) {
    PUT32(ARM_TIMER_CTL, 0x003E0000); // clear free-standing timer
    PUT32(ARM_TIMER_LOD, timeout - 1); // countdown to T-1
    PUT32(ARM_TIMER_RLD, timeout - 1); // reload when runover
    PUT32(ARM_TIMER_DIV, 0x000000F9); // scale down the timer to 1Mhz
    PUT32(ARM_TIMER_CLI, 0); // clear INT
    PUT32(ARM_TIMER_CTL, 0x003E0082); // clear, enable 16bit timer
}

static inline void HAL_TIMER_WAIT() {
    // wait untile the timer resets after the given timeout
    while (1) if (GET32(ARM_TIMER_RIS)) break;
    PUT32(ARM_TIMER_CLI, 0);


}

#endif  /* __INC_HAL_H */


/*  ----------------------------------------- LICENSE FOR UART AND TIMER */
// Copyright (c) 2012 David Welch dwelch@dwelch.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

A sd/loader.s => sd/loader.s +33 -0
@@ 0,0 1,33 @@

.globl _start
_start:
    mov sp,#0x8000
    bl notmain
hang: b hang

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.globl dummy
dummy:
    bx lr


;@-------------------------------------------------------------------------
;@
;@ Copyright (c) 2012 David Welch dwelch@dwelch.com
;@
;@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
;@
;@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
;@
;@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
;@
;@-------------------------------------------------------------------------

A sd/memmap => sd/memmap +12 -0
@@ 0,0 1,12 @@

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}


A sd/sd.c => sd/sd.c +247 -0
@@ 0,0 1,247 @@
#include "hal.h"

/* ------------------------- EMMC -----*/
#define EMMC_BASE       0x20300000
#define EMMC_ARG1       EMMC_BASE + 0x8
#define EMMC_CMDTM      EMMC_BASE + 0xc
#define EMMC_RESP0      EMMC_BASE + 0x10
#define EMMC_RESP1      EMMC_BASE + 0x14
#define EMMC_RESP2      EMMC_BASE + 0x18
#define EMMC_RESP3      EMMC_BASE + 0x1C
#define EMMC_STATUS     EMMC_BASE + 0x24
#define EMMC_CONTROL0   EMMC_BASE + 0x28
#define EMMC_CONTROL1   EMMC_BASE + 0x2C
#define EMMC_INTERRUPT  EMMC_BASE + 0x30
#define EMMC_IRPT_MASK  EMMC_BASE + 0x34
#define EMMC_IRPT_EN    EMMC_BASE + 0x38

#define FREQ_SETUP  400000  // 400 Khz
#define FREQ_NORMAL 25000000  // 25 Mhz

#define CMD_NO_RESP         0
#define CMD_136BIT_RESP     1
#define CMD_48BIT_RESP      2
#define CMD_BUSY48BIT_RESP  3

#define INT_AUTO_ERROR   0x01000000									// ACMD_ERR bit in register
#define INT_DATA_END_ERR 0x00400000									// DEND_ERR bit in register
#define INT_DATA_CRC_ERR 0x00200000									// DCRC_ERR bit in register
#define INT_DATA_TIMEOUT 0x00100000									// DTO_ERR bit in register
#define INT_INDEX_ERROR  0x00080000									// CBAD_ERR bit in register
#define INT_END_ERROR    0x00040000									// CEND_ERR bit in register
#define INT_CRC_ERROR    0x00020000									// CCRC_ERR bit in register
#define INT_CMD_TIMEOUT  0x00010000									// CTO_ERR bit in register
#define INT_ERR          0x00008000									// ERR bit in register
#define INT_ENDBOOT      0x00004000									// ENDBOOT bit in register
#define INT_BOOTACK      0x00002000									// BOOTACK bit in register
#define INT_RETUNE       0x00001000									// RETUNE bit in register
#define INT_CARD         0x00000100									// CARD bit in register
#define INT_READ_RDY     0x00000020									// READ_RDY bit in register
#define INT_WRITE_RDY    0x00000010									// WRITE_RDY bit in register
#define INT_BLOCK_GAP    0x00000004									// BLOCK_GAP bit in register
#define INT_DATA_DONE    0x00000002									// DATA_DONE bit in register
#define INT_CMD_DONE     0x00000001									// CMD_DONE bit in register
#define INT_ERROR_MASK   (INT_CRC_ERROR|INT_END_ERROR|INT_INDEX_ERROR| \
                          INT_DATA_TIMEOUT|INT_DATA_CRC_ERR|INT_DATA_END_ERR| \
                          INT_ERR|INT_AUTO_ERROR)
#define INT_ALL_MASK     (INT_CMD_DONE|INT_DATA_DONE|INT_READ_RDY|INT_WRITE_RDY|INT_ERROR_MASK)


/* ------------------------- ACMD41 ---*/
#define ACMD41_HCS           0x40000000
#define ACMD41_SDXC_POWER    0x10000000
#define ACMD41_S18R          0x04000000
#define ACMD41_VOLTAGE       0x00ff8000
    // PI DOES NOT SUPPORT VOLTAGE SWITCH
#define ACMD41_ARG_HC        (ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE)
#define ACMD41_ARG_SC        (ACMD41_VOLTAGE)


#define PANIC() while(1);

void delay_ms(uint32_t ms) {
    /* requires timer to be inited to 10 microsecs timeout */
    uint32_t counter = 0;
    HAL_TIMER_WAIT(); // wait until reset
    while (counter < ms) {
        HAL_TIMER_WAIT();
        counter += 10;
    }
}


void sd_set_clock(uint32_t freq) {
    uart_puts("WAITING FOR CLEARANCE");
    uint32_t ra = GET32(EMMC_STATUS);
    while (ra & (1<<0) || ra & (1<<1)) ra = GET32(EMMC_STATUS);

    uart_puts("DISABLING CLOCK");
    ra = GET32(EMMC_CONTROL1);
    ra &= ~(1<<2); // clear CLK_EN
    PUT32(EMMC_CONTROL1, ra);
    delay_ms(10);

    uart_puts("SETTING CLOCK SPEED TO SETUP");
	/* Calculate the divisor for new clock setting */
    // Pi SD frequency is always 41.66667Mhz on baremetal
	// Optimized from `(41666667 + freq - 1) / freq` to avoid 64bit division(?)
    uint32_t cdiv = (freq == FREQ_SETUP) ? 105 : 4;
	uint32_t divlo = (cdiv & 0xff) << 8;		// Create divisor low bits value
    uint32_t divhi = ((cdiv & 0x300) >> 2);     // Create divisor high bits value
    ra = GET32(EMMC_CONTROL1);
	PUT32(EMMC_CONTROL1, (ra & 0xffff001f) | divlo | divhi);
    delay_ms(10);

    uart_puts("ENABLING CLOCK");
    PUT32(EMMC_CONTROL1, GET32(EMMC_CONTROL1) | (1<<2));                         // Enable the clock
	while (!(GET32(EMMC_CONTROL1) & (1<<1))) continue;  // Wait for stable clock
    uart_puts("CLOCK SET!");
}

void sd_wait_for_interrupt(uint32_t mask) {
    uint32_t tMask = mask | INT_ERROR_MASK;
    uint32_t ra = GET32(EMMC_INTERRUPT);
    while (!(ra & tMask)) ra = GET32(EMMC_INTERRUPT);

    if ((ra & INT_CMD_TIMEOUT) | (ra & INT_DATA_TIMEOUT)) {
        uart_puts("timeout!"); PANIC();
    }
    if (ra & INT_ERROR_MASK) {
        uart_puts("error!"); PANIC();
    }
    PUT32(EMMC_INTERRUPT, mask); // clear interrupt
}

void sd_send_cmd(uint8_t cmd_index, uint8_t rsp_type, uint32_t arg) {
    // Does not process response!
    uart_puts("WAITING FOR CLEARANCE");
    while (GET32(EMMC_STATUS) & (1<<0));

    PUT32(EMMC_INTERRUPT, GET32(EMMC_INTERRUPT)); // Clear interrupt
    PUT32(EMMC_ARG1, arg);
    PUT32(EMMC_CMDTM, (cmd_index << 24) | (rsp_type <<16));

    sd_wait_for_interrupt(INT_CMD_DONE);
}

void sd_reset_card() {
    uart_puts("RESETTING SD");
    PUT32(EMMC_CONTROL0, 0);
    PUT32(EMMC_CONTROL1, 0);


    PUT32(EMMC_CONTROL1, (1<<24)); // Reset complete host circuit
    while(GET32(EMMC_CONTROL1) & (1<<24)); // Wait until SRST_HC is cleared
    uart_puts("RESET OK!");

    PUT32(EMMC_CONTROL1, (0xE <<16)); // DATA_TOUNIT
    PUT32(EMMC_CONTROL1, 1); // CLK_INTLEN
    HAL_TIMER_WAIT(); HAL_TIMER_WAIT();

    sd_set_clock(FREQ_SETUP);

	/* Enable interrupts for command completion values */
	PUT32(EMMC_IRPT_EN, 0xFFFFFFFF);
	PUT32(EMMC_IRPT_MASK, 0xFFFFFFFF);

    sd_send_cmd(0x00, CMD_NO_RESP, 0);
    uart_puts("RESET COMPLETE");
}

void uart_itoa(uint32_t i) {
/*
 * Extremely inefficient itoa implementation!
 */
    uint8_t leaded = 0;
    // max digit is 10
    for (int j = 0; j < 10; j++) {
        uint32_t _i = i;
        for ( int k = 10; k > j+1; k--) _i /= 10;

        _i = _i % 10;
        if (_i == 0 && !leaded) continue;
        HAL_UART_PUTC(_i + 48);
        leaded = 1;
    }
    if (!leaded) HAL_UART_PUTC('0');
}

void sd_init() {
    uart_puts("INITING SD");

	// Send SEND_IF_COND,0x000001AA (CMD8) voltage range 0x1 check pattern 0xAA
	// If voltage range and check pattern don't match, look for older card.
    sd_send_cmd(0x08, CMD_48BIT_RESP, 0x000001AA);
    uint32_t arg = (GET32(EMMC_RESP0) == 0x000001AA) ? ACMD41_ARG_HC : ACMD41_ARG_SC; // Type 2 : Type 1
    if (arg == ACMD41_ARG_SC) {
        uart_puts("NOT SUPPORTING SC CARD DUE TO PARTIAL IMPLEMENTATION"); PANIC(); }

    uint32_t ra = 0;
    while (!(ra & ((uint32_t) 1<<31))) {
        delay_ms(400000);
        uart_puts("POWERING UP...");
        sd_send_cmd(0x37, CMD_NO_RESP, 0); // Prepare for APP CMD
        delay_ms(100);
        sd_send_cmd(0x29, CMD_48BIT_RESP, arg);
        delay_ms(1000);
        ra = GET32(EMMC_RESP0); // SD OCR register
    }
    uart_puts("POWERED UP");
    if (!(ra & (1<<30))) {//check the capacity bit if HC
        uart_puts("NOT SUPPORTING SC CARD DUE TO PARTIAL IMPLEMENTATION"); PANIC(); }

    sd_send_cmd(0x02, CMD_136BIT_RESP, 0); // Get the CID
    //GET32(EMMC_RESP0); GET32(EMMC_RESP1); GET32(EMMC_RESP2); GET32(EMMC_RESP3);

    sd_send_cmd(0x03, CMD_48BIT_RESP, 0); // Get RCA
    uint32_t rca = GET32(EMMC_RESP0) &  0xffff0000;
    uart_itoa(rca);
    uart_puts(" RCA");

    sd_send_cmd(0x09, CMD_136BIT_RESP, rca); // Get CSD, note that data seems to be in weird order
    uint8_t buf[16] = { 0 };
	__attribute__((aligned(4))) uint32_t* p;
    p = (uint32_t*) &buf[12];
    *p = GET32(EMMC_RESP0);
    p = (uint32_t*) &buf[8];
    *p = GET32(EMMC_RESP1);
    p = (uint32_t*) &buf[4];
    *p = GET32(EMMC_RESP2);
    p = (uint32_t*) &buf[0];
    *p = GET32(EMMC_RESP3);
    // Order (from 127..0): 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12

    uint8_t csd_ver = ((buf[2] & 0xC0) >> 6) + 1;
    uart_itoa(csd_ver); uart_puts(" CSD VER");
    if (csd_ver != 2) { uart_puts("ERR CSD VER NOT SUPPORTED"); PANIC(); }

    uint32_t capacity = (uint32_t)(buf[11] & 0x3F) << 16; // @69-64
    capacity += (uint32_t)buf[10] << 8;                   // @63-56
    capacity += (uint32_t)buf[9];                         // @55-48
    capacity *= 512;
    uart_itoa(capacity >> 10);
    uart_puts("MB ");

    sd_set_clock(FREQ_NORMAL);

    sd_send_cmd(0x07, CMD_BUSY48BIT_RESP, rca);

    // attempt to use 1bit bus width (default)
    // Assume HC+ card, blocklen is 512
    uart_puts("INIT COMPLETE");

}

int notmain()
{
    HAL_UART_INIT();
    HAL_TIMER_INIT(10); // 10 microsecs
    uart_puts("HELO SD");

    sd_reset_card();
    sd_init();


    return(0);
}