/* * $Id: sdmmc_spi.c,v 1.4 2007/12/25 14:10:51 nbrk Exp $ */ #include #include #include #include #include #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. */ struct spisdmmc_dd { struct spi_bus_handle *ss_sbhp; struct sdmmc_bus_handle ss_smbh; }; 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 = { sizeof(struct spisdmmc_dd), spisdmmc_attach, NULL, NULL }; int spisdmmc_attach(struct device *self, uint32_t loc, uint8_t flags) { struct spisdmmc_dd *ddp = self->dv_devdata; ddp->ss_sbhp = self->dv_parent->dv_aux; 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); } uint8_t spisdmmc_init(void *dd) { struct spisdmmc_dd *ddp = dd; uint8_t i, resp; /* * Initialize SD/MMC card so it can accept generic read/write commands. * Remember that we should hold CS signal low during all transactions (cmd, resp, data). */ /* CS high */ spi_cs_high(ddp->ss_sbhp); /* wait 80 pulses */ for (i =0; i < 9; i++) spi_transmit(ddp->ss_sbhp, 0xff); /* * Send CMD0_GO_IDLE_STATE * CS line will go low in send_command and up before return here. */ resp = spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, 0x95); printf("spisdmmc_init: CMD0 reply 0x%x\n", resp); return(0); } uint8_t 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; uint8_t to = 0; /* construct frame */ sdcf = sdmmc_command(cmd, arg, crc); // 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); /* transmit 32 bit argument MSB first */ spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 24 ); 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 */ 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. */ };