1d52ebf10SThomas Chou /* 2d52ebf10SThomas Chou * generic mmc spi driver 3d52ebf10SThomas Chou * 4d52ebf10SThomas Chou * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> 5d52ebf10SThomas Chou * Licensed under the GPL-2 or later. 6d52ebf10SThomas Chou */ 7d52ebf10SThomas Chou #include <common.h> 8d52ebf10SThomas Chou #include <malloc.h> 9d52ebf10SThomas Chou #include <part.h> 10d52ebf10SThomas Chou #include <mmc.h> 11d52ebf10SThomas Chou #include <spi.h> 12d52ebf10SThomas Chou #include <crc.h> 13d52ebf10SThomas Chou #include <linux/crc7.h> 14*6f67b69bSYoshinori Sato #include <asm/byteorder.h> 15d52ebf10SThomas Chou 16d52ebf10SThomas Chou /* MMC/SD in SPI mode reports R1 status always */ 17d52ebf10SThomas Chou #define R1_SPI_IDLE (1 << 0) 18d52ebf10SThomas Chou #define R1_SPI_ERASE_RESET (1 << 1) 19d52ebf10SThomas Chou #define R1_SPI_ILLEGAL_COMMAND (1 << 2) 20d52ebf10SThomas Chou #define R1_SPI_COM_CRC (1 << 3) 21d52ebf10SThomas Chou #define R1_SPI_ERASE_SEQ (1 << 4) 22d52ebf10SThomas Chou #define R1_SPI_ADDRESS (1 << 5) 23d52ebf10SThomas Chou #define R1_SPI_PARAMETER (1 << 6) 24d52ebf10SThomas Chou /* R1 bit 7 is always zero, reuse this bit for error */ 25d52ebf10SThomas Chou #define R1_SPI_ERROR (1 << 7) 26d52ebf10SThomas Chou 27d52ebf10SThomas Chou /* Response tokens used to ack each block written: */ 28d52ebf10SThomas Chou #define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) 29d52ebf10SThomas Chou #define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) 30d52ebf10SThomas Chou #define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) 31d52ebf10SThomas Chou #define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) 32d52ebf10SThomas Chou 33d52ebf10SThomas Chou /* Read and write blocks start with these tokens and end with crc; 34d52ebf10SThomas Chou * on error, read tokens act like a subset of R2_SPI_* values. 35d52ebf10SThomas Chou */ 36d52ebf10SThomas Chou #define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ 37d52ebf10SThomas Chou #define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ 38d52ebf10SThomas Chou #define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ 39d52ebf10SThomas Chou 40d52ebf10SThomas Chou /* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ 41d52ebf10SThomas Chou #define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) 42d52ebf10SThomas Chou 43d52ebf10SThomas Chou /* bus capability */ 44d52ebf10SThomas Chou #define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) 45d52ebf10SThomas Chou #define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ 46d52ebf10SThomas Chou 47d52ebf10SThomas Chou /* timeout value */ 48d52ebf10SThomas Chou #define CTOUT 8 49d52ebf10SThomas Chou #define RTOUT 3000000 /* 1 sec */ 50d52ebf10SThomas Chou #define WTOUT 3000000 /* 1 sec */ 51d52ebf10SThomas Chou 52d52ebf10SThomas Chou static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) 53d52ebf10SThomas Chou { 54d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 55d52ebf10SThomas Chou u8 cmdo[7]; 56d52ebf10SThomas Chou u8 r1; 57d52ebf10SThomas Chou int i; 58d52ebf10SThomas Chou cmdo[0] = 0xff; 59d52ebf10SThomas Chou cmdo[1] = MMC_SPI_CMD(cmdidx); 60d52ebf10SThomas Chou cmdo[2] = cmdarg >> 24; 61d52ebf10SThomas Chou cmdo[3] = cmdarg >> 16; 62d52ebf10SThomas Chou cmdo[4] = cmdarg >> 8; 63d52ebf10SThomas Chou cmdo[5] = cmdarg; 64d52ebf10SThomas Chou cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; 65d52ebf10SThomas Chou spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); 66d52ebf10SThomas Chou for (i = 0; i < CTOUT; i++) { 67d52ebf10SThomas Chou spi_xfer(spi, 1 * 8, NULL, &r1, 0); 68d52ebf10SThomas Chou if (i && (r1 & 0x80) == 0) /* r1 response */ 69d52ebf10SThomas Chou break; 70d52ebf10SThomas Chou } 71d52ebf10SThomas Chou debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); 72d52ebf10SThomas Chou return r1; 73d52ebf10SThomas Chou } 74d52ebf10SThomas Chou 75d52ebf10SThomas Chou static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, 76d52ebf10SThomas Chou u32 bcnt, u32 bsize) 77d52ebf10SThomas Chou { 78d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 79d52ebf10SThomas Chou u8 *buf = xbuf; 80d52ebf10SThomas Chou u8 r1; 81d52ebf10SThomas Chou u16 crc; 82d52ebf10SThomas Chou int i; 83d52ebf10SThomas Chou while (bcnt--) { 84d52ebf10SThomas Chou for (i = 0; i < RTOUT; i++) { 85d52ebf10SThomas Chou spi_xfer(spi, 1 * 8, NULL, &r1, 0); 86d52ebf10SThomas Chou if (r1 != 0xff) /* data token */ 87d52ebf10SThomas Chou break; 88d52ebf10SThomas Chou } 89d52ebf10SThomas Chou debug("%s:tok%d %x\n", __func__, i, r1); 90d52ebf10SThomas Chou if (r1 == SPI_TOKEN_SINGLE) { 91d52ebf10SThomas Chou spi_xfer(spi, bsize * 8, NULL, buf, 0); 92d52ebf10SThomas Chou spi_xfer(spi, 2 * 8, NULL, &crc, 0); 93d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON 94*6f67b69bSYoshinori Sato if (be_to_cpu16(cyg_crc16(buf, bsize)) != crc) { 9593bfd616SPantelis Antoniou debug("%s: CRC error\n", mmc->cfg->name); 96d52ebf10SThomas Chou r1 = R1_SPI_COM_CRC; 97d52ebf10SThomas Chou break; 98d52ebf10SThomas Chou } 99d52ebf10SThomas Chou #endif 100d52ebf10SThomas Chou r1 = 0; 101d52ebf10SThomas Chou } else { 102d52ebf10SThomas Chou r1 = R1_SPI_ERROR; 103d52ebf10SThomas Chou break; 104d52ebf10SThomas Chou } 105d52ebf10SThomas Chou buf += bsize; 106d52ebf10SThomas Chou } 107d52ebf10SThomas Chou return r1; 108d52ebf10SThomas Chou } 109d52ebf10SThomas Chou 110d52ebf10SThomas Chou static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, 111d52ebf10SThomas Chou u32 bcnt, u32 bsize, int multi) 112d52ebf10SThomas Chou { 113d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 114d52ebf10SThomas Chou const u8 *buf = xbuf; 115d52ebf10SThomas Chou u8 r1; 116d52ebf10SThomas Chou u16 crc; 117d52ebf10SThomas Chou u8 tok[2]; 118d52ebf10SThomas Chou int i; 119d52ebf10SThomas Chou tok[0] = 0xff; 120d52ebf10SThomas Chou tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; 121d52ebf10SThomas Chou while (bcnt--) { 122d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON 123*6f67b69bSYoshinori Sato crc = cpu_to_be16(cyg_crc16((u8 *)buf, bsize)); 124d52ebf10SThomas Chou #endif 125d52ebf10SThomas Chou spi_xfer(spi, 2 * 8, tok, NULL, 0); 126d52ebf10SThomas Chou spi_xfer(spi, bsize * 8, buf, NULL, 0); 127d52ebf10SThomas Chou spi_xfer(spi, 2 * 8, &crc, NULL, 0); 128d52ebf10SThomas Chou for (i = 0; i < CTOUT; i++) { 129d52ebf10SThomas Chou spi_xfer(spi, 1 * 8, NULL, &r1, 0); 130d52ebf10SThomas Chou if ((r1 & 0x10) == 0) /* response token */ 131d52ebf10SThomas Chou break; 132d52ebf10SThomas Chou } 133d52ebf10SThomas Chou debug("%s:tok%d %x\n", __func__, i, r1); 134d52ebf10SThomas Chou if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { 135d52ebf10SThomas Chou for (i = 0; i < WTOUT; i++) { /* wait busy */ 136d52ebf10SThomas Chou spi_xfer(spi, 1 * 8, NULL, &r1, 0); 137d52ebf10SThomas Chou if (i && r1 == 0xff) { 138d52ebf10SThomas Chou r1 = 0; 139d52ebf10SThomas Chou break; 140d52ebf10SThomas Chou } 141d52ebf10SThomas Chou } 142d52ebf10SThomas Chou if (i == WTOUT) { 143d52ebf10SThomas Chou debug("%s:wtout %x\n", __func__, r1); 144d52ebf10SThomas Chou r1 = R1_SPI_ERROR; 145d52ebf10SThomas Chou break; 146d52ebf10SThomas Chou } 147d52ebf10SThomas Chou } else { 148d52ebf10SThomas Chou debug("%s: err %x\n", __func__, r1); 149d52ebf10SThomas Chou r1 = R1_SPI_COM_CRC; 150d52ebf10SThomas Chou break; 151d52ebf10SThomas Chou } 152d52ebf10SThomas Chou buf += bsize; 153d52ebf10SThomas Chou } 154d52ebf10SThomas Chou if (multi && bcnt == -1) { /* stop multi write */ 155d52ebf10SThomas Chou tok[1] = SPI_TOKEN_STOP_TRAN; 156d52ebf10SThomas Chou spi_xfer(spi, 2 * 8, tok, NULL, 0); 157d52ebf10SThomas Chou for (i = 0; i < WTOUT; i++) { /* wait busy */ 158d52ebf10SThomas Chou spi_xfer(spi, 1 * 8, NULL, &r1, 0); 159d52ebf10SThomas Chou if (i && r1 == 0xff) { 160d52ebf10SThomas Chou r1 = 0; 161d52ebf10SThomas Chou break; 162d52ebf10SThomas Chou } 163d52ebf10SThomas Chou } 164d52ebf10SThomas Chou if (i == WTOUT) { 165d52ebf10SThomas Chou debug("%s:wstop %x\n", __func__, r1); 166d52ebf10SThomas Chou r1 = R1_SPI_ERROR; 167d52ebf10SThomas Chou } 168d52ebf10SThomas Chou } 169d52ebf10SThomas Chou return r1; 170d52ebf10SThomas Chou } 171d52ebf10SThomas Chou 172d52ebf10SThomas Chou static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, 173d52ebf10SThomas Chou struct mmc_data *data) 174d52ebf10SThomas Chou { 175d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 176d52ebf10SThomas Chou u8 r1; 177d52ebf10SThomas Chou int i; 178d52ebf10SThomas Chou int ret = 0; 179d6b2e508SMarek Vasut debug("%s:cmd%d %x %x\n", __func__, 180d6b2e508SMarek Vasut cmd->cmdidx, cmd->resp_type, cmd->cmdarg); 181d52ebf10SThomas Chou spi_claim_bus(spi); 182d52ebf10SThomas Chou spi_cs_activate(spi); 183d52ebf10SThomas Chou r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); 184d52ebf10SThomas Chou if (r1 == 0xff) { /* no response */ 185d52ebf10SThomas Chou ret = NO_CARD_ERR; 186d52ebf10SThomas Chou goto done; 187d52ebf10SThomas Chou } else if (r1 & R1_SPI_COM_CRC) { 188d52ebf10SThomas Chou ret = COMM_ERR; 189d52ebf10SThomas Chou goto done; 190d52ebf10SThomas Chou } else if (r1 & ~R1_SPI_IDLE) { /* other errors */ 191d52ebf10SThomas Chou ret = TIMEOUT; 192d52ebf10SThomas Chou goto done; 193d52ebf10SThomas Chou } else if (cmd->resp_type == MMC_RSP_R2) { 194d52ebf10SThomas Chou r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); 195d52ebf10SThomas Chou for (i = 0; i < 4; i++) 196*6f67b69bSYoshinori Sato cmd->response[i] = be32_to_cpu(cmd->response[i]); 197d52ebf10SThomas Chou debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], 198d52ebf10SThomas Chou cmd->response[2], cmd->response[3]); 199d52ebf10SThomas Chou } else if (!data) { 200d52ebf10SThomas Chou switch (cmd->cmdidx) { 201d52ebf10SThomas Chou case SD_CMD_APP_SEND_OP_COND: 202d52ebf10SThomas Chou case MMC_CMD_SEND_OP_COND: 203d52ebf10SThomas Chou cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; 204d52ebf10SThomas Chou break; 205d52ebf10SThomas Chou case SD_CMD_SEND_IF_COND: 206d52ebf10SThomas Chou case MMC_CMD_SPI_READ_OCR: 207d52ebf10SThomas Chou spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); 208*6f67b69bSYoshinori Sato cmd->response[0] = be32_to_cpu(cmd->response[0]); 209d52ebf10SThomas Chou debug("r32 %x\n", cmd->response[0]); 210d52ebf10SThomas Chou break; 211ed018b21SThomas Chou case MMC_CMD_SEND_STATUS: 212ed018b21SThomas Chou spi_xfer(spi, 1 * 8, NULL, cmd->response, 0); 213ed018b21SThomas Chou cmd->response[0] = (cmd->response[0] & 0xff) ? 214ed018b21SThomas Chou MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA; 215ed018b21SThomas Chou break; 216d52ebf10SThomas Chou } 217d52ebf10SThomas Chou } else { 218d52ebf10SThomas Chou debug("%s:data %x %x %x\n", __func__, 219d52ebf10SThomas Chou data->flags, data->blocks, data->blocksize); 220d52ebf10SThomas Chou if (data->flags == MMC_DATA_READ) 221d52ebf10SThomas Chou r1 = mmc_spi_readdata(mmc, data->dest, 222d52ebf10SThomas Chou data->blocks, data->blocksize); 223d52ebf10SThomas Chou else if (data->flags == MMC_DATA_WRITE) 224d52ebf10SThomas Chou r1 = mmc_spi_writedata(mmc, data->src, 225d52ebf10SThomas Chou data->blocks, data->blocksize, 226d52ebf10SThomas Chou (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); 227d52ebf10SThomas Chou if (r1 & R1_SPI_COM_CRC) 228d52ebf10SThomas Chou ret = COMM_ERR; 229d52ebf10SThomas Chou else if (r1) /* other errors */ 230d52ebf10SThomas Chou ret = TIMEOUT; 231d52ebf10SThomas Chou } 232d52ebf10SThomas Chou done: 233d52ebf10SThomas Chou spi_cs_deactivate(spi); 234d52ebf10SThomas Chou spi_release_bus(spi); 235d52ebf10SThomas Chou return ret; 236d52ebf10SThomas Chou } 237d52ebf10SThomas Chou 238d52ebf10SThomas Chou static void mmc_spi_set_ios(struct mmc *mmc) 239d52ebf10SThomas Chou { 240d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 24193bfd616SPantelis Antoniou 242d52ebf10SThomas Chou debug("%s: clock %u\n", __func__, mmc->clock); 243d52ebf10SThomas Chou if (mmc->clock) 244d52ebf10SThomas Chou spi_set_speed(spi, mmc->clock); 245d52ebf10SThomas Chou } 246d52ebf10SThomas Chou 247d52ebf10SThomas Chou static int mmc_spi_init_p(struct mmc *mmc) 248d52ebf10SThomas Chou { 249d52ebf10SThomas Chou struct spi_slave *spi = mmc->priv; 250d52ebf10SThomas Chou spi_set_speed(spi, MMC_SPI_MIN_CLOCK); 251d52ebf10SThomas Chou spi_claim_bus(spi); 252d52ebf10SThomas Chou /* cs deactivated for 100+ clock */ 253d52ebf10SThomas Chou spi_xfer(spi, 18 * 8, NULL, NULL, 0); 254d52ebf10SThomas Chou spi_release_bus(spi); 255d52ebf10SThomas Chou return 0; 256d52ebf10SThomas Chou } 257d52ebf10SThomas Chou 258ab769f22SPantelis Antoniou static const struct mmc_ops mmc_spi_ops = { 259ab769f22SPantelis Antoniou .send_cmd = mmc_spi_request, 260ab769f22SPantelis Antoniou .set_ios = mmc_spi_set_ios, 261ab769f22SPantelis Antoniou .init = mmc_spi_init_p, 262ab769f22SPantelis Antoniou }; 263ab769f22SPantelis Antoniou 26493bfd616SPantelis Antoniou static struct mmc_config mmc_spi_cfg = { 26593bfd616SPantelis Antoniou .name = "MMC_SPI", 26693bfd616SPantelis Antoniou .ops = &mmc_spi_ops, 26793bfd616SPantelis Antoniou .host_caps = MMC_MODE_SPI, 26893bfd616SPantelis Antoniou .voltages = MMC_SPI_VOLTAGE, 26993bfd616SPantelis Antoniou .f_min = MMC_SPI_MIN_CLOCK, 27093bfd616SPantelis Antoniou .part_type = PART_TYPE_DOS, 27193bfd616SPantelis Antoniou .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT, 27293bfd616SPantelis Antoniou }; 27393bfd616SPantelis Antoniou 274d52ebf10SThomas Chou struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) 275d52ebf10SThomas Chou { 276d52ebf10SThomas Chou struct mmc *mmc; 27793bfd616SPantelis Antoniou struct spi_slave *spi; 278d52ebf10SThomas Chou 27993bfd616SPantelis Antoniou spi = spi_setup_slave(bus, cs, speed, mode); 28093bfd616SPantelis Antoniou if (spi == NULL) 281d52ebf10SThomas Chou return NULL; 28293bfd616SPantelis Antoniou 28393bfd616SPantelis Antoniou mmc_spi_cfg.f_max = speed; 28493bfd616SPantelis Antoniou 28593bfd616SPantelis Antoniou mmc = mmc_create(&mmc_spi_cfg, spi); 28693bfd616SPantelis Antoniou if (mmc == NULL) { 28793bfd616SPantelis Antoniou spi_free_slave(spi); 288d52ebf10SThomas Chou return NULL; 289d52ebf10SThomas Chou } 290d52ebf10SThomas Chou return mmc; 291d52ebf10SThomas Chou } 292