1 /*
2 * Copyright 2008, Freescale Semiconductor, Inc
3 * Andy Fleming
4 *
5 * Based vaguely on the Linux code
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 */
9
10 #include <config.h>
11 #include <common.h>
12 #include <dm.h>
13 #include <part.h>
14 #include <div64.h>
15 #include <linux/math64.h>
16 #include "mmc_private.h"
17
mmc_erase_t(struct mmc * mmc,ulong start,lbaint_t blkcnt)18 static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
19 {
20 struct mmc_cmd cmd;
21 ulong end;
22 int err, start_cmd, end_cmd;
23 uint erase_mode;
24
25 if (mmc->high_capacity) {
26 end = start + blkcnt - 1;
27 } else {
28 end = (start + blkcnt - 1) * mmc->write_bl_len;
29 start *= mmc->write_bl_len;
30 }
31
32 /*
33 * The SD card have just one erase mode, the erase command
34 * perform an erase on the sector(s).
35 * The Emmc have four erase mode. We use the trim mode with
36 * cmd.cmdarg equal to MMC_TRIM_ARG which command performs an
37 * erase on the sector(s).
38 */
39 if (IS_SD(mmc)) {
40 start_cmd = SD_CMD_ERASE_WR_BLK_START;
41 end_cmd = SD_CMD_ERASE_WR_BLK_END;
42 erase_mode = MMC_ERASE_ARG;
43 } else {
44 start_cmd = MMC_CMD_ERASE_GROUP_START;
45 end_cmd = MMC_CMD_ERASE_GROUP_END;
46 if (mmc->esr.mmc_can_trim)
47 erase_mode = MMC_TRIM_ARG;
48 else
49 erase_mode = MMC_ERASE_ARG;
50 }
51
52 cmd.cmdidx = start_cmd;
53 cmd.cmdarg = start;
54 cmd.resp_type = MMC_RSP_R1;
55
56 err = mmc_send_cmd(mmc, &cmd, NULL);
57 if (err)
58 goto err_out;
59
60 cmd.cmdidx = end_cmd;
61 cmd.cmdarg = end;
62
63 err = mmc_send_cmd(mmc, &cmd, NULL);
64 if (err)
65 goto err_out;
66
67 cmd.cmdidx = MMC_CMD_ERASE;
68 cmd.cmdarg = erase_mode;
69 cmd.resp_type = MMC_RSP_R1b;
70
71 err = mmc_send_cmd(mmc, &cmd, NULL);
72 if (err)
73 goto err_out;
74
75 return 0;
76
77 err_out:
78 puts("mmc erase failed\n");
79 return err;
80 }
81
82 #ifdef CONFIG_BLK
mmc_berase(struct udevice * dev,lbaint_t start,lbaint_t blkcnt)83 ulong mmc_berase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt)
84 #else
85 ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt)
86 #endif
87 {
88 #ifdef CONFIG_BLK
89 struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
90 #endif
91 int dev_num = block_dev->devnum;
92 int err = 0;
93 u32 start_rem, blkcnt_rem;
94 struct mmc *mmc = find_mmc_device(dev_num);
95 lbaint_t blk = 0, blk_r = 0;
96 int timeout = 1000;
97 int mode = 0;
98
99 if (!mmc)
100 return -1;
101
102 if (!blkcnt)
103 return 0;
104
105 err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num,
106 block_dev->hwpart);
107
108 if (err < 0)
109 return -1;
110
111 if (!IS_SD(mmc)) {
112 if (mmc->esr.mmc_can_trim)
113 mode = 1;
114 }
115
116 if (mode) {
117 err = mmc_erase_t(mmc, start, blkcnt);
118 if (err)
119 return err;
120 if (mmc_send_status(mmc, timeout))
121 return 0;
122
123 return blkcnt;
124 } else {
125 /*
126 * We want to see if the requested start or total block
127 * count are unaligned. We discard the whole numbers and
128 * only care about the remainder.
129 */
130 err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
131 err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
132 if (start_rem || blkcnt_rem)
133 printf("\n\nCaution! Your devices Erase group is 0x%x\n"
134 "The erase range would be change to "
135 "0x" LBAF "~0x" LBAF "\n\n",
136 mmc->erase_grp_size,
137 start & ~(mmc->erase_grp_size - 1),
138 ((start + blkcnt + mmc->erase_grp_size)
139 & ~(mmc->erase_grp_size - 1)) - 1);
140 while (blk < blkcnt) {
141 if (IS_SD(mmc) && mmc->ssr.au) {
142 blk_r = ((blkcnt - blk) > mmc->ssr.au) ?
143 mmc->ssr.au : (blkcnt - blk);
144 } else {
145 blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
146 mmc->erase_grp_size : (blkcnt - blk);
147 }
148 err = mmc_erase_t(mmc, start + blk, blk_r);
149 if (err)
150 break;
151
152 blk += blk_r;
153
154 /* Waiting for the ready status */
155 if (mmc_send_status(mmc, timeout))
156 return 0;
157 }
158 return blk;
159 }
160 }
161
mmc_write_blocks(struct mmc * mmc,lbaint_t start,lbaint_t blkcnt,const void * src)162 static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
163 lbaint_t blkcnt, const void *src)
164 {
165 struct mmc_cmd cmd;
166 struct mmc_data data;
167 int timeout = 1000;
168 ulong writen_cnt = blkcnt;
169
170 if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) {
171 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
172 start + blkcnt, mmc_get_blk_desc(mmc)->lba);
173 return 0;
174 }
175
176 if (blkcnt == 0)
177 return 0;
178 else if (blkcnt == 1)
179 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
180 else
181 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
182
183 if (mmc->high_capacity)
184 cmd.cmdarg = start;
185 else
186 cmd.cmdarg = start * mmc->write_bl_len;
187
188 cmd.resp_type = MMC_RSP_R1;
189
190 data.src = src;
191 data.blocks = blkcnt;
192 data.blocksize = mmc->write_bl_len;
193 data.flags = MMC_DATA_WRITE;
194
195 if (mmc_send_cmd(mmc, &cmd, &data)) {
196 printf("mmc write failed\n");
197 writen_cnt = 0;
198 }
199
200 /* SPI multiblock writes terminate using a special
201 * token, not a STOP_TRANSMISSION request.
202 */
203 if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
204 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
205 cmd.cmdarg = 0;
206 cmd.resp_type = MMC_RSP_R1b;
207 if (mmc_send_cmd(mmc, &cmd, NULL)) {
208 printf("mmc fail to send stop cmd\n");
209 return 0;
210 }
211 }
212
213 /* Waiting for the ready status */
214 if (mmc_send_status(mmc, timeout))
215 return 0;
216
217 return writen_cnt;
218 }
219
220 #ifdef CONFIG_BLK
mmc_bwrite(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,const void * src)221 ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
222 const void *src)
223 #else
224 ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
225 const void *src)
226 #endif
227 {
228 #ifdef CONFIG_BLK
229 struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
230 #endif
231 int dev_num = block_dev->devnum;
232 lbaint_t cur, blocks_todo = blkcnt;
233 int err;
234
235 struct mmc *mmc = find_mmc_device(dev_num);
236 if (!mmc)
237 return 0;
238
239 err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
240 if (err < 0)
241 return 0;
242
243 if (mmc_set_blocklen(mmc, mmc->write_bl_len))
244 return 0;
245
246 do {
247 cur = (blocks_todo > mmc->cfg->b_max) ?
248 mmc->cfg->b_max : blocks_todo;
249 if (mmc_write_blocks(mmc, start, cur, src) != cur) {
250 /* retry again with Open-ended Multiple block write */
251 if (mmc_write_blocks(mmc, start, cur, src) != cur)
252 return 0;
253 }
254 blocks_todo -= cur;
255 start += cur;
256 src += cur * mmc->write_bl_len;
257 } while (blocks_todo > 0);
258
259 return blkcnt;
260 }
261