version 1.1, 2007/12/20 15:23:15 |
version 1.4, 2007/12/25 14:10:51 |
|
|
#include <dev/sdmmc/sdmmcvar.h> |
#include <dev/sdmmc/sdmmcvar.h> |
#include <libkern/printf.h> |
#include <libkern/printf.h> |
|
|
|
#define SPISDMMC_DEBUG |
|
|
|
#ifdef SPISDMMC_DEBUG |
|
#define DPRINTF(x...) do { printf(x); } while (0) |
|
#else |
|
#define DPRINTF(x...) { } |
|
#endif |
|
|
|
#define SPISDMMC_RESP_TIMEOUT 64 |
|
|
/* |
/* |
* SD/MMC conversation over SPI bus. |
* SD/MMC conversation over SPI bus. |
*/ |
*/ |
struct spisdmmc_dd { |
struct spisdmmc_dd { |
struct spi_bus_handle *ss_sbhp; |
struct spi_bus_handle *ss_sbhp; |
|
|
// struct sdmmc_bus_handle; |
struct sdmmc_bus_handle ss_smbh; |
}; |
}; |
|
|
int spisdmmc_attach(struct device *, uint32_t, uint8_t); |
int spisdmmc_attach(struct device *, uint32_t, uint8_t); |
|
uint8_t spisdmmc_init(void *dd); |
|
uint8_t spisdmmc_send_command(void *dd, uint8_t cmd, uint32_t arg, uint8_t crc); |
|
uint8_t spisdmmc_set_block_size(void *dd, uint16_t size); |
|
uint8_t spisdmmc_read_block(void *dd, uint32_t addr, char *buffp); |
|
|
|
|
|
|
struct driver spisdmmc_dr = { |
struct driver spisdmmc_dr = { |
sizeof(struct spisdmmc_dd), |
sizeof(struct spisdmmc_dd), |
spisdmmc_attach, |
spisdmmc_attach, |
|
|
|
|
printf("SPI SD/MMC\n"); |
printf("SPI SD/MMC\n"); |
|
|
|
/* |
|
* Interface to children. |
|
*/ |
|
ddp->ss_smbh.sb_init = spisdmmc_init; |
|
ddp->ss_smbh.sb_send_command = spisdmmc_send_command; |
|
ddp->ss_smbh.sb_set_block_size = spisdmmc_set_block_size; |
|
// ddp->ss_smbh.sb_read_block = spisdmmc_read_block; |
|
// ddp->ss_smbh.sb_write_block = spisdmmc_write_block; |
|
ddp->ss_smbh.sb_dd = ddp; |
|
|
|
self->dv_aux = &ddp->ss_smbh; |
|
|
return(0); |
return(0); |
} |
} |
|
|
|
|
int |
uint8_t |
spisdmmc_init(struct spisdmmc_dd *ddp) |
spisdmmc_init(void *dd) |
{ |
{ |
|
struct spisdmmc_dd *ddp = dd; |
uint8_t i, resp; |
uint8_t i, resp; |
|
|
/* |
/* |
* Apply 80 clock pulses. |
* Initialize SD/MMC card so it can accept generic read/write commands. |
* XXX assert CS. (NPCS0) |
* Remember that we should hold CS signal low during all transactions (cmd, resp, data). |
*/ |
*/ |
for (i = 0; i < 9; i++) |
|
spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
|
/* |
/* CS high */ |
* Apply 16 clocks. |
spi_cs_high(ddp->ss_sbhp); |
* XXX deassert CS. |
/* wait 80 pulses */ |
*/ |
for (i =0; i < 9; i++) |
for (i = 0; i < 2; i ++) |
|
spi_transmit(ddp->ss_sbhp, 0xff); |
spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
/* |
/* |
* Send CMD0_GO_IDLE_STATE. |
* Send CMD0_GO_IDLE_STATE |
|
* CS line will go low in send_command and up before return here. |
*/ |
*/ |
spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, CMD0_HARDCODED_CRC); |
resp = spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, 0x95); |
if (spisdmmc_get_response(ddp) != 0) { |
printf("spisdmmc_init: CMD0 reply 0x%x\n", resp); |
printf("spisdmmc_init: CMD0_GO_IDLE_STATE failed with %d\n", resp); |
|
|
|
return(-1); |
return(0); |
} |
|
|
|
} |
} |
|
|
|
|
void |
uint8_t |
spisdmmc_send_command(struct spisdmmc_dd *ddp, uint8_t cmd, uint32_t arg, uint32_t crc) |
spisdmmc_send_command(void *dd, uint8_t cmd, uint32_t arg, uint8_t crc) |
{ |
{ |
|
struct spisdmmc_dd *ddp = dd; |
|
uint8_t resp; |
|
/* |
|
* Construct command frame and send command to the card. |
|
* Try to read reply SASPI_RESP_TIMEOUT times and return it if it's R1 response. |
|
* Return 255 if timeout. |
|
*/ |
struct sdmmc_cmdframe sdcf; |
struct sdmmc_cmdframe sdcf; |
uint8_t i; |
uint8_t to = 0; |
|
|
/* construct frame */ |
/* construct frame */ |
sdcf = sdmmc_command(cmd, arg, crc); |
sdcf = sdmmc_command(cmd, arg, crc); |
|
|
/* transmit token */ |
// printf("spisdmmc_send_command: sending cmd %d arg 0x%x crc 0x%x\n", sdcf.sc_cmd & ~0xc0, sdcf.sc_arg, sdcf.sc_crc); |
|
/* CS low */ |
|
spi_cs_low(ddp->ss_sbhp); |
|
|
|
/* transmit cmd */ |
spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd); |
spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd); |
|
|
/* transmit 32 bit argument XXX MSB first */ |
/* transmit 32 bit argument MSB first */ |
for(i = 0; i < 4; i++) |
spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 24 ); |
spi_transmit(ddp->ss_sbhp, ((sdcf.sc_arg & (0xff000000 >> i)) >> (8 * (4 - i + 1))) ); |
spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 16 ); |
|
spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 8 ); |
|
spi_transmit(ddp->ss_sbhp, sdcf.sc_arg ); |
|
|
/* transmit CRC field */ |
/* transmit CRC field */ |
spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd) |
spi_transmit(ddp->ss_sbhp, sdcf.sc_crc); |
|
|
|
/* CS high */ |
|
spi_cs_high(ddp->ss_sbhp); |
|
|
|
/* XXX Apply NCR clock pulses */ |
|
spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
|
/* CS low */ |
|
spi_cs_low(ddp->ss_sbhp); |
|
|
|
/* read response */ |
|
/* XXX what about non-R1 responses? */ |
|
while(to < SPISDMMC_RESP_TIMEOUT) { |
|
resp = spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
|
// if (resp & 0x80 == 0) { |
|
if (resp != 0xff) { |
|
/* seems like R1 response */ |
|
spi_cs_high(ddp->ss_sbhp); |
|
|
|
return(resp); |
|
} |
|
|
|
to++; |
|
} |
|
|
|
/* timeout */ |
|
spi_cs_high(ddp->ss_sbhp); |
|
|
|
DPRINTF("spisdmmc_send_command: response timeout for cmd %d arg 0x%x crc 0x%x\n", cmd, arg, crc); |
|
|
|
return(0xff); /* error */ |
} |
} |
|
|
|
|
|
uint8_t |
|
spisdmmc_set_block_size(void *dd, uint16_t blksize) |
|
{ |
|
struct spisdmmc_dd *ddp = dd; |
|
uint8_t resp; |
|
/* |
|
* Set block size (CMD16). |
|
*/ |
|
|
|
return(0); |
|
} |
|
|
|
|
|
uint8_t |
|
spisdmmc_read_block(void *dd, uint32_t addr, char *buff) |
|
{ |
|
struct spisdmmc_dd *ddp = dd; |
|
/* |
|
* Read block. |
|
*/ |
|
}; |
|
|