Blame view

drivers/mmc/sdhci.c 15.5 KB
af62a5578   Lei Wen   MMC: add sdhci ge...
1
2
3
4
  /*
   * Copyright 2011, Marvell Semiconductor Inc.
   * Lei Wen <leiwen@marvell.com>
   *
1a4596601   Wolfgang Denk   Add GPL-2.0+ SPDX...
5
   * SPDX-License-Identifier:	GPL-2.0+
af62a5578   Lei Wen   MMC: add sdhci ge...
6
7
8
9
10
11
   *
   * Back ported to the 8xx platform (from the 8260 platform) by
   * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
   */
  
  #include <common.h>
2a809093f   Simon Glass   dm: mmc: sdhci: R...
12
  #include <errno.h>
af62a5578   Lei Wen   MMC: add sdhci ge...
13
14
15
  #include <malloc.h>
  #include <mmc.h>
  #include <sdhci.h>
492d3223b   Stefan Roese   mmc: sdhci.c: Add...
16
17
18
  #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER)
  void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER;
  #else
af62a5578   Lei Wen   MMC: add sdhci ge...
19
  void *aligned_buffer;
492d3223b   Stefan Roese   mmc: sdhci.c: Add...
20
  #endif
af62a5578   Lei Wen   MMC: add sdhci ge...
21
22
23
24
25
26
27
28
29
30
  
  static void sdhci_reset(struct sdhci_host *host, u8 mask)
  {
  	unsigned long timeout;
  
  	/* Wait max 100 ms */
  	timeout = 100;
  	sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
  	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
  		if (timeout == 0) {
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
31
32
33
  			printf("%s: Reset 0x%x never completed.
  ",
  			       __func__, (int)mask);
af62a5578   Lei Wen   MMC: add sdhci ge...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  			return;
  		}
  		timeout--;
  		udelay(1000);
  	}
  }
  
  static void sdhci_cmd_done(struct sdhci_host *host, struct mmc_cmd *cmd)
  {
  	int i;
  	if (cmd->resp_type & MMC_RSP_136) {
  		/* CRC is stripped so we need to do some shifting. */
  		for (i = 0; i < 4; i++) {
  			cmd->response[i] = sdhci_readl(host,
  					SDHCI_RESPONSE + (3-i)*4) << 8;
  			if (i != 3)
  				cmd->response[i] |= sdhci_readb(host,
  						SDHCI_RESPONSE + (3-i)*4-1);
  		}
  	} else {
  		cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE);
  	}
  }
  
  static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data)
  {
  	int i;
  	char *offs;
  	for (i = 0; i < data->blocksize; i += 4) {
  		offs = data->dest + i;
  		if (data->flags == MMC_DATA_READ)
  			*(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER);
  		else
  			sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER);
  	}
  }
  
  static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,
  				unsigned int start_addr)
  {
a004abde8   Lei Wen   mmc: sdhci: add t...
74
  	unsigned int stat, rdy, mask, timeout, block = 0;
7dde50d70   Alex Deymo   mmc: sdhci: Wait ...
75
  	bool transfer_done = false;
45a68fe26   Masahiro Yamada   mmc: move some SD...
76
  #ifdef CONFIG_MMC_SDHCI_SDMA
804c7f422   Jaehoon Chung   mmc: sdhci: add t...
77
  	unsigned char ctrl;
2c011847c   Juhyun \(Justin\) Oh   Fix wrong sdhci h...
78
  	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
804c7f422   Jaehoon Chung   mmc: sdhci: add t...
79
  	ctrl &= ~SDHCI_CTRL_DMA_MASK;
2c011847c   Juhyun \(Justin\) Oh   Fix wrong sdhci h...
80
  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
804c7f422   Jaehoon Chung   mmc: sdhci: add t...
81
  #endif
af62a5578   Lei Wen   MMC: add sdhci ge...
82

5d48e4224   Jaehoon Chung   mmc: sdhci: incre...
83
  	timeout = 1000000;
af62a5578   Lei Wen   MMC: add sdhci ge...
84
85
86
87
88
  	rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL;
  	mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE;
  	do {
  		stat = sdhci_readl(host, SDHCI_INT_STATUS);
  		if (stat & SDHCI_INT_ERROR) {
61f2e5ee1   Masahiro Yamada   mmc: sdhci: chang...
89
90
91
  			pr_debug("%s: Error detected in status(0x%X)!
  ",
  				 __func__, stat);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
92
  			return -EIO;
af62a5578   Lei Wen   MMC: add sdhci ge...
93
  		}
7dde50d70   Alex Deymo   mmc: sdhci: Wait ...
94
  		if (!transfer_done && (stat & rdy)) {
af62a5578   Lei Wen   MMC: add sdhci ge...
95
96
97
98
99
  			if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
  				continue;
  			sdhci_writel(host, rdy, SDHCI_INT_STATUS);
  			sdhci_transfer_pio(host, data);
  			data->dest += data->blocksize;
7dde50d70   Alex Deymo   mmc: sdhci: Wait ...
100
101
102
103
104
105
106
107
  			if (++block >= data->blocks) {
  				/* Keep looping until the SDHCI_INT_DATA_END is
  				 * cleared, even if we finished sending all the
  				 * blocks.
  				 */
  				transfer_done = true;
  				continue;
  			}
af62a5578   Lei Wen   MMC: add sdhci ge...
108
  		}
45a68fe26   Masahiro Yamada   mmc: move some SD...
109
  #ifdef CONFIG_MMC_SDHCI_SDMA
7dde50d70   Alex Deymo   mmc: sdhci: Wait ...
110
  		if (!transfer_done && (stat & SDHCI_INT_DMA_END)) {
af62a5578   Lei Wen   MMC: add sdhci ge...
111
  			sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS);
3e81c7724   Lei Wen   mmc: sdhci: fix s...
112
  			start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1);
af62a5578   Lei Wen   MMC: add sdhci ge...
113
114
115
116
  			start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE;
  			sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
  		}
  #endif
