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