From a6163888f3c56123b1db313743c6147ba498732c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 8 Aug 2014 14:42:07 +0300 Subject: Add third_party libs --- third_party/fatfs/port/dual-disk-driver.c | 264 ++++++++++ third_party/fatfs/port/fat_usbmsc.c | 151 ++++++ third_party/fatfs/port/mmc-dk-tm4c123g.c | 725 ++++++++++++++++++++++++++++ third_party/fatfs/port/mmc-dk-tm4c129x.c | 740 +++++++++++++++++++++++++++++ third_party/fatfs/port/mmc-ek-lm4f232.c | 725 ++++++++++++++++++++++++++++ third_party/fatfs/port/mmc-ek-tm4c1294xl.c | 740 +++++++++++++++++++++++++++++ third_party/fatfs/port/sample-mmc.c | 559 ++++++++++++++++++++++ 7 files changed, 3904 insertions(+) create mode 100644 third_party/fatfs/port/dual-disk-driver.c create mode 100644 third_party/fatfs/port/fat_usbmsc.c create mode 100644 third_party/fatfs/port/mmc-dk-tm4c123g.c create mode 100644 third_party/fatfs/port/mmc-dk-tm4c129x.c create mode 100644 third_party/fatfs/port/mmc-ek-lm4f232.c create mode 100644 third_party/fatfs/port/mmc-ek-tm4c1294xl.c create mode 100644 third_party/fatfs/port/sample-mmc.c (limited to 'third_party/fatfs/port') diff --git a/third_party/fatfs/port/dual-disk-driver.c b/third_party/fatfs/port/dual-disk-driver.c new file mode 100644 index 0000000..68e0fe8 --- /dev/null +++ b/third_party/fatfs/port/dual-disk-driver.c @@ -0,0 +1,264 @@ +/*-----------------------------------------------------------------------*/ +/* Dual-disk wrapper allowing operating of two different drives */ +/* underneath the FatFs layer without modification of the existing */ +/* single-unit drivers for those drives. */ +/* */ +/* This file was modified from a sample driver available from the FatFs */ +/* web site. */ +/*-----------------------------------------------------------------------*/ + +#include +#include +#include "inc/hw_types.h" +#include "fatfs/src/diskio.h" +#include "fatfs/src/ff.h" + +/*-----------------------------------------------------------------------*/ +/* Configuration */ +/*-----------------------------------------------------------------------*/ +/* This wrapper allows two independent, low level, single drive FatFs */ +/* drivers to be used simultaneously to provide a FatFs implementation */ +/* with two physical drives. Configuration is set using two #define */ +/* labels, one each from the following two groups, to indicate which */ +/* drivers to use for each of the logical drives supported. */ +/* */ +/* DISK0_EK_LM4F232H5QD - Logical disk 0 is an EK-LM4F232H5QD SD Card. */ +/* DISK0_USB_MSC - Logical disk 0 is a USB Mass Storage Class */ +/* device. */ +/* */ +/* DISK1_EK_LM4F232H5QD - Logical disk 1 is an EK-LM4F232H5QD SD Card. */ +/* DISK1_USB_MSC - Logical disk 1 is a USB Mass Storage Class */ +/* device. */ +/* */ +/* The same driver cannot be used to support both logical drives. */ +/* */ +/* Note that the USB MSC driver does not support a timer function so we */ +/* need to undef DRIVEn_TIMERPROC whenever this driver is configured. */ +/*-----------------------------------------------------------------------*/ +#if defined(DISK0_EK_LM4F232H5QD) +#define DRIVE0_DRIVER "mmc-ek-lm4f232.c" +#define DRIVE0_TIMERPROC disk0_timerproc +#elif defined(DISK0_DK_TM4C123G) +#define DRIVE0_DRIVER "mmc-dk-tm4c123g.c" +#define DRIVE0_TIMERPROC disk0_timerproc +#elif defined(DISK0_DK_TM4C129XNCZAD) +#define DRIVE0_DRIVER "mmc-dk-tm4c129x.c" +#define DRIVE0_TIMERPROC disk0_timerproc +#elif defined(DISK0_USB_MSC) +#define DRIVE0_DRIVER "fat_usbmsc.c" +#undef DRIVE0_TIMERPROC +#else +#error "Unrecognized/undefined driver for DISK0!" +#endif + +#if defined(DISK1_EK_LM4F232H5QD) +#define DRIVE1_DRIVER "mmc-ek-lm4f232.c" +#define DRIVE1_TIMERPROC disk1_timerproc +#elif defined(DISK1_DK_TM4C123G) +#define DRIVE1_DRIVER "mmc-dk-tm4c123g.c" +#define DRIVE1_TIMERPROC disk1_timerproc +#elif defined(DISK1_DK_TM4C129XNCZAD) +#define DRIVE1_DRIVER "mmc-dk-tm4c129x.c" +#define DRIVE1_TIMERPROC disk1_timerproc +#elif defined(DISK1_USB_MSC) +#define DRIVE1_DRIVER "fat_usbmsc.c" +#undef DRIVE0_TIMERPROC +#else +#error "Unrecognized/undefined driver for DISK1!" +#endif + +/*-----------------------------------------------------------------------*/ +/* The first low level driver */ +/*-----------------------------------------------------------------------*/ +#define disk_initialize disk0_initialize +#define disk_status disk0_status +#define disk_read disk0_read +#define disk_write disk0_write +#define disk_ioctl disk0_ioctl +#define disk_timerproc disk0_timerproc +#define get_fattime disk0_get_fattime + +#include DRIVE0_DRIVER + +#undef disk_initialize +#undef disk_status +#undef disk_read +#undef disk_write +#undef disk_ioctl +#undef disk_timerproc +#undef get_fattime + +/*-----------------------------------------------------------------------*/ +/* The second low level driver */ +/*-----------------------------------------------------------------------*/ +#define disk_initialize disk1_initialize +#define disk_status disk1_status +#define disk_read disk1_read +#define disk_write disk1_write +#define disk_ioctl disk1_ioctl +#define disk_timerproc disk1_timerproc +#define get_fattime disk1_get_fattime + +#include DRIVE1_DRIVER + +#undef disk_initialize +#undef disk_status +#undef disk_read +#undef disk_write +#undef disk_ioctl +#undef disk_timerproc +#undef get_fattime + +/*-----------------------------------------------------------------------*/ +/* Timer tick function. */ +/*-----------------------------------------------------------------------*/ +/* When using an SD card driver, this function must be called every 10mS */ +/* by the application code. Note that this is not part of the device */ +/* driver interface that is called directly by FatFs. */ +/* */ +void disk_timerproc (void) +{ +#ifdef DRIVE0_TIMERPROC + disk0_timerproc(); +#endif + +#ifdef DRIVE1_TIMERPROC + disk1_timerproc(); +#endif +} + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ +DSTATUS +disk_initialize( + BYTE bValue) /* Physical drive number */ +{ + /* Depending upon the physical drive, call the relevant low level */ + /* driver. Note that we call each with physical drive number 0 since the */ + /* low level drivers typically expect this (they only support a single */ + /* drive). */ + if(bValue == 0) + { + return(disk0_initialize(0)); + } + else + { + return(disk1_initialize(0)); + } +} + +/*-----------------------------------------------------------------------*/ +/* Returns the current status of a drive */ +/*-----------------------------------------------------------------------*/ +DSTATUS disk_status ( + BYTE drv) /* Physical drive number */ +{ + /* Depending upon the physical drive, call the relevant low level */ + /* driver. Note that we call each with physical drive number 0 since the */ + /* low level drivers typically expect this (they only support a single */ + /* drive). */ + if(drv == 0) + { + return(disk0_status(0)); + } + else + { + return(disk1_status(0)); + } + +} + +/*-----------------------------------------------------------------------*/ +/* This function reads sector(s) from the disk drive */ +/*-----------------------------------------------------------------------*/ +DRESULT disk_read ( + BYTE drv, /* Physical drive number */ + BYTE* buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Sector number */ + BYTE count) /* Sector count (1..255) */ +{ + /* Depending upon the physical drive, call the relevant low level */ + /* driver. Note that we call each with physical drive number 0 since the */ + /* low level drivers typically expect this (they only support a single */ + /* drive). */ + if(drv == 0) + { + return(disk0_read(0, buff, sector, count)); + } + else + { + return(disk1_read(0, buff, sector, count)); + } +} + + + +/*-----------------------------------------------------------------------*/ +/* This function writes sector(s) to the disk drive */ +/*-----------------------------------------------------------------------*/ +#if _READONLY == 0 +DRESULT disk_write ( + BYTE ucDrive, /* Physical drive number (0) */ + const BYTE* buff, /* Pointer to the data to be written */ + DWORD sector, /* Sector number */ + BYTE count) /* Sector count (1..255) */ +{ + /* Depending upon the physical drive, call the relevant low level */ + /* driver. Note that we call each with physical drive number 0 since the */ + /* low level drivers typically expect this (they only support a single */ + /* drive). */ + if(ucDrive == 0) + { + return(disk0_write(0, buff, sector, count)); + } + else + { + return(disk1_write(0, buff, sector, count)); + } +} +#endif /* _READONLY */ + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive number (0) */ + BYTE ctrl, /* Control code */ + void *buff) /* Buffer to send/receive control data */ +{ + /* Depending upon the physical drive, call the relevant low level */ + /* driver. Note that we call each with physical drive number 0 since the */ + /* low level drivers typically expect this (they only support a single */ + /* drive). */ + if(drv == 0) + { + return(disk0_ioctl(0, ctrl, buff)); + } + else + { + return(disk1_ioctl(0, ctrl, buff)); + } +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ +/* */ +/* Note that each driver implements the same function but, */ +/* since the function doesn't take any parameter, we can't */ +/* decide automatically which driver's get_fattime() to */ +/* invoke. Instead, we control this with a #define */ +/* which defaults to calling the first driver. */ +/* */ +DWORD get_fattime (void) +{ +#ifndef DRIVE1_TIME_MASTER + return(disk0_get_fattime()); +#else + return(disk1_get_fattime()); +#endif +} diff --git a/third_party/fatfs/port/fat_usbmsc.c b/third_party/fatfs/port/fat_usbmsc.c new file mode 100644 index 0000000..a49e439 --- /dev/null +++ b/third_party/fatfs/port/fat_usbmsc.c @@ -0,0 +1,151 @@ +/*-----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* TivaWare USB MSC module */ +/*-----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ + +/* + * This file was modified from a sample available from the FatFs + * web site. It was modified to work with the TivaWare USB Library. + */ +#include +#include +#include "inc/hw_types.h" +#include "driverlib/sysctl.h" +#include "usblib/usblib.h" +#include "usblib/usbmsc.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhmsc.h" +#include "fatfs/src/diskio.h" +#include "fatfs/src/ff.h" + +extern tUSBHMSCInstance *g_psMSCInstance; + +static volatile +DSTATUS USBStat = STA_NOINIT; /* Disk status */ + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS +disk_initialize( + BYTE bValue) /* Physical drive number (0) */ +{ + /* Set the not initialized flag again. If all goes well and the disk is */ + /* present, this will be cleared at the end of the function. */ + USBStat |= STA_NOINIT; + + /* Find out if drive is ready yet. */ + if (USBHMSCDriveReady(g_psMSCInstance)) return(FR_NOT_READY); + + /* Clear the not init flag. */ + USBStat &= ~STA_NOINIT; + + return 0; +} + + + +/*-----------------------------------------------------------------------*/ +/* Returns the current status of a drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv) /* Physical drive number (0) */ +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return USBStat; +} + + + +/*-----------------------------------------------------------------------*/ +/* This function reads sector(s) from the disk drive */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive number (0) */ + BYTE* buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Physical drive nmuber (0) */ + BYTE count) /* Sector count (1..255) */ +{ + if(USBStat & STA_NOINIT) + { + return(RES_NOTRDY); + } + + /* READ BLOCK */ + if (USBHMSCBlockRead(g_psMSCInstance, sector, buff, count) == 0) + return RES_OK; + + return RES_ERROR; +} + + + +/*-----------------------------------------------------------------------*/ +/* This function writes sector(s) to the disk drive */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE ucDrive, /* Physical drive number (0) */ + const BYTE* buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count) /* Sector count (1..255) */ +{ + if (ucDrive || !count) return RES_PARERR; + if (USBStat & STA_NOINIT) return RES_NOTRDY; + if (USBStat & STA_PROTECT) return RES_WRPRT; + + /* WRITE BLOCK */ + if(USBHMSCBlockWrite(g_psMSCInstance, sector, (unsigned char *)buff, + count) == 0) + return RES_OK; + + return RES_ERROR; +} +#endif /* _READONLY */ + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive number (0) */ + BYTE ctrl, /* Control code */ + void *buff) /* Buffer to send/receive control data */ +{ + if(USBStat & STA_NOINIT) + { + return(RES_NOTRDY); + } + + switch(ctrl) + { + case CTRL_SYNC: + return(RES_OK); + + default: + return(RES_PARERR); + } +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; +} diff --git a/third_party/fatfs/port/mmc-dk-tm4c123g.c b/third_party/fatfs/port/mmc-dk-tm4c123g.c new file mode 100644 index 0000000..1e9cf5b --- /dev/null +++ b/third_party/fatfs/port/mmc-dk-tm4c123g.c @@ -0,0 +1,725 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + +/* + * This file was modified from a sample available from the FatFs + * web site. It was modified to work with an DK-TM4C123G development + * board. + */ + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "fatfs/src/diskio.h" + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + +/* Peripheral definitions for DK-TM4C123G board */ +// SSI port +#define SDC_SSI_BASE SSI0_BASE +#define SDC_SSI_SYSCTL_PERIPH SYSCTL_PERIPH_SSI0 + +// GPIO for SSI pins +#define SDC_GPIO_PORT_BASE GPIO_PORTA_BASE +#define SDC_GPIO_SYSCTL_PERIPH SYSCTL_PERIPH_GPIOA +#define SDC_SSI_CLK GPIO_PIN_2 +#define SDC_SSI_TX GPIO_PIN_5 +#define SDC_SSI_RX GPIO_PIN_4 +#define SDC_SSI_FSS GPIO_PIN_3 +#define SDC_SSI_PINS (SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK | \ + SDC_SSI_FSS) + +// asserts the CS pin to the card +static +void SELECT (void) +{ + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, 0); +} + +// de-asserts the CS pin to the card +static +void DESELECT (void) +{ + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS); +} + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + +static +BYTE PowerFlag = 0; /* indicates if "power" is on */ + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +void xmit_spi(BYTE dat) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */ +} + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */ + + return (BYTE)ui32RcvDat; +} + + +static +void rcvr_spi_m (BYTE *dst) +{ + *dst = rcvr_spi(); +} + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Send 80 or so clock transitions with CS and DI held high. This is */ +/* required after card power up to get it into SPI mode */ +/*-----------------------------------------------------------------------*/ +static +void send_initial_clock_train(void) +{ + unsigned int i; + uint32_t ui32Dat; + + /* Ensure CS is held high. */ + DESELECT(); + + /* Switch the SSI TX line to a GPIO and drive it high too. */ + ROM_GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX); + + /* Send 10 bytes over the SSI. This causes the clock to wiggle the */ + /* required number of times. */ + for(i = 0 ; i < 10 ; i++) + { + /* Write DUMMY data. SSIDataPut() waits until there is room in the */ + /* FIFO. */ + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); + + /* Flush data read during data write. */ + ROM_SSIDataGet(SDC_SSI_BASE, &ui32Dat); + } + + /* Revert to hardware control of the SSI TX line. */ + ROM_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX); +} + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + /* + * This doesn't really turn the power on, but initializes the + * SSI port and pins needed to talk to the card. + */ + + /* Enable the peripherals used to drive the SDC on SSI */ + ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH); + ROM_SysCtlPeripheralEnable(SDC_GPIO_SYSCTL_PERIPH); + + /* + * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS) + * signal is directly driven to ensure that we can hold it low through a + * complete transaction with the SD card. + */ + ROM_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK); + ROM_GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_FSS); + + /* + * Set the SSI output pins to 4MA drive strength and engage the + * pull-up on the receive line. + */ + MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_RX, GPIO_STRENGTH_4MA, + GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_CLK | SDC_SSI_TX | SDC_SSI_FSS, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + + /* Configure the SSI0 port */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, ROM_SysCtlClockGet(), + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8); + ROM_SSIEnable(SDC_SSI_BASE); + + /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */ + /* to be able to accept a native command. */ + send_initial_clock_train(); + + PowerFlag = 1; +} + +// set the SSI speed to the max setting +static +void set_max_speed(void) +{ + unsigned long i; + + /* Disable the SSI */ + ROM_SSIDisable(SDC_SSI_BASE); + + /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */ + i = ROM_SysCtlClockGet() / 2; + if(i > 12500000) + { + i = 12500000; + } + + /* Configure the SSI0 port to run at 12.5MHz */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, ROM_SysCtlClockGet(), + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8); + + /* Enable the SSI */ + ROM_SSIEnable(SDC_SSI_BASE); +} + +static +void power_off (void) +{ + PowerFlag = 0; +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return PowerFlag; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 100; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0xff; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + +/*-----------------------------------------------------------------------* + * Send the special command used to terminate a multi-sector read. + * + * This is the only command which can be sent while the SDCard is sending + * data. The SDCard spec indicates that the data transfer will stop 2 bytes + * after the 6 byte CMD12 command is sent and that the card will then send + * 0xFF for between 2 and 6 more bytes before the R1 response byte. This + * response will be followed by another 0xFF byte. In testing, however, it + * seems that some cards don't send the 2 to 6 0xFF bytes between the end of + * data transmission and the response code. This function, therefore, merely + * reads 10 bytes and, if the last one read is 0xFF, returns the value of the + * latest non-0xFF byte as the response code. + * + *-----------------------------------------------------------------------*/ + +static +BYTE send_cmd12 (void) +{ + BYTE n, res, val; + + /* For CMD12, we don't wait for the card to be idle before we send + * the new command. + */ + + /* Send command packet - the argument for CMD12 is ignored. */ + xmit_spi(CMD12); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + + /* Read up to 10 bytes from the card, remembering the value read if it's + not 0xFF */ + for(n = 0; n < 10; n++) + { + val = rcvr_spi(); + if(val != 0xFF) + { + res = val; + } + } + + return res; /* Return with the response value */ +} + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + send_initial_clock_train(); /* Ensure the card is in SPI mode */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + set_max_speed(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd12(); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + +// case MMC_GET_TYPE : /* Get card type flags (1 byte) */ +// *ptr = CardType; +// res = RES_OK; +// break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ +// BYTE n, s; + BYTE n; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; + +} diff --git a/third_party/fatfs/port/mmc-dk-tm4c129x.c b/third_party/fatfs/port/mmc-dk-tm4c129x.c new file mode 100644 index 0000000..55917c4 --- /dev/null +++ b/third_party/fatfs/port/mmc-dk-tm4c129x.c @@ -0,0 +1,740 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + +/* + * This file was modified from a sample available from the FatFs + * web site. It was modified to work with an DK-TM4C129X development + * board. + */ + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "fatfs/src/diskio.h" + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + +/* Peripheral definitions for DK-TM4C129X board */ +/* SSI port */ +#define SDC_SSI_BASE SSI3_BASE +#define SDC_SSI_SYSCTL_PERIPH SYSCTL_PERIPH_SSI3 + +/* GPIO for SSI pins */ +/* CLK pin */ +#define SDC_SSI_CLK_GPIO_PORT_BASE GPIO_PORTQ_BASE +#define SDC_SSI_CLK GPIO_PIN_0 +/* TX pin */ +#define SDC_SSI_TX_GPIO_PORT_BASE GPIO_PORTF_BASE +#define SDC_SSI_TX GPIO_PIN_0 +/* RX pin */ +#define SDC_SSI_RX_GPIO_PORT_BASE GPIO_PORTQ_BASE +#define SDC_SSI_RX GPIO_PIN_2 +/* CS pin */ +#define SDC_SSI_FSS_GPIO_PORT_BASE GPIO_PORTH_BASE +#define SDC_SSI_FSS GPIO_PIN_4 + +/* must be supplied by the application */ +extern uint32_t g_ui32SysClock; + +/* asserts the CS pin to the card */ +static +void SELECT (void) +{ + ROM_GPIOPinWrite(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, 0); +} + +/* de-asserts the CS pin to the card */ +static +void DESELECT (void) +{ + ROM_GPIOPinWrite(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS); +} + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + +static +BYTE PowerFlag = 0; /* indicates if "power" is on */ + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +void xmit_spi(BYTE dat) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */ +} + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */ + + return (BYTE)ui32RcvDat; +} + + +static +void rcvr_spi_m (BYTE *dst) +{ + *dst = rcvr_spi(); +} + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Send 80 or so clock transitions with CS and DI held high. This is */ +/* required after card power up to get it into SPI mode */ +/*-----------------------------------------------------------------------*/ +static +void send_initial_clock_train(void) +{ + unsigned int i; + uint32_t ui32Dat; + + /* Ensure CS is held high. */ + DESELECT(); + + /* Switch the SSI TX line to a GPIO and drive it high too. */ + ROM_GPIOPinTypeGPIOOutput(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinWrite(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX); + + /* Send 10 bytes over the SSI. This causes the clock to wiggle the */ + /* required number of times. */ + for(i = 0 ; i < 10 ; i++) + { + /* Write DUMMY data. SSIDataPut() waits until there is room in the */ + /* FIFO. */ + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); + + /* Flush data read during data write. */ + ROM_SSIDataGet(SDC_SSI_BASE, &ui32Dat); + } + + /* Revert to hardware control of the SSI TX line. */ + ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); +} + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + /* + * This doesn't really turn the power on, but initializes the + * SSI port and pins needed to talk to the card. + */ + + /* Enable the peripherals used to drive the SDC on SSI */ + ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH); + + /* + * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS) + * signal is directly driven to ensure that we can hold it low through a + * complete transaction with the SD card. + */ + ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinTypeSSI(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX); + ROM_GPIOPinTypeSSI(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK); + ROM_GPIOPinTypeGPIOOutput(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS); + + /* + * Set the SSI output pins to 4MA drive strength and engage the + * pull-up on the receive line. + */ + MAP_GPIOPadConfigSet(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + MAP_GPIOPadConfigSet(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + MAP_GPIOPadConfigSet(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + + /* Configure the SSI3 port */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock, + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8); + ROM_SSIEnable(SDC_SSI_BASE); + + /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */ + /* to be able to accept a native command. */ + send_initial_clock_train(); + + PowerFlag = 1; +} + +// set the SSI speed to the max setting +static +void set_max_speed(void) +{ + unsigned long i; + + /* Disable the SSI */ + ROM_SSIDisable(SDC_SSI_BASE); + + /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */ + i = g_ui32SysClock / 2; + if(i > 12500000) + { + i = 12500000; + } + + /* Configure the SSI0 port to run at 12.5MHz */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock, + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8); + + /* Enable the SSI */ + ROM_SSIEnable(SDC_SSI_BASE); +} + +static +void power_off (void) +{ + PowerFlag = 0; +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return PowerFlag; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 100; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0xff; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + +/*-----------------------------------------------------------------------* + * Send the special command used to terminate a multi-sector read. + * + * This is the only command which can be sent while the SDCard is sending + * data. The SDCard spec indicates that the data transfer will stop 2 bytes + * after the 6 byte CMD12 command is sent and that the card will then send + * 0xFF for between 2 and 6 more bytes before the R1 response byte. This + * response will be followed by another 0xFF byte. In testing, however, it + * seems that some cards don't send the 2 to 6 0xFF bytes between the end of + * data transmission and the response code. This function, therefore, merely + * reads 10 bytes and, if the last one read is 0xFF, returns the value of the + * latest non-0xFF byte as the response code. + * + *-----------------------------------------------------------------------*/ + +static +BYTE send_cmd12 (void) +{ + BYTE n, res, val; + + /* For CMD12, we don't wait for the card to be idle before we send + * the new command. + */ + + /* Send command packet - the argument for CMD12 is ignored. */ + xmit_spi(CMD12); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + + /* Read up to 10 bytes from the card, remembering the value read if it's + not 0xFF */ + for(n = 0; n < 10; n++) + { + val = rcvr_spi(); + if(val != 0xFF) + { + res = val; + } + } + + return res; /* Return with the response value */ +} + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + send_initial_clock_train(); /* Ensure the card is in SPI mode */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + set_max_speed(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd12(); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + +// case MMC_GET_TYPE : /* Get card type flags (1 byte) */ +// *ptr = CardType; +// res = RES_OK; +// break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ +// BYTE n, s; + BYTE n; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; + +} diff --git a/third_party/fatfs/port/mmc-ek-lm4f232.c b/third_party/fatfs/port/mmc-ek-lm4f232.c new file mode 100644 index 0000000..5f58525 --- /dev/null +++ b/third_party/fatfs/port/mmc-ek-lm4f232.c @@ -0,0 +1,725 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + +/* + * This file was modified from a sample available from the FatFs + * web site. It was modified to work with an EK-LM4F232 evaluation + * board. + */ + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "fatfs/src/diskio.h" + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + +/* Peripheral definitions for EK-LM4F232 board */ +// SSI port +#define SDC_SSI_BASE SSI0_BASE +#define SDC_SSI_SYSCTL_PERIPH SYSCTL_PERIPH_SSI0 + +// GPIO for SSI pins +#define SDC_GPIO_PORT_BASE GPIO_PORTA_BASE +#define SDC_GPIO_SYSCTL_PERIPH SYSCTL_PERIPH_GPIOA +#define SDC_SSI_CLK GPIO_PIN_2 +#define SDC_SSI_TX GPIO_PIN_5 +#define SDC_SSI_RX GPIO_PIN_4 +#define SDC_SSI_FSS GPIO_PIN_3 +#define SDC_SSI_PINS (SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK | \ + SDC_SSI_FSS) + +// asserts the CS pin to the card +static +void SELECT (void) +{ + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, 0); +} + +// de-asserts the CS pin to the card +static +void DESELECT (void) +{ + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS); +} + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + +static +BYTE PowerFlag = 0; /* indicates if "power" is on */ + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +void xmit_spi(BYTE dat) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */ +} + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */ + + return (BYTE)ui32RcvDat; +} + + +static +void rcvr_spi_m (BYTE *dst) +{ + *dst = rcvr_spi(); +} + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Send 80 or so clock transitions with CS and DI held high. This is */ +/* required after card power up to get it into SPI mode */ +/*-----------------------------------------------------------------------*/ +static +void send_initial_clock_train(void) +{ + unsigned int i; + uint32_t ui32Dat; + + /* Ensure CS is held high. */ + DESELECT(); + + /* Switch the SSI TX line to a GPIO and drive it high too. */ + ROM_GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX); + + /* Send 10 bytes over the SSI. This causes the clock to wiggle the */ + /* required number of times. */ + for(i = 0 ; i < 10 ; i++) + { + /* Write DUMMY data. SSIDataPut() waits until there is room in the */ + /* FIFO. */ + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); + + /* Flush data read during data write. */ + ROM_SSIDataGet(SDC_SSI_BASE, &ui32Dat); + } + + /* Revert to hardware control of the SSI TX line. */ + ROM_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX); +} + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + /* + * This doesn't really turn the power on, but initializes the + * SSI port and pins needed to talk to the card. + */ + + /* Enable the peripherals used to drive the SDC on SSI */ + ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH); + ROM_SysCtlPeripheralEnable(SDC_GPIO_SYSCTL_PERIPH); + + /* + * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS) + * signal is directly driven to ensure that we can hold it low through a + * complete transaction with the SD card. + */ + ROM_GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK); + ROM_GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_FSS); + + /* + * Set the SSI output pins to 4MA drive strength and engage the + * pull-up on the receive line. + */ + MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_RX, GPIO_STRENGTH_4MA, + GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_CLK | SDC_SSI_TX | SDC_SSI_FSS, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + + /* Configure the SSI0 port */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, ROM_SysCtlClockGet(), + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8); + ROM_SSIEnable(SDC_SSI_BASE); + + /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */ + /* to be able to accept a native command. */ + send_initial_clock_train(); + + PowerFlag = 1; +} + +// set the SSI speed to the max setting +static +void set_max_speed(void) +{ + unsigned long i; + + /* Disable the SSI */ + ROM_SSIDisable(SDC_SSI_BASE); + + /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */ + i = ROM_SysCtlClockGet() / 2; + if(i > 12500000) + { + i = 12500000; + } + + /* Configure the SSI0 port to run at 12.5MHz */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, ROM_SysCtlClockGet(), + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8); + + /* Enable the SSI */ + ROM_SSIEnable(SDC_SSI_BASE); +} + +static +void power_off (void) +{ + PowerFlag = 0; +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return PowerFlag; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 100; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0xff; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + +/*-----------------------------------------------------------------------* + * Send the special command used to terminate a multi-sector read. + * + * This is the only command which can be sent while the SDCard is sending + * data. The SDCard spec indicates that the data transfer will stop 2 bytes + * after the 6 byte CMD12 command is sent and that the card will then send + * 0xFF for between 2 and 6 more bytes before the R1 response byte. This + * response will be followed by another 0xFF byte. In testing, however, it + * seems that some cards don't send the 2 to 6 0xFF bytes between the end of + * data transmission and the response code. This function, therefore, merely + * reads 10 bytes and, if the last one read is 0xFF, returns the value of the + * latest non-0xFF byte as the response code. + * + *-----------------------------------------------------------------------*/ + +static +BYTE send_cmd12 (void) +{ + BYTE n, res, val; + + /* For CMD12, we don't wait for the card to be idle before we send + * the new command. + */ + + /* Send command packet - the argument for CMD12 is ignored. */ + xmit_spi(CMD12); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + + /* Read up to 10 bytes from the card, remembering the value read if it's + not 0xFF */ + for(n = 0; n < 10; n++) + { + val = rcvr_spi(); + if(val != 0xFF) + { + res = val; + } + } + + return res; /* Return with the response value */ +} + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + send_initial_clock_train(); /* Ensure the card is in SPI mode */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + set_max_speed(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd12(); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + +// case MMC_GET_TYPE : /* Get card type flags (1 byte) */ +// *ptr = CardType; +// res = RES_OK; +// break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ +// BYTE n, s; + BYTE n; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; + +} diff --git a/third_party/fatfs/port/mmc-ek-tm4c1294xl.c b/third_party/fatfs/port/mmc-ek-tm4c1294xl.c new file mode 100644 index 0000000..ab80e14 --- /dev/null +++ b/third_party/fatfs/port/mmc-ek-tm4c1294xl.c @@ -0,0 +1,740 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + +/* + * This file was modified from a sample available from the FatFs + * web site. It was modified to work with an EK-TM4C129EXL evaluation + * board. + */ + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/ssi.h" +#include "driverlib/sysctl.h" +#include "fatfs/src/diskio.h" + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + +/* Peripheral definitions for EK-TM4C129EXL board */ +/* SSI port */ +#define SDC_SSI_BASE SSI3_BASE +#define SDC_SSI_SYSCTL_PERIPH SYSCTL_PERIPH_SSI3 + +/* GPIO for SSI pins */ +/* CLK pin */ +#define SDC_SSI_CLK_GPIO_PORT_BASE GPIO_PORTQ_BASE +#define SDC_SSI_CLK GPIO_PIN_0 +/* TX pin */ +#define SDC_SSI_TX_GPIO_PORT_BASE GPIO_PORTF_BASE +#define SDC_SSI_TX GPIO_PIN_0 +/* RX pin */ +#define SDC_SSI_RX_GPIO_PORT_BASE GPIO_PORTQ_BASE +#define SDC_SSI_RX GPIO_PIN_2 +/* CS pin */ +#define SDC_SSI_FSS_GPIO_PORT_BASE GPIO_PORTH_BASE +#define SDC_SSI_FSS GPIO_PIN_4 + +/* must be supplied by the application */ +extern uint32_t g_ui32SysClock; + +/* asserts the CS pin to the card */ +static +void SELECT (void) +{ + ROM_GPIOPinWrite(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, 0); +} + +/* de-asserts the CS pin to the card */ +static +void DESELECT (void) +{ + ROM_GPIOPinWrite(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, SDC_SSI_FSS); +} + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + +static +BYTE PowerFlag = 0; /* indicates if "power" is on */ + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +void xmit_spi(BYTE dat) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, dat); /* Write the data to the tx fifo */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* flush data read during the write */ +} + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + uint32_t ui32RcvDat; + + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); /* write dummy data */ + + ROM_SSIDataGet(SDC_SSI_BASE, &ui32RcvDat); /* read data frm rx fifo */ + + return (BYTE)ui32RcvDat; +} + + +static +void rcvr_spi_m (BYTE *dst) +{ + *dst = rcvr_spi(); +} + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Send 80 or so clock transitions with CS and DI held high. This is */ +/* required after card power up to get it into SPI mode */ +/*-----------------------------------------------------------------------*/ +static +void send_initial_clock_train(void) +{ + unsigned int i; + uint32_t ui32Dat; + + /* Ensure CS is held high. */ + DESELECT(); + + /* Switch the SSI TX line to a GPIO and drive it high too. */ + ROM_GPIOPinTypeGPIOOutput(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinWrite(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX); + + /* Send 10 bytes over the SSI. This causes the clock to wiggle the */ + /* required number of times. */ + for(i = 0 ; i < 10 ; i++) + { + /* Write DUMMY data. SSIDataPut() waits until there is room in the */ + /* FIFO. */ + ROM_SSIDataPut(SDC_SSI_BASE, 0xFF); + + /* Flush data read during data write. */ + ROM_SSIDataGet(SDC_SSI_BASE, &ui32Dat); + } + + /* Revert to hardware control of the SSI TX line. */ + ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); +} + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + /* + * This doesn't really turn the power on, but initializes the + * SSI port and pins needed to talk to the card. + */ + + /* Enable the peripherals used to drive the SDC on SSI */ + ROM_SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH); + + /* + * Configure the appropriate pins to be SSI instead of GPIO. The FSS (CS) + * signal is directly driven to ensure that we can hold it low through a + * complete transaction with the SD card. + */ + ROM_GPIOPinTypeSSI(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX); + ROM_GPIOPinTypeSSI(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX); + ROM_GPIOPinTypeSSI(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK); + ROM_GPIOPinTypeGPIOOutput(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS); + + /* + * Set the SSI output pins to 4MA drive strength and engage the + * pull-up on the receive line. + */ + MAP_GPIOPadConfigSet(SDC_SSI_RX_GPIO_PORT_BASE, SDC_SSI_RX, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + MAP_GPIOPadConfigSet(SDC_SSI_CLK_GPIO_PORT_BASE, SDC_SSI_CLK, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + MAP_GPIOPadConfigSet(SDC_SSI_TX_GPIO_PORT_BASE, SDC_SSI_TX, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + MAP_GPIOPadConfigSet(SDC_SSI_FSS_GPIO_PORT_BASE, SDC_SSI_FSS, + GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD); + + /* Configure the SSI3 port */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock, + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 400000, 8); + ROM_SSIEnable(SDC_SSI_BASE); + + /* Set DI and CS high and apply more than 74 pulses to SCLK for the card */ + /* to be able to accept a native command. */ + send_initial_clock_train(); + + PowerFlag = 1; +} + +// set the SSI speed to the max setting +static +void set_max_speed(void) +{ + unsigned long i; + + /* Disable the SSI */ + ROM_SSIDisable(SDC_SSI_BASE); + + /* Set the maximum speed as half the system clock, with a max of 12.5 MHz. */ + i = g_ui32SysClock / 2; + if(i > 12500000) + { + i = 12500000; + } + + /* Configure the SSI0 port to run at 12.5MHz */ + ROM_SSIConfigSetExpClk(SDC_SSI_BASE, g_ui32SysClock, + SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, i, 8); + + /* Enable the SSI */ + ROM_SSIEnable(SDC_SSI_BASE); +} + +static +void power_off (void) +{ + PowerFlag = 0; +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return PowerFlag; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 100; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0xff; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + +/*-----------------------------------------------------------------------* + * Send the special command used to terminate a multi-sector read. + * + * This is the only command which can be sent while the SDCard is sending + * data. The SDCard spec indicates that the data transfer will stop 2 bytes + * after the 6 byte CMD12 command is sent and that the card will then send + * 0xFF for between 2 and 6 more bytes before the R1 response byte. This + * response will be followed by another 0xFF byte. In testing, however, it + * seems that some cards don't send the 2 to 6 0xFF bytes between the end of + * data transmission and the response code. This function, therefore, merely + * reads 10 bytes and, if the last one read is 0xFF, returns the value of the + * latest non-0xFF byte as the response code. + * + *-----------------------------------------------------------------------*/ + +static +BYTE send_cmd12 (void) +{ + BYTE n, res, val; + + /* For CMD12, we don't wait for the card to be idle before we send + * the new command. + */ + + /* Send command packet - the argument for CMD12 is ignored. */ + xmit_spi(CMD12); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + xmit_spi(0); + + /* Read up to 10 bytes from the card, remembering the value read if it's + not 0xFF */ + for(n = 0; n < 10; n++) + { + val = rcvr_spi(); + if(val != 0xFF) + { + res = val; + } + } + + return res; /* Return with the response value */ +} + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + send_initial_clock_train(); /* Ensure the card is in SPI mode */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + set_max_speed(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd12(); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + +// case MMC_GET_TYPE : /* Get card type flags (1 byte) */ +// *ptr = CardType; +// res = RES_OK; +// break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ +// BYTE n, s; + BYTE n; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + +} + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; + +} diff --git a/third_party/fatfs/port/sample-mmc.c b/third_party/fatfs/port/sample-mmc.c new file mode 100644 index 0000000..db98971 --- /dev/null +++ b/third_party/fatfs/port/sample-mmc.c @@ -0,0 +1,559 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SDC (in SPI mode) control module (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + + +#include +#include "diskio.h" + + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD9 (0x40+9) /* SEND_CSD */ +#define CMD10 (0x40+10) /* SEND_CID */ +#define CMD12 (0x40+12) /* STOP_TRANSMISSION */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */ +#define CMD23 (0x40+23) /* SET_BLOCK_COUNT */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */ +#define CMD41 (0x40+41) /* SEND_OP_COND (ACMD) */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + + +/* Port Controls (Platform dependent) */ +#define SELECT() PORTB &= ~1 /* MMC CS = L */ +#define DESELECT() PORTB |= 1 /* MMC CS = H */ + +#define SOCKPORT PINB /* Socket contact port */ +#define SOCKWP 0x20 /* Write protect switch (PB5) */ +#define SOCKINS 0x10 /* Card detect switch (PB4) */ + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static volatile +BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +static +BYTE CardType; /* b0:MMC, b1:SDC, b2:Block addressing */ + + + +/*-----------------------------------------------------------------------*/ +/* Transmit a byte to MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +#define xmit_spi(dat) SPDR=(dat); loop_until_bit_is_set(SPSR,SPIF) + + + +/*-----------------------------------------------------------------------*/ +/* Receive a byte from MMC via SPI (Platform dependent) */ +/*-----------------------------------------------------------------------*/ + +static +BYTE rcvr_spi (void) +{ + SPDR = 0xFF; + loop_until_bit_is_set(SPSR, SPIF); + return SPDR; +} + +/* Alternative macro to receive data fast */ +#define rcvr_spi_m(dst) SPDR=0xFF; loop_until_bit_is_set(SPSR,SPIF); *(dst)=SPDR + + + +/*-----------------------------------------------------------------------*/ +/* Wait for card ready */ +/*-----------------------------------------------------------------------*/ + +static +BYTE wait_ready (void) +{ + BYTE res; + + + Timer2 = 50; /* Wait for ready in timeout of 500ms */ + rcvr_spi(); + do + res = rcvr_spi(); + while ((res != 0xFF) && Timer2); + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Power Control (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* When the target system does not support socket power control, there */ +/* is nothing to do in these functions and chk_power always returns 1. */ + +static +void power_on (void) +{ + PORTE &= ~0x80; /* Socket power ON */ + for (Timer1 = 3; Timer1; ); /* Wait for 30ms */ + PORTB = 0b10110101; /* Enable drivers */ + DDRB = 0b11000111; + SPCR = 0b01010000; /* Initialize SPI port (Mode 0) */ + SPSR = 0b00000001; +} + +static +void power_off (void) +{ + SELECT(); /* Wait for card ready */ + wait_ready(); + DESELECT(); + rcvr_spi(); + + SPCR = 0; /* Disable SPI function */ + DDRB = 0b11000000; /* Disable drivers */ + PORTB = 0b10110000; + PORTE |= 0x80; /* Socket power OFF */ + Stat |= STA_NOINIT; /* Set STA_NOINIT */ +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return (PORTE & 0x80) ? 0 : 1; +} + + + +/*-----------------------------------------------------------------------*/ +/* Receive a data packet from MMC */ +/*-----------------------------------------------------------------------*/ + +static +BOOL rcvr_datablock ( + BYTE *buff, /* Data buffer to store received data */ + UINT btr /* Byte count (must be even number) */ +) +{ + BYTE token; + + + Timer1 = 10; + do { /* Wait for data packet in timeout of 100ms */ + token = rcvr_spi(); + } while ((token == 0xFF) && Timer1); + if(token != 0xFE) return FALSE; /* If not valid data token, retutn with error */ + + do { /* Receive the data block into buffer */ + rcvr_spi_m(buff++); + rcvr_spi_m(buff++); + } while (btr -= 2); + rcvr_spi(); /* Discard CRC */ + rcvr_spi(); + + return TRUE; /* Return with success */ +} + + + +/*-----------------------------------------------------------------------*/ +/* Send a data packet to MMC */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +static +BOOL xmit_datablock ( + const BYTE *buff, /* 512 byte data block to be transmitted */ + BYTE token /* Data/Stop token */ +) +{ + BYTE resp, wc; + + + if (wait_ready() != 0xFF) return FALSE; + + xmit_spi(token); /* Xmit data token */ + if (token != 0xFD) { /* Is data token */ + wc = 0; + do { /* Xmit the 512 byte data block to MMC */ + xmit_spi(*buff++); + xmit_spi(*buff++); + } while (--wc); + xmit_spi(0xFF); /* CRC (Dummy) */ + xmit_spi(0xFF); + resp = rcvr_spi(); /* Reveive data response */ + if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */ + return FALSE; + } + + return TRUE; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* Command byte */ + DWORD arg /* Argument */ +) +{ + BYTE n, res; + + + if (wait_ready() != 0xFF) return 0xFF; + + /* Send command packet */ + xmit_spi(cmd); /* Command */ + xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ + xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ + xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ + xmit_spi((BYTE)arg); /* Argument[7..0] */ + n = 0; + if (cmd == CMD0) n = 0x95; /* CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* CRC for CMD8(0x1AA) */ + xmit_spi(n); + + /* Receive command response */ + if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do + res = rcvr_spi(); + while ((res & 0x80) && --n); + + return res; /* Return with the response value */ +} + + + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + BYTE n, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */ + + SELECT(); /* CS = L */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDC Ver2+ */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + do { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0) break; /* ACMD41 with HCS bit */ + } while (Timer1); + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? 6 : 2; + } + } + } else { /* SDC Ver1 or MMC */ + ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1; /* SDC : MMC */ + do { + if (ty == 2) { + if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break; /* ACMD41 */ + } else { + if (send_cmd(CMD1, 0) == 0) break; /* CMD1 */ + } + } while (Timer1); + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Select R/W block length */ + ty = 0; + } + } + CardType = ty; + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block read */ + if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ + && rcvr_datablock(buff, 512)) + count = 0; + } + else { /* Multiple block read */ + if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */ + do { + if (!rcvr_datablock(buff, 512)) break; + buff += 512; + } while (--count); + send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & 4)) sector *= 512; /* Convert to byte address if needed */ + + SELECT(); /* CS = L */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & 2) { + send_cmd(CMD55, 0); send_cmd(CMD23, count); /* ACMD23 */ + } + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + + return count ? RES_ERROR : RES_OK; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE n, csd[16], *ptr = buff; + WORD csize; + + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + SELECT(); /* CS = L */ + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* MMC or SDC ver 1.XX */ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + if (wait_ready() == 0xFF) + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 0; n < 4; n++) + *ptr++ = rcvr_spi(); + res = RES_OK; + } + + case MMC_GET_TYPE : /* Get card type flags (1 byte) */ + *ptr = CardType; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + DESELECT(); /* CS = H */ + rcvr_spi(); /* Idle (Release DO) */ + } + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Device Timer Interrupt Procedure (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +/* This function must be called in period of 10ms */ + +void disk_timerproc (void) +{ + static BYTE pv; + BYTE n, s; + + + n = Timer1; /* 100Hz decrement timer */ + if (n) Timer1 = --n; + n = Timer2; + if (n) Timer2 = --n; + + n = pv; + pv = SOCKPORT & (SOCKWP | SOCKINS); /* Sample socket switch */ + + if (n == pv) { /* Have contacts stabled? */ + s = Stat; + + if (pv & SOCKWP) /* WP is H (write protected) */ + s |= STA_PROTECT; + else /* WP is L (write enabled) */ + s &= ~STA_PROTECT; + + if (pv & SOCKINS) /* INS = H (Socket empty) */ + s |= (STA_NODISK | STA_NOINIT); + else /* INS = L (Card inserted) */ + s &= ~STA_NODISK; + + Stat = s; + } +} + -- cgit v1.3.1