a004abde8   Lei Wen   mmc: sdhci: add t...
117
118
119
  		if (timeout-- > 0)
  			udelay(10);
  		else {
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
120
121
  			printf("%s: Transfer data timeout
  ", __func__);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
122
  			return -ETIMEDOUT;
a004abde8   Lei Wen   mmc: sdhci: add t...
123
  		}
af62a5578   Lei Wen   MMC: add sdhci ge...
124
125
126
  	} while (!(stat & SDHCI_INT_DATA_END));
  	return 0;
  }
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
127
128
129
130
131
  /*
   * No command will be sent by driver if card is busy, so driver must wait
   * for card ready state.
   * Every time when card is busy after timeout then (last) timeout value will be
   * increased twice but only if it doesn't exceed global defined maximum.
65a25b208   Masahiro Yamada   mmc: sdhci: drop ...
132
   * Each function call will use last timeout value.
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
133
   */
65a25b208   Masahiro Yamada   mmc: sdhci: drop ...
134
  #define SDHCI_CMD_MAX_TIMEOUT			3200
d8ce77b28   Masahiro Yamada   mmc: sdhci: drop ...
135
  #define SDHCI_CMD_DEFAULT_TIMEOUT		100
d90bb4393   Steve Rae   mmc: increase MMC...
136
  #define SDHCI_READ_STATUS_TIMEOUT		1000
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
137

e7881d85a   Simon Glass   dm: mmc: Drop CON...
138
  #ifdef CONFIG_DM_MMC
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
139
140
141
142
143
144
  static int sdhci_send_command(struct udevice *dev, struct mmc_cmd *cmd,
  			      struct mmc_data *data)
  {
  	struct mmc *mmc = mmc_get_mmc_dev(dev);
  
  #else
6588c78bf   Jeroen Hofstee   sdhci: make local...
145
  static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
146
  			      struct mmc_data *data)
