version 1.1, 2007/12/20 15:23:15 |
version 1.2, 2007/12/21 17:45:26 |
|
|
}; |
}; |
|
|
int spisdmmc_attach(struct device *, uint32_t, uint8_t); |
int spisdmmc_attach(struct device *, uint32_t, uint8_t); |
|
void spisdmmc_send_command(struct spisdmmc_dd *ddp, uint8_t cmd, uint32_t arg, uint32_t crc); |
|
uint8_t spisdmmc_get_response(struct spisdmmc_dd *ddp); |
|
int spisdmmc_set_block_size(struct spisdmmc_dd *ddp, uint16_t blksize); |
|
void spisdmmc_read_block(struct spisdmmc_dd *ddp, uint16_t nbytes, char *buffp); |
|
uint8_t spisdmmc_get_token(struct spisdmmc_dd *ddp); |
|
void spisdmmc_read_cid(struct spisdmmc_dd *ddp, struct sdmmc_cid *cidp); |
|
|
|
|
|
|
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"); |
|
|
|
spisdmmc_init(ddp); |
|
|
return(0); |
return(0); |
} |
} |
|
|
|
|
spisdmmc_init(struct spisdmmc_dd *ddp) |
spisdmmc_init(struct spisdmmc_dd *ddp) |
{ |
{ |
uint8_t i, resp; |
uint8_t i, resp; |
|
struct sdmmc_cid cid; |
|
|
/* |
/* |
* Apply 80 clock pulses. |
* Apply 80 clock pulses. |
* XXX assert CS. (NPCS0) |
* XXX assert CS. (NPCS0) |
*/ |
*/ |
|
spi_cs_high(ddp->ss_sbhp); |
for (i = 0; i < 9; i++) |
for (i = 0; i < 9; i++) |
spi_transmit(ddp->ss_sbhp, 0xff); |
spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
/* |
/* |
* Apply 16 clocks. |
* Apply 16 clocks. |
* XXX deassert CS. |
* XXX deassert CS. |
*/ |
*/ |
for (i = 0; i < 2; 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. |
*/ |
*/ |
|
spi_cs_low(ddp->ss_sbhp); |
spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, CMD0_HARDCODED_CRC); |
spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, CMD0_HARDCODED_CRC); |
if (spisdmmc_get_response(ddp) != 0) { |
|
|
resp = spisdmmc_get_response(ddp); |
|
|
|
if (resp != 0) { |
printf("spisdmmc_init: CMD0_GO_IDLE_STATE failed with %d\n", resp); |
printf("spisdmmc_init: CMD0_GO_IDLE_STATE failed with %d\n", resp); |
|
|
return(-1); |
return(-1); |
} |
} |
|
printf("spisdmmc_init: Card replied to CMD0_GO_IDLE_STATE with 0x%x\n", resp); |
|
|
|
/* |
|
* Okay. Try to initialize the card. |
|
* Periodically send CMD1 and poll for response with 0x01 cleared (card exit Idle state). |
|
* CRC is off so we can set it 0xff. |
|
* XXX send 0xff before. |
|
*/ |
|
// spi_transmit(ddp->ss_sbhp, 0xff); |
|
do { |
|
spisdmmc_send_command(ddp, CMD1_SEND_OP_COND, 0, 0xff); |
|
resp = spisdmmc_get_response(ddp); |
|
} while(resp != 0x00); |
|
|
|
printf("spisdmmc_init: Card initialization completed\n"); |
|
|
|
/* |
|
* Set block size to 512 bytes. |
|
*/ |
|
if(spisdmmc_set_block_size(ddp, SDMMC_BLOCK_SIZE) == -1) { |
|
printf("spisdmmc_init: failed to set block size\n"); |
|
return(-1); |
|
} |
|
printf("spisdmmc_init: block size set to %d\n", SDMMC_BLOCK_SIZE); |
|
|
|
spisdmmc_read_cid(ddp, &cid); |
|
printf("cid.pnm: %s%s%s%s%s\n", cid.cid_pnm[0], cid.cid_pnm[1], cid.cid_pnm[2], cid.cid_pnm[3], cid.cid_pnm[4]); |
|
|
|
return(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); |
|
/* 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 XXX MSB first */ |
|
|
spi_transmit(ddp->ss_sbhp, ((sdcf.sc_arg & (0xff000000 >> i)) >> (8 * (4 - i + 1))) ); |
spi_transmit(ddp->ss_sbhp, ((sdcf.sc_arg & (0xff000000 >> i)) >> (8 * (4 - i + 1))) ); |
|
|
/* transmit CRC field */ |
/* transmit CRC field */ |
spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd) |
spi_transmit(ddp->ss_sbhp, sdcf.sc_crc); |
|
|
|
/* XXX Apply NCR clock pulses */ |
|
// spi_transmit(ddp->ss_sbhp, 0xff); |
} |
} |
|
|
|
|
|
uint8_t |
|
spisdmmc_get_response(struct spisdmmc_dd *ddp) |
|
{ |
|
uint8_t i, resp; |
|
/* |
|
* Get response from card. |
|
* After command response comes in 8 to 64 pulses (all this bits will be 1). |
|
*/ |
|
|
|
for(i = 0; i < 64; i++) { |
|
resp = spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
|
if (resp == 0x00 || resp == 0x01) |
|
return(resp); |
|
} |
|
} |
|
|
|
uint8_t |
|
spisdmmc_get_token(struct spisdmmc_dd *ddp) |
|
{ |
|
uint16_t i, token; |
|
/* |
|
* Get token. |
|
*/ |
|
|
|
for(i = 0; i < 1000; i++) { |
|
token = spi_transmit(ddp->ss_sbhp, 0xff); |
|
|
|
if (token == TOK_READ_BLOCK || token == TOK_WRITE_BLOCK_MULTIPLE) |
|
return(token); |
|
} |
|
} |
|
|
|
|
|
int |
|
spisdmmc_set_block_size(struct spisdmmc_dd *ddp, uint16_t blksize) |
|
{ |
|
uint8_t resp; |
|
/* |
|
* Set block size (CMD16). |
|
*/ |
|
spisdmmc_send_command(ddp,CMD16_SET_BLOCKLEN, blksize, 0xff); |
|
resp = spisdmmc_get_response(ddp); |
|
if (resp != 0x00) { |
|
printf("spisdmmc_set_block_size: set block size to %d failed (0x%x)\n", blksize, resp); |
|
return(-1); |
|
} |
|
return(0); |
|
} |
|
|
|
void |
|
spisdmmc_read_cid(struct spisdmmc_dd *ddp, struct sdmmc_cid *cidp) |
|
{ |
|
uint8_t resp; |
|
struct sdmmc_cid cid; |
|
/* |
|
* Read Card ID register. |
|
*/ |
|
spisdmmc_send_command(ddp, CMD10_SEND_CID, 0, 0xff); |
|
resp = spisdmmc_get_response(ddp); |
|
printf("spisdmmc_read_cid: got r1 response 0x%x\n", resp); |
|
|
|
/* |
|
* Wait R1 response. |
|
*/ |
|
if (resp != 0x00) { |
|
printf("spisdmmc_read_cid: bad response 0x%x\n", resp); |
|
return; |
|
} |
|
|
|
spisdmmc_read_block(ddp, 16, (char *)&cid); |
|
} |
|
|
|
|
|
void |
|
spisdmmc_read_block(struct spisdmmc_dd *ddp, uint16_t nbytes, char *buffp) |
|
{ |
|
uint8_t token; |
|
uint16_t crc; |
|
/* |
|
* Read block. |
|
*/ |
|
token = spisdmmc_get_token(ddp); |
|
printf("spisdmmc_read_block: got token 0x%x\n", token); |
|
if (token != TOK_READ_BLOCK) { |
|
printf("spisdmmc_read_block: error, foreign token\n"); |
|
return; |
|
} |
|
/* |
|
* Okay, next is the data. |
|
*/ |
|
while(nbytes != 0) { |
|
*buffp = spi_transmit(ddp->ss_sbhp, 0xff); |
|
printf("byte read 0x%x\n", *buffp); |
|
|
|
buffp++; |
|
nbytes--; |
|
} |
|
|
|
/* |
|
* Read CRC. |
|
*/ |
|
crc = spi_transmit(ddp->ss_sbhp, 0xff); |
|
crc <<= 8; |
|
crc = spi_transmit(ddp->ss_sbhp, 0xff); |
|
}; |
|
|