/* Fuzix SD card driver */
/* 2014-12-28 Will Sowerbutts */
/* 2015-01-04 WRS updated to new blkdev API */
+/* 2015-01-25 WRS updated to newer blkdev API */
/* */
/* Based on UZI-socz80 SD card driver, which was itself based on: */
/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2007 */
static void sd_init_drive(uint8_t drive);
static int sd_spi_init(uint8_t drive);
static void sd_spi_release(uint8_t drive);
-static int sd_spi_wait_ready(uint8_t drive);
-static bool sd_spi_transmit_sector(uint8_t drive, void *ptr, unsigned int length);
-static bool sd_spi_receive_sector(uint8_t drive, void *ptr, unsigned int length);
+static bool sd_spi_wait_ready(uint8_t drive);
+static bool sd_spi_receive_prepare(uint8_t drive);
static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg);
-static uint32_t sd_get_size_sectors(uint8_t drive);
-static bool devsd_transfer_sector(uint8_t drive, uint32_t lba, void *buffer, bool read_notwrite)
+static uint8_t devsd_transfer_sector(void)
{
- uint8_t attempt;
+ uint8_t attempt, drive, reply;
bool success;
- if(!(sd_card_type[drive] & CT_BLOCK))
- lba <<= 9; /* multiply by 512 to convert block to byte address */
+ drive = blk_op.blkdev->driver_data & DRIVE_NR_MASK;
for(attempt=0; attempt<8; attempt++){
- if(sd_send_command(drive, read_notwrite ? CMD17 : CMD24, lba) == 0){
- if(read_notwrite)
- success = sd_spi_receive_sector(drive, buffer, 512);
- else
- success = sd_spi_transmit_sector(drive, buffer, 512);
+ if(sd_send_command(drive, blk_op.is_read ? CMD17 : CMD24,
+ /* for byte addressed cards, shift LBA to convert to byte address */
+ (blk_op.blkdev->driver_data & CT_BLOCK) ? blk_op.lba : (blk_op.lba << 9)
+ ) == 0){
+ if(blk_op.is_read){
+ success = sd_spi_receive_prepare(drive);
+ if(success)
+ sd_spi_receive_sector(drive);
+ }else{
+ success = false;
+ if(sd_spi_wait_ready(drive)){
+ sd_spi_transmit_byte(drive, 0xFE);
+ sd_spi_transmit_sector(drive);
+ sd_spi_transmit_byte(drive, 0xFF); /* dummy CRC */
+ sd_spi_transmit_byte(drive, 0xFF);
+ reply = sd_spi_receive_byte(drive);
+ if((reply & 0x1f) != 0x05)
+ return false; /* failed */
+ return true; /* hooray! */
+ }
+ }
}else
success = false;
}
udata.u_error = EIO;
- return -1;
+ return 0;
}
static void sd_spi_release(uint8_t drive)
sd_spi_receive_byte(drive);
}
-static int sd_spi_wait_ready(uint8_t drive)
+static bool sd_spi_wait_ready(uint8_t drive)
{
- unsigned char res;
+ uint8_t res;
timer_t timer;
- timer = set_timer_ms(100);
+ timer = set_timer_ms(500);
sd_spi_receive_byte(drive);
- do{
+
+ while(true){
res = sd_spi_receive_byte(drive);
+ if(res == 0xFF)
+ return true;
if(timer_expired(timer)){
- kputs("sd_spi_wait_ready: timeout\n");
+ kputs("sd: timeout\n");
break;
}
- }while ((res != 0xFF));
-
- return res;
-}
+ }
-static bool sd_spi_transmit_sector(uint8_t drive, void *ptr, unsigned int length)
-{
- unsigned char reply;
-
- if(sd_spi_wait_ready(drive) != 0xFF)
- return false; /* failed */
-
- sd_spi_transmit_byte(drive, 0xFE);
- sd_spi_transmit_block(drive, ptr, length);
- sd_spi_transmit_byte(drive, 0xFF); /* dummy CRC */
- sd_spi_transmit_byte(drive, 0xFF);
- reply = sd_spi_receive_byte(drive);
- if((reply & 0x1f) != 0x05)
- return false; /* failed */
- return true; /* hooray! */
+ return false;
}
-static bool sd_spi_receive_sector(uint8_t drive, void *ptr, unsigned int length)
+static bool sd_spi_receive_prepare(uint8_t drive)
{
unsigned int timer;
unsigned char b;
do{
b = sd_spi_receive_byte(drive);
if(timer_expired(timer)){
- kputs("sd_spi_receive_sector: timeout\n");
+ kputs("sd: timeout\n");
return false;
}
}while(b == 0xFF);
- if(b != 0xFE)
- return false; /* failed */
-
- return sd_spi_receive_block(drive, ptr, length); /* returns true on success */
+ return (b == 0xFE); /* true on success */
}
static int sd_send_command(uint8_t drive, unsigned char cmd, uint32_t arg)
/* Select the card and wait for ready */
sd_spi_raise_cs(drive);
sd_spi_lower_cs(drive);
- if (sd_spi_wait_ready(drive) != 0xFF)
+ if(!sd_spi_wait_ready(drive))
return 0xFF;
/* Send command packet */
sd_spi_transmit_byte(drive, (unsigned char)arg); /* Argument[7..0] */
#else
/* sdcc sadly unable to figure this out for itself yet */
- p = (unsigned char *)&arg;
- sd_spi_transmit_byte(drive, p[3]); /* Argument[31..24] */
- sd_spi_transmit_byte(drive, p[2]); /* Argument[23..16] */
- sd_spi_transmit_byte(drive, p[1]); /* Argument[15..8] */
- sd_spi_transmit_byte(drive, p[0]); /* Argument[7..0] */
+ p = ((unsigned char *)&arg)+3;
+ sd_spi_transmit_byte(drive, *(p--)); /* Argument[31..24] */
+ sd_spi_transmit_byte(drive, *(p--)); /* Argument[23..16] */
+ sd_spi_transmit_byte(drive, *(p--)); /* Argument[15..8] */
+ sd_spi_transmit_byte(drive, *p); /* Argument[7..0] */
#endif
/* there's only a few commands (in native mode) that need correct CRCs */
n = 0x01; /* Dummy CRC + Stop */
static void sd_init_drive(uint8_t drive)
{
blkdev_t *blk;
- uint32_t sector_count;
+ unsigned char csd[16], n;
+ uint8_t card_type;
kprintf("SD drive %d: ", drive);
- sd_card_type[drive] = sd_spi_init(drive);
+ card_type = sd_spi_init(drive);
- if(!(sd_card_type[drive] & (~CT_BLOCK))){
+ if(!(card_type & (~CT_BLOCK))){
kprintf("no card found\n");
return;
}
+
+ blk = blkdev_alloc();
+ if(!blk)
+ return;
+
+ blk->transfer = devsd_transfer_sector;
+ blk->driver_data = (drive & DRIVE_NR_MASK) | card_type;
/* read and compute card size */
- sector_count = sd_get_size_sectors(drive);
- if(!sector_count){
- kputs("weird card\n");
- return;
- }
- blk = blkdev_alloc();
- if (blk) {
- blk->transfer = devsd_transfer_sector;
- blk->drive_number = drive;
- blk->drive_lba_count = sector_count;
- blkdev_scan(blk, 0);
+ if(sd_send_command(drive, CMD9, 0) == 0 && sd_spi_receive_prepare(drive)){
+ for(n=0; n<16; n++)
+ csd[n] = sd_spi_receive_byte(drive);
+ if ((csd[0] >> 6) == 1) {
+ /* SDC ver 2.00 */
+ blk->drive_lba_count = ((uint32_t)csd[9]
+ + (uint32_t)((unsigned int)csd[8] << 8) + 1) << 10;
+ } else {
+ /* SDC ver 1.XX or MMC*/
+ n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
+ blk->drive_lba_count = (csd[8] >> 6) + ((unsigned int)csd[7] << 2)
+ + ((unsigned int)(csd[6] & 3) << 10) + 1;
+ blk->drive_lba_count <<= (n-9);
+ }
}
+ sd_spi_release(drive);
+
+ blkdev_scan(blk, 0);
}
static int sd_spi_init(uint8_t drive)
for (n = 20; n; n--)
sd_spi_receive_byte(drive); /* 160 dummy clocks */
- card_type = 0;
+ card_type = CT_NONE;
/* Enter Idle state */
if (sd_send_command(drive, CMD0, 0) == 1) {
/* initialisation timeout 1 second */
while(!timer_expired(timer) && sd_send_command(drive, cmd, 0));
/* Set R/W block length to 512 */
if(timer_expired || sd_send_command(drive, CMD16, 512) != 0)
- card_type = 0;
+ card_type = CT_NONE;
}
}
sd_spi_release(drive);
return card_type;
}
- return 0; /* failed */
-}
-
-static uint32_t sd_get_size_sectors(uint8_t drive)
-{
- unsigned char csd[16], n;
- uint32_t sectors = 0;
-
- if(sd_send_command(drive, CMD9, 0) == 0 && sd_spi_receive_sector(drive, csd, 16)){
- if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
- sectors = ((uint32_t)csd[9] + (uint32_t)((unsigned int)csd[8] << 8) + 1) << 10;
- } else { /* SDC ver 1.XX or MMC*/
- n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
- sectors = (csd[8] >> 6) + ((unsigned int)csd[7] << 2) + ((unsigned int)(csd[6] & 3) << 10) + 1;
- sectors = sectors << (n - 9);
- }
- }
- sd_spi_release(drive);
- return sectors;
+ return CT_NONE; /* failed */
}
Define DEVICE_SD if SD hardware is present on your platform.
Define SD_DRIVE_COUNT to the number of SD cards your hardware
- supports (range 1--4).
+ supports (at most 16)
Provide the platform-specific SPI functions listed below to
drive the SPI bus on your hardware, for an example of how to
do this see platform-n8vem-mark4/devsdspi.c
+
+ The required functions are:
+
+ - sd_spi_clock(): switch between a slow clock (100--400kHz) for
+ initialisation, and a fast (up to 20--25MHz) for normal operation.
+
+ - sd_spi_raise_cs(), sd_spi_lower_cs(): raise or lower the CS line.
+
+ - sd_spi_transmit_byte(): transmit a single byte
+
+ - sd_spi_receive_byte(): receive a single byte
+
+ - sd_spi_transmit_sector(): transmit a 512-byte sector (params in blk_op)
+
+ - sd_spi_receive_sector(): receive a 512-byte sector (params in blk_op)
*/
void sd_spi_lower_cs(uint8_t drive);
void sd_spi_transmit_byte(uint8_t drive, uint8_t byte);
uint8_t sd_spi_receive_byte(uint8_t drive);
-bool sd_spi_receive_block(uint8_t drive, uint8_t *ptr, unsigned int length);
-bool sd_spi_transmit_block(uint8_t drive, uint8_t *ptr, unsigned int length);
+bool sd_spi_receive_sector(uint8_t drive);
+bool sd_spi_transmit_sector(uint8_t drive);
/* Definitions for MMC/SDC command */
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
#define CMD55 (0x40+55) /* APP_CMD */
#define CMD58 (0x40+58) /* READ_OCR */
-#define CT_NONE 0x00
-#define CT_MMC 0x01
-#define CT_SD1 0x02
-#define CT_SD2 0x04
-#define CT_SDC (CT_SD1|CT_SD2)
-#define CT_BLOCK 0x08
+/* Use the top four bits of driver_data field of blkdev_t for the card type */
+#define CT_NONE 0x00
+#define CT_MMC 0x10
+#define CT_SD1 0x20
+#define CT_SD2 0x40
+#define CT_BLOCK 0x80 /* set if block addressed, unset if byte addressed */
+#define CT_SDC (CT_SD1|CT_SD2)
+
+/* Low four bits of driver_data are available to store drive number */
+#define DRIVE_NR_MASK 0x0F
#endif /* __DEVSD_DOT_H__ */