af62a5578   Lei Wen   MMC: add sdhci ge...
147
  {
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
148
  #endif
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
149
  	struct sdhci_host *host = mmc->priv;
af62a5578   Lei Wen   MMC: add sdhci ge...
150
151
152
153
  	unsigned int stat = 0;
  	int ret = 0;
  	int trans_bytes = 0, is_aligned = 1;
  	u32 mask, flags, mode;
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
154
  	unsigned int time = 0, start_addr = 0;
19d2e3423   Simon Glass   dm: mmc: Convert ...
155
  	int mmc_dev = mmc_get_blk_desc(mmc)->devnum;
29905a451   Stefan Roese   mmc: sdhci: Use t...
156
  	unsigned start = get_timer(0);
af62a5578   Lei Wen   MMC: add sdhci ge...
157

56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
158
  	/* Timeout unit - ms */
d8ce77b28   Masahiro Yamada   mmc: sdhci: drop ...
159
  	static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT;
af62a5578   Lei Wen   MMC: add sdhci ge...
160

af62a5578   Lei Wen   MMC: add sdhci ge...
161
162
163
164
165
166
167
168
  	mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
  
  	/* We shouldn't wait for data inihibit for stop commands, even
  	   though they might use busy signaling */
  	if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
  		mask &= ~SDHCI_DATA_INHIBIT;
  
  	while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
169
  		if (time >= cmd_timeout) {
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
170
  			printf("%s: MMC: %d busy ", __func__, mmc_dev);
65a25b208   Masahiro Yamada   mmc: sdhci: drop ...
171
  			if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) {
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
172
173
174
175
176
177
178
  				cmd_timeout += cmd_timeout;
  				printf("timeout increasing to: %u ms.
  ",
  				       cmd_timeout);
  			} else {
  				puts("timeout.
  ");
915ffa521   Jaehoon Chung   mmc: use the gene...
179
  				return -ECOMM;
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
180
  			}
af62a5578   Lei Wen   MMC: add sdhci ge...
181
  		}
56b34bc61   Przemyslaw Marczak   mmc: sdhci: Avoid...
182
  		time++;
af62a5578   Lei Wen   MMC: add sdhci ge...
183
184
  		udelay(1000);
  	}
713e6815d   Jorge Ramirez-Ortiz   mmc: sdhci: don't...
185
  	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
af62a5578   Lei Wen   MMC: add sdhci ge...
186
187
188
189
190
191
192
  	mask = SDHCI_INT_RESPONSE;
  	if (!(cmd->resp_type & MMC_RSP_PRESENT))
  		flags = SDHCI_CMD_RESP_NONE;
  	else if (cmd->resp_type & MMC_RSP_136)
  		flags = SDHCI_CMD_RESP_LONG;
  	else if (cmd->resp_type & MMC_RSP_BUSY) {
  		flags = SDHCI_CMD_RESP_SHORT_BUSY;
17ea3c862   Jaehoon Chung   mmc: sdhci: set t...
193
194
  		if (data)
  			mask |= SDHCI_INT_DATA_END;
af62a5578   Lei Wen   MMC: add sdhci ge...
195
196
197
198
199
200
201
202
203
  	} else
  		flags = SDHCI_CMD_RESP_SHORT;
  
  	if (cmd->resp_type & MMC_RSP_CRC)
  		flags |= SDHCI_CMD_CRC;
  	if (cmd->resp_type & MMC_RSP_OPCODE)
  		flags |= SDHCI_CMD_INDEX;
  	if (data)
  		flags |= SDHCI_CMD_DATA;
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
204
  	/* Set Transfer mode regarding to data flag */
bb7b4ef37   Heinrich Schuchardt   mmc: sdhci: do no...
205
  	if (data) {
af62a5578   Lei Wen   MMC: add sdhci ge...
206
207
208
209
210
211
212
213
  		sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
  		mode = SDHCI_TRNS_BLK_CNT_EN;
  		trans_bytes = data->blocks * data->blocksize;
  		if (data->blocks > 1)
  			mode |= SDHCI_TRNS_MULTI;
  
  		if (data->flags == MMC_DATA_READ)
  			mode |= SDHCI_TRNS_READ;
45a68fe26   Masahiro Yamada   mmc: move some SD...
214
  #ifdef CONFIG_MMC_SDHCI_SDMA
af62a5578   Lei Wen   MMC: add sdhci ge...
215
  		if (data->flags == MMC_DATA_READ)
3c1fcb770   Rob Herring   sdhci: fix warnin...
216
  			start_addr = (unsigned long)data->dest;
af62a5578   Lei Wen   MMC: add sdhci ge...
217
  		else
3c1fcb770   Rob Herring   sdhci: fix warnin...
218
  			start_addr = (unsigned long)data->src;
af62a5578   Lei Wen   MMC: add sdhci ge...
219
220
221
  		if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
  				(start_addr & 0x7) != 0x0) {
  			is_aligned = 0;
3c1fcb770   Rob Herring   sdhci: fix warnin...
222
  			start_addr = (unsigned long)aligned_buffer;
af62a5578   Lei Wen   MMC: add sdhci ge...
223
224
225
  			if (data->flags != MMC_DATA_READ)
  				memcpy(aligned_buffer, data->src, trans_bytes);
  		}
492d3223b   Stefan Roese   mmc: sdhci.c: Add...
226
227
228
229
230
231
232
233
234
235
  #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER)
  		/*
  		 * Always use this bounce-buffer when
  		 * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined
  		 */
  		is_aligned = 0;
  		start_addr = (unsigned long)aligned_buffer;
  		if (data->flags != MMC_DATA_READ)
  			memcpy(aligned_buffer, data->src, trans_bytes);
  #endif
