Blame view

drivers/mmc/dw_mmc.c 8.63 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) {
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
120
121
122
123
  			printf("Timeout on data busy
  ");
  			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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  	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;
  		}
  	}
  
  	if (i == retry)
  		return TIMEOUT;
  
  	if (mask & DWMCI_INTMSK_RTO) {
  		debug("Response Timeout..
  ");
  		return TIMEOUT;
  	} else if (mask & DWMCI_INTMSK_RE) {
  		debug("Response Error..
  ");
  		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)) {
  				debug("DATA ERROR!
  ");
  				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...
219
220
  
  		bounce_buffer_stop(&bbstate);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
221
222
223
224
225
226
227
228
229
230
231
232
  	}
  
  	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...
233
  	if ((freq == host->clock) || (freq == 0))
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
234
  		return 0;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
235
  	/*
b44fe83a5   Jaehoon Chung   mmc: dw_mmc: chan...
236
  	 * If host->get_mmc_clk didn't define,
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
237
238
239
  	 * then assume that host->bus_hz is source clock value.
  	 * host->bus_hz should be set from user.
  	 */
b44fe83a5   Jaehoon Chung   mmc: dw_mmc: chan...
240
  	if (host->get_mmc_clk)
d3e016cc2   Rajeshwari S Shinde   MMC: DWMMC: Corre...
241
  		sclk = host->get_mmc_clk(host);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
242
243
244
245
246
247
248
  	else if (host->bus_hz)
  		sclk = host->bus_hz;
  	else {
  		printf("Didn't get source clock value..
  ");
  		return -EINVAL;
  	}
6ace153d1   Chin Liang See   mmc/dw_mmc: Fix c...
249
250
251
252
  	if (sclk == freq)
  		div = 0;	/* bypass mode */
  	else
  		div = DIV_ROUND_UP(sclk, 2 * freq);
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  
  	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) {
  			printf("TIMEOUT error!!
  ");
  			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) {
  			printf("TIMEOUT error!!
  ");
  			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...
293
294
  	struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
  	u32 ctype, regs;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
  
  	debug("Buswidth = %d, clock: %d
  ",mmc->bus_width, mmc->clock);
  
  	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...
313
314
315
316
317
318
319
  	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...
320
321
322
323
324
325
  	if (host->clksel)
  		host->clksel(host);
  }
  
  static int dwmci_init(struct mmc *mmc)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
326
  	struct dwmci_host *host = mmc->priv;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
327

18ab67559   Jaehoon Chung   mmc: dw_mmc: remo...
328
329
  	if (host->board_init)
  		host->board_init(host);
6f0b7caa6   Rajeshwari Shinde   DWMMC: SMDK5420: ...
330

757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
331
332
333
334
335
336
337
  	dwmci_writel(host, DWMCI_PWREN, 1);
  
  	if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) {
  		debug("%s[%d] Fail-reset!!
  ",__func__,__LINE__);
  		return -1;
  	}
9c50e35ff   Amar   DWMMC: Initialise...
338
  	/* Enumerate at 400KHz */
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
339
  	dwmci_setup_bus(host, mmc->cfg->f_min);
9c50e35ff   Amar   DWMMC: Initialise...
340

757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
341
342
343
344
345
346
347
  	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...
348
349
  	if (host->fifoth_val) {
  		dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
9c50e35ff   Amar   DWMMC: Initialise...
350
  	}
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
351
352
353
354
355
356
  
  	dwmci_writel(host, DWMCI_CLKENA, 0);
  	dwmci_writel(host, DWMCI_CLKSRC, 0);
  
  	return 0;
  }
ab769f227   Pantelis Antoniou   mmc: Remove ops f...
357
358
359
360
361
  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...
362
363
  int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
364
365
366
367
  	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...
368

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

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
371
  	host->cfg.host_caps = host->caps;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
372
373
  
  	if (host->buswidth == 8) {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
374
375
  		host->cfg.host_caps |= MMC_MODE_8BIT;
  		host->cfg.host_caps &= ~MMC_MODE_4BIT;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
376
  	} else {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
377
378
  		host->cfg.host_caps |= MMC_MODE_4BIT;
  		host->cfg.host_caps &= ~MMC_MODE_8BIT;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
379
  	}
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
380
381
382
  	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...
383

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
384
385
386
  	host->mmc = mmc_create(&host->cfg, host);
  	if (host->mmc == NULL)
  		return -1;
757bff49b   Jaehoon Chung   mmc: dw-mmc: supp...
387

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