blob: 5488d481fc83839362db0d165da9c4a5fc0141f7 [file] [log] [blame]
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -07001/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdlib.h>
30#include <stdint.h>
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070031#include <mmc_wrapper.h>
Channagoud Kadabia17fc422013-09-11 12:25:34 -070032#include <mmc_sdhci.h>
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070033#include <sdhci.h>
Channagoud Kadabia17fc422013-09-11 12:25:34 -070034#include <ufs.h>
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070035#include <target.h>
Oliver Wangcee448d2013-10-22 18:40:13 +080036#include <string.h>
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070037
38/*
Channagoud Kadabia17fc422013-09-11 12:25:34 -070039 * Weak function for UFS.
40 * These are needed to avoid link errors for platforms which
41 * do not support UFS. Its better to keep this inside the
42 * mmc wrapper.
43 */
44__WEAK int ufs_write(struct ufs_dev *dev, uint64_t data_addr, addr_t in, uint32_t len)
45{
46 return 0;
47}
48
49__WEAK int ufs_read(struct ufs_dev *dev, uint64_t data_addr, addr_t in, uint32_t len)
50{
51 return 0;
52}
53
54__WEAK uint32_t ufs_get_page_size(struct ufs_dev *dev)
55{
56 return 0;
57}
58
59__WEAK uint32_t ufs_get_serial_num(struct ufs_dev *dev)
60{
61 return 0;
62}
63
64__WEAK uint64_t ufs_get_dev_capacity(struct ufs_dev *dev)
65{
66 return 0;
67}
68
Sundarajan Srinivasan7b74ed22013-11-01 16:01:06 -070069__WEAK uint32_t ufs_get_erase_blk_size(struct ufs_dev *dev)
70{
71 return 0;
72}
73
74__WEAK int ufs_erase(struct ufs_dev* dev, uint64_t start_lba, uint32_t num_blocks)
75{
76 return 0;
77}
Channagoud Kadabia17fc422013-09-11 12:25:34 -070078/*
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070079 * Function: get mmc card
80 * Arg : None
81 * Return : Pointer to mmc card structure
82 * Flow : Get the card pointer from the device structure
83 */
84static struct mmc_card *get_mmc_card()
85{
Channagoud Kadabia17fc422013-09-11 12:25:34 -070086 void *dev;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070087 struct mmc_card *card;
88
89 dev = target_mmc_device();
Channagoud Kadabia17fc422013-09-11 12:25:34 -070090 card = &((struct mmc_device*)dev)->card;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070091
92 return card;
93}
94
95/*
96 * Function: mmc_write
97 * Arg : Data address on card, data length, i/p buffer
98 * Return : 0 on Success, non zero on failure
99 * Flow : Write the data from in to the card
100 */
101uint32_t mmc_write(uint64_t data_addr, uint32_t data_len, void *in)
102{
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700103 uint32_t val = 0;
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700104 int ret = 0;
105 uint32_t block_size = 0;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700106 uint32_t write_size = SDHCI_ADMA_MAX_TRANS_SZ;
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700107 uint8_t *sptr = (uint8_t *)in;
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700108 void *dev;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700109
110 dev = target_mmc_device();
111
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700112 block_size = mmc_get_device_blocksize();
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700113
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700114 ASSERT(!(data_addr % block_size));
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700115
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700116 if (data_len % block_size)
117 data_len = ROUNDUP(data_len, block_size);
118
119 if (target_boot_device_emmc())
120 {
121 /* TODO: This function is aware of max data that can be
122 * tranferred using sdhci adma mode, need to have a cleaner
123 * implementation to keep this function independent of sdhci
124 * limitations
125 */
126 while (data_len > write_size) {
127 val = mmc_sdhci_write((struct mmc_device *)dev, (void *)sptr, (data_addr / block_size), (write_size / block_size));
128 if (val)
129 {
130 dprintf(CRITICAL, "Failed Writing block @ %x\n", (data_addr / block_size));
131 return val;
132 }
133 sptr += write_size;
134 data_addr += write_size;
135 data_len -= write_size;
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700136 }
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700137
138 if (data_len)
139 val = mmc_sdhci_write((struct mmc_device *)dev, (void *)sptr, (data_addr / block_size), (data_len / block_size));
140
141 if (val)
142 dprintf(CRITICAL, "Failed Writing block @ %x\n", (data_addr / block_size));
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700143 }
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700144 else
145 {
146 arch_clean_invalidate_cache_range((addr_t)in, data_len);
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700147
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700148 ret = ufs_write((struct ufs_dev *)dev, data_addr, (addr_t)in, (data_len / block_size));
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700149
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700150 if (ret)
151 {
152 dprintf(CRITICAL, "Error: UFS write failed writing to block: %llu\n", data_addr);
153 val = 1;
154 }
155 }
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700156
157 return val;
158}
159
160/*
161 * Function: mmc_read
162 * Arg : Data address on card, o/p buffer & data length
163 * Return : 0 on Success, non zero on failure
164 * Flow : Read data from the card to out
165 */
166uint32_t mmc_read(uint64_t data_addr, uint32_t *out, uint32_t data_len)
167{
168 uint32_t ret = 0;
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700169 uint32_t block_size;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700170 uint32_t read_size = SDHCI_ADMA_MAX_TRANS_SZ;
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700171 void *dev;
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700172 uint8_t *sptr = (uint8_t *)out;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700173
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700174 dev = target_mmc_device();
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700175 block_size = mmc_get_device_blocksize();
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700176
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700177 ASSERT(!(data_addr % block_size));
178 ASSERT(!(data_len % block_size));
179
180
181 if (target_boot_device_emmc())
182 {
183 /* TODO: This function is aware of max data that can be
184 * tranferred using sdhci adma mode, need to have a cleaner
185 * implementation to keep this function independent of sdhci
186 * limitations
187 */
188 while (data_len > read_size) {
189 ret = mmc_sdhci_read((struct mmc_device *)dev, (void *)sptr, (data_addr / block_size), (read_size / block_size));
190 if (ret)
191 {
192 dprintf(CRITICAL, "Failed Reading block @ %x\n", (data_addr / block_size));
193 return ret;
194 }
195 sptr += read_size;
196 data_addr += read_size;
197 data_len -= read_size;
198 }
199
200 if (data_len)
201 ret = mmc_sdhci_read((struct mmc_device *)dev, (void *)sptr, (data_addr / block_size), (data_len / block_size));
202
203 if (ret)
204 dprintf(CRITICAL, "Failed Reading block @ %x\n", (data_addr / block_size));
205 }
206 else
207 {
208 ret = ufs_read((struct ufs_dev *) dev, data_addr, (addr_t)out, (data_len / block_size));
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700209 if (ret)
210 {
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700211 dprintf(CRITICAL, "Error: UFS read failed writing to block: %llu\n", data_addr);
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700212 }
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700213
214 arch_invalidate_cache_range((addr_t)out, data_len);
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700215 }
216
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700217 return ret;
218}
219
Sundarajan Srinivasan7b74ed22013-11-01 16:01:06 -0700220
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700221/*
Oliver Wangcee448d2013-10-22 18:40:13 +0800222 * Function: mmc get erase unit size
223 * Arg : None
224 * Return : Returns the erase unit size of the storage
225 * Flow : Get the erase unit size from the card
226 */
227
228uint32_t mmc_get_eraseunit_size()
229{
230 uint32_t erase_unit_sz = 0;
231
232 if (target_boot_device_emmc()) {
233 struct mmc_device *dev;
234 struct mmc_card *card;
235
236 dev = target_mmc_device();
237 card = &dev->card;
238 /*
239 * Calculate the erase unit size,
240 * 1. Based on emmc 4.5 spec for emmc card
241 * 2. Use SD Card Status info for SD cards
242 */
243 if (MMC_CARD_MMC(card))
244 {
245 /*
246 * Calculate the erase unit size as per the emmc specification v4.5
247 */
248 if (dev->card.ext_csd[MMC_ERASE_GRP_DEF])
249 erase_unit_sz = (MMC_HC_ERASE_MULT * dev->card.ext_csd[MMC_HC_ERASE_GRP_SIZE]) / MMC_BLK_SZ;
250 else
251 erase_unit_sz = (dev->card.csd.erase_grp_size + 1) * (dev->card.csd.erase_grp_mult + 1);
252 }
253 else
254 erase_unit_sz = dev->card.ssr.au_size * dev->card.ssr.num_aus;
255 }
256
257 return erase_unit_sz;
258}
259
260/*
261 * Function: Zero out blk_len blocks at the blk_addr by writing zeros. The
262 * function can be used when we want to erase the blocks not
263 * aligned with the mmc erase group.
264 * Arg : Block address & length
265 * Return : Returns 0
266 * Flow : Erase the card from specified addr
267 */
268
269static uint32_t mmc_zero_out(struct mmc_device* dev, uint32_t blk_addr, uint32_t num_blks)
270{
271 uint32_t *out;
272 uint32_t block_size;
273 int i;
274
275 dprintf(INFO, "erasing 0x%x:0x%x\n", blk_addr, num_blks);
276 block_size = mmc_get_device_blocksize();
277
278 /* Assume there are at least block_size bytes available in the heap */
279 out = memalign(CACHE_LINE, ROUNDUP(block_size, CACHE_LINE));
280
281 if (!out)
282 {
283 dprintf(CRITICAL, "Error allocating memory\n");
284 return 1;
285 }
286 memset((void *)out, 0, ROUNDUP(block_size, CACHE_LINE));
287
288 for (i = 0; i < num_blks; i++)
289 {
290 if (mmc_sdhci_write(dev, out, blk_addr + i, 1))
291 {
292 dprintf(CRITICAL, "failed to erase the partition: %x\n", blk_addr);
293 free(out);
294 return 1;
295 }
296 }
297 free(out);
298 return 0;
299}
300
301/*
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700302 * Function: mmc erase card
303 * Arg : Block address & length
304 * Return : Returns 0
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700305 * Flow : Erase the card from specified addr
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700306 */
307uint32_t mmc_erase_card(uint64_t addr, uint64_t len)
308{
Oliver Wangcee448d2013-10-22 18:40:13 +0800309 struct mmc_device *dev;
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700310 uint32_t block_size;
Oliver Wangcee448d2013-10-22 18:40:13 +0800311 uint32_t unaligned_blks;
312 uint32_t head_unit;
313 uint32_t tail_unit;
314 uint32_t erase_unit_sz;
315 uint32_t blk_addr;
316 uint32_t blk_count;
317 uint64_t blks_to_erase;
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700318
Sundarajan Srinivasan7b74ed22013-11-01 16:01:06 -0700319 block_size = mmc_get_device_blocksize();
320
321 dev = target_mmc_device();
322
323 ASSERT(!(addr % block_size));
324 ASSERT(!(len % block_size));
325
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700326 if (target_boot_device_emmc())
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700327 {
Oliver Wangcee448d2013-10-22 18:40:13 +0800328 erase_unit_sz = mmc_get_eraseunit_size();
329 dprintf(SPEW, "erase_unit_sz:0x%x\n", erase_unit_sz);
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700330
Oliver Wangcee448d2013-10-22 18:40:13 +0800331 blk_addr = addr / block_size;
332 blk_count = len / block_size;
333
Oliver Wangcee448d2013-10-22 18:40:13 +0800334 dprintf(INFO, "Erasing card: 0x%x:0x%x\n", blk_addr, blk_count);
335
336 head_unit = blk_addr / erase_unit_sz;
337 tail_unit = (blk_addr + blk_count - 1) / erase_unit_sz;
338
339 if (tail_unit - head_unit <= 1)
340 {
341 dprintf(INFO, "SDHCI unit erase not required\n");
342 return mmc_zero_out(dev, blk_addr, blk_count);
343 }
344
345 unaligned_blks = erase_unit_sz - (blk_addr % erase_unit_sz);
346
347 if (unaligned_blks < erase_unit_sz)
348 {
349 dprintf(SPEW, "Handling unaligned head blocks\n");
350 if (mmc_zero_out(dev, blk_addr, unaligned_blks))
351 return 1;
352
353 blk_addr += unaligned_blks;
354 blk_count -= unaligned_blks;
355 }
356
357 unaligned_blks = blk_count % erase_unit_sz;
358 blks_to_erase = blk_count - unaligned_blks;
359
360 dprintf(SPEW, "Performing SDHCI erase: 0x%x:0x%x\n", blk_addr, blks_to_erase);
361 if (mmc_sdhci_erase((struct mmc_device *)dev, blk_addr, blks_to_erase * block_size))
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700362 {
363 dprintf(CRITICAL, "MMC erase failed\n");
364 return 1;
365 }
Oliver Wangcee448d2013-10-22 18:40:13 +0800366
367 blk_addr += blks_to_erase;
368
369 if (unaligned_blks)
370 {
371 dprintf(SPEW, "Handling unaligned tail blocks\n");
372 if (mmc_zero_out(dev, blk_addr, unaligned_blks))
373 return 1;
374 }
375
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700376 }
Sundarajan Srinivasan7b74ed22013-11-01 16:01:06 -0700377 else
378 {
379 if(ufs_erase((struct ufs_dev *)dev, addr, (len / block_size)))
380 {
381 dprintf(CRITICAL, "mmc_erase_card: UFS erase failed\n");
382 return 1;
383 }
384 }
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700385
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700386 return 0;
387}
388
389/*
390 * Function: mmc get psn
391 * Arg : None
392 * Return : Returns the product serial number
393 * Flow : Get the PSN from card
394 */
395uint32_t mmc_get_psn(void)
396{
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700397 if (target_boot_device_emmc())
398 {
399 struct mmc_card *card;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700400
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700401 card = get_mmc_card();
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700402
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700403 return card->cid.psn;
404 }
405 else
406 {
407 void *dev;
408
409 dev = target_mmc_device();
410
411 return ufs_get_serial_num((struct ufs_dev *)dev);
412 }
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700413}
414
415/*
416 * Function: mmc get capacity
417 * Arg : None
418 * Return : Returns the density of the emmc card
419 * Flow : Get the density from card
420 */
421uint64_t mmc_get_device_capacity()
422{
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700423 if (target_boot_device_emmc())
424 {
425 struct mmc_card *card;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700426
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700427 card = get_mmc_card();
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700428
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700429 return card->capacity;
430 }
431 else
432 {
433 void *dev;
434
435 dev = target_mmc_device();
436
437 return ufs_get_dev_capacity((struct ufs_dev *)dev);
438 }
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700439}
440
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700441/*
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700442 * Function: mmc get blocksize
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700443 * Arg : None
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700444 * Return : Returns the block size of the storage
445 * Flow : Get the block size form the card
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700446 */
447uint32_t mmc_get_device_blocksize()
448{
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700449 if (target_boot_device_emmc())
450 {
451 struct mmc_card *card;
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700452
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700453 card = get_mmc_card();
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700454
Channagoud Kadabia17fc422013-09-11 12:25:34 -0700455 return card->block_size;
456 }
457 else
458 {
459 void *dev;
460
461 dev = target_mmc_device();
462
463 return ufs_get_page_size((struct ufs_dev *)dev);
464 }
465}
466
467/*
468 * Function: storage page size
469 * Arg : None
470 * Return : Returns the page size for the card
471 * Flow : Get the page size for storage
472 */
473uint32_t mmc_page_size()
474{
475 if (target_boot_device_emmc())
476 {
477 return BOARD_KERNEL_PAGESIZE;
478 }
479 else
480 {
481 void *dev;
482
483 dev = target_mmc_device();
484
485 return ufs_get_page_size((struct ufs_dev *)dev);
486 }
487}
488
489/*
490 * Function: mmc device sleep
491 * Arg : None
492 * Return : Clean up function for storage
493 * Flow : Put the mmc card to sleep
494 */
495void mmc_device_sleep()
496{
497 void *dev;
498 dev = target_mmc_device();
499
500 if (target_boot_device_emmc())
501 {
502 mmc_put_card_to_sleep((struct mmc_device *)dev);
503 }
Channagoud Kadabi96c629e2013-09-10 14:21:30 -0700504}