af62a5578   Lei Wen   MMC: add sdhci ge...
236
237
238
239
240
241
242
243
  		sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
  		mode |= SDHCI_TRNS_DMA;
  #endif
  		sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
  				data->blocksize),
  				SDHCI_BLOCK_SIZE);
  		sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
  		sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
5e1c23cd3   Kevin Liu   mmc: sdhci: add t...
244
245
  	} else if (cmd->resp_type & MMC_RSP_BUSY) {
  		sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
af62a5578   Lei Wen   MMC: add sdhci ge...
246
247
248
  	}
  
  	sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);
45a68fe26   Masahiro Yamada   mmc: move some SD...
249
  #ifdef CONFIG_MMC_SDHCI_SDMA
bb7b4ef37   Heinrich Schuchardt   mmc: sdhci: do no...
250
  	if (data) {
fa7720b21   Kevin Liu   mmc: sdhci: only ...
251
252
253
  		trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE);
  		flush_cache(start_addr, trans_bytes);
  	}
af62a5578   Lei Wen   MMC: add sdhci ge...
254
255
  #endif
  	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
29905a451   Stefan Roese   mmc: sdhci: Use t...
256
  	start = get_timer(0);
af62a5578   Lei Wen   MMC: add sdhci ge...
257
258
259
260
  	do {
  		stat = sdhci_readl(host, SDHCI_INT_STATUS);
  		if (stat & SDHCI_INT_ERROR)
  			break;
af62a5578   Lei Wen   MMC: add sdhci ge...
261

bae4a1fdf   Masahiro Yamada   mmc: sdhci: clean...
262
263
264
265
266
267
268
  		if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) {
  			if (host->quirks & SDHCI_QUIRK_BROKEN_R1B) {
  				return 0;
  			} else {
  				printf("%s: Timeout for status update!
  ",
  				       __func__);
915ffa521   Jaehoon Chung   mmc: use the gene...
269
  				return -ETIMEDOUT;
bae4a1fdf   Masahiro Yamada   mmc: sdhci: clean...
270
  			}
3a6383207   Jaehoon Chung   mmc: sdhci: add t...
271
  		}
bae4a1fdf   Masahiro Yamada   mmc: sdhci: clean...
272
  	} while ((stat & mask) != mask);
3a6383207   Jaehoon Chung   mmc: sdhci: add t...
273

af62a5578   Lei Wen   MMC: add sdhci ge...
274
275
276
277
278
279
280
281
  	if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
  		sdhci_cmd_done(host, cmd);
  		sdhci_writel(host, mask, SDHCI_INT_STATUS);
  	} else
  		ret = -1;
  
  	if (!ret && data)
  		ret = sdhci_transfer_data(host, data, start_addr);
13243f2ea   Tushar Behera   mmc: sdhci: Add a...
282
283
  	if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD)
  		udelay(1000);
af62a5578   Lei Wen   MMC: add sdhci ge...
284
285
286
287
288
289
290
291
292
293
294
295
  	stat = sdhci_readl(host, SDHCI_INT_STATUS);
  	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
  	if (!ret) {
  		if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
  				!is_aligned && (data->flags == MMC_DATA_READ))
  			memcpy(data->dest, aligned_buffer, trans_bytes);
  		return 0;
  	}
  
  	sdhci_reset(host, SDHCI_RESET_CMD);
  	sdhci_reset(host, SDHCI_RESET_DATA);
  	if (stat & SDHCI_INT_TIMEOUT)
915ffa521   Jaehoon Chung   mmc: use the gene...
296
  		return -ETIMEDOUT;
af62a5578   Lei Wen   MMC: add sdhci ge...
297
  	else
915ffa521   Jaehoon Chung   mmc: use the gene...
298
  		return -ECOMM;
af62a5578   Lei Wen   MMC: add sdhci ge...
299
300
301
302
  }
  
  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
303
  	struct sdhci_host *host = mmc->priv;
899fb9e35   Stefan Roese   mmc: sdhci: Clear...
304
  	unsigned int div, clk = 0, timeout;
af62a5578   Lei Wen   MMC: add sdhci ge...
305

