Blame view

drivers/mmc/dw_mmc.c 9.08 KB
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
1
2
3
4
5
  /*
   * (C) Copyright 2012 SAMSUNG Electronics
   * Jaehoon Chung <jh80.chung@samsung.com>
   * Rajeshawari Shinde <rajeshwari.s@samsung.com>
   *
1a4596601   Wolfgang Denk   Add GPL-2.0+ SPDX...
6
   * SPDX-License-Identifier:	GPL-2.0+
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
7
   */
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
8
  #include <bouncebuf.h>
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
9
10
11
12
  #include <common.h>
  #include <malloc.h>
  #include <mmc.h>
  #include <dwmmc.h>
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  #include <asm-generic/errno.h>
  
  #define PAGE_SIZE 4096
  
  static int dwmci_wait_reset(struct dwmci_host *host, u32 value)
  {
  	unsigned long timeout = 1000;
  	u32 ctrl;
  
  	dwmci_writel(host, DWMCI_CTRL, value);
  
  	while (timeout--) {
  		ctrl = dwmci_readl(host, DWMCI_CTRL);
  		if (!(ctrl & DWMCI_RESET_ALL))
  			return 1;
  	}
  	return 0;
  }
  
  static void dwmci_set_idma_desc(struct dwmci_idmac *idmac,
  		u32 desc0, u32 desc1, u32 desc2)
  {
  	struct dwmci_idmac *desc = idmac;
  
  	desc->flags = desc0;
  	desc->cnt = desc1;
  	desc->addr = desc2;
  	desc->next_addr = (unsigned int)desc + sizeof(struct dwmci_idmac);
  }
  
  static void dwmci_prepare_data(struct dwmci_host *host,
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
44
45
46
  			       struct mmc_data *data,
  			       struct dwmci_idmac *cur_idmac,
  			       void *bounce_buffer)
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
47
48
49
  {
  	unsigned long ctrl;
  	unsigned int i = 0, flags, cnt, blk_cnt;
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
50
  	ulong data_start, data_end;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
51
52
53
54
55
56
57
58
  
  
  	blk_cnt = data->blocks;
  
  	dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
  
  	data_start = (ulong)cur_idmac;
  	dwmci_writel(host, DWMCI_DBADDR, (unsigned int)cur_idmac);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
59
60
61
62
63
64
65
66
67
68
  	do {
  		flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ;
  		flags |= (i == 0) ? DWMCI_IDMAC_FS : 0;
  		if (blk_cnt <= 8) {
  			flags |= DWMCI_IDMAC_LD;
  			cnt = data->blocksize * blk_cnt;
  		} else
  			cnt = data->blocksize * 8;
  
  		dwmci_set_idma_desc(cur_idmac, flags, cnt,
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
69
  				    (u32)bounce_buffer + (i * PAGE_SIZE));
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
70

21bd5761a   Mischa Jonker   mmc/dw_mmc: Alloc...
71
  		if (blk_cnt <= 8)
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  			break;
  		blk_cnt -= 8;
  		cur_idmac++;
  		i++;
  	} while(1);
  
  	data_end = (ulong)cur_idmac;
  	flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN);
  
  	ctrl = dwmci_readl(host, DWMCI_CTRL);
  	ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN;
  	dwmci_writel(host, DWMCI_CTRL, ctrl);
  
  	ctrl = dwmci_readl(host, DWMCI_BMOD);
  	ctrl |= DWMCI_BMOD_IDMAC_FB | DWMCI_BMOD_IDMAC_EN;
  	dwmci_writel(host, DWMCI_BMOD, ctrl);
  
  	dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
  	dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks);
  }
  
  static int dwmci_set_transfer_mode(struct dwmci_host *host,
  		struct mmc_data *data)
  {
  	unsigned long mode;
  
  	mode = DWMCI_CMD_DATA_EXP;
  	if (data->flags & MMC_DATA_WRITE)
  		mode |= DWMCI_CMD_RW;
  
  	return mode;
  }
  
  static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
  		struct mmc_data *data)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
