|  | @@ -0,0 +1,761 @@
 | 
	
		
			
				|  |  | +/* ----------------------------------------------------------------------- *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *   Copyright 2010-2021 H. Peter Anvin - All Rights Reserved
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *   This program is free software; you can redistribute it and/or modify
 | 
	
		
			
				|  |  | + *   it under the terms of the GNU General Public License as published by
 | 
	
		
			
				|  |  | + *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
	
		
			
				|  |  | + *   Boston MA 02110-1301, USA; either version 2 of the License, or
 | 
	
		
			
				|  |  | + *   (at your option) any later version; incorporated herein by reference.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ----------------------------------------------------------------------- */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * sdcard.c
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * SD card block driver
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Note: the handling of read operations is tricky, because they pick up
 | 
	
		
			
				|  |  | + * the results from the *previous* transaction.  Therefore there are some
 | 
	
		
			
				|  |  | + * serious subtleties, especially with which byte position in the shift
 | 
	
		
			
				|  |  | + * register the result ends up in and what the size flag should be set to.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <stdbool.h>
 | 
	
		
			
				|  |  | +#include <stddef.h>
 | 
	
		
			
				|  |  | +#include <string.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "fw.h"
 | 
	
		
			
				|  |  | +#include "console.h"
 | 
	
		
			
				|  |  | +#include "io.h"
 | 
	
		
			
				|  |  | +#include "systime.h"
 | 
	
		
			
				|  |  | +#include "ff.h"
 | 
	
		
			
				|  |  | +#include "diskio.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define SECTOR_SHIFT	9
 | 
	
		
			
				|  |  | +#define SECTOR_SIZE	(1UL << SECTOR_SHIFT)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Command codes, including the leading 01 sequence */
 | 
	
		
			
				|  |  | +enum sdcard_cmd {
 | 
	
		
			
				|  |  | +    CMD_GO_IDLE_STATE 		= 0x40,	/* a.k.a. reset */
 | 
	
		
			
				|  |  | +    CMD_SEND_OP_COND		= 0x41,
 | 
	
		
			
				|  |  | +    CMD_SWITCH_FUNC		= 0x46,
 | 
	
		
			
				|  |  | +    CMD_SEND_IF_COND		= 0x48,
 | 
	
		
			
				|  |  | +    CMD_SEND_CSD		= 0x49,
 | 
	
		
			
				|  |  | +    CMD_SEND_CID		= 0x4a,
 | 
	
		
			
				|  |  | +    CMD_STOP_TRANSMISSION	= 0x4c,
 | 
	
		
			
				|  |  | +    CMD_SEND_STATUS		= 0x4d,
 | 
	
		
			
				|  |  | +    CMD_SET_BLOCKLEN		= 0x50,
 | 
	
		
			
				|  |  | +    CMD_READ_SINGLE_BLOCK	= 0x51,
 | 
	
		
			
				|  |  | +    CMD_READ_MULTIPLE_BLOCK	= 0x52,
 | 
	
		
			
				|  |  | +    CMD_WRITE_BLOCK		= 0x58,
 | 
	
		
			
				|  |  | +    CMD_WRITE_MULTIPLE_BLOCK	= 0x59,
 | 
	
		
			
				|  |  | +    CMD_PROGRAM_CSD		= 0x5b,
 | 
	
		
			
				|  |  | +    CMD_SET_WRITE_PROT		= 0x5c,
 | 
	
		
			
				|  |  | +    CMD_CLR_WRITE_PROT		= 0x5d,
 | 
	
		
			
				|  |  | +    CMD_SEND_WRITE_PROT		= 0x5e,
 | 
	
		
			
				|  |  | +    CMD_ERASE_WR_BLK_START_ADDR	= 0x60,
 | 
	
		
			
				|  |  | +    CMD_ERASE_WR_BLK_END_ADDR	= 0x61,
 | 
	
		
			
				|  |  | +    CMD_ERASE			= 0x66,
 | 
	
		
			
				|  |  | +    CMD_LOCK_UNLOCK		= 0x6a,
 | 
	
		
			
				|  |  | +    CMD_APP_CMD			= 0x77, /* == ACMD prefix */
 | 
	
		
			
				|  |  | +    CMD_GEN_CMD			= 0x78,
 | 
	
		
			
				|  |  | +    CMD_READ_OCR		= 0x7a,
 | 
	
		
			
				|  |  | +    CMD_CRC_ON_OFF		= 0x7b
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +enum sdcard_acmd {
 | 
	
		
			
				|  |  | +    ACMD_SD_STATUS		= 0x4d,
 | 
	
		
			
				|  |  | +    ACMD_SEND_NUM_WR_BLOCKS	= 0x56,
 | 
	
		
			
				|  |  | +    ACMD_SET_WR_BLK_ERASE_COUNT	= 0x57,
 | 
	
		
			
				|  |  | +    ACMD_SD_SEND_OP_COND	= 0x69,
 | 
	
		
			
				|  |  | +    ACMD_SET_CLR_CARD_DETECT	= 0x6a,
 | 
	
		
			
				|  |  | +    ACMD_SEND_SCR		= 0x73
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct sdcard_csd {
 | 
	
		
			
				|  |  | +    uint32_t raw[4];
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct sdcard_cid {
 | 
	
		
			
				|  |  | +    uint32_t raw[4];
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct sdcard_info {
 | 
	
		
			
				|  |  | +    DSTATUS	 status;
 | 
	
		
			
				|  |  | +    int8_t   card_type;
 | 
	
		
			
				|  |  | +    unsigned long lbasize;
 | 
	
		
			
				|  |  | +    uint32_t if_cond;
 | 
	
		
			
				|  |  | +    uint32_t ocr;
 | 
	
		
			
				|  |  | +    struct sdcard_csd csd;
 | 
	
		
			
				|  |  | +    struct sdcard_cid cid;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct sdcard_info sdc = {
 | 
	
		
			
				|  |  | +    .status = STA_NOINIT
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* < 0 if it should be left on, 0 for off, > 0 for off after timeout */
 | 
	
		
			
				|  |  | +static volatile int8_t sdcard_led;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Called by the timer interrupt
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +void sdcard_timer_tick(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#if 0
 | 
	
		
			
				|  |  | +    if (sdcard_led > 0) {
 | 
	
		
			
				|  |  | +	if (--sdcard_led == 0)
 | 
	
		
			
				|  |  | +	    *IO_SYS_LED &= ~0x01;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Enable LED
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void sdcard_led_on(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#if 0
 | 
	
		
			
				|  |  | +    sdcard_led = -1;
 | 
	
		
			
				|  |  | +    *IO_SYS_LED |= 0x01;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Disable LED after timeout
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void sdcard_led_off(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    sdcard_led = 4;		/* 4 ticks @ 64 Hz = 62.5 ms */
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int sdcard_send_cmd(uint8_t opcode, uint32_t argument)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    uint8_t crc7;
 | 
	
		
			
				|  |  | +    int i;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!opcode)
 | 
	
		
			
				|  |  | +	return 0;		/* No command */
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    sd_writeb(opcode, SD_GO8|SD_CLEARCRC);
 | 
	
		
			
				|  |  | +    sd_writel(argument, SD_BE|SD_GO32);
 | 
	
		
			
				|  |  | +    crc7 = sd_crc7_wr();
 | 
	
		
			
				|  |  | +    sd_writeb(crc7, SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* The spec says a reply within 8 cycles, cut it some slack */
 | 
	
		
			
				|  |  | +    for (i = 16; i; i--) {
 | 
	
		
			
				|  |  | +	int8_t status = sd_readb(0);
 | 
	
		
			
				|  |  | +	if (status >= 0) /* Bit 7 = 0 for a valid reply */
 | 
	
		
			
				|  |  | +	    return status;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return -1;		/* Error */
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int sdcard_send_acmd(uint8_t opcode, uint32_t argument)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* CMD55 = application command follows */
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_APP_CMD, 0);
 | 
	
		
			
				|  |  | +    if (rv & 0x04)		/* Unknown command (very old card)? */
 | 
	
		
			
				|  |  | +	return rv;
 | 
	
		
			
				|  |  | +    return sdcard_send_cmd(opcode, argument);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Read a data block of length len, with a timeout of (timeout)
 | 
	
		
			
				|  |  | + * byte-times.  Note that the minimum length is 4 by spec;
 | 
	
		
			
				|  |  | + * this function may fail if this is not the case.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * The input buffer is allowed to be unaligned.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +union xptr {
 | 
	
		
			
				|  |  | +	uint32_t *l;
 | 
	
		
			
				|  |  | +	uint16_t *w;
 | 
	
		
			
				|  |  | +	uint8_t  *b;
 | 
	
		
			
				|  |  | +	size_t    a;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +union xcptr {
 | 
	
		
			
				|  |  | +	const uint32_t *l;
 | 
	
		
			
				|  |  | +	const uint16_t *w;
 | 
	
		
			
				|  |  | +	const uint8_t  *b;
 | 
	
		
			
				|  |  | +	size_t    a;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int sdcard_read_block(void *buf, int len, int timeout,
 | 
	
		
			
				|  |  | +			     uint8_t xcmd, uint32_t xarg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    uint8_t tok;
 | 
	
		
			
				|  |  | +    uint16_t zcrc;
 | 
	
		
			
				|  |  | +    union xptr p;
 | 
	
		
			
				|  |  | +    int i;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    p.b = buf;
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    for (;;) {
 | 
	
		
			
				|  |  | +	tok = sd_readb(SD_GO8|SD_CLEARCRC);
 | 
	
		
			
				|  |  | +	if (tok == 0xfe)
 | 
	
		
			
				|  |  | +	    break;
 | 
	
		
			
				|  |  | +	if (tok < 0xfe) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard_read_block: bad token: %02x\n", tok);
 | 
	
		
			
				|  |  | +	    return -1; /* Bad token */
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if (!--timeout) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard_read_block: reply timeout\n");
 | 
	
		
			
				|  |  | +	    return -1; /* Timeout */
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * At this point the first byte after the token is latched into
 | 
	
		
			
				|  |  | +     * the input shift register. After dealing with alignment,
 | 
	
		
			
				|  |  | +     * use dummy reads to shift in the rest of the first longword.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    /* Shift in bytes if needed for alignment */
 | 
	
		
			
				|  |  | +    if (p.a & 1) {
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +	len--;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    sd_readb(SD_GO8);		/* Now total of 2 bytes latched */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (p.a & 2) {
 | 
	
		
			
				|  |  | +	*p.w++ = sd_readh(SD_GO16);
 | 
	
		
			
				|  |  | +	len -= 2;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    sd_readh(SD_GO16);		/* Now total of 4 bytes latched */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Are we supposed to send a command in parallel?
 | 
	
		
			
				|  |  | +     * This is unsafe for small blocks, but we only need to do this
 | 
	
		
			
				|  |  | +     * for full sectors (used to send STOP_TRANSMISSION).
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    if (xcmd)
 | 
	
		
			
				|  |  | +	len -= 6;	/* Handle the last 6 bytes specially */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    while (len >= 8) {
 | 
	
		
			
				|  |  | +	*p.l++ = sd_readl(SD_GO32);
 | 
	
		
			
				|  |  | +	len -= 4;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (len & 4)
 | 
	
		
			
				|  |  | +	*p.l++ = sd_readl(SD_GO16); /* Consume latched lword + shift in CRC */
 | 
	
		
			
				|  |  | +    if (len & 2)
 | 
	
		
			
				|  |  | +	*p.w++ = sd_readh(SD_GO16);
 | 
	
		
			
				|  |  | +    if (len & 1)
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(SD_GO8 | 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * If we're sending a command in parallel, then we have to
 | 
	
		
			
				|  |  | +     * read in the last 6 bytes in parallel with transmitting the
 | 
	
		
			
				|  |  | +     * command out.  Because pb may be misaligned at this point,
 | 
	
		
			
				|  |  | +     * do the latch reads/writes to memory as byte I/O.
 | 
	
		
			
				|  |  | +     * We still have 2 bytes of data latched, and should end
 | 
	
		
			
				|  |  | +     * with the same (for the CRC).
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    if (xcmd) {
 | 
	
		
			
				|  |  | +	uint8_t crc7;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(1);
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(0);
 | 
	
		
			
				|  |  | +	sd_writeb(xcmd, SD_GO8|SD_CLEARCRC);
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(0);
 | 
	
		
			
				|  |  | +	sd_writel(xarg, SD_GO32|SD_BE);
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(3);
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(2);
 | 
	
		
			
				|  |  | +	*p.b++ = sd_readb(1);
 | 
	
		
			
				|  |  | +	crc7 = sd_crc7_wr();
 | 
	
		
			
				|  |  | +	sd_writeb(crc7, SD_GO8);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Now the CRC is latched in the shift register, and the CRC
 | 
	
		
			
				|  |  | +     * in the CRC generator should be zero.  Shift in the first
 | 
	
		
			
				|  |  | +     * byte after the CRC for the next round.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    zcrc = sd_crc16_rd();
 | 
	
		
			
				|  |  | +    sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (zcrc != 0x0000) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard_read_block: CRC error (zcrc = %04x)\n", zcrc);
 | 
	
		
			
				|  |  | +	return -1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Read a number of sectors; returns the number of sectors read.
 | 
	
		
			
				|  |  | + * The input buffer may be unaligned.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int sdcard_read_sectors(void *buf, uint32_t lba, int count)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +    int okcount = 0;
 | 
	
		
			
				|  |  | +    static const uint16_t stop_transmission[3] =
 | 
	
		
			
				|  |  | +	{ (0x40|CMD_STOP_TRANSMISSION) << 8, 0x0000, 0x0061 };
 | 
	
		
			
				|  |  | +    uint8_t resp;
 | 
	
		
			
				|  |  | +    uint8_t *p = buf;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!count)
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdcard_led_on();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: reading %d sectors at %u to %p\n", count, lba, buf);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (sdc.card_type == 1)
 | 
	
		
			
				|  |  | +	lba <<= SECTOR_SHIFT;	/* Convert to a byte address */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_READ_MULTIPLE_BLOCK, lba);
 | 
	
		
			
				|  |  | +    if (rv) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard: read_multiple error %02x\n", rv);
 | 
	
		
			
				|  |  | +	goto out;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    while (count--) {
 | 
	
		
			
				|  |  | +	rv = sdcard_read_block(p, SECTOR_SIZE, 200000,
 | 
	
		
			
				|  |  | +			       count ? 0 : CMD_STOP_TRANSMISSION, 0);
 | 
	
		
			
				|  |  | +	if (rv)
 | 
	
		
			
				|  |  | +	    goto out;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	okcount++;
 | 
	
		
			
				|  |  | +	p += SECTOR_SIZE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* The first byte after the stop command is undefined */
 | 
	
		
			
				|  |  | +    sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Wait for the response to the STOP TRANSMISSION command */
 | 
	
		
			
				|  |  | +    do {
 | 
	
		
			
				|  |  | +	resp = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +    } while ((int8_t)resp < 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (resp) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard: read_sectors: terminate command error %02x\n",
 | 
	
		
			
				|  |  | +	       resp);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +out:
 | 
	
		
			
				|  |  | +    sdcard_led_off();
 | 
	
		
			
				|  |  | +    return okcount;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DRESULT disk_read(BYTE drive, BYTE *buffer,
 | 
	
		
			
				|  |  | +		  LBA_t sectornumber, UINT sectorcount)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    (void)drive;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_read_sectors(buffer, sectornumber, sectorcount);
 | 
	
		
			
				|  |  | +    return (rv == sectorcount) ? RES_OK : RES_ERROR;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Read CSD/CID
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int sdcard_read_reg(uint8_t opcode, void *buf)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +    uint32_t *bp = buf;
 | 
	
		
			
				|  |  | +    unsigned int i;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(opcode, 0);
 | 
	
		
			
				|  |  | +    if (rv)
 | 
	
		
			
				|  |  | +	return rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_read_block(buf, 16, 2000, 0, 0);
 | 
	
		
			
				|  |  | +    if (rv)
 | 
	
		
			
				|  |  | +	return rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < 4; i++)
 | 
	
		
			
				|  |  | +	bp[i] = __builtin_bswap32(bp[i]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Write a number of sectors; returns the number of sectors written.
 | 
	
		
			
				|  |  | + * The buffer may be unaligned.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int sdcard_write_sectors(const void *buf, uint32_t lba, int count)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +    int okcount = 0;
 | 
	
		
			
				|  |  | +    uint16_t crc;
 | 
	
		
			
				|  |  | +    uint8_t resp;
 | 
	
		
			
				|  |  | +    union xcptr p;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!count)
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    p.b = buf;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdcard_led_on();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: writing %d sectors at %u from %p\n", count, lba, buf);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (sdc.card_type == 1)
 | 
	
		
			
				|  |  | +	lba <<= SECTOR_SHIFT;	/* Convert to a byte address */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_WRITE_MULTIPLE_BLOCK, lba);
 | 
	
		
			
				|  |  | +    if (rv) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard: write_multiple error %02x\n", rv);
 | 
	
		
			
				|  |  | +	goto out;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    while (count--) {
 | 
	
		
			
				|  |  | +	unsigned int podd = p.a & 3;
 | 
	
		
			
				|  |  | +	size_t endloop = (p.a + SECTOR_SIZE) & ~3;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Start block token */
 | 
	
		
			
				|  |  | +	sd_writeb(0xfc, SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Clear the CRC generator; dummy cycle */
 | 
	
		
			
				|  |  | +	sd_writeb(~0, SD_CLEARCRC);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (podd & 1)
 | 
	
		
			
				|  |  | +	    sd_writeb(*p.b++, SD_GO8);
 | 
	
		
			
				|  |  | +	if (podd & 2)
 | 
	
		
			
				|  |  | +	    sd_writeh(*p.w++, SD_GO16);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	while (p.a < endloop)
 | 
	
		
			
				|  |  | +	    sd_writel(*p.l++, SD_GO32);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	podd = -podd;
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	if (podd & 2)
 | 
	
		
			
				|  |  | +	    sd_writeh(*p.w++, SD_GO16);
 | 
	
		
			
				|  |  | +	if (podd & 1)
 | 
	
		
			
				|  |  | +	    sd_writeb(*p.b++, SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	sd_writeh(sd_crc16_wr(), SD_GO16);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Wait for data response token */
 | 
	
		
			
				|  |  | +	do {
 | 
	
		
			
				|  |  | +	    resp = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +	} while ((resp & 0x11) != 0x01);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	resp &= ~0xe0;
 | 
	
		
			
				|  |  | +	if (resp != 0x05) {
 | 
	
		
			
				|  |  | +	    /*
 | 
	
		
			
				|  |  | +	     * Things are confusing here... the spec says
 | 
	
		
			
				|  |  | +	     * that on error we are supposed to issue a
 | 
	
		
			
				|  |  | +	     * STOP_TRANSMISSION command, which isn't the normal
 | 
	
		
			
				|  |  | +	     * thing to do for a write... figure this out later.
 | 
	
		
			
				|  |  | +	     */
 | 
	
		
			
				|  |  | +	    break;	/* Error */
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Wait until the card is ready for the next block */
 | 
	
		
			
				|  |  | +	do {
 | 
	
		
			
				|  |  | +	    resp = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +	} while (resp == 0x00);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	okcount++;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Send stop transmission token */
 | 
	
		
			
				|  |  | +    sd_writeb(0xfd, SD_GO8);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Wait for the card to go busy, then unbusy */
 | 
	
		
			
				|  |  | +    do {
 | 
	
		
			
				|  |  | +	resp = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +    } while (resp != 0x00);
 | 
	
		
			
				|  |  | +    do {
 | 
	
		
			
				|  |  | +	resp = sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +    } while (resp == 0x00);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +out:
 | 
	
		
			
				|  |  | +    sdcard_led_off();
 | 
	
		
			
				|  |  | +    return okcount;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DRESULT disk_write(BYTE drive, const BYTE *buffer, LBA_t sectornumber,
 | 
	
		
			
				|  |  | +		   UINT sectorcount)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (drive != 0)
 | 
	
		
			
				|  |  | +	return STA_NOINIT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_write_sectors(buffer, sectornumber, sectorcount);
 | 
	
		
			
				|  |  | +    return (rv == sectorcount) ? RES_OK : RES_ERROR;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DRESULT disk_ioctl(BYTE drive, BYTE command, void *buffer)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (drive != 0)
 | 
	
		
			
				|  |  | +	return STA_NOINIT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    switch (command) {
 | 
	
		
			
				|  |  | +    case CTRL_SYNC:
 | 
	
		
			
				|  |  | +	return RES_OK;
 | 
	
		
			
				|  |  | +    case GET_SECTOR_SIZE:
 | 
	
		
			
				|  |  | +	*(WORD *)buffer = 512;
 | 
	
		
			
				|  |  | +	return RES_OK;
 | 
	
		
			
				|  |  | +    case GET_SECTOR_COUNT:
 | 
	
		
			
				|  |  | +	*(DWORD *)buffer = sdc.lbasize;
 | 
	
		
			
				|  |  | +	return RES_OK;
 | 
	
		
			
				|  |  | +    case GET_BLOCK_SIZE:
 | 
	
		
			
				|  |  | +	*(DWORD *)buffer = 1; /* XXX */
 | 
	
		
			
				|  |  | +	return RES_OK;
 | 
	
		
			
				|  |  | +    default:
 | 
	
		
			
				|  |  | +	return RES_PARERR;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DWORD get_fattime(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    return SYSCLOCK_DATETIME;	/* Already in FAT format */
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static unsigned long sdcard_compute_size(struct sdcard_info *sdi)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    unsigned int c_size;
 | 
	
		
			
				|  |  | +    unsigned int c_size_mult;
 | 
	
		
			
				|  |  | +    unsigned int read_bl_len;
 | 
	
		
			
				|  |  | +    unsigned long lbasize;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdi->card_type = (sdi->csd.raw[0] >> 30)+1;
 | 
	
		
			
				|  |  | +    switch (sdi->card_type) {
 | 
	
		
			
				|  |  | +    case 1:			/* Classic SD/MMC card */
 | 
	
		
			
				|  |  | +	c_size = ((sdi->csd.raw[2] & 0x3ff) << 2) +
 | 
	
		
			
				|  |  | +	    (sdi->csd.raw[3] >> 30);
 | 
	
		
			
				|  |  | +	c_size_mult = (sdi->csd.raw[2] >> 15) & 7;
 | 
	
		
			
				|  |  | +	read_bl_len = (sdi->csd.raw[1] >> 16) & 0xf;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	lbasize = (c_size + 1) << (c_size_mult + read_bl_len + 2 - 9);
 | 
	
		
			
				|  |  | +	break;
 | 
	
		
			
				|  |  | +    case 2:			/* SDHC/SDXC/eMMC card */
 | 
	
		
			
				|  |  | +	c_size = ((sdi->csd.raw[1] & 0x3f) << 16) +
 | 
	
		
			
				|  |  | +	    (sdi->csd.raw[2] >> 16);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	lbasize = c_size << 10;
 | 
	
		
			
				|  |  | +	break;
 | 
	
		
			
				|  |  | +    default:
 | 
	
		
			
				|  |  | +	sdi->card_type = 0;
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return sdi->lbasize = lbasize;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const char *sdcard_type_name(uint8_t type)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    static const char * const names[] = {
 | 
	
		
			
				|  |  | +	"unknown",
 | 
	
		
			
				|  |  | +	"SD/MMC",
 | 
	
		
			
				|  |  | +	"SDHC/SDXC/eMMC"
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (type >= sizeof names/sizeof names[0])
 | 
	
		
			
				|  |  | +	type = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return names[type];
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void sdcard_try_high_speed(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    int rv;
 | 
	
		
			
				|  |  | +    uint8_t swdata[64];		/* Response from CMD6 */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!(sdc.csd.raw[1] & (1 << 30)))
 | 
	
		
			
				|  |  | +	return;			/* Cmd group 10 = CMD6 not supported */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Try to switch to high speed mode */
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_SWITCH_FUNC, 0x80fffff1);
 | 
	
		
			
				|  |  | +    if (rv)
 | 
	
		
			
				|  |  | +	return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    rv = sdcard_read_block(swdata, sizeof swdata, 2000, 0, 0);
 | 
	
		
			
				|  |  | +    if (rv)
 | 
	
		
			
				|  |  | +	return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if ((swdata[47] & 0x0f) != 1)
 | 
	
		
			
				|  |  | +	return;			/* Failed to switch to high speed mode */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Success, now switch mode! */
 | 
	
		
			
				|  |  | +    sd_readl(SD_GO32);		/* Issue at least 8 clocks; go for 32 */
 | 
	
		
			
				|  |  | +    sd_set_mode(SD_50MHZ, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: switched to high speed\n");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DSTATUS disk_initialize(BYTE drive)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    uint16_t  status;
 | 
	
		
			
				|  |  | +    uint32_t try_sdhc;
 | 
	
		
			
				|  |  | +    bool is_sd;
 | 
	
		
			
				|  |  | +    int i, rv;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (drive != 0)
 | 
	
		
			
				|  |  | +	return STA_NOINIT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    memset(&sdc, 0, sizeof sdc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if 0
 | 
	
		
			
				|  |  | +    status = /* Check card detect if present */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!(status & 0x04)) {
 | 
	
		
			
				|  |  | +	con_printf("No memory card installed\n");
 | 
	
		
			
				|  |  | +	return sdc.status = STA_NOINIT|STA_NODISK;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdcard_led_on();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Generate 256 clock cycles in slow mode, with CS# high */
 | 
	
		
			
				|  |  | +    sd_set_mode(SD_SLOW, false);
 | 
	
		
			
				|  |  | +    for (i = 0; i < 8; i++)
 | 
	
		
			
				|  |  | +	sd_writel(~0, SD_GO32);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Assert CS# and send reset command; if no response assume no disk */
 | 
	
		
			
				|  |  | +    sd_set_mode(SD_SLOW, true);
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_GO_IDLE_STATE, 0);
 | 
	
		
			
				|  |  | +    if (rv != 0x01) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard: CMD0 error %02x\n", rv);
 | 
	
		
			
				|  |  | +	sdcard_led_off();
 | 
	
		
			
				|  |  | +	return sdc.status = STA_NOINIT | STA_NODISK;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Switch to 20 MHz */
 | 
	
		
			
				|  |  | +    sd_set_mode(SD_20MHZ, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Enable command CRC checking (ignore result) */
 | 
	
		
			
				|  |  | +    sdcard_send_cmd(CMD_CRC_ON_OFF, 0x0001);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Probe for extended features */
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Bit  7:0 = check pattern
 | 
	
		
			
				|  |  | +     * Bit 11:8 = supply voltage (3.3 V)
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_SEND_IF_COND, 0x01aa);
 | 
	
		
			
				|  |  | +    if ((rv & 0x04) == 0) {
 | 
	
		
			
				|  |  | +	/* CMD8 supported */
 | 
	
		
			
				|  |  | +	if (rv & ~0x03) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard; CMD8 error %02x\n", rv);
 | 
	
		
			
				|  |  | +	    sdcard_led_off();
 | 
	
		
			
				|  |  | +	    return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Shift in additional data bytes */
 | 
	
		
			
				|  |  | +	sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +	sd_readh(SD_GO16);
 | 
	
		
			
				|  |  | +	sdc.if_cond = sd_readl(SD_GO16|SD_BE);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	con_printf("sdcard: CMD8 returned 0x%08x\n", sdc.if_cond);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if ((sdc.if_cond & 0x1ff) != 0x1aa) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard: CMD8 reports unusable card (0x%x)\n",
 | 
	
		
			
				|  |  | +		   sdc.if_cond);
 | 
	
		
			
				|  |  | +	    sdcard_led_off();
 | 
	
		
			
				|  |  | +	    return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	try_sdhc = 1 << 30;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +	try_sdhc = 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* Initialize card */
 | 
	
		
			
				|  |  | +    do {
 | 
	
		
			
				|  |  | +	rv = sdcard_send_acmd(ACMD_SD_SEND_OP_COND, try_sdhc);
 | 
	
		
			
				|  |  | +	if (rv & 0x04)
 | 
	
		
			
				|  |  | +	    break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (rv & ~0x01) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard: ACMD41 error %02x\n", rv);
 | 
	
		
			
				|  |  | +	    sdcard_led_off();
 | 
	
		
			
				|  |  | +	    return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    } while (rv);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!rv) {
 | 
	
		
			
				|  |  | +	/* ACMD41 successful; this is an SD card: can switch to 25 MHz */
 | 
	
		
			
				|  |  | +	is_sd = true;
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	sd_set_mode(SD_25MHZ, true);
 | 
	
		
			
				|  |  | +	rv = sdcard_send_cmd(CMD_READ_OCR, try_sdhc);
 | 
	
		
			
				|  |  | +	if (rv) {
 | 
	
		
			
				|  |  | +	    con_printf("sdcard: CMD58 error %02x\n", rv);
 | 
	
		
			
				|  |  | +	    sdcard_led_off();
 | 
	
		
			
				|  |  | +	    return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	/* Shift in additional data bytes */
 | 
	
		
			
				|  |  | +	sd_readb(SD_GO8);
 | 
	
		
			
				|  |  | +	sd_readh(SD_GO16);
 | 
	
		
			
				|  |  | +	sdc.ocr = sd_readl(SD_GO16|SD_BE);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +	/* ACMD41 unsupported, try CMD1 */
 | 
	
		
			
				|  |  | +	is_sd = false;
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +	do {
 | 
	
		
			
				|  |  | +	    rv = sdcard_send_cmd(CMD_SEND_OP_COND, 0);
 | 
	
		
			
				|  |  | +	    if (rv & ~0x01) {
 | 
	
		
			
				|  |  | +		con_printf("sdcard: CMD1 error %02x\n", rv);
 | 
	
		
			
				|  |  | +		sdcard_led_off();
 | 
	
		
			
				|  |  | +		return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +	    }
 | 
	
		
			
				|  |  | +	} while (rv);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Set block length -- some cards power up to a larger block size
 | 
	
		
			
				|  |  | +     * than 512 bytes even though that violates the spec.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    rv = sdcard_send_cmd(CMD_SET_BLOCKLEN, 512);
 | 
	
		
			
				|  |  | +    if (rv) {
 | 
	
		
			
				|  |  | +	con_printf("sdcard: CMD16 error %02x\n", rv);
 | 
	
		
			
				|  |  | +	sdcard_led_off();
 | 
	
		
			
				|  |  | +	return sdc.status = STA_NOINIT;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Read CSD and CID
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    if (sdcard_read_reg(CMD_SEND_CSD, &sdc.csd))
 | 
	
		
			
				|  |  | +	memset(&sdc.csd, 0, sizeof sdc.csd);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: CSD: %08x %08x %08x %08x\n",
 | 
	
		
			
				|  |  | +	   sdc.csd.raw[0], sdc.csd.raw[1], sdc.csd.raw[2], sdc.csd.raw[3]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (sdcard_read_reg(CMD_SEND_CID, &sdc.cid))
 | 
	
		
			
				|  |  | +	memset(&sdc.cid, 0, sizeof sdc.cid);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: CID: %08x %08x %08x %08x\n",
 | 
	
		
			
				|  |  | +	   sdc.cid.raw[0], sdc.cid.raw[1], sdc.cid.raw[2], sdc.cid.raw[3]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdcard_compute_size(&sdc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    con_printf("sdcard: %s card found, capacity %u sectors\n",
 | 
	
		
			
				|  |  | +	   sdcard_type_name(sdc.card_type), sdc.lbasize);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /*
 | 
	
		
			
				|  |  | +     * Try to switch to 50 MHz (optional)
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    if (is_sd)
 | 
	
		
			
				|  |  | +	sdcard_try_high_speed();
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    sdc.status = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    sdcard_led_off();
 | 
	
		
			
				|  |  | +    return sdc.status;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +DSTATUS disk_status(BYTE drive)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (drive != 0)
 | 
	
		
			
				|  |  | +	return STA_NOINIT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return sdc.status;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static FATFS sd_fs;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int disk_init(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    return f_mount(&sd_fs, "", 1);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 |