79667b7b7   Wenyou Yang   mmc: sdhci: Fix t...
306
307
308
309
310
311
312
313
  	/* Wait max 20 ms */
  	timeout = 200;
  	while (sdhci_readl(host, SDHCI_PRESENT_STATE) &
  			   (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
  		if (timeout == 0) {
  			printf("%s: Timeout to wait cmd & data inhibit
  ",
  			       __func__);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
314
  			return -EBUSY;
79667b7b7   Wenyou Yang   mmc: sdhci: Fix t...
315
316
317
318
319
  		}
  
  		timeout--;
  		udelay(100);
  	}
899fb9e35   Stefan Roese   mmc: sdhci: Clear...
320
  	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
af62a5578   Lei Wen   MMC: add sdhci ge...
321
322
323
  
  	if (clock == 0)
  		return 0;
113e5dfcd   Jaehoon Chung   mmc: sdhci: use t...
324
  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
325
326
327
328
329
330
  		/*
  		 * Check if the Host Controller supports Programmable Clock
  		 * Mode.
  		 */
  		if (host->clk_mul) {
  			for (div = 1; div <= 1024; div++) {
0e0dcc191   Wenyou Yang   mmc: sdhci: Fix m...
331
  				if ((host->max_clk / div) <= clock)
af62a5578   Lei Wen   MMC: add sdhci ge...
332
333
  					break;
  			}
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
334
335
336
337
338
339
340
341
342
  
  			/*
  			 * Set Programmable Clock Mode in the Clock
  			 * Control register.
  			 */
  			clk = SDHCI_PROG_CLOCK_MODE;
  			div--;
  		} else {
  			/* Version 3.00 divisors must be a multiple of 2. */
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
343
  			if (host->max_clk <= clock) {
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
344
345
346
347
348
  				div = 1;
  			} else {
  				for (div = 2;
  				     div < SDHCI_MAX_DIV_SPEC_300;
  				     div += 2) {
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
349
  					if ((host->max_clk / div) <= clock)
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
350
351
352
353
  						break;
  				}
  			}
  			div >>= 1;
af62a5578   Lei Wen   MMC: add sdhci ge...
354
355
356
357
  		}
  	} else {
  		/* Version 2.00 divisors must be a power of 2. */
  		for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
358
  			if ((host->max_clk / div) <= clock)
af62a5578   Lei Wen   MMC: add sdhci ge...
359
360
  				break;
  		}
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
361
  		div >>= 1;
af62a5578   Lei Wen   MMC: add sdhci ge...
362
  	}
af62a5578   Lei Wen   MMC: add sdhci ge...
363

bf9c4d146   Masahiro Yamada   mmc: sdhci: fix N...
364
  	if (host->ops && host->ops->set_clock)
62226b686   Jaehoon Chung   mmc: sdhci: move ...
365
  		host->ops->set_clock(host, div);
b09ed6e4f   Jaehoon Chung   mmc: s5p_sdhci: a...
366

6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
367
  	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
af62a5578   Lei Wen   MMC: add sdhci ge...
368
369
370
371
372
373
374
375
376
377
  	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
  		<< SDHCI_DIVIDER_HI_SHIFT;
  	clk |= SDHCI_CLOCK_INT_EN;
  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
  
  	/* Wait max 20 ms */
  	timeout = 20;
  	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
  		& SDHCI_CLOCK_INT_STABLE)) {
  		if (timeout == 0) {
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
378
379
380
  			printf("%s: Internal clock never stabilised.
  ",
  			       __func__);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
381
  			return -EBUSY;
af62a5578   Lei Wen   MMC: add sdhci ge...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  		}
  		timeout--;
  		udelay(1000);
  	}
  
  	clk |= SDHCI_CLOCK_CARD_EN;
  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
  	return 0;
  }
  
  static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
  {
  	u8 pwr = 0;
  
  	if (power != (unsigned short)-1) {
  		switch (1 << power) {
  		case MMC_VDD_165_195:
  			pwr = SDHCI_POWER_180;
  			break;
  		case MMC_VDD_29_30:
  		case MMC_VDD_30_31:
  			pwr = SDHCI_POWER_300;
  			break;
  		case MMC_VDD_32_33:
  		case MMC_VDD_33_34:
  			pwr = SDHCI_POWER_330;
  			break;
  		}
  	}
  
  	if (pwr == 0) {
  		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
  		return;
  	}
  
  	pwr |= SDHCI_POWER_ON;
  
  	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
  }
