blob: bd07b20f5fa5acef6fe332f8266cbb9fc70c9099 [file] [log] [blame]
Paul Burton8d30cc92013-09-09 15:30:26 +01001/*
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 <part.h>
Tom Rini58e31942015-06-11 20:53:31 -040013#include <div64.h>
14#include <linux/math64.h>
Paul Burton8d30cc92013-09-09 15:30:26 +010015#include "mmc_private.h"
16
17static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
18{
19 struct mmc_cmd cmd;
20 ulong end;
21 int err, start_cmd, end_cmd;
22
23 if (mmc->high_capacity) {
24 end = start + blkcnt - 1;
25 } else {
26 end = (start + blkcnt - 1) * mmc->write_bl_len;
27 start *= mmc->write_bl_len;
28 }
29
30 if (IS_SD(mmc)) {
31 start_cmd = SD_CMD_ERASE_WR_BLK_START;
32 end_cmd = SD_CMD_ERASE_WR_BLK_END;
33 } else {
34 start_cmd = MMC_CMD_ERASE_GROUP_START;
35 end_cmd = MMC_CMD_ERASE_GROUP_END;
36 }
37
38 cmd.cmdidx = start_cmd;
39 cmd.cmdarg = start;
40 cmd.resp_type = MMC_RSP_R1;
41
42 err = mmc_send_cmd(mmc, &cmd, NULL);
43 if (err)
44 goto err_out;
45
46 cmd.cmdidx = end_cmd;
47 cmd.cmdarg = end;
48
49 err = mmc_send_cmd(mmc, &cmd, NULL);
50 if (err)
51 goto err_out;
52
53 cmd.cmdidx = MMC_CMD_ERASE;
Eric Nelson957e0662015-12-07 07:50:01 -070054 cmd.cmdarg = MMC_ERASE_ARG;
Paul Burton8d30cc92013-09-09 15:30:26 +010055 cmd.resp_type = MMC_RSP_R1b;
56
57 err = mmc_send_cmd(mmc, &cmd, NULL);
58 if (err)
59 goto err_out;
60
61 return 0;
62
63err_out:
64 puts("mmc erase failed\n");
65 return err;
66}
67
Simon Glasse3394752016-02-29 15:25:34 -070068unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start,
Stephen Warrene73f2962015-12-07 11:38:48 -070069 lbaint_t blkcnt)
Paul Burton8d30cc92013-09-09 15:30:26 +010070{
Simon Glass2f26fff2016-02-29 15:25:51 -070071 int dev_num = block_dev->devnum;
Paul Burton8d30cc92013-09-09 15:30:26 +010072 int err = 0;
Tom Rini58e31942015-06-11 20:53:31 -040073 u32 start_rem, blkcnt_rem;
Paul Burton8d30cc92013-09-09 15:30:26 +010074 struct mmc *mmc = find_mmc_device(dev_num);
75 lbaint_t blk = 0, blk_r = 0;
76 int timeout = 1000;
77
78 if (!mmc)
79 return -1;
80
Simon Glass11f2bb62016-05-01 13:52:29 -060081 err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num,
82 block_dev->hwpart);
Stephen Warren1e0f92a2015-12-07 11:38:49 -070083 if (err < 0)
84 return -1;
85
Tom Rini58e31942015-06-11 20:53:31 -040086 /*
87 * We want to see if the requested start or total block count are
88 * unaligned. We discard the whole numbers and only care about the
89 * remainder.
90 */
91 err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
92 err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
93 if (start_rem || blkcnt_rem)
Paul Burton8d30cc92013-09-09 15:30:26 +010094 printf("\n\nCaution! Your devices Erase group is 0x%x\n"
95 "The erase range would be change to "
96 "0x" LBAF "~0x" LBAF "\n\n",
97 mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
98 ((start + blkcnt + mmc->erase_grp_size)
99 & ~(mmc->erase_grp_size - 1)) - 1);
100
101 while (blk < blkcnt) {
102 blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
103 mmc->erase_grp_size : (blkcnt - blk);
104 err = mmc_erase_t(mmc, start + blk, blk_r);
105 if (err)
106 break;
107
108 blk += blk_r;
109
110 /* Waiting for the ready status */
111 if (mmc_send_status(mmc, timeout))
112 return 0;
113 }
114
115 return blk;
116}
117
118static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
119 lbaint_t blkcnt, const void *src)
120{
121 struct mmc_cmd cmd;
122 struct mmc_data data;
123 int timeout = 1000;
124
Simon Glasse5db1152016-05-01 13:52:35 -0600125 if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) {
Paul Burton8d30cc92013-09-09 15:30:26 +0100126 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
Simon Glasse5db1152016-05-01 13:52:35 -0600127 start + blkcnt, mmc_get_blk_desc(mmc)->lba);
Paul Burton8d30cc92013-09-09 15:30:26 +0100128 return 0;
129 }
130
131 if (blkcnt == 0)
132 return 0;
133 else if (blkcnt == 1)
134 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
135 else
136 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
137
138 if (mmc->high_capacity)
139 cmd.cmdarg = start;
140 else
141 cmd.cmdarg = start * mmc->write_bl_len;
142
143 cmd.resp_type = MMC_RSP_R1;
144
145 data.src = src;
146 data.blocks = blkcnt;
147 data.blocksize = mmc->write_bl_len;
148 data.flags = MMC_DATA_WRITE;
149
150 if (mmc_send_cmd(mmc, &cmd, &data)) {
151 printf("mmc write failed\n");
152 return 0;
153 }
154
155 /* SPI multiblock writes terminate using a special
156 * token, not a STOP_TRANSMISSION request.
157 */
158 if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
159 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
160 cmd.cmdarg = 0;
161 cmd.resp_type = MMC_RSP_R1b;
162 if (mmc_send_cmd(mmc, &cmd, NULL)) {
163 printf("mmc fail to send stop cmd\n");
164 return 0;
165 }
166 }
167
168 /* Waiting for the ready status */
169 if (mmc_send_status(mmc, timeout))
170 return 0;
171
172 return blkcnt;
173}
174
Simon Glasse3394752016-02-29 15:25:34 -0700175ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
Stephen Warrene73f2962015-12-07 11:38:48 -0700176 const void *src)
Paul Burton8d30cc92013-09-09 15:30:26 +0100177{
Simon Glass2f26fff2016-02-29 15:25:51 -0700178 int dev_num = block_dev->devnum;
Paul Burton8d30cc92013-09-09 15:30:26 +0100179 lbaint_t cur, blocks_todo = blkcnt;
Stephen Warren1e0f92a2015-12-07 11:38:49 -0700180 int err;
Paul Burton8d30cc92013-09-09 15:30:26 +0100181
182 struct mmc *mmc = find_mmc_device(dev_num);
183 if (!mmc)
184 return 0;
185
Simon Glass11f2bb62016-05-01 13:52:29 -0600186 err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
Stephen Warren1e0f92a2015-12-07 11:38:49 -0700187 if (err < 0)
188 return 0;
189
Paul Burton8d30cc92013-09-09 15:30:26 +0100190 if (mmc_set_blocklen(mmc, mmc->write_bl_len))
191 return 0;
192
193 do {
Pantelis Antoniou2c850462014-03-11 19:34:20 +0200194 cur = (blocks_todo > mmc->cfg->b_max) ?
195 mmc->cfg->b_max : blocks_todo;
Paul Burton8d30cc92013-09-09 15:30:26 +0100196 if (mmc_write_blocks(mmc, start, cur, src) != cur)
197 return 0;
198 blocks_todo -= cur;
199 start += cur;
200 src += cur * mmc->write_bl_len;
201 } while (blocks_todo > 0);
202
203 return blkcnt;
204}