blob: 0b690ecb4bd526a7851d03b638f9ee58266d978c [file] [log] [blame]
Thomas Choud52ebf12010-12-24 13:12:21 +00001/*
2 * generic mmc spi driver
3 *
4 * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
Bhargav Shah05e35d42019-07-08 04:10:48 +00005 * Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com>
6 *
Thomas Choud52ebf12010-12-24 13:12:21 +00007 * Licensed under the GPL-2 or later.
8 */
9#include <common.h>
Jaehoon Chung915ffa52016-07-19 16:33:36 +090010#include <errno.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.h>
Thomas Choud52ebf12010-12-24 13:12:21 +000012#include <malloc.h>
13#include <part.h>
14#include <mmc.h>
Bhargav Shah05e35d42019-07-08 04:10:48 +000015#include <stdlib.h>
Philipp Tomsicha740ee92018-11-25 19:22:18 +010016#include <u-boot/crc.h>
Thomas Choud52ebf12010-12-24 13:12:21 +000017#include <linux/crc7.h>
Yoshinori Sato6f67b692015-06-01 15:22:37 +090018#include <asm/byteorder.h>
Bhargav Shah05e35d42019-07-08 04:10:48 +000019#include <dm.h>
20#include <spi.h>
Thomas Choud52ebf12010-12-24 13:12:21 +000021
22/* MMC/SD in SPI mode reports R1 status always */
Bhargav Shah05e35d42019-07-08 04:10:48 +000023#define R1_SPI_IDLE BIT(0)
24#define R1_SPI_ERASE_RESET BIT(1)
25#define R1_SPI_ILLEGAL_COMMAND BIT(2)
26#define R1_SPI_COM_CRC BIT(3)
27#define R1_SPI_ERASE_SEQ BIT(4)
28#define R1_SPI_ADDRESS BIT(5)
29#define R1_SPI_PARAMETER BIT(6)
Thomas Choud52ebf12010-12-24 13:12:21 +000030/* R1 bit 7 is always zero, reuse this bit for error */
Bhargav Shah05e35d42019-07-08 04:10:48 +000031#define R1_SPI_ERROR BIT(7)
Thomas Choud52ebf12010-12-24 13:12:21 +000032
33/* Response tokens used to ack each block written: */
34#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
35#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
36#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1)
37#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1)
38
39/* Read and write blocks start with these tokens and end with crc;
40 * on error, read tokens act like a subset of R2_SPI_* values.
41 */
Bhargav Shah05e35d42019-07-08 04:10:48 +000042/* single block write multiblock read */
43#define SPI_TOKEN_SINGLE 0xfe
44/* multiblock write */
45#define SPI_TOKEN_MULTI_WRITE 0xfc
46/* terminate multiblock write */
47#define SPI_TOKEN_STOP_TRAN 0xfd
Thomas Choud52ebf12010-12-24 13:12:21 +000048
49/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
Bhargav Shah05e35d42019-07-08 04:10:48 +000050#define MMC_SPI_CMD(x) (0x40 | (x))
Thomas Choud52ebf12010-12-24 13:12:21 +000051
52/* bus capability */
Bhargav Shah05e35d42019-07-08 04:10:48 +000053#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34)
54#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */
55#define MMC_SPI_MAX_CLOCK 25000000 /* SD/MMC legacy speed */
Thomas Choud52ebf12010-12-24 13:12:21 +000056
57/* timeout value */
Bhargav Shah05e35d42019-07-08 04:10:48 +000058#define CMD_TIMEOUT 8
59#define READ_TIMEOUT 3000000 /* 1 sec */
60#define WRITE_TIMEOUT 3000000 /* 1 sec */
Thomas Choud52ebf12010-12-24 13:12:21 +000061
Bin Mengd3302392019-08-30 21:15:33 -070062struct mmc_spi_plat {
Bhargav Shah05e35d42019-07-08 04:10:48 +000063 struct mmc_config cfg;
64 struct mmc mmc;
65};
66
Bin Mengd3302392019-08-30 21:15:33 -070067struct mmc_spi_priv {
68 struct spi_slave *spi;
69};
70
Bhargav Shah05e35d42019-07-08 04:10:48 +000071static int mmc_spi_sendcmd(struct udevice *dev,
72 ushort cmdidx, u32 cmdarg, u32 resp_type,
73 u8 *resp, u32 resp_size,
74 bool resp_match, u8 resp_match_value)
Thomas Choud52ebf12010-12-24 13:12:21 +000075{
Bhargav Shah05e35d42019-07-08 04:10:48 +000076 int i, rpos = 0, ret = 0;
77 u8 cmdo[7], r;
78
79 debug("%s: cmd%d cmdarg=0x%x resp_type=0x%x "
80 "resp_size=%d resp_match=%d resp_match_value=0x%x\n",
81 __func__, cmdidx, cmdarg, resp_type,
82 resp_size, resp_match, resp_match_value);
83
Thomas Choud52ebf12010-12-24 13:12:21 +000084 cmdo[0] = 0xff;
85 cmdo[1] = MMC_SPI_CMD(cmdidx);
86 cmdo[2] = cmdarg >> 24;
87 cmdo[3] = cmdarg >> 16;
88 cmdo[4] = cmdarg >> 8;
89 cmdo[5] = cmdarg;
90 cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
Anup Patela7060292019-07-17 04:23:38 +000091 ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, SPI_XFER_BEGIN);
Bhargav Shah05e35d42019-07-08 04:10:48 +000092 if (ret)
93 return ret;
Thomas Choud52ebf12010-12-24 13:12:21 +000094
Bhargav Shah05e35d42019-07-08 04:10:48 +000095 ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0);
96 if (ret)
97 return ret;
98
99 if (!resp || !resp_size)
100 return 0;
101
102 debug("%s: cmd%d", __func__, cmdidx);
103
104 if (resp_match) {
105 r = ~resp_match_value;
106 i = CMD_TIMEOUT;
107 while (i--) {
108 ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0);
109 if (ret)
110 return ret;
111 debug(" resp%d=0x%x", rpos, r);
112 rpos++;
113 if (r == resp_match_value)
Thomas Choud52ebf12010-12-24 13:12:21 +0000114 break;
115 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000116 if (!i && (r != resp_match_value))
117 return -ETIMEDOUT;
118 }
119
120 for (i = 0; i < resp_size; i++) {
121 if (i == 0 && resp_match) {
122 resp[i] = resp_match_value;
123 continue;
124 }
125 ret = dm_spi_xfer(dev, 1 * 8, NULL, &r, 0);
126 if (ret)
127 return ret;
128 debug(" resp%d=0x%x", rpos, r);
129 rpos++;
130 resp[i] = r;
131 }
132
133 debug("\n");
134
135 return 0;
136}
137
138static int mmc_spi_readdata(struct udevice *dev,
139 void *xbuf, u32 bcnt, u32 bsize)
140{
141 u16 crc;
142 u8 *buf = xbuf, r1;
143 int i, ret = 0;
144
145 while (bcnt--) {
146 for (i = 0; i < READ_TIMEOUT; i++) {
147 ret = dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0);
148 if (ret)
149 return ret;
150 if (r1 == SPI_TOKEN_SINGLE)
151 break;
152 }
153 debug("%s: data tok%d 0x%x\n", __func__, i, r1);
Thomas Choud52ebf12010-12-24 13:12:21 +0000154 if (r1 == SPI_TOKEN_SINGLE) {
Bhargav Shah05e35d42019-07-08 04:10:48 +0000155 ret = dm_spi_xfer(dev, bsize * 8, NULL, buf, 0);
156 if (ret)
157 return ret;
158 ret = dm_spi_xfer(dev, 2 * 8, NULL, &crc, 0);
159 if (ret)
160 return ret;
Thomas Choud52ebf12010-12-24 13:12:21 +0000161#ifdef CONFIG_MMC_SPI_CRC_ON
Bhargav Shah05e35d42019-07-08 04:10:48 +0000162 if (be16_to_cpu(crc16_ccitt(0, buf, bsize)) != crc) {
163 debug("%s: data crc error\n", __func__);
Thomas Choud52ebf12010-12-24 13:12:21 +0000164 r1 = R1_SPI_COM_CRC;
165 break;
166 }
167#endif
168 r1 = 0;
169 } else {
170 r1 = R1_SPI_ERROR;
171 break;
172 }
173 buf += bsize;
174 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000175
176 if (r1 & R1_SPI_COM_CRC)
177 ret = -ECOMM;
178 else if (r1) /* other errors */
179 ret = -ETIMEDOUT;
180
181 return ret;
Thomas Choud52ebf12010-12-24 13:12:21 +0000182}
183
Bhargav Shah05e35d42019-07-08 04:10:48 +0000184static int mmc_spi_writedata(struct udevice *dev, const void *xbuf,
185 u32 bcnt, u32 bsize, int multi)
Thomas Choud52ebf12010-12-24 13:12:21 +0000186{
Thomas Choud52ebf12010-12-24 13:12:21 +0000187 const u8 *buf = xbuf;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000188 u8 r1, tok[2];
Thomas Choud52ebf12010-12-24 13:12:21 +0000189 u16 crc;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000190 int i, ret = 0;
191
Thomas Choud52ebf12010-12-24 13:12:21 +0000192 tok[0] = 0xff;
193 tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000194
Thomas Choud52ebf12010-12-24 13:12:21 +0000195 while (bcnt--) {
196#ifdef CONFIG_MMC_SPI_CRC_ON
Stefan Roeseecb57f62016-03-03 09:34:12 +0100197 crc = cpu_to_be16(crc16_ccitt(0, (u8 *)buf, bsize));
Thomas Choud52ebf12010-12-24 13:12:21 +0000198#endif
Bhargav Shah05e35d42019-07-08 04:10:48 +0000199 dm_spi_xfer(dev, 2 * 8, tok, NULL, 0);
200 dm_spi_xfer(dev, bsize * 8, buf, NULL, 0);
201 dm_spi_xfer(dev, 2 * 8, &crc, NULL, 0);
202 for (i = 0; i < CMD_TIMEOUT; i++) {
203 dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0);
Thomas Choud52ebf12010-12-24 13:12:21 +0000204 if ((r1 & 0x10) == 0) /* response token */
205 break;
206 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000207 debug("%s: data tok%d 0x%x\n", __func__, i, r1);
Thomas Choud52ebf12010-12-24 13:12:21 +0000208 if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
Bhargav Shah05e35d42019-07-08 04:10:48 +0000209 debug("%s: data accepted\n", __func__);
210 for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */
211 dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0);
Thomas Choud52ebf12010-12-24 13:12:21 +0000212 if (i && r1 == 0xff) {
213 r1 = 0;
214 break;
215 }
216 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000217 if (i == WRITE_TIMEOUT) {
218 debug("%s: data write timeout 0x%x\n",
219 __func__, r1);
Thomas Choud52ebf12010-12-24 13:12:21 +0000220 r1 = R1_SPI_ERROR;
221 break;
222 }
223 } else {
Bhargav Shah05e35d42019-07-08 04:10:48 +0000224 debug("%s: data error 0x%x\n", __func__, r1);
Thomas Choud52ebf12010-12-24 13:12:21 +0000225 r1 = R1_SPI_COM_CRC;
226 break;
227 }
228 buf += bsize;
229 }
230 if (multi && bcnt == -1) { /* stop multi write */
231 tok[1] = SPI_TOKEN_STOP_TRAN;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000232 dm_spi_xfer(dev, 2 * 8, tok, NULL, 0);
233 for (i = 0; i < WRITE_TIMEOUT; i++) { /* wait busy */
234 dm_spi_xfer(dev, 1 * 8, NULL, &r1, 0);
Thomas Choud52ebf12010-12-24 13:12:21 +0000235 if (i && r1 == 0xff) {
236 r1 = 0;
237 break;
238 }
239 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000240 if (i == WRITE_TIMEOUT) {
241 debug("%s: data write timeout 0x%x\n", __func__, r1);
Thomas Choud52ebf12010-12-24 13:12:21 +0000242 r1 = R1_SPI_ERROR;
243 }
244 }
Thomas Choud52ebf12010-12-24 13:12:21 +0000245
Bhargav Shah05e35d42019-07-08 04:10:48 +0000246 if (r1 & R1_SPI_COM_CRC)
Jaehoon Chung915ffa52016-07-19 16:33:36 +0900247 ret = -ECOMM;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000248 else if (r1) /* other errors */
Jaehoon Chung915ffa52016-07-19 16:33:36 +0900249 ret = -ETIMEDOUT;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000250
Thomas Choud52ebf12010-12-24 13:12:21 +0000251 return ret;
252}
253
Bhargav Shah05e35d42019-07-08 04:10:48 +0000254static int dm_mmc_spi_set_ios(struct udevice *dev)
Thomas Choud52ebf12010-12-24 13:12:21 +0000255{
Jaehoon Chung07b0b9c2016-12-30 15:30:16 +0900256 return 0;
Thomas Choud52ebf12010-12-24 13:12:21 +0000257}
258
Bhargav Shah05e35d42019-07-08 04:10:48 +0000259static int dm_mmc_spi_request(struct udevice *dev, struct mmc_cmd *cmd,
260 struct mmc_data *data)
Thomas Choud52ebf12010-12-24 13:12:21 +0000261{
Bhargav Shah05e35d42019-07-08 04:10:48 +0000262 int i, multi, ret = 0;
263 u8 *resp = NULL;
264 u32 resp_size = 0;
265 bool resp_match = false;
266 u8 resp8 = 0, resp40[5] = { 0 }, resp_match_value = 0;
Thomas Choud52ebf12010-12-24 13:12:21 +0000267
Bhargav Shah05e35d42019-07-08 04:10:48 +0000268 dm_spi_claim_bus(dev);
Pantelis Antoniouab769f22014-02-26 19:28:45 +0200269
Bhargav Shah05e35d42019-07-08 04:10:48 +0000270 for (i = 0; i < 4; i++)
271 cmd->response[i] = 0;
Pantelis Antoniou93bfd612014-03-11 19:34:20 +0200272
Bhargav Shah05e35d42019-07-08 04:10:48 +0000273 switch (cmd->cmdidx) {
274 case SD_CMD_APP_SEND_OP_COND:
275 case MMC_CMD_SEND_OP_COND:
276 resp = &resp8;
277 resp_size = sizeof(resp8);
278 cmd->cmdarg = 0x40000000;
279 break;
280 case SD_CMD_SEND_IF_COND:
281 resp = (u8 *)&resp40[0];
282 resp_size = sizeof(resp40);
283 resp_match = true;
284 resp_match_value = R1_SPI_IDLE;
285 break;
286 case MMC_CMD_SPI_READ_OCR:
287 resp = (u8 *)&resp40[0];
288 resp_size = sizeof(resp40);
289 break;
290 case MMC_CMD_SEND_STATUS:
291 case MMC_CMD_SET_BLOCKLEN:
292 case MMC_CMD_SPI_CRC_ON_OFF:
293 case MMC_CMD_STOP_TRANSMISSION:
294 resp = &resp8;
295 resp_size = sizeof(resp8);
296 resp_match = true;
297 resp_match_value = 0x0;
298 break;
299 case MMC_CMD_SEND_CSD:
300 case MMC_CMD_SEND_CID:
301 case MMC_CMD_READ_SINGLE_BLOCK:
302 case MMC_CMD_READ_MULTIPLE_BLOCK:
303 case MMC_CMD_WRITE_SINGLE_BLOCK:
304 case MMC_CMD_WRITE_MULTIPLE_BLOCK:
305 break;
306 default:
307 resp = &resp8;
308 resp_size = sizeof(resp8);
309 resp_match = true;
310 resp_match_value = R1_SPI_IDLE;
311 break;
312 };
Thomas Choud52ebf12010-12-24 13:12:21 +0000313
Bhargav Shah05e35d42019-07-08 04:10:48 +0000314 ret = mmc_spi_sendcmd(dev, cmd->cmdidx, cmd->cmdarg, cmd->resp_type,
315 resp, resp_size, resp_match, resp_match_value);
316 if (ret)
317 goto done;
Pantelis Antoniou93bfd612014-03-11 19:34:20 +0200318
Bhargav Shah05e35d42019-07-08 04:10:48 +0000319 switch (cmd->cmdidx) {
320 case SD_CMD_APP_SEND_OP_COND:
321 case MMC_CMD_SEND_OP_COND:
322 cmd->response[0] = (resp8 & R1_SPI_IDLE) ? 0 : OCR_BUSY;
323 break;
324 case SD_CMD_SEND_IF_COND:
325 case MMC_CMD_SPI_READ_OCR:
326 cmd->response[0] = resp40[4];
327 cmd->response[0] |= (uint)resp40[3] << 8;
328 cmd->response[0] |= (uint)resp40[2] << 16;
329 cmd->response[0] |= (uint)resp40[1] << 24;
330 break;
331 case MMC_CMD_SEND_STATUS:
332 cmd->response[0] = (resp8 & 0xff) ?
333 MMC_STATUS_ERROR : MMC_STATUS_RDY_FOR_DATA;
334 break;
335 case MMC_CMD_SEND_CID:
336 case MMC_CMD_SEND_CSD:
337 ret = mmc_spi_readdata(dev, cmd->response, 1, 16);
338 if (ret)
339 return ret;
340 for (i = 0; i < 4; i++)
341 cmd->response[i] =
342 cpu_to_be32(cmd->response[i]);
343 break;
344 default:
345 cmd->response[0] = resp8;
346 break;
Thomas Choud52ebf12010-12-24 13:12:21 +0000347 }
Bhargav Shah05e35d42019-07-08 04:10:48 +0000348
349 debug("%s: cmd%d resp0=0x%x resp1=0x%x resp2=0x%x resp3=0x%x\n",
350 __func__, cmd->cmdidx, cmd->response[0], cmd->response[1],
351 cmd->response[2], cmd->response[3]);
352
353 if (data) {
354 debug("%s: data flags=0x%x blocks=%d block_size=%d\n",
355 __func__, data->flags, data->blocks, data->blocksize);
356 multi = (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK);
357 if (data->flags == MMC_DATA_READ)
358 ret = mmc_spi_readdata(dev, data->dest,
359 data->blocks, data->blocksize);
360 else if (data->flags == MMC_DATA_WRITE)
361 ret = mmc_spi_writedata(dev, data->src,
362 data->blocks, data->blocksize,
363 multi);
364 }
365
366done:
Anup Patela7060292019-07-17 04:23:38 +0000367 dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
368
Bhargav Shah05e35d42019-07-08 04:10:48 +0000369 dm_spi_release_bus(dev);
370
371 return ret;
Thomas Choud52ebf12010-12-24 13:12:21 +0000372}
Bhargav Shah05e35d42019-07-08 04:10:48 +0000373
374static int mmc_spi_probe(struct udevice *dev)
375{
376 struct mmc_spi_priv *priv = dev_get_priv(dev);
Bin Mengd3302392019-08-30 21:15:33 -0700377 struct mmc_spi_plat *plat = dev_get_platdata(dev);
Bhargav Shah05e35d42019-07-08 04:10:48 +0000378 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
379 char *name;
380
381 priv->spi = dev_get_parent_priv(dev);
382 if (!priv->spi->max_hz)
383 priv->spi->max_hz = MMC_SPI_MAX_CLOCK;
384 priv->spi->speed = 0;
385 priv->spi->mode = SPI_MODE_0;
386 priv->spi->wordlen = 8;
387
388 name = malloc(strlen(dev->parent->name) + strlen(dev->name) + 4);
389 if (!name)
390 return -ENOMEM;
391 sprintf(name, "%s:%s", dev->parent->name, dev->name);
392
Bin Mengd3302392019-08-30 21:15:33 -0700393 plat->cfg.name = name;
394 plat->cfg.host_caps = MMC_MODE_SPI;
395 plat->cfg.voltages = MMC_SPI_VOLTAGE;
396 plat->cfg.f_min = MMC_SPI_MIN_CLOCK;
397 plat->cfg.f_max = priv->spi->max_hz;
398 plat->cfg.part_type = PART_TYPE_DOS;
399 plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000400
Bin Mengd3302392019-08-30 21:15:33 -0700401 plat->mmc.cfg = &plat->cfg;
402 plat->mmc.priv = priv;
403 plat->mmc.dev = dev;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000404
Bin Mengd3302392019-08-30 21:15:33 -0700405 upriv->mmc = &plat->mmc;
Bhargav Shah05e35d42019-07-08 04:10:48 +0000406
407 return 0;
408}
409
410static int mmc_spi_bind(struct udevice *dev)
411{
Bin Mengd3302392019-08-30 21:15:33 -0700412 struct mmc_spi_plat *plat = dev_get_platdata(dev);
Bhargav Shah05e35d42019-07-08 04:10:48 +0000413
Bin Mengd3302392019-08-30 21:15:33 -0700414 return mmc_bind(dev, &plat->mmc, &plat->cfg);
Bhargav Shah05e35d42019-07-08 04:10:48 +0000415}
416
417static const struct dm_mmc_ops mmc_spi_ops = {
418 .send_cmd = dm_mmc_spi_request,
419 .set_ios = dm_mmc_spi_set_ios,
420};
421
422static const struct udevice_id dm_mmc_spi_match[] = {
423 { .compatible = "mmc-spi-slot" },
424 { /* sentinel */ }
425};
426
427U_BOOT_DRIVER(mmc_spi) = {
428 .name = "mmc_spi",
429 .id = UCLASS_MMC,
430 .of_match = dm_mmc_spi_match,
431 .ops = &mmc_spi_ops,
432 .probe = mmc_spi_probe,
433 .bind = mmc_spi_bind,
Bin Mengd3302392019-08-30 21:15:33 -0700434 .platdata_auto_alloc_size = sizeof(struct mmc_spi_plat),
Bhargav Shah05e35d42019-07-08 04:10:48 +0000435 .priv_auto_alloc_size = sizeof(struct mmc_spi_priv),
436};