e7881d85a   Simon Glass   dm: mmc: Drop CON...
421
  #ifdef CONFIG_DM_MMC
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
422
423
424
425
  static int sdhci_set_ios(struct udevice *dev)
  {
  	struct mmc *mmc = mmc_get_mmc_dev(dev);
  #else
07b0b9c00   Jaehoon Chung   mmc: change the s...
426
  static int sdhci_set_ios(struct mmc *mmc)
af62a5578   Lei Wen   MMC: add sdhci ge...
427
  {
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
428
  #endif
af62a5578   Lei Wen   MMC: add sdhci ge...
429
  	u32 ctrl;
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
430
  	struct sdhci_host *host = mmc->priv;
af62a5578   Lei Wen   MMC: add sdhci ge...
431

bf9c4d146   Masahiro Yamada   mmc: sdhci: fix N...
432
  	if (host->ops && host->ops->set_control_reg)
62226b686   Jaehoon Chung   mmc: sdhci: move ...
433
  		host->ops->set_control_reg(host);
236bfecff   Jaehoon Chung   mmc: add the quir...
434

af62a5578   Lei Wen   MMC: add sdhci ge...
435
436
437
438
439
440
441
  	if (mmc->clock != host->clock)
  		sdhci_set_clock(mmc, mmc->clock);
  
  	/* Set bus width */
  	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
  	if (mmc->bus_width == 8) {
  		ctrl &= ~SDHCI_CTRL_4BITBUS;
113e5dfcd   Jaehoon Chung   mmc: sdhci: use t...
442
443
  		if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ||
  				(host->quirks & SDHCI_QUIRK_USE_WIDE8))
af62a5578   Lei Wen   MMC: add sdhci ge...
444
445
  			ctrl |= SDHCI_CTRL_8BITBUS;
  	} else {
f88a429f1   Matt Reimer   mmc: sdhci: fix b...
446
447
  		if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ||
  				(host->quirks & SDHCI_QUIRK_USE_WIDE8))
af62a5578   Lei Wen   MMC: add sdhci ge...
448
449
450
451
452
453
454
455
456
457
458
  			ctrl &= ~SDHCI_CTRL_8BITBUS;
  		if (mmc->bus_width == 4)
  			ctrl |= SDHCI_CTRL_4BITBUS;
  		else
  			ctrl &= ~SDHCI_CTRL_4BITBUS;
  	}
  
  	if (mmc->clock > 26000000)
  		ctrl |= SDHCI_CTRL_HISPD;
  	else
  		ctrl &= ~SDHCI_CTRL_HISPD;
236bfecff   Jaehoon Chung   mmc: add the quir...
459
460
  	if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)
  		ctrl &= ~SDHCI_CTRL_HISPD;
af62a5578   Lei Wen   MMC: add sdhci ge...
461
  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
07b0b9c00   Jaehoon Chung   mmc: change the s...
462

210841c69   Stefan Roese   mmc: sdhci: Add s...
463
464
465
  	/* If available, call the driver specific "post" set_ios() function */
  	if (host->ops && host->ops->set_ios_post)
  		host->ops->set_ios_post(host);
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
466
  	return 0;
af62a5578   Lei Wen   MMC: add sdhci ge...
467
  }
6588c78bf   Jeroen Hofstee   sdhci: make local...
468
  static int sdhci_init(struct mmc *mmc)
af62a5578   Lei Wen   MMC: add sdhci ge...
469
  {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
470
  	struct sdhci_host *host = mmc->priv;
af62a5578   Lei Wen   MMC: add sdhci ge...
471

8d549b61d   Masahiro Yamada   mmc: sdhci: move ...
472
  	sdhci_reset(host, SDHCI_RESET_ALL);
af62a5578   Lei Wen   MMC: add sdhci ge...
473
474
475
  	if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) {
  		aligned_buffer = memalign(8, 512*1024);
  		if (!aligned_buffer) {
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
476
477
478
  			printf("%s: Aligned buffer alloc failed!!!
  ",
  			       __func__);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
479
  			return -ENOMEM;
af62a5578   Lei Wen   MMC: add sdhci ge...
480
481
  		}
  	}
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
482
  	sdhci_set_power(host, fls(mmc->cfg->voltages) - 1);
470dcc751   Joe Hershberger   mmc: Add a SDHCI ...
483

bf9c4d146   Masahiro Yamada   mmc: sdhci: fix N...
484
  	if (host->ops && host->ops->get_cd)
6f88a3a5d   Jaehoon Chung   mmc: sdhci: remov...
485
  		host->ops->get_cd(host);
470dcc751   Joe Hershberger   mmc: Add a SDHCI ...
486

ce0c1bc13   Łukasz Majewski   mmc:sdhci:fix: Ch...
487
  	/* Enable only interrupts served by the SD controller */