108
  	struct dwmci_host *host = mmc->priv;
2136d2263   Mischa Jonker   mmc/dw_mmc: Fix D...
109
  	ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac,
21bd5761a   Mischa Jonker   mmc/dw_mmc: Alloc...
110
  				 data ? DIV_ROUND_UP(data->blocks, 8) : 0);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
111
112
113
114
  	int flags = 0, i;
  	unsigned int timeout = 100000;
  	u32 retry = 10000;
  	u32 mask, ctrl;
9c50e35ff   Amar   DWMMC: Initialise...
115
  	ulong start = get_timer(0);
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
116
  	struct bounce_buffer bbstate;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
117
118
  
  	while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
9c50e35ff   Amar   DWMMC: Initialise...
119
  		if (get_timer(start) > timeout) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
120
121
  			printf("%s: Timeout on data busy
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
122
123
  			return TIMEOUT;
  		}
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
124
125
126
  	}
  
  	dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
127
128
129
130
131
132
133
134
135
136
137
138
139
  	if (data) {
  		if (data->flags == MMC_DATA_READ) {
  			bounce_buffer_start(&bbstate, (void*)data->dest,
  					    data->blocksize *
  					    data->blocks, GEN_BB_WRITE);
  		} else {
  			bounce_buffer_start(&bbstate, (void*)data->src,
  					    data->blocksize *
  					    data->blocks, GEN_BB_READ);
  		}
  		dwmci_prepare_data(host, data, cur_idmac,
  				   bbstate.bounce_buffer);
  	}
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
140

