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