30e6d979f   Darwin Rambo   mmc: Minor cleanu...
488
489
  	sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
  		     SDHCI_INT_ENABLE);
ce0c1bc13   Łukasz Majewski   mmc:sdhci:fix: Ch...
490
491
  	/* Mask all sdhci interrupt sources */
  	sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
af62a5578   Lei Wen   MMC: add sdhci ge...
492

af62a5578   Lei Wen   MMC: add sdhci ge...
493
494
  	return 0;
  }
e7881d85a   Simon Glass   dm: mmc: Drop CON...
495
  #ifdef CONFIG_DM_MMC
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
496
497
498
499
500
501
  int sdhci_probe(struct udevice *dev)
  {
  	struct mmc *mmc = mmc_get_mmc_dev(dev);
  
  	return sdhci_init(mmc);
  }
ab769f227   Pantelis Antoniou   mmc: Remove ops f...
502

ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
503
504
505
506
507
  const struct dm_mmc_ops sdhci_ops = {
  	.send_cmd	= sdhci_send_command,
  	.set_ios	= sdhci_set_ios,
  };
  #else
ab769f227   Pantelis Antoniou   mmc: Remove ops f...
508
509
510
511
512
  static const struct mmc_ops sdhci_ops = {
  	.send_cmd	= sdhci_send_command,
  	.set_ios	= sdhci_set_ios,
  	.init		= sdhci_init,
  };
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
513
  #endif
ab769f227   Pantelis Antoniou   mmc: Remove ops f...
514

14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
515
  int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
516
  		u32 f_max, u32 f_min)
af62a5578   Lei Wen   MMC: add sdhci ge...
517
  {
6dffdbc3a   Wenyou Yang   mmc: sdhci: Add t...
518
  	u32 caps, caps_1;
14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
519
520
  
  	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
15bd09959   Masahiro Yamada   mmc: sdhci: move ...
521

45a68fe26   Masahiro Yamada   mmc: move some SD...
522
  #ifdef CONFIG_MMC_SDHCI_SDMA
15bd09959   Masahiro Yamada   mmc: sdhci: move ...
523
524
525
526
527
528
529
  	if (!(caps & SDHCI_CAN_DO_SDMA)) {
  		printf("%s: Your controller doesn't support SDMA!!
  ",
  		       __func__);
  		return -EINVAL;
  	}
  #endif
895549a2d   Jaehoon Chung   mmc: sdhci: use t...
530
531
532
533
534
  	if (host->quirks & SDHCI_QUIRK_REG32_RW)
  		host->version =
  			sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16;
  	else
  		host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
535
536
  
  	cfg->name = host->name;
e7881d85a   Simon Glass   dm: mmc: Drop CON...
537
  #ifndef CONFIG_DM_MMC
2a809093f   Simon Glass   dm: mmc: sdhci: R...
538
  	cfg->ops = &sdhci_ops;
af62a5578   Lei Wen   MMC: add sdhci ge...
539
  #endif
0e0dcc191   Wenyou Yang   mmc: sdhci: Fix m...
540
541
542
543
544
545
546
  
  	/* Check whether the clock multiplier is supported or not */
  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
  		caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
  		host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
  				SDHCI_CLOCK_MUL_SHIFT;
  	}
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
547
  	if (host->max_clk == 0) {
14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
548
  		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
549
  			host->max_clk = (caps & SDHCI_CLOCK_V3_BASE_MASK) >>
2a809093f   Simon Glass   dm: mmc: sdhci: R...
550
  				SDHCI_CLOCK_BASE_SHIFT;
af62a5578   Lei Wen   MMC: add sdhci ge...
551
  		else
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
552
  			host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >>
2a809093f   Simon Glass   dm: mmc: sdhci: R...
553
  				SDHCI_CLOCK_BASE_SHIFT;
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
554
  		host->max_clk *= 1000000;
0e0dcc191   Wenyou Yang   mmc: sdhci: Fix m...
555
556
  		if (host->clk_mul)
  			host->max_clk *= host->clk_mul;
af62a5578   Lei Wen   MMC: add sdhci ge...
557
  	}
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
558
  	if (host->max_clk == 0) {
6c67954c9   Masahiro Yamada   mmc: sdhci: move ...
559
560
561
  		printf("%s: Hardware doesn't specify base clock frequency
  ",
  		       __func__);
2a809093f   Simon Glass   dm: mmc: sdhci: R...
562
  		return -EINVAL;
6c67954c9   Masahiro Yamada   mmc: sdhci: move ...
563
  	}
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
564
565
566
567
568
569
  	if (f_max && (f_max < host->max_clk))
  		cfg->f_max = f_max;
  	else
  		cfg->f_max = host->max_clk;
  	if (f_min)
  		cfg->f_min = f_min;