757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  	dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg);
  
  	if (data)
  		flags = dwmci_set_transfer_mode(host, data);
  
  	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
  		return -1;
  
  	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
  		flags |= DWMCI_CMD_ABORT_STOP;
  	else
  		flags |= DWMCI_CMD_PRV_DAT_WAIT;
  
  	if (cmd->resp_type & MMC_RSP_PRESENT) {
  		flags |= DWMCI_CMD_RESP_EXP;
  		if (cmd->resp_type & MMC_RSP_136)
  			flags |= DWMCI_CMD_RESP_LENGTH;
  	}
  
  	if (cmd->resp_type & MMC_RSP_CRC)
  		flags |= DWMCI_CMD_CHECK_CRC;
  
  	flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
  
  	debug("Sending CMD%d
  ",cmd->cmdidx);
  
  	dwmci_writel(host, DWMCI_CMD, flags);
  
  	for (i = 0; i < retry; i++) {
  		mask = dwmci_readl(host, DWMCI_RINTSTS);
  		if (mask & DWMCI_INTMSK_CDONE) {
  			if (!data)
  				dwmci_writel(host, DWMCI_RINTSTS, mask);
  			break;
  		}
  	}
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
178
179
180
  	if (i == retry) {
  		printf("%s: Timeout.
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
181
  		return TIMEOUT;
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
182
  	}
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
183
184
  
  	if (mask & DWMCI_INTMSK_RTO) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
185
186
187
188
189
190
191
192
193
194
  		/*
  		 * Timeout here is not necessarily fatal. (e)MMC cards
  		 * will splat here when they receive CMD55 as they do
  		 * not support this command and that is exactly the way
  		 * to tell them apart from SD cards. Thus, this output
  		 * below shall be debug(). eMMC cards also do not favor
  		 * CMD8, please keep that in mind.
  		 */
  		debug("%s: Response Timeout.
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
195
196
  		return TIMEOUT;
  	} else if (mask & DWMCI_INTMSK_RE) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
197
198
  		printf("%s: Response Error.
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  		return -1;
  	}
  
  
  	if (cmd->resp_type & MMC_RSP_PRESENT) {
  		if (cmd->resp_type & MMC_RSP_136) {
  			cmd->response[0] = dwmci_readl(host, DWMCI_RESP3);
  			cmd->response[1] = dwmci_readl(host, DWMCI_RESP2);
  			cmd->response[2] = dwmci_readl(host, DWMCI_RESP1);
  			cmd->response[3] = dwmci_readl(host, DWMCI_RESP0);
  		} else {
  			cmd->response[0] = dwmci_readl(host, DWMCI_RESP0);
  		}
  	}
  
  	if (data) {
  		do {
  			mask = dwmci_readl(host, DWMCI_RINTSTS);
  			if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
218
219
  				printf("%s: DATA ERROR!
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
220
221
222
223
224
225
226
227
228
  				return -1;
  			}
  		} while (!(mask & DWMCI_INTMSK_DTO));
  
  		dwmci_writel(host, DWMCI_RINTSTS, mask);
  
  		ctrl = dwmci_readl(host, DWMCI_CTRL);
  		ctrl &= ~(DWMCI_DMA_EN);
  		dwmci_writel(host, DWMCI_CTRL, ctrl);
2a7a210e2   Alexey Brodkin   mmc/dwmmc: use bo...
229
230
  
  		bounce_buffer_stop(&bbstate);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
231
232
233
234
235
236
237
238
239
240
241
242
  	}
  
  	udelay(100);
  
  	return 0;
  }
  
  static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
  {
  	u32 div, status;
  	int timeout = 10000;
  	unsigned long sclk;
9c50e35ff   Amar   DWMMC: Initialise...
243
  	if ((freq == host->clock) || (freq == 0))
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
244
  		return 0;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
245
  	/*
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
246
  	 * If host->get_mmc_clk isn't defined,
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
247
  	 * then assume that host->bus_hz is source clock value.
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
248
  	 * host->bus_hz should be set by user.
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
249
  	 */
b44fe83a5   Jaehoon Chung   mmc: dw_mmc: chan...
250
  	if (host->get_mmc_clk)
d3e016cc2   Rajeshwari S Shinde   MMC: DWMMC: Corre...
251
  		sclk = host->get_mmc_clk(host);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
252
253
254
  	else if (host->bus_hz)
  		sclk = host->bus_hz;
  	else {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
255
256
  		printf("%s: Didn't get source clock value.
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
257
258
  		return -EINVAL;
  	}
6ace153d1   Chin Liang See   mmc/dw_mmc: Fix c...
259
260
261
262
  	if (sclk == freq)
  		div = 0;	/* bypass mode */
  	else
  		div = DIV_ROUND_UP(sclk, 2 * freq);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
263
264
265
266
267
268
269
270
271
272
273
  
  	dwmci_writel(host, DWMCI_CLKENA, 0);
  	dwmci_writel(host, DWMCI_CLKSRC, 0);
  
  	dwmci_writel(host, DWMCI_CLKDIV, div);
  	dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
  			DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
  
  	do {
  		status = dwmci_readl(host, DWMCI_CMD);
  		if (timeout-- < 0) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
274
275
  			printf("%s: Timeout!
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  			return -ETIMEDOUT;
  		}
  	} while (status & DWMCI_CMD_START);
  
  	dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE |
  			DWMCI_CLKEN_LOW_PWR);
  
  	dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
  			DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
  
  	timeout = 10000;
  	do {
  		status = dwmci_readl(host, DWMCI_CMD);
  		if (timeout-- < 0) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
290
291
  			printf("%s: Timeout!
  ", __func__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
292
293
294
295
296
297
298
299
300
301
302
  			return -ETIMEDOUT;
  		}
  	} while (status & DWMCI_CMD_START);
  
  	host->clock = freq;
  
  	return 0;
  }
  
  static void dwmci_set_ios(struct mmc *mmc)
  {
045bdcd0b   Jaehoon Chung   mmc: dw_mmc: supp...
303
304
  	struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
  	u32 ctype, regs;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
305

f33c93058   Pavel Machek   mmc: dw_mmc: clea...
306
307
  	debug("Buswidth = %d, clock: %d
  ", mmc->bus_width, mmc->clock);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  
  	dwmci_setup_bus(host, mmc->clock);
  	switch (mmc->bus_width) {
  	case 8:
  		ctype = DWMCI_CTYPE_8BIT;
  		break;
  	case 4:
  		ctype = DWMCI_CTYPE_4BIT;
  		break;
  	default:
  		ctype = DWMCI_CTYPE_1BIT;
  		break;
  	}
  
  	dwmci_writel(host, DWMCI_CTYPE, ctype);
045bdcd0b   Jaehoon Chung   mmc: dw_mmc: supp...
323
324
325
326
327
328
329
  	regs = dwmci_readl(host, DWMCI_UHS_REG);
  	if (mmc->card_caps & MMC_MODE_DDR_52MHz)
  		regs |= DWMCI_DDR_MODE;
  	else
  		regs &= DWMCI_DDR_MODE;
  
  	dwmci_writel(host, DWMCI_UHS_REG, regs);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
330
331
332
333
334
335
  	if (host->clksel)
  		host->clksel(host);
  }
  
  static int dwmci_init(struct mmc *mmc)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
336
  	struct dwmci_host *host = mmc->priv;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
337

18ab67559   Jaehoon Chung   mmc: dw_mmc: remo...
338
339
  	if (host->board_init)
  		host->board_init(host);
6f0b7caa6   Rajeshwari Shinde   DWMMC: SMDK5420: ...
340

757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
341
342
343
  	dwmci_writel(host, DWMCI_PWREN, 1);
  
  	if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) {
f33c93058   Pavel Machek   mmc: dw_mmc: clea...
344
345
  		printf("%s[%d] Fail-reset!!
  ", __func__, __LINE__);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
346
347
  		return -1;
  	}
9c50e35ff   Amar   DWMMC: Initialise...
348
  	/* Enumerate at 400KHz */
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
349
  	dwmci_setup_bus(host, mmc->cfg->f_min);
9c50e35ff   Amar   DWMMC: Initialise...
350

757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
351
352
353
354
355
356
357
  	dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF);
  	dwmci_writel(host, DWMCI_INTMASK, 0);
  
  	dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF);
  
  	dwmci_writel(host, DWMCI_IDINTEN, 0);
  	dwmci_writel(host, DWMCI_BMOD, 1);
9108b315f   Alexey Brodkin   mmc/dwmmc: modify...
358
359
  	if (host->fifoth_val) {
  		dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
9c50e35ff   Amar   DWMMC: Initialise...
360
  	}
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
361
362
363
364
365
366
  
  	dwmci_writel(host, DWMCI_CLKENA, 0);
  	dwmci_writel(host, DWMCI_CLKSRC, 0);
  
  	return 0;
  }
ab769f227   Pantelis Antoniou   mmc: Remove ops f...
367
368
369
370
371
  static const struct mmc_ops dwmci_ops = {
  	.send_cmd	= dwmci_send_cmd,
  	.set_ios	= dwmci_set_ios,
  	.init		= dwmci_init,
  };
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
372
373
  int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
374
375
376
377
  	host->cfg.name = host->name;
  	host->cfg.ops = &dwmci_ops;
  	host->cfg.f_min = min_clk;
  	host->cfg.f_max = max_clk;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
378

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
379
  	host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
380

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
381
  	host->cfg.host_caps = host->caps;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
382
383
  
  	if (host->buswidth == 8) {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
384
385
  		host->cfg.host_caps |= MMC_MODE_8BIT;
  		host->cfg.host_caps &= ~MMC_MODE_4BIT;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
386
  	} else {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
387
388
  		host->cfg.host_caps |= MMC_MODE_4BIT;
  		host->cfg.host_caps &= ~MMC_MODE_8BIT;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
389
  	}
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
390
391
392
  	host->cfg.host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_HC;
  
  	host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
393

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
394
395
396
  	host->mmc = mmc_create(&host->cfg, host);
  	if (host->mmc == NULL)
  		return -1;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
397

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
398
  	return 0;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
399
  }