blob: d51673831b4c19e6838eac1cf2d61b60bb1129c6 [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>
31#include <mmc_sdhci.h>
32#include <mmc_wrapper.h>
33#include <sdhci.h>
34#include <target.h>
Oliver Wang0e0dfa72013-10-22 18:40:13 +080035#include <string.h>
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070036
37/*
38 * Function: get mmc card
39 * Arg : None
40 * Return : Pointer to mmc card structure
41 * Flow : Get the card pointer from the device structure
42 */
43static struct mmc_card *get_mmc_card()
44{
45 struct mmc_device *dev;
46 struct mmc_card *card;
47
48 dev = target_mmc_device();
49 card = &dev->card;
50
51 return card;
52}
53
54/*
55 * Function: mmc_write
56 * Arg : Data address on card, data length, i/p buffer
57 * Return : 0 on Success, non zero on failure
58 * Flow : Write the data from in to the card
59 */
60uint32_t mmc_write(uint64_t data_addr, uint32_t data_len, void *in)
61{
Channagoud Kadabi53f1be72013-06-05 17:56:11 -070062 uint32_t val = 0;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070063 uint32_t write_size = SDHCI_ADMA_MAX_TRANS_SZ;
Channagoud Kadabi53f1be72013-06-05 17:56:11 -070064 uint8_t *sptr = (uint8_t *)in;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070065 struct mmc_device *dev;
66
67 dev = target_mmc_device();
68
69 ASSERT(!(data_addr % MMC_BLK_SZ));
70
71 if (data_len % MMC_BLK_SZ)
72 data_len = ROUNDUP(data_len, MMC_BLK_SZ);
73
74 /* TODO: This function is aware of max data that can be
75 * tranferred using sdhci adma mode, need to have a cleaner
76 * implementation to keep this function independent of sdhci
77 * limitations
78 */
79 while (data_len > write_size) {
Channagoud Kadabi53f1be72013-06-05 17:56:11 -070080 val = mmc_sdhci_write(dev, (void *)sptr, (data_addr / MMC_BLK_SZ), (write_size / MMC_BLK_SZ));
81 if (val)
82 {
83 dprintf(CRITICAL, "Failed Writing block @ %x\n", (data_addr / MMC_BLK_SZ));
84 return val;
85 }
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070086 sptr += write_size;
87 data_addr += write_size;
88 data_len -= write_size;
89 }
90
91 if (data_len)
Channagoud Kadabi53f1be72013-06-05 17:56:11 -070092 val = mmc_sdhci_write(dev, (void *)sptr, (data_addr / MMC_BLK_SZ), (data_len / MMC_BLK_SZ));
93
94 if (val)
95 dprintf(CRITICAL, "Failed Writing block @ %x\n", (data_addr / MMC_BLK_SZ));
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -070096
97 return val;
98}
99
100/*
101 * Function: mmc_read
102 * Arg : Data address on card, o/p buffer & data length
103 * Return : 0 on Success, non zero on failure
104 * Flow : Read data from the card to out
105 */
106uint32_t mmc_read(uint64_t data_addr, uint32_t *out, uint32_t data_len)
107{
108 uint32_t ret = 0;
109 uint32_t read_size = SDHCI_ADMA_MAX_TRANS_SZ;
110 struct mmc_device *dev;
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700111 uint8_t *sptr = (uint8_t *)out;
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700112
113 ASSERT(!(data_addr % MMC_BLK_SZ));
114 ASSERT(!(data_len % MMC_BLK_SZ));
115
116 dev = target_mmc_device();
117
118 /* TODO: This function is aware of max data that can be
119 * tranferred using sdhci adma mode, need to have a cleaner
120 * implementation to keep this function independent of sdhci
121 * limitations
122 */
123 while (data_len > read_size) {
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700124 ret = mmc_sdhci_read(dev, (void *)sptr, (data_addr / MMC_BLK_SZ), (read_size / MMC_BLK_SZ));
125 if (ret)
126 {
127 dprintf(CRITICAL, "Failed Reading block @ %x\n", (data_addr / MMC_BLK_SZ));
128 return ret;
129 }
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700130 sptr += read_size;
131 data_addr += read_size;
132 data_len -= read_size;
133 }
134
135 if (data_len)
Channagoud Kadabi53f1be72013-06-05 17:56:11 -0700136 ret = mmc_sdhci_read(dev, (void *)sptr, (data_addr / MMC_BLK_SZ), (data_len / MMC_BLK_SZ));
137
138 if (ret)
139 dprintf(CRITICAL, "Failed Reading block @ %x\n", (data_addr / MMC_BLK_SZ));
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700140
141 return ret;
142}
143
144/*
Oliver Wang0e0dfa72013-10-22 18:40:13 +0800145 * Function: mmc get erase unit size
146 * Arg : None
147 * Return : Returns the erase unit size of the storage
148 * Flow : Get the erase unit size from the card
149 */
150
151uint32_t mmc_get_eraseunit_size()
152{
153 uint32_t erase_unit_sz = 0;
154 struct mmc_device *dev;
155 struct mmc_card *card;
156
157 dev = target_mmc_device();
158 card = &dev->card;
159 /*
160 * Calculate the erase unit size,
161 * 1. Based on emmc 4.5 spec for emmc card
162 * 2. Use SD Card Status info for SD cards
163 */
164 if (MMC_CARD_MMC(card))
165 {
166 /*
167 * Calculate the erase unit size as per the emmc specification v4.5
168 */
169 if (dev->card.ext_csd[MMC_ERASE_GRP_DEF])
170 erase_unit_sz = (MMC_HC_ERASE_MULT * dev->card.ext_csd[MMC_HC_ERASE_GRP_SIZE]) / MMC_BLK_SZ;
171 else
172 erase_unit_sz = (dev->card.csd.erase_grp_size + 1) * (dev->card.csd.erase_grp_mult + 1);
173 }
174 else
175 erase_unit_sz = dev->card.ssr.au_size * dev->card.ssr.num_aus;
176
177 return erase_unit_sz;
178}
179
180/*
181 * Function: Zero out blk_len blocks at the blk_addr by writing zeros. The
182 * function can be used when we want to erase the blocks not
183 * aligned with the mmc erase group.
184 * Arg : Block address & length
185 * Return : Returns 0
186 * Flow : Erase the card from specified addr
187 */
188
189static uint32_t mmc_zero_out(struct mmc_device* dev, uint32_t blk_addr, uint32_t num_blks)
190{
191 uint32_t *out;
192 uint32_t block_size;
193 int i;
194
195 dprintf(INFO, "erasing 0x%x:0x%x\n", blk_addr, num_blks);
196 block_size = mmc_get_device_blocksize();
197
198 /* Assume there are at least block_size bytes available in the heap */
199 out = memalign(CACHE_LINE, ROUNDUP(block_size, CACHE_LINE));
200
201 if (!out)
202 {
203 dprintf(CRITICAL, "Error allocating memory\n");
204 return 1;
205 }
206 memset((void *)out, 0, ROUNDUP(block_size, CACHE_LINE));
207
208 for (i = 0; i < num_blks; i++)
209 {
210 if (mmc_sdhci_write(dev, out, blk_addr + i, 1))
211 {
212 dprintf(CRITICAL, "failed to erase the partition: %x\n", blk_addr);
213 free(out);
214 return 1;
215 }
216 }
217 free(out);
218 return 0;
219}
220
221/*
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700222 * Function: mmc erase card
223 * Arg : Block address & length
224 * Return : Returns 0
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700225 * Flow : Erase the card from specified addr
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700226 */
227uint32_t mmc_erase_card(uint64_t addr, uint64_t len)
228{
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700229 struct mmc_device *dev;
Oliver Wang0e0dfa72013-10-22 18:40:13 +0800230 uint32_t block_size;
231 uint32_t unaligned_blks;
232 uint32_t head_unit;
233 uint32_t tail_unit;
234 uint32_t erase_unit_sz;
235 uint32_t blk_addr;
236 uint32_t blk_count;
237 uint64_t blks_to_erase;
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700238
Oliver Wang0e0dfa72013-10-22 18:40:13 +0800239 block_size = mmc_get_device_blocksize();
240 erase_unit_sz = mmc_get_eraseunit_size();
241 dprintf(SPEW, "erase_unit_sz:0x%x\n", erase_unit_sz);
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700242
243 ASSERT(!(addr % MMC_BLK_SZ));
244 ASSERT(!(len % MMC_BLK_SZ));
245
Oliver Wang0e0dfa72013-10-22 18:40:13 +0800246 blk_addr = addr / block_size;
247 blk_count = len / block_size;
248
249 dev = target_mmc_device();
250
251 dprintf(INFO, "Erasing card: 0x%x:0x%x\n", blk_addr, blk_count);
252
253 head_unit = blk_addr / erase_unit_sz;
254 tail_unit = (blk_addr + blk_count - 1) / erase_unit_sz;
255
256 if (tail_unit - head_unit <= 1)
257 {
258 dprintf(INFO, "SDHCI unit erase not required\n");
259 return mmc_zero_out(dev, blk_addr, blk_count);
260 }
261
262 unaligned_blks = erase_unit_sz - (blk_addr % erase_unit_sz);
263
264 if (unaligned_blks < erase_unit_sz)
265 {
266 dprintf(SPEW, "Handling unaligned head blocks\n");
267 if (mmc_zero_out(dev, blk_addr, unaligned_blks))
268 return 1;
269
270 blk_addr += unaligned_blks;
271 blk_count -= unaligned_blks;
272 }
273
274 unaligned_blks = blk_count % erase_unit_sz;
275 blks_to_erase = blk_count - unaligned_blks;
276
277 dprintf(SPEW, "Performing SDHCI erase: 0x%x:0x%x\n", blk_addr, blks_to_erase);
278 if (mmc_sdhci_erase((struct mmc_device *)dev, blk_addr, blks_to_erase * block_size))
Channagoud Kadabid87c6b12013-05-29 15:22:03 -0700279 {
280 dprintf(CRITICAL, "MMC erase failed\n");
281 return 1;
282 }
Oliver Wang0e0dfa72013-10-22 18:40:13 +0800283
284 blk_addr += blks_to_erase;
285
286 if (unaligned_blks)
287 {
288 dprintf(SPEW, "Handling unaligned tail blocks\n");
289 if (mmc_zero_out(dev, blk_addr, unaligned_blks))
290 return 1;
291 }
292
Channagoud Kadabi4ed511f2013-03-11 16:10:18 -0700293 return 0;
294}
295
296/*
297 * Function: mmc get psn
298 * Arg : None
299 * Return : Returns the product serial number
300 * Flow : Get the PSN from card
301 */
302uint32_t mmc_get_psn(void)
303{
304 struct mmc_card *card;
305
306 card = get_mmc_card();
307
308 return card->cid.psn;
309}
310
311/*
312 * Function: mmc get capacity
313 * Arg : None
314 * Return : Returns the density of the emmc card
315 * Flow : Get the density from card
316 */
317uint64_t mmc_get_device_capacity()
318{
319 struct mmc_card *card;
320
321 card = get_mmc_card();
322
323 return card->capacity;
324}
325
Channagoud Kadabi57eb6302013-09-10 14:21:30 -0700326/*
327 * Function: mmc get pagesize
328 * Arg : None
329 * Return : Returns the density of the emmc card
330 * Flow : Get the density from card
331 */
332uint32_t mmc_get_device_blocksize()
333{
334 struct mmc_card *card;
335
336 card = get_mmc_card();
337
338 return card->block_size;
339}
Aparna Mallavarapu53731bb2014-04-14 18:24:38 +0530340
341/*
342 * Function: storage page size
343 * Arg : None
344 * Return : Returns the page size for the card
345 * Flow : Get the page size for storage
346 */
347uint32_t mmc_page_size()
348{
349 return BOARD_KERNEL_PAGESIZE;
350}