af62a5578   Lei Wen   MMC: add sdhci ge...
570
  	else {
14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
571
  		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
2a809093f   Simon Glass   dm: mmc: sdhci: R...
572
  			cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_300;
af62a5578   Lei Wen   MMC: add sdhci ge...
573
  		else
2a809093f   Simon Glass   dm: mmc: sdhci: R...
574
  			cfg->f_min = cfg->f_max / SDHCI_MAX_DIV_SPEC_200;
af62a5578   Lei Wen   MMC: add sdhci ge...
575
  	}
2a809093f   Simon Glass   dm: mmc: sdhci: R...
576
  	cfg->voltages = 0;
af62a5578   Lei Wen   MMC: add sdhci ge...
577
  	if (caps & SDHCI_CAN_VDD_330)
2a809093f   Simon Glass   dm: mmc: sdhci: R...
578
  		cfg->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
af62a5578   Lei Wen   MMC: add sdhci ge...
579
  	if (caps & SDHCI_CAN_VDD_300)
2a809093f   Simon Glass   dm: mmc: sdhci: R...
580
  		cfg->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
af62a5578   Lei Wen   MMC: add sdhci ge...
581
  	if (caps & SDHCI_CAN_VDD_180)
2a809093f   Simon Glass   dm: mmc: sdhci: R...
582
  		cfg->voltages |= MMC_VDD_165_195;
236bfecff   Jaehoon Chung   mmc: add the quir...
583

3137e645e   Masahiro Yamada   mmc: sdhci: move ...
584
585
  	if (host->quirks & SDHCI_QUIRK_BROKEN_VOLTAGE)
  		cfg->voltages |= host->voltages;
be165fbbf   Masahiro Yamada   mmc: sdhci: do no...
586
  	cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
3fd0a9ba8   Jaehoon Chung   mmc: sdhci: combi...
587
588
  
  	/* Since Host Controller Version3.0 */
14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
589
  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
ecd7b246f   Jaehoon Chung   mmc: sdhci: disab...
590
591
  		if (!(caps & SDHCI_CAN_DO_8BIT))
  			cfg->host_caps &= ~MMC_MODE_8BIT;
1695b29a5   Jagannadha Sutradharudu Teki   mmc: sdhci: Enabl...
592
  	}
429790026   Siva Durga Prasad Paladugu   mmc: sdhci: Clear...
593

14bed52d2   Jaehoon Chung   mmc: sdhci: remov...
594
595
  	if (host->host_caps)
  		cfg->host_caps |= host->host_caps;
429790026   Siva Durga Prasad Paladugu   mmc: sdhci: Clear...
596

2a809093f   Simon Glass   dm: mmc: sdhci: R...
597
  	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
598

2a809093f   Simon Glass   dm: mmc: sdhci: R...
599
600
  	return 0;
  }
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
601
602
603
604
605
606
  #ifdef CONFIG_BLK
  int sdhci_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg)
  {
  	return mmc_bind(dev, mmc, cfg);
  }
  #else
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
607
  int add_sdhci(struct sdhci_host *host, u32 f_max, u32 f_min)
2a809093f   Simon Glass   dm: mmc: sdhci: R...
608
  {
6c67954c9   Masahiro Yamada   mmc: sdhci: move ...
609
  	int ret;
6d0e34bf4   Stefan Herbrechtsmeier   mmc: sdhci: Disti...
610
  	ret = sdhci_setup_cfg(&host->cfg, host, f_max, f_min);
6c67954c9   Masahiro Yamada   mmc: sdhci: move ...
611
612
  	if (ret)
  		return ret;
2a809093f   Simon Glass   dm: mmc: sdhci: R...
613

93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
614
615
616
617
  	host->mmc = mmc_create(&host->cfg, host);
  	if (host->mmc == NULL) {
  		printf("%s: mmc create fail!
  ", __func__);
2cb5d67c1   Jaehoon Chung   mmc: sdhci: use t...
618
  		return -ENOMEM;
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
619
  	}
af62a5578   Lei Wen   MMC: add sdhci ge...
620
621
622
  
  	return 0;
  }
ef1e4eda6   Simon Glass   dm: mmc: sdhci: S...